Merge: lib/app: update doc for http_request and the improved -m
authorJean Privat <jean@pryen.org>
Fri, 3 Jun 2016 01:44:20 +0000 (21:44 -0400)
committerJean Privat <jean@pryen.org>
Fri, 3 Jun 2016 01:44:20 +0000 (21:44 -0400)
Pull-Request: #2155
Reviewed-by: Jean Privat <jean@pryen.org>

165 files changed:
NOTICE
README.md
contrib/benitlux/src/client/base.nit
contrib/benitlux/src/client/features/translations.nit
contrib/benitlux/src/client/views/user_views.nit
contrib/benitlux/src/server/benitlux_social.nit
contrib/friendz/src/friendz.nit
contrib/nitiwiki/src/wiki_base.nit
contrib/nitiwiki/src/wiki_html.nit
contrib/nitiwiki/src/wiki_links.nit
contrib/nitrpg/src/test_helper.nit
contrib/refund/src/refund_json.nit
contrib/simplan/simplan.nit
contrib/tinks/src/game/framework.nit
contrib/xymus_net/.gitignore [new file with mode: 0644]
contrib/xymus_net/Makefile [new file with mode: 0644]
contrib/xymus_net/README.md [new file with mode: 0644]
contrib/xymus_net/package.ini [new file with mode: 0644]
contrib/xymus_net/xymus_net.nit [moved from lib/nitcorn/examples/src/xymus_net.nit with 96% similarity]
examples/fibonacci.nit
examples/rosettacode/perlin_noise.nit
lib/a_star.nit
lib/android/input_events.nit
lib/bucketed_game.nit
lib/core/collection/abstract_collection.nit
lib/core/collection/array.nit
lib/core/collection/list.nit
lib/core/error.nit
lib/core/exec.nit
lib/core/file.nit
lib/core/math.nit
lib/core/re.nit
lib/core/text/abstract_text.nit
lib/core/text/flat.nit
lib/core/text/ropes.nit
lib/crapto/crapto.nit
lib/crapto/examples/repeating_key_xor_cipher.txt [new file with mode: 0644]
lib/crapto/examples/repeating_key_xor_solve.nit [new file with mode: 0644]
lib/crapto/xor.nit [new file with mode: 0644]
lib/crypto/basic_ciphers.nit [moved from lib/crypto.nit with 91% similarity]
lib/crypto/crypto.nit [new file with mode: 0644]
lib/crypto/package.ini [moved from lib/crypto.ini with 89% similarity]
lib/crypto/xor_ciphers.nit [new file with mode: 0644]
lib/gamnit/cameras.nit
lib/gamnit/limit_fps.nit
lib/mnit/mnit_fps.nit
lib/nitcorn/README.md
lib/nitcorn/examples/Makefile
lib/nitcorn/file_server.nit
lib/noise.nit
lib/performance_analysis.nit
lib/popcorn/README.md
lib/popcorn/examples/angular/example_angular.nit
lib/popcorn/examples/middlewares/example_advanced_logger.nit
lib/popcorn/examples/middlewares/example_simple_logger.nit
lib/popcorn/examples/static_files/example_static_default.nit [new file with mode: 0644]
lib/popcorn/examples/static_files/public/default.html [new file with mode: 0644]
lib/popcorn/pop_handlers.nit
lib/popcorn/pop_middlewares.nit
lib/popcorn/popcorn.nit
lib/popcorn/tests/res/test_example_angular.res
lib/popcorn/tests/res/test_example_static_default.res [new file with mode: 0644]
lib/popcorn/tests/res/test_example_static_multiple.res
lib/popcorn/tests/test_example_advanced_logger.nit
lib/popcorn/tests/test_example_angular.nit
lib/popcorn/tests/test_example_simple_logger.nit
lib/popcorn/tests/test_example_static_default.nit [new file with mode: 0644]
lib/postgresql/native_postgres.nit
lib/postgresql/postgres.nit [new file with mode: 0644]
lib/readline.ini [new file with mode: 0644]
lib/readline.nit [new file with mode: 0644]
lib/realtime.nit
lib/serialization/caching.nit
share/man/nitc.md
share/man/nitdoc.md
share/man/nitunit.md
share/nitweb/directives/entity/card.html [new file with mode: 0644]
share/nitweb/directives/entity/doc.html [new file with mode: 0644]
share/nitweb/directives/entity/link.html [new file with mode: 0644]
share/nitweb/directives/entity/list.html [new file with mode: 0644]
share/nitweb/directives/entity/location.html [new file with mode: 0644]
share/nitweb/directives/entity/signature.html [new file with mode: 0644]
share/nitweb/directives/entity/tag.html [new file with mode: 0644]
share/nitweb/directives/group-block.html [new file with mode: 0644]
share/nitweb/directives/ui-filter-button-vis.html [new file with mode: 0644]
share/nitweb/directives/ui-filter-field.html [new file with mode: 0644]
share/nitweb/directives/ui-filter-form.html [new file with mode: 0644]
share/nitweb/directives/ui-filter-group-vis.html [new file with mode: 0644]
share/nitweb/index.html [new file with mode: 0644]
share/nitweb/javascripts/entities.js [new file with mode: 0644]
share/nitweb/javascripts/model.js [new file with mode: 0644]
share/nitweb/javascripts/nitweb.js [new file with mode: 0644]
share/nitweb/javascripts/ui.js [new file with mode: 0644]
share/nitweb/stylesheets/nitweb.css [new file with mode: 0644]
share/nitweb/stylesheets/nitweb_bootstrap.css [new file with mode: 0644]
share/nitweb/views/class.html [new file with mode: 0644]
share/nitweb/views/classdef.html [new file with mode: 0644]
share/nitweb/views/group.html [new file with mode: 0644]
share/nitweb/views/index.html [new file with mode: 0644]
share/nitweb/views/module.html [new file with mode: 0644]
share/nitweb/views/package.html [new file with mode: 0644]
share/nitweb/views/propdef.html [new file with mode: 0644]
share/nitweb/views/property.html [new file with mode: 0644]
src/catalog.nit
src/doc/doc_phases/doc_html.nit
src/interpreter/dynamic_loading_ffi/on_demand_compiler.nit
src/model/model.nit
src/model/model_collect.nit
src/model/model_json.nit
src/model/model_visitor.nit
src/nitunit.nit
src/nitweb.nit
src/rapid_type_analysis.nit
src/testing/testing_base.nit
src/testing/testing_doc.nit
src/testing/testing_suite.nit
src/toolcontext.nit
src/web/model_api.nit [new file with mode: 0644]
src/web/web.nit
src/web/web_actions.nit
src/web/web_base.nit
src/web/web_views.nit
tests/Darwin.skip
tests/base_gen_infinite2.nit [moved from tests/test_realtime.nit with 57% similarity]
tests/listfull.sh
tests/niti.skip
tests/nitunit.args
tests/nitvm.skip
tests/repeating_key_xor_solve.args [new file with mode: 0644]
tests/sav/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitcatalog_args1.res
tests/sav/nitce/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitcg/fixme/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitcs/fixme/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitcsg/fixme/base_gen_infinite2.res [new file with mode: 0644]
tests/sav/nitunit_args1.res
tests/sav/nitunit_args4.res
tests/sav/nitunit_args5.res
tests/sav/nitunit_args6.res
tests/sav/nitunit_args7.res
tests/sav/nitunit_args8.res
tests/sav/nitunit_args9.res
tests/sav/repeating_key_xor_solve.res [new file with mode: 0644]
tests/sav/repeating_key_xor_solve_args1.res [new file with mode: 0644]
tests/sav/test_copy_to_native.res [new file with mode: 0644]
tests/sav/test_copy_to_native_alt1.res [new file with mode: 0644]
tests/sav/test_copy_to_native_alt2.res [new file with mode: 0644]
tests/sav/test_model_visitor_args2.res
tests/sav/test_nativestring_fill_from.res [new file with mode: 0644]
tests/sav/test_nativestring_fill_from_alt1.res [new file with mode: 0644]
tests/sav/test_nativestring_fill_from_alt2.res [new file with mode: 0644]
tests/sav/test_nativestring_fill_from_alt3.res [new file with mode: 0644]
tests/sav/test_new_native_alt1.res
tests/sav/test_postgres_nity.res [moved from tests/sav/xymus_net.res with 100% similarity]
tests/sav/test_readline.res [new file with mode: 0644]
tests/sav/test_realtime.res [deleted file]
tests/test_copy_to_native.nit [new file with mode: 0644]
tests/test_kill_process.nit
tests/test_nativestring_fill_from.nit [new file with mode: 0644]
tests/test_nitcorn.nit
tests/test_nitunit.nit
tests/test_postgres_nity.nit [new file with mode: 0644]
tests/test_readline.inputs [new file with mode: 0644]
tests/test_readline.nit [new file with mode: 0644]
tests/tests.sh

diff --git a/NOTICE b/NOTICE
index 2e08fad..514ed50 100644 (file)
--- a/NOTICE
+++ b/NOTICE
@@ -2,39 +2,49 @@ This product includes software developed as part of the Nit Language
 project ( http://nitlanguage.org ).
 
 Files: *
-Copyright: 2004-2015 Jean Privat <jean@pryen.org>
+Copyright: 2004-2016 Jean Privat <jean@pryen.org>
            2006-2008 Floréal Morandat <morandat@lirmm.fr>
            2009      Julien Chevalier <chevjulien@gmail.com>
            2009-2011 Jean-Sebastien Gelinas <calestar@gmail.com>
-           2009-2015 Alexis Laferrière <alexis.laf@xymus.net>
+           2009-2016 Alexis Laferrière <alexis.laf@xymus.net>
            2011      Matthieu Auger <matthieu.auger@gmail.com>
-           2011-2015 Alexandre Terrasa <alexandre@moz-code.org>
+           2011-2016 Alexandre Terrasa <alexandre@moz-code.org>
            2012      Alexandre Pennetier <alexandre.pennetier@me.com>
-           2013-2015 Lucas Bajolet <r4pass@hotmail.com>
+           2013-2016 Lucas Bajolet <r4pass@hotmail.com>
            2013      Stefan Lage <lagestfan@gmail.com>
            2013      Nathan Heu <heu.nathan@courrier.uqam.ca>
            2013      Matthieu Lucas <lucasmatthieu@gmail.com>
-           2014-2015 Romain Chanoir <romain.chanoir@viacesi.fr>
-           2014      Frédéric Vachon <fredvac@gmail.com>
+           2014-2016 Romain Chanoir <romain.chanoir@viacesi.fr>
+           2014-2015 Frédéric Vachon <fredvac@gmail.com>
            2014      Johan Kayser <johan.kayser@viacesi.fr>
            2014-2015 Julien Pagès <julien.projet@gmail.com>
            2014      Geoffrey Hecht <geoffrey.hecht@gmail.com>
-           2014      Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
+           2014-2016 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
            2015      Arthur Delamare <arthur.delamare@viacesi.fr>
+           2015-2016 Mehdi Ait Younes <overpex@gmail.com>
+           2015      Renata Carvalho <renatawm@gmail.com>
+           2015      Simon Zeni <simonzeni@gmail.com>
+           2015      Anis Boubaker <anis@boubaker.ca>
+           2015      Istvan SZALAI <szalai972@gmail.com>
+           2015      Hervé Matysiak <herve.matysiak@viacesi.fr>
+           2015      Jean-Philippe Caissy <jean-philippe.caissy@shopify.com>
+           2015      Alexandre Blondin Massé <alexandre.blondin.masse@gmail.com>
+           2015-2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
+           2016      Tony Gaetani <tony.gaetani@gmail.com>
 License: Apache 2.0 (see LICENSE)
 Comment: The main license of the work, except for the following exceptions
 
 Files: lib/*
        clib/*
        share/nitdoc/*
-Copyright: 2004-2015 Jean Privat <jean@pryen.org>
+Copyright: 2004-2016 Jean Privat <jean@pryen.org>
            2006-2008 Floréal Morandat <morandat@lirmm.fr>
            2009-2011 Jean-Sebastien Gelinas <calestar@gmail.com>
-           2009-2015 Alexis Laferrière <alexis.laf@xymus.net>
+           2009-2016 Alexis Laferrière <alexis.laf@xymus.net>
            2009      Julien Chevalier <chevjulien@gmail.com>
-           2011-2015 Alexandre Terrasa <alexandre@moz-concept.com>
+           2011-2016 Alexandre Terrasa <alexandre@moz-concept.com>
            2012      Alexandre Pennetier <alexandre.pennetier@me.com>
-           2013-2015 Lucas Bajolet <r4pass@hotmail.com>
+           2013-2016 Lucas Bajolet <r4pass@hotmail.com>
            2013      Nathan Heu <heu.nathan@courrier.uqam.ca>
            2013      Matthieu Lucas <lucasmatthieu@gmail.com>
            2013      Stefan Lage <lagestfan@gmail.com>
@@ -48,7 +58,11 @@ Copyright: 2004-2015 Jean Privat <jean@pryen.org>
            2014      Maxime Leroy <maxime.leroy76@gmail.com>
            2014      Johann Dubois <johann.dubois@outlook.com>
            2014-2015 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
-           2014      Alexandre Blondin Massé <alexandre.blondin.masse@gmail.com>
+           2014-2015 Alexandre Blondin Massé <alexandre.blondin.masse@gmail.com>
+           2015      Mehdi Ait Younes <overpex@gmail.com>
+           2015      Budi Kurniawan <budi2020@gmail.com>
+           2015-2016 Philippe Pepos Petitclerc <ppeposp@gmail.com>
+           2015-2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
 Licence: BSD 2 (see LICENSE-BSD)
 Comment: Use of libraries and resources is basically unrestricted. We hold the copyright
          on the compiler and the tools but not on the programs made by the users.
index d84731a..1a5dd3c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -18,7 +18,7 @@ Requirements:
  * gcc         http://gcc.gnu.org/ (or a compatible C compiler)
  * pkg-config  http://www.freedesktop.org/wiki/Software/pkg-config/
  * ccache      http://ccache.samba.org/        to improve recompilation
- * libgc-dev   http://www.hpl.hp.com/personal/Hans_Boehm/gc/
+ * libgc-dev   http://hboehm.info/gc/
  * graphviz    http://www.graphviz.org/        to enable graphs with the nitdoc tool
  * libunwind   http://nongnu.org/libunwind
 
index bcc1efd..438a125 100644 (file)
@@ -117,14 +117,17 @@ class BenitluxHttpRequest
                        app.user = null
                        return true
                else if res isa BenitluxError then
-                       app.feedback((res.user_message or else res.message).t)
+                       feedback((res.user_message or else res.message).t)
                        return true
                else if res isa Error then
-                       app.feedback res.message.t
+                       feedback res.message.t
                        return true
                end
                return false
        end
+
+       # Show feedback pertinent to the user, defaults to a platform specific popup
+       fun feedback(text: String) do app.feedback text
 end
 
 # Async request with services to act on the windows of the app
index 02deb61..be42f57 100644 (file)
@@ -63,13 +63,23 @@ do
        map["Welcome %0!"] = "Bienvenue %0!"
        map["Logged in as %0"] = "Connecté en tant que %0"
        map["Username"] = "Nom d'utilisateur"
-       map["Invalid name"] = "Nom d'utilisateur invalide"
        map["Password"] = "Mot de passe"
-       map["Passwords must be composed of at least 6 characters."] = "Le mot de passe doit avoir au moins 6 charactères."
+       map["Repeat password"] = "Répéter le mot de passe"
        map["Email"] = "Courriel"
        map["Login"] = "Se connecter"
+       map["Loging in..."] = "Authentification..."
        map["Logout"] = "Se déconnecter"
        map["Signup"] = "Créer un compte"
+       map["Signing up..."] = "Création du compte..."
+
+       map["Passwords must be composed of at least 6 characters."] = "Le mot de passe doit avoir au moins 6 charactères."
+       map["Fill the following fields to sign up."] = "Remplissez les champs suivants pour créer un compte."
+
+       map["Passwords do not match."] = "Les mots de passe ne correspondent pas."
+       map["Invalid username."] = "Nom d'utilisateur invalide."
+       map["Invalid password."] = "Mot de passe invalide."
+       map["Username already in use."] = "Le nom d'utilisateur est déjà réservé."
+       map["Invalid username and password combination."] = "La combinaison de nom et mot de passe n'est pas reconnue."
 
        # Social views
        map["Follow"] = "Suivre"
index e45fcb5..b69b1db 100644 (file)
@@ -86,38 +86,50 @@ end
 class SignupWindow
        super Window
 
-       # Main window layout
-       var layout = new ListLayout(parent=self)
+       private var list = new ListLayout(parent=self)
+       private var lbl_feedback = new Label(parent=list, text="Welcome")
 
-       private var lbl_welcome = new Label(parent=layout, text="Welcome")
+       private var layout_login = new VerticalLayout(parent=list)
+
+       # ---
+       # First the login options
 
        # Name
-       private var name_line = new HorizontalLayout(parent=layout)
+       private var name_line = new HorizontalLayout(parent=layout_login)
        private var lbl_name = new Label(parent=name_line, text="Username".t)
        private var txt_name = new TextInput(parent=name_line, text=app.user)
 
-       # Pass
-       private var pass_line = new HorizontalLayout(parent=layout)
+       # Password
+       private var pass_line = new HorizontalLayout(parent=layout_login)
        private var lbl_pass = new Label(parent=pass_line, text="Password".t)
        private var txt_pass = new TextInput(parent=pass_line, is_password=true)
-       private var lbl_pass_desc = new Label(parent=layout,
+       private var lbl_pass_desc = new Label(parent=layout_login, size = 0.5,
                text="Passwords must be composed of at least 6 characters.".t)
 
-       private var but_login = new Button(parent=layout, text="Login".t)
+       private var but_login = new Button(parent=layout_login, text="Login".t)
+
+       # ---
+       # Then, the signup options
+
+       private var layout_register = new VerticalLayout(parent=list)
+
+       private var lbl_signup_desc = new Label(parent=layout_register, size = 0.5,
+               text="Fill the following fields to sign up.".t)
+
+       # Repeat password
+       private var pass_line2 = new HorizontalLayout(parent=layout_register)
+       private var lbl_pass2 = new Label(parent=pass_line2, text="Repeat password".t)
+       private var txt_pass2 = new TextInput(parent=pass_line2, is_password=true)
 
        # Email
-       private var email_line = new HorizontalLayout(parent=layout)
+       private var email_line = new HorizontalLayout(parent=layout_register)
        private var lbl_email = new Label(parent=email_line, text="Email".t)
        private var txt_email = new TextInput(parent=email_line)
 
-       private var but_signup = new Button(parent=layout, text="Signup".t)
-
-       private var lbl_feedback = new Label(parent=layout, text="")
+       private var but_signup = new Button(parent=layout_register, text="Signup".t)
 
        init
        do
-               lbl_pass_desc.size = 0.5
-
                for c in [but_login, but_signup] do
                        c.observers.add self
                end
@@ -133,25 +145,32 @@ class SignupWindow
 
                                var name = txt_name.text
                                if name == null or not name.name_is_ok then
-                                       feedback "Invalid name".t
+                                       feedback "Invalid username.".t
                                        return
                                end
 
                                var pass = txt_pass.text
                                if pass == null or not pass.pass_is_ok then
-                                       feedback "Invalid password".t
+                                       feedback "Invalid password.".t
                                        return
                                end
 
                                if sender == but_login then
+                                       feedback "Logging in...".t
                                        (new LoginOrSignupAction(self, "rest/login?name={name}&pass={pass.pass_hash}")).start
                                else if sender == but_signup then
+                                       if pass != txt_pass2.text then
+                                               feedback "Passwords do not match.".t
+                                               return
+                                       end
+
                                        var email = txt_email.text
                                        if email == null or email.is_empty then
                                                feedback "Invalid email".t
                                                return
                                        end
 
+                                       feedback "Signing up...".t
                                        (new LoginOrSignupAction(self, "rest/signup?name={name}&pass={pass.pass_hash}&email={email}")).start
                                end
                        end
@@ -189,27 +208,6 @@ class LoginOrSignupAction
 
                app.on_log_in
        end
-end
-
-# Async request for signing up
-class SignupAction
-       super WindowHttpRequest
 
-       redef type W: SignupWindow
-
-       init do affected_views.add_all([window.but_signup])
-
-       redef fun on_load(res)
-       do
-               if intercept_error(res) then return
-
-               if not res isa LoginResult then
-                       on_fail new Error("Server sent unexpected data {res or else "null"}")
-                       return
-               end
-
-               app.token = res.token
-               app.user = res.user.name
-               app.on_log_in
-       end
+       redef fun feedback(text) do window.feedback text
 end
index bdd83b4..7764c75 100644 (file)
@@ -182,7 +182,7 @@ GROUP BY beer0, beer1""") else
                # Check if already in user
                var stmt = select("ROWID FROM users WHERE lower({user.to_sql_string}) = lower(name)")
                assert stmt != null else print_error "Select 'sign_up' failed with: {error or else "?"}"
-               if not stmt.iterator.to_a.is_empty then return "Username already in use"
+               if not stmt.iterator.to_a.is_empty then return "Username already in use."
 
                # Check email use
                stmt = select("ROWID FROM users WHERE lower({email.to_sql_string}) = lower(email)")
index c0554fe..762eb1d 100644 (file)
@@ -1609,22 +1609,10 @@ redef class App
                game.save
        end
 
-       # Maximum wanted frame per second
-       var max_fps = 30
-
-       # clock used to track FPS
-       private var clock = new Clock
-
        redef fun frame_core(display)
        do
                game.step
                game.draw(display)
-               var dt = clock.lapse
-               var target_dt = 1000000000 / max_fps
-               if dt.sec == 0 and dt.nanosec < target_dt then
-                       var sleep_t = target_dt - dt.nanosec
-                       sys.nanosleep(0, sleep_t)
-               end
        end
 
        redef fun input(input_event)
index 70cf35f..d3adc26 100644 (file)
@@ -94,7 +94,7 @@ class Nitiwiki
                                var entry = entries[path]
                                if not entry.is_dirty then continue
                                var name = entry.name
-                               if entry.has_source then name = entry.src_path.to_s
+                               if entry.has_source then name = entry.src_path.as(not null)
                                if entry.is_new then
                                        print " + {name}"
                                else
@@ -141,7 +141,7 @@ class Nitiwiki
        fun need_render(src, target: String): Bool do
                if force_render then return true
                if not target.file_exists then return true
-               return src.file_stat.mtime >= target.file_stat.mtime
+               return src.file_stat.as(not null).mtime >= target.file_stat.as(not null).mtime
        end
 
        # Create a new `WikiSection`.
@@ -244,7 +244,7 @@ class Nitiwiki
        # Used to translate ids in beautiful page names.
        fun pretty_name(name: String): String do
                name = name.replace("_", " ")
-               name = name.capitalized
+               name = name.capitalized(keep_upper=true)
                return name
        end
 end
@@ -283,7 +283,7 @@ abstract class WikiEntry
        # Returns `-1` if not `has_source`.
        fun create_time: Int do
                if not has_source then return -1
-               return src_full_path.file_stat.ctime
+               return src_full_path.as(not null).file_stat.as(not null).ctime
        end
 
        # Entry last modification time.
@@ -291,7 +291,7 @@ abstract class WikiEntry
        # Returns `-1` if not `has_source`.
        fun last_edit_time: Int do
                if not has_source then return -1
-               return src_full_path.file_stat.mtime
+               return src_full_path.as(not null).file_stat.as(not null).mtime
        end
 
        # Entry list rendering time.
@@ -299,7 +299,7 @@ abstract class WikiEntry
        # Returns `-1` if `is_new`.
        fun last_render_time: Int do
                if is_new then return -1
-               return out_full_path.file_stat.mtime
+               return out_full_path.file_stat.as(not null).mtime
        end
 
        # Entries hierarchy
@@ -400,7 +400,7 @@ abstract class WikiEntry
        # then returns the main wiki template file.
        fun template_file: String do
                if is_root then return wiki.config.template_file
-               return parent.template_file
+               return parent.as(not null).template_file
        end
 
        # Header template file for `self`.
@@ -408,7 +408,7 @@ abstract class WikiEntry
        # Behave like `template_file`.
        fun header_file: String do
                if is_root then return wiki.config.header_file
-               return parent.header_file
+               return parent.as(not null).header_file
        end
 
        # Footer template file for `self`.
@@ -416,7 +416,7 @@ abstract class WikiEntry
        # Behave like `template_file`.
        fun footer_file: String do
                if is_root then return wiki.config.footer_file
-               return parent.footer_file
+               return parent.as(not null).footer_file
        end
 
        # Menu template file for `self`.
@@ -424,7 +424,7 @@ abstract class WikiEntry
        # Behave like `template_file`.
        fun menu_file: String do
                if is_root then return wiki.config.menu_file
-               return parent.menu_file
+               return parent.as(not null).menu_file
        end
 
        # Display the entry `name`.
@@ -442,7 +442,7 @@ class WikiSection
 
        redef fun title do
                if has_config then
-                       var title = config.title
+                       var title = config.as(not null).title
                        if title != null then return title
                end
                return super
@@ -452,7 +452,7 @@ class WikiSection
        #
        # Hidden section are rendered but not linked in menus.
        fun is_hidden: Bool do
-               if has_config then return config.is_hidden
+               if has_config then return config.as(not null).is_hidden
                return false
        end
 
@@ -461,7 +461,7 @@ class WikiSection
                if parent == null then
                        return wiki.config.source_dir
                else
-                       return wiki.expand_path(parent.src_path, name)
+                       return wiki.expand_path(parent.as(not null).src_path, name)
                end
        end
 
@@ -486,41 +486,41 @@ class WikiSection
        # Also check custom config.
        redef fun template_file do
                if has_config then
-                       var tpl = config.template_file
+                       var tpl = config.as(not null).template_file
                        if tpl != null then return tpl
                end
                if is_root then return wiki.config.template_file
-               return parent.template_file
+               return parent.as(not null).template_file
        end
 
        # Also check custom config.
        redef fun header_file do
                if has_config then
-                       var tpl = config.header_file
+                       var tpl = config.as(not null).header_file
                        if tpl != null then return tpl
                end
                if is_root then return wiki.config.header_file
-               return parent.header_file
+               return parent.as(not null).header_file
        end
 
        # Also check custom config.
        redef fun footer_file do
                if has_config then
-                       var tpl = config.footer_file
+                       var tpl = config.as(not null).footer_file
                        if tpl != null then return tpl
                end
                if is_root then return wiki.config.footer_file
-               return parent.footer_file
+               return parent.as(not null).footer_file
        end
 
        # Also check custom config.
        redef fun menu_file do
                if has_config then
-                       var tpl = config.menu_file
+                       var tpl = config.as(not null).menu_file
                        if tpl != null then return tpl
                end
                if is_root then return wiki.config.menu_file
-               return parent.menu_file
+               return parent.as(not null).menu_file
        end
 end
 
@@ -534,7 +534,8 @@ class WikiArticle
        # Articles can only have `WikiSection` as parents.
        redef type PARENT: WikiSection
 
-       redef fun title: String do
+       redef fun title do
+               var parent = self.parent
                if name == "index" and parent != null then return parent.title
                return super
        end
@@ -551,7 +552,7 @@ class WikiArticle
                content = md
        end
 
-       redef var src_full_path: nullable String = null
+       redef var src_full_path = null
 
        redef fun src_path do
                var src_full_path = self.src_full_path
@@ -567,7 +568,7 @@ class WikiArticle
        # REQUIRE: `has_source`.
        var md: nullable String is lazy do
                if not has_source then return null
-               var file = new FileReader.open(src_full_path.to_s)
+               var file = new FileReader.open(src_full_path.as(not null))
                var md = file.read_all
                file.close
                return md
@@ -578,7 +579,7 @@ class WikiArticle
        redef fun is_dirty do
                if super then return true
                if has_source then
-                       return wiki.need_render(src_full_path.to_s, out_full_path)
+                       return wiki.need_render(src_full_path.as(not null), out_full_path)
                end
                return false
        end
@@ -779,7 +780,7 @@ class WikiConfig
        var sidebar_blocks: Array[String] is lazy do
                var res = new Array[String]
                if not has_key("wiki.sidebar.blocks") then return res
-               for val in at("wiki.sidebar.blocks").values do
+               for val in at("wiki.sidebar.blocks").as(not null).values do
                        res.add val
                end
                return res
index ab79f09..15ae4d7 100644 (file)
@@ -70,7 +70,8 @@ end
 redef class WikiSection
 
        # Output directory (where to ouput the HTML pages for this section).
-       redef fun out_path: String do
+       redef fun out_path do
+               var parent = self.parent
                if parent == null then
                        return wiki.config.out_dir
                else
@@ -104,7 +105,7 @@ redef class WikiSection
        # Copy attached files from `src_path` to `out_path`.
        private fun copy_files do
                assert has_source
-               var dir = src_full_path.to_s
+               var dir = src_full_path.as(not null).to_s
                for name in dir.files do
                        if name == wiki.config_filename then continue
                        if name.has_suffix(".md") then continue
@@ -167,7 +168,8 @@ end
 
 redef class WikiArticle
 
-       redef fun out_path: String do
+       redef fun out_path do
+               var parent = self.parent
                if parent == null then
                        return wiki.expand_path(wiki.config.out_dir, "{name}.html")
                else
index ac3e171..33dc61f 100644 (file)
@@ -31,7 +31,7 @@ redef class Nitiwiki
        # Returns `null` if no article can be found.
        fun lookup_entry_by_name(context: WikiEntry, name: String): nullable WikiEntry do
                var section: nullable WikiEntry = context.parent or else context
-               var res = section.lookup_entry_by_name(name)
+               var res = section.as(not null).lookup_entry_by_name(name)
                if res != null then return res
                while section != null do
                        if section.name == name then return section
@@ -52,7 +52,7 @@ redef class Nitiwiki
        # Returns `null` if no article can be found.
        fun lookup_entry_by_title(context: WikiEntry, title: String): nullable WikiEntry do
                var section: nullable WikiEntry = context.parent or else context
-               var res = section.lookup_entry_by_title(title)
+               var res = section.as(not null).lookup_entry_by_title(title)
                if res != null then return res
                while section != null do
                        if section.title.to_lower == title.to_lower then return section
@@ -200,6 +200,7 @@ redef class WikiArticle
        fun is_index: Bool do return name == "index"
 
        redef fun href do
+               var parent = self.parent
                if parent == null then
                        return "{name}.html"
                else
index 1ac8314..904862c 100644 (file)
@@ -57,7 +57,8 @@ abstract class NitrpgTestHelper
 
        # Gen a test db with a random name (to avoid race conditions).
        fun gen_test_db: MongoDb do
-               var db_name = "test_nitrpg_{get_time}_{1000.rand}"
+               var testid = "NIT_TESTING_ID".environ.to_i
+               var db_name = "test_nitrpg_{testid}"
                var db = load_db(db_name)
                test_dbs.add db
                return db
index 33032b3..4da71f5 100644 (file)
@@ -72,6 +72,9 @@ redef class RefundProcessor
                if json isa JsonParseError then
                        die("Wrong input file ({json.message})")
                        abort
+               else if json == null then
+                       die("Unable to parse input file as json (got null)")
+                       abort
                else if not json isa JsonObject then
                        die("Wrong input type (expected JsonObject got {json.class_name})")
                        abort
@@ -130,7 +133,7 @@ redef class RefundProcessor
                return new RefundStats.from_json(content)
        end
 
-       redef fun save_stats(stats: RefundStats) do
+       redef fun save_stats(stats) do
                write_output(stats.to_json.to_pretty_json, stats_file)
        end
 end
@@ -165,13 +168,19 @@ redef class ReclamationSheet
                proc.check_key(json, "reclamations")
                var res = new Array[Reclamation]
                var recls = json["reclamations"]
-               if not recls isa JsonArray then
+               if recls == null then
+                       proc.die("Wrong type for `number` (expected JsonArray got null)")
+                       abort
+               else if not recls isa JsonArray then
                        proc.die("Wrong type for `number` (expected JsonArray got {recls.class_name})")
                        abort
                end
                var i = 0
                for obj in recls do
-                       if not obj isa JsonObject then
+                       if obj == null then
+                               proc.die("Wrong type for `reclamations#{i}` (expected JsonObject got null)")
+                               abort
+                       else if not obj isa JsonObject then
                                proc.die("Wrong type for `reclamations#{i}` " +
                                        "(expected JsonObject got {obj.class_name})")
                                abort
@@ -197,7 +206,10 @@ redef class ReclFile
        init from_json(proc: RefundProcessor, json: JsonObject) do
                proc.check_key(json, "dossier")
                var id = json["dossier"]
-               if not id isa String then
+               if id == null then
+                       proc.die("Wrong type for `dossier` (expected String got null)")
+                       abort
+               else if not id isa String then
                        proc.die("Wrong type for `dossier` (expected String got {id.class_name})")
                        abort
                end
@@ -232,7 +244,10 @@ redef class ReclMonth
        init from_json(proc: RefundProcessor, json: JsonObject) do
                proc.check_key(json, "mois")
                var month = json["mois"]
-               if not month isa String then
+               if month == null then
+                       proc.die("Wrong type for `mois` (expected String got null)")
+                       return
+               else if not month isa String then
                        proc.die("Wrong type for `mois` (expected String got {month.class_name})")
                        return
                end
@@ -264,7 +279,10 @@ redef class ReclDate
        init from_json(proc: RefundProcessor, json: JsonObject) do
                proc.check_key(json, "date")
                var date = json["date"]
-               if not date isa String then
+               if date == null then
+                       proc.die("Wrong type for `date` (expected String got null)")
+                       abort
+               else if not date isa String then
                        proc.die("Wrong type for `date` (expected String got {date.class_name})")
                        abort
                end
@@ -302,7 +320,10 @@ redef class Reclamation
        private fun parse_care_id(proc: RefundProcessor, json: JsonObject): Int do
                proc.check_key(json, "soin")
                var id = json["soin"]
-               if not id isa Int then
+               if id == null then
+                       proc.die("Wrong type for `soin` (expected Int got null)")
+                       abort
+               else if not id isa Int then
                        proc.die("Wrong type for `soin` (expected Int got {id.class_name})")
                        abort
                end
@@ -313,7 +334,10 @@ redef class Reclamation
        private fun parse_fees(proc: RefundProcessor, json: JsonObject): Dollar do
                proc.check_key(json, "montant")
                var fees = json["montant"]
-               if not fees isa String then
+               if fees == null then
+                       proc.die("Wrong type for `fees` (expected String got null)")
+                       abort
+               else if not fees isa String then
                        proc.die("Wrong type for `fees` (expected String got {fees.class_name})")
                        abort
                end
index ba32497..301c564 100644 (file)
@@ -80,7 +80,7 @@ class PlanProblem
                        print n.message
                        exit 1
                end
-               n = n.children.first.children.first.as(not null)
+               n = n.children.first.as(not null).children.first.as(not null)
                if n isa Nplan then
                        print "Error: expected a problem, got a plan."
                        exit 1
@@ -88,7 +88,7 @@ class PlanProblem
                assert n isa Nproblem
 
                # Load all locations
-               for n2 in n.n_locations.n_list.children do
+               for n2 in n.n_locations.n_list.as(not null).children do
                        var e = new Location(locations.length, n2.n_name.text, n2.n_x.text.to_f, n2.n_y.text.to_f)
                        assert not locations.has_key(e.name)
                        locations[e.name] = e
@@ -97,7 +97,7 @@ class PlanProblem
 
                # Load all roads
                var nbr = 0
-               for n2 in n.n_roads.n_list.children do
+               for n2 in n.n_roads.n_list.as(not null).children do
                        var o = locations.get_or_null(n2.n_orig.text)
                        var d = locations.get_or_null(n2.n_dest.text)
                        assert o != null and d != null
@@ -132,7 +132,7 @@ class PlanProblem
 
                # Load the robot
                var robot = null
-               for n2 in n.n_robots.n_list.children do
+               for n2 in n.n_robots.n_list.as(not null).children do
                        var name = n2.n_name.text
                        robot = locations.get_or_null(n2.n_emplacement.text)
                        assert name == robot_name and robot != null
@@ -142,7 +142,7 @@ class PlanProblem
 
                # Load the parcels
                var parcel_locations = new Array[nullable Location]
-               for n2 in n.n_parcels.n_list.children do
+               for n2 in n.n_parcels.n_list.as(not null).children do
                        var name = n2.n_name.text
                        var e = locations.get_or_null(n2.n_emplacement.text)
                        assert e != null
@@ -155,7 +155,7 @@ class PlanProblem
                print "# {parcels.length} parcels"
 
                # Load the goal of parcels
-               for n2 in n.n_goal.n_list.children do
+               for n2 in n.n_goal.n_list.as(not null).children do
                        var parcel = parcel_by_name.get_or_null(n2.n_name.text)
                        var e = locations.get_or_null(n2.n_emplacement.text)
                        assert parcel != null and e != null
@@ -179,7 +179,7 @@ class PlanProblem
                        print n.message
                        exit 1
                end
-               n = n.children.first.children.first.as(not null)
+               n = n.children.first.as(not null).children.first.as(not null)
                if n isa Nproblem then
                        print "Error: expected a plan, got a problem."
                        exit 1
@@ -189,7 +189,7 @@ class PlanProblem
                var res = new Plan(self)
                var e = initial_state
                var cost = 0.0
-               for n2 in n.n_actions.children do
+               for n2 in n.n_actions.as(not null).children do
                        if n2 isa Naction_load then
                                var parcel = parcel_by_name.get_or_null(n2.n_parcel.text)
                                assert parcel != null
index 49c74b0..9472da6 100644 (file)
@@ -41,7 +41,7 @@ class TGame
                var dt = clock.lapse
                tick += 1
 
-               var turn = new TTurn(self, tick, dt.to_f, dt.millisec)
+               var turn = new TTurn(self, tick, dt, ((dt-dt.floor)*1000.0).to_i)
                return turn
        end
 
diff --git a/contrib/xymus_net/.gitignore b/contrib/xymus_net/.gitignore
new file mode 100644 (file)
index 0000000..b2285d4
--- /dev/null
@@ -0,0 +1 @@
+xymus.net
diff --git a/contrib/xymus_net/Makefile b/contrib/xymus_net/Makefile
new file mode 100644 (file)
index 0000000..eff98e3
--- /dev/null
@@ -0,0 +1,9 @@
+all: xymus.net
+
+xymus.net: ../benitlux/src/server/benitlux_restful.nit $(shell ../../bin/nitls -M xymus_net.nit)
+       ../../bin/nitc -o $@ xymus_net.nit
+
+../benitlux/src/server/benitlux_restful.nit:
+       make -C ../benitlux src/server/benitlux_restful.nit
+
+pre-build: ../benitlux/src/server/benitlux_restful.nit
diff --git a/contrib/xymus_net/README.md b/contrib/xymus_net/README.md
new file mode 100644 (file)
index 0000000..5bb2c31
--- /dev/null
@@ -0,0 +1,5 @@
+Web server source and config of xymus.net
+
+This module acts also as an example to merge multiple `nitcorn` projects into one server.
+
+See the server online at http://xymus.net/.
diff --git a/contrib/xymus_net/package.ini b/contrib/xymus_net/package.ini
new file mode 100644 (file)
index 0000000..249a39a
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=xymus_net
+tags=web,example
+maintainer=Alexis Laferrière <alexis.laf@xymus.net>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/contrib/xymus.net/
+git=https://github.com/nitlang/nit.git
+git.directory=contrib/xymus.net/
+homepage=http://xymus.net/
+issues=https://github.com/nitlang/nit/issues
similarity index 96%
rename from lib/nitcorn/examples/src/xymus_net.nit
rename to contrib/xymus_net/xymus_net.nit
index 1233db4..726cb6a 100644 (file)
@@ -1,6 +1,6 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
-# Copyright 2014-2015 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2014-2016 Alexis Laferrière <alexis.laf@xymus.net>
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import privileges
 # Use actions defined by contribs
 import tnitter
 import benitlux::benitlux_controller
+import benitlux::benitlux_restful
 import opportunity::opportunity_controller
 import nitiwiki::wiki_edit
 
@@ -173,6 +174,7 @@ var vps_vh = new VirtualHost("vps.xymus.net:80")
 var tnitter_vh = new VirtualHost("tnitter.xymus.net:80")
 var pep8_vh = new VirtualHost("pep8.xymus.net:80")
 var benitlux_vh = new VirtualHost("benitlux.xymus.net:80")
+var benitlux_admin_vh = new VirtualHost("localhost:8081")
 
 var factory = new HttpFactory.and_libevent
 factory.config.virtual_hosts.add default_vh
@@ -180,6 +182,7 @@ factory.config.virtual_hosts.add vps_vh
 factory.config.virtual_hosts.add tnitter_vh
 factory.config.virtual_hosts.add pep8_vh
 factory.config.virtual_hosts.add benitlux_vh
+factory.config.virtual_hosts.add benitlux_admin_vh
 
 # Ports are open, drop to a low-privileged user if we are root
 var user_group = new UserGroup("nitcorn", "nitcorn")
@@ -228,6 +231,8 @@ benitlux_vh.routes.add new Route("/push/", benitlux_push)
 benitlux_vh.routes.add new Route("/static/", shared_file_server)
 benitlux_vh.routes.add new Route(null, benitlux_sub)
 
+benitlux_admin_vh.routes.add new Route(null, new BenitluxAdminAction(benitlux_db))
+
 # Opportunity service
 var opportunity = new OpportunityWelcome
 var opportunity_rest = new OpportunityRESTAction
index d4330ba..66cbfb0 100644 (file)
@@ -21,12 +21,9 @@ redef class Int
        # Calculate the self-th element of the fibonacci sequence.
        fun fibonacci: Int
        do
-               if self < 2 then
-                       return 1
-               else
-                       return (self-2).fibonacci + (self-1).fibonacci
-               end
-       end 
+               if self < 2 then return self
+               return (self-2).fibonacci + (self-1).fibonacci
+       end
 end
 
 # Print usage and exit.
index 0733bbf..a5cd314 100644 (file)
@@ -8,69 +8,7 @@
 # See: <http://rosettacode.org/wiki/Perlin_noise>
 module perlin_noise
 
-redef universal Float
-       # Smoothened `self`
-       fun fade: Float do return self*self*self*(self*(self*6.0-15.0)+10.0)
-end
-
-# Improved noise
-class ImprovedNoise
-       # Permutations
-       var p: Array[Int] = [151,160,137,91,90,15,
-               131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
-               190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
-               88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
-               77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
-               102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
-               135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
-               5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
-               223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
-               129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
-               251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
-               49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
-               138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]
-
-       # Noise value in [-1..1] at 3d coordinates `x, y, z`
-       fun noise(x, y, z: Float): Float
-       do
-               var xx = x.to_i & 255
-               var yy = y.to_i & 255
-               var zz = z.to_i & 255
-
-               x -= x.floor
-               y -= y.floor
-               z -= z.floor
-
-               var u = x.fade
-               var v = y.fade
-               var w = z.fade
-
-               var a  = p[xx  ] + yy
-               var aa = p[a   ] + zz
-               var ab = p[a+1 ] + zz
-               var b  = p[xx+1] + yy
-               var ba = p[b   ] + zz
-               var bb = p[b+1 ] + zz
-
-               return w.lerp(v.lerp(u.lerp(grad(p[aa  ], x,     y,     z    ),
-                                           grad(p[ba  ], x-1.0, y,     z    )),
-                                    u.lerp(grad(p[ab  ], x,     y-1.0, z    ),
-                                           grad(p[bb  ], x-1.0, y-1.0, z    ))),
-                      v.lerp(u.lerp(grad(p[aa+1], x,     y,     z-1.0),
-                                           grad(p[ba+1], x-1.0, y,     z-1.0)),
-                                    u.lerp(grad(p[ab+1], x,     y-1.0, z-1.0),
-                                           grad(p[bb+1], x-1.0, y-1.0, z-1.0))))
-       end
-
-       # Value at a corner of the grid
-       fun grad(hash: Int, x, y, z: Float): Float
-       do
-               var h = hash & 15
-               var u = if h < 8 then x else y
-               var v = if h < 4 then y else if h == 12 or h == 14 then x else z
-               return (if h.is_even then u else -u) + (if h & 2 == 0 then v else -v)
-       end
-end
+import noise
 
 var map = new ImprovedNoise
 print map.noise(3.14, 42.0, 7.0).to_precision(17)
index 7b7ce02..cc0cba3 100644 (file)
@@ -188,6 +188,14 @@ class Node
                end
        end
 
+       # Find the closest node accepted by `cond` under `max_cost`
+       fun find_closest(max_cost: Int, context: PathContext, cond: nullable TargetCondition[N]): nullable N
+       do
+               var path = path_to_alts(null, max_cost, context, cond)
+               if path == null then return null
+               return path.nodes.last
+       end
+
        # We customize the serialization process to avoid problems with recursive
        # serialization engines. These engines, such as `JsonSerializer`,
        # are at danger to serialize the graph as a very deep tree.
index 8aea378..5ee9d87 100644 (file)
@@ -70,6 +70,8 @@ private extern class NativeAndroidMotionEvent `{AInputEvent *`}
        `}
 
        fun action: AMotionEventAction `{ return AMotionEvent_getAction(self); `}
+
+       fun native_down_time: Int `{ return AMotionEvent_getDownTime(self); `}
 end
 
 private extern class AMotionEventAction `{ int32_t `}
@@ -149,6 +151,11 @@ class AndroidMotionEvent
                        return null
                end
        end
+
+       # Time when the user originally pressed down to start a stream of position events
+       #
+       # The return value is in the `java.lang.System.nanoTime()` time base.
+       fun down_time: Int do return native.native_down_time
 end
 
 # A pointer event
index b5542e4..28129a5 100644 (file)
@@ -26,6 +26,7 @@
 module bucketed_game is serialize
 
 import serialization
+import counter
 
 # Something acting on the game
 abstract class Turnable[G: Game]
@@ -65,6 +66,9 @@ class Buckets[G: Game]
        private var buckets: Array[BUCKET] =
                [for b in n_buckets.times do new HashSet[Bucketable[G]]] is lazy
 
+       # Stats on delays asked when adding an event with `act_in` and `act_next`
+       private var delays = new Counter[Int]
+
        # Add the Bucketable event `e` at `at_tick`.
        fun add_at(e: Bucketable[G], at_tick: Int)
        do
@@ -106,6 +110,26 @@ class Buckets[G: Game]
                        end
                end
        end
+
+       # Get some statistics on both the current held events and historic expired events
+       fun stats: String
+       do
+               var entries = 0
+               var instances = new HashSet[Bucketable[G]]
+               var max = 0
+               var min = 100000
+               for bucket in buckets do
+                       var len = bucket.length
+                       entries += len
+                       instances.add_all bucket
+                       min = min.min(len)
+                       max = max.max(len)
+               end
+               var avg = entries.to_f / buckets.length.to_f
+
+               return "{buckets.length} buckets; uniq/tot:{instances.length}/{entries}, avg:{avg.to_precision(1)}, min:{min}, max:{max}\n" +
+                       "history:{delays.sum}, avg:{delays.avg}, min:{delays[delays.min.as(not null)]}, max:{delays[delays.max.as(not null)]}"
+       end
 end
 
 # Game related event
@@ -123,16 +147,16 @@ end
 # Game logic on the client
 class ThinGame
 
-       # Game tick when `self` should act.
+       # Current game tick
        #
        # Default is 0.
-       var tick: Int = 0 is protected writable
+       var tick: Int = 0 is writable
 end
 
 # Game turn on the client
 class ThinGameTurn[G: ThinGame]
 
-       # Game tick when `self` should act.
+       # Game tick when happened this turn
        var tick: Int is protected writable
 
        # Game events occurred for `self`.
@@ -153,10 +177,18 @@ class GameTurn[G: Game]
        end
 
        # Insert the Bucketable event `e` to be executed at next tick.
-       fun act_next(e: Bucketable[G]) do game.buckets.add_at(e, tick + 1)
+       fun act_next(e: Bucketable[G])
+       do
+               game.buckets.add_at(e, tick + 1)
+               game.buckets.delays.inc(1)
+       end
 
        # Insert the Bucketable event `e` to be executed at tick `t`.
-       fun act_in(e: Bucketable[G], t: Int) do game.buckets.add_at(e, tick + t)
+       fun act_in(e: Bucketable[G], t: Int)
+       do
+               game.buckets.add_at(e, tick + t)
+               game.buckets.delays.inc(t)
+       end
 
        # Add and `apply` a game `event`.
        fun add_event( event : GameEvent )
index 5cffbec..5ed911f 100644 (file)
@@ -457,7 +457,9 @@ interface Set[E]
                var res = 23 + length
                # Note: the order of the elements must not change the hash value.
                # So, unlike usual hash functions, the accumulator is not combined with itself.
-               for e in self do res += e.hash
+               for e in self do
+                       if e != null then res += e.hash
+               end
                return res
        end
 
index 79e7a70..f01bbb5 100644 (file)
@@ -321,7 +321,7 @@ class Array[E]
        redef fun [](index)
        do
                assert index: index >= 0 and index < _length
-               return _items[index]
+               return _items.as(not null)[index]
        end
 
        redef fun []=(index, item)
@@ -333,7 +333,7 @@ class Array[E]
                if _length <= index then
                        _length = index + 1
                end
-               _items[index] = item
+               _items.as(not null)[index] = item
        end
 
        redef fun add(item)
@@ -343,7 +343,7 @@ class Array[E]
                        enlarge(l + 1)
                end
                _length = l + 1
-               _items[l] = item
+               _items.as(not null)[l] = item
        end
 
        # Slight optimization for arrays
@@ -358,13 +358,13 @@ class Array[E]
                if items isa Array[E] then
                        var k = 0
                        while l < nl do
-                               _items[l] = items._items[k]
+                               _items.as(not null)[l] = items._items.as(not null)[k]
                                l += 1
                                k += 1
                        end
                else
                        for item in items do
-                               _items[l] = item
+                               _items.as(not null)[l] = item
                                l += 1
                        end
                end
@@ -404,7 +404,7 @@ class Array[E]
                if cap <= c then return
                while c <= cap do c = c * 2 + 2
                var a = new NativeArray[E](c)
-               if _capacity > 0 then _items.copy_to(a, _length)
+               if _capacity > 0 then _items.as(not null).copy_to(a, _length)
                _items = a
                _capacity = c
        end
@@ -474,9 +474,10 @@ class Array[E]
                # Efficient implementation
                var l = length
                if l != o.length then return false
+               if l == 0 then return true
                var i = 0
-               var it = _items
-               var oit = o._items
+               var it = _items.as(not null)
+               var oit = o._items.as(not null)
                while i < l do
                        if it[i] != oit[i] then return false
                        i += 1
@@ -921,10 +922,11 @@ class ArrayCmp[E: nullable Comparable]
 
        redef fun <=>(o)
        do
-               var it = _items
-               var oit = o._items
                var i = 0
                var l = length
+               if l == 0 then return 0
+               var it = _items.as(not null)
+               var oit = o._items.as(not null)
                var ol = o.length
                var len
                if l < ol then len = l else len = ol
index ab81a37..241238b 100644 (file)
@@ -21,21 +21,21 @@ class List[E]
 
 # Access
 
-       redef fun [](index) do return get_node(index).item
+       redef fun [](index) do return get_node(index).as(not null).item
 
-       redef fun []=(index, item) do get_node(index).item = item
+       redef fun []=(index, item) do get_node(index).as(not null).item = item
 
        # O(1)
-       redef fun first do return _head.item
+       redef fun first do return _head.as(not null).item
 
        # O(1)
-       redef fun first=(e) do _head.item = e
+       redef fun first=(e) do _head.as(not null).item = e
 
        # O(1)
-       redef fun last do return _tail.item
+       redef fun last do return _tail.as(not null).item
 
        # O(1)
-       redef fun last=(e) do _tail.item = e
+       redef fun last=(e) do _tail.as(not null).item = e
 
 # Queries
 
@@ -87,11 +87,12 @@ class List[E]
        redef fun push(e)
        do
                var node = new ListNode[E](e)
-               if _tail == null then
+               var tail = _tail
+               if tail == null then
                        _head = node
                else
-                       _tail.next = node
-                       node.prev = _tail
+                       tail.next = node
+                       node.prev = tail
                end
                _tail = node
                length += 1
@@ -101,11 +102,12 @@ class List[E]
        redef fun unshift(e)
        do
                var node = new ListNode[E](e)
-               if _head == null then
+               var head = _head
+               if head == null then
                        _tail = node
                else
-                       node.next = _head
-                       _head.prev = node
+                       node.next = head
+                       head.prev = node
                end
                _head = node
                length += 1
@@ -127,11 +129,12 @@ class List[E]
        # O(1)
        fun link(l: List[E])
        do
-               if _tail == null then
+               var tail = _tail
+               if tail == null then
                        _head = l._head
                else if l._head != null then
-                       _tail.next = l._head
-                       _tail.next.prev = _tail
+                       tail.next = l._head
+                       tail.next.as(not null).prev = tail
                end
                _tail = l._tail
                length += l.length
@@ -143,13 +146,13 @@ class List[E]
        # O(1)
        redef fun pop
        do
-               var node = _tail
+               var node = _tail.as(not null)
                _tail = node.prev
                node.prev = null
                if _tail == null then
                        _head = null
                else
-                       _tail.next = null
+                       _tail.as(not null).next = null
                end
                length -= 1
                return node.item
@@ -158,13 +161,13 @@ class List[E]
        # O(1)
        redef fun shift
        do
-               var node = _head
+               var node = _head.as(not null)
                _head = node.next
                node.next = null
                if _head == null then
                        _tail = null
                else
-                       _head.prev = null
+                       _head.as(not null).prev = null
                end
                length -= 1
                return node.item
@@ -236,14 +239,14 @@ class List[E]
                        if node.next == null then
                                _tail = null
                        else
-                               node.next.prev = null
+                               node.next.as(not null).prev = null
                        end
                else if node.next == null then
                        _tail = node.prev
-                       node.prev.next = null
+                       node.prev.as(not null).next = null
                else
-                       node.prev.next = node.next
-                       node.next.prev = node.prev
+                       node.prev.as(not null).next = node.next
+                       node.next.as(not null).prev = node.prev
                end
        end
 
@@ -266,16 +269,16 @@ end
 # This is the iterator class of List
 class ListIterator[E]
        super IndexedIterator[E]
-       redef fun item do return _node.item
+       redef fun item do return _node.as(not null).item
 
        # Set item `e` at self `index`.
-       fun item=(e: E) do _node.item = e
+       fun item=(e: E) do _node.as(not null).item = e
 
        redef fun is_ok do return not _node == null
 
        redef fun next
        do
-               _node = _node.next
+               _node = _node.as(not null).next
                _index += 1
        end
 
@@ -312,7 +315,7 @@ private class ListReverseIterator[E]
 
        redef fun next
        do
-               _node = _node.prev
+               _node = _node.as(not null).prev
                _index -= 1
        end
 
index ac13301..6a58480 100644 (file)
@@ -86,6 +86,6 @@ class MaybeError[V, E: Error]
        redef fun to_s do
                var e = maybe_error
                if e != null then return e.to_s
-               return value.to_s
+               return value.as(not null).to_s
        end
 end
index 10c5374..6c03c7f 100644 (file)
@@ -98,6 +98,7 @@ class Process
                var args = new FlatBuffer
                var l = 1 # Number of elements in args
                args.append(command)
+               var arguments = self.arguments
                if arguments != null then
                        for a in arguments do
                                args.add('\0')
@@ -324,6 +325,9 @@ redef class Sys
        do
                return command.to_cstring.system
        end
+
+       # The pid of the program
+       fun pid: Int `{ return getpid(); `}
 end
 
 redef class NativeString
index def1cd2..8bf8078 100644 (file)
@@ -49,23 +49,24 @@ abstract class FileStream
        # Return null in case of error
        fun file_stat: nullable FileStat
        do
-               var stat = _file.file_stat
+               var stat = _file.as(not null).file_stat
                if stat.address_is_null then return null
                return new FileStat(stat)
        end
 
        # File descriptor of this file
-       fun fd: Int do return _file.fileno
+       fun fd: Int do return _file.as(not null).fileno
 
        redef fun close
        do
-               if _file == null then return
-               if _file.address_is_null then
+               var file = _file
+               if file == null then return
+               if file.address_is_null then
                        if last_error != null then return
                        last_error = new IOError("Cannot close unopened file")
                        return
                end
-               var i = _file.io_close
+               var i = file.io_close
                if i != 0 then
                        last_error = new IOError("Close failed due to error {sys.errno.strerror}")
                end
@@ -83,7 +84,7 @@ abstract class FileStream
        # * `buffer_mode_none`
        fun set_buffering_mode(buf_size, mode: Int) do
                if buf_size <= 0 then buf_size = 512
-               if _file.set_buffering_type(buf_size, mode) != 0 then
+               if _file.as(not null).set_buffering_type(buf_size, mode) != 0 then
                        last_error = new IOError("Error while changing buffering type for FileStream, returned error {sys.errno.strerror}")
                end
        end
@@ -105,10 +106,10 @@ class FileReader
        #     assert l == f.read_line
        fun reopen
        do
-               if not eof and not _file.address_is_null then close
+               if not eof and not _file.as(not null).address_is_null then close
                last_error = null
-               _file = new NativeFile.io_open_read(path.to_cstring)
-               if _file.address_is_null then
+               _file = new NativeFile.io_open_read(path.as(not null).to_cstring)
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Cannot open `{path.as(not null)}`: {sys.errno.strerror}")
                        end_reached = true
                        return
@@ -126,8 +127,8 @@ class FileReader
 
        redef fun fill_buffer
        do
-               var nb = _file.io_read(_buffer, _buffer_capacity)
-               if last_error == null and _file.ferror then
+               var nb = _file.as(not null).io_read(_buffer, _buffer_capacity)
+               if last_error == null and _file.as(not null).ferror then
                        last_error = new IOError("Cannot read `{path.as(not null)}`: {sys.errno.strerror}")
                        end_reached = true
                end
@@ -158,7 +159,7 @@ class FileReader
                self.path = path
                prepare_buffer(100)
                _file = new NativeFile.io_open_read(path.to_cstring)
-               if _file.address_is_null then
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Cannot open `{path}`: {sys.errno.strerror}")
                        end_reached = true
                end
@@ -171,7 +172,7 @@ class FileReader
                self.path = ""
                prepare_buffer(1)
                _file = fd.fd_to_stream(read_only)
-               if _file.address_is_null then
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Error: Converting fd {fd} to stream failed with '{sys.errno.strerror}'")
                        end_reached = true
                end
@@ -223,13 +224,13 @@ class FileWriter
                        last_error = new IOError("Cannot write to non-writable stream")
                        return
                end
-               if _file.address_is_null then
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Writing on a null stream")
                        _is_writable = false
                        return
                end
 
-               var err = _file.write_byte(value)
+               var err = _file.as(not null).write_byte(value)
                if err != 1 then
                        # Big problem
                        last_error = new IOError("Problem writing a byte: {err}")
@@ -251,12 +252,12 @@ class FileWriter
                        last_error = new IOError("Cannot write to non-writable stream")
                        return
                end
-               if _file.address_is_null then
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Writing on a null stream")
                        _is_writable = false
                        return
                end
-               var err = _file.io_write(native, from, len)
+               var err = _file.as(not null).io_write(native, from, len)
                if err != len then
                        # Big problem
                        last_error = new IOError("Problem in writing : {err} {len} \n")
@@ -269,7 +270,7 @@ class FileWriter
                _file = new NativeFile.io_open_write(path.to_cstring)
                self.path = path
                _is_writable = true
-               if _file.address_is_null then
+               if _file.as(not null).address_is_null then
                        last_error = new IOError("Cannot open `{path}`: {sys.errno.strerror}")
                        is_writable = false
                end
@@ -280,7 +281,7 @@ class FileWriter
                self.path = ""
                _file = fd.fd_to_stream(wipe_write)
                _is_writable = true
-                if _file.address_is_null then
+                if _file.as(not null).address_is_null then
                         last_error = new IOError("Error: Opening stream from file descriptor {fd} failed with '{sys.errno.strerror}'")
                        _is_writable = false
                end
@@ -663,6 +664,19 @@ class Path
                return res
        end
 
+       # Is `self` the path to an existing directory ?
+       #
+       # ~~~nit
+       # assert ".".to_path.is_dir
+       # assert not "/etc/issue".to_path.is_dir
+       # assert not "/should/not/exist".to_path.is_dir
+       # ~~~
+       fun is_dir: Bool do
+               var st = stat
+               if st == null then return false
+               return st.is_dir
+       end
+
        # Delete a directory and all of its content
        #
        # Does not go through symbolic links and may get stuck in a cycle if there
index 0f8325f..7b51e2a 100644 (file)
@@ -169,6 +169,16 @@ redef class Int
                end
                return res
        end
+
+       # Is `self` a power of two ?
+       #
+       # ~~~nit
+       # assert not 3.is_pow2
+       # assert 2.is_pow2
+       # assert 1.is_pow2
+       # assert not 0.is_pow2
+       # ~~~
+       fun is_pow2: Bool do return self != 0 and (self & self - 1) == 0
 end
 
 redef class Byte
index 917ec4f..0ac109c 100644 (file)
@@ -183,7 +183,7 @@ class Regex
        # Cache of a single `regmatch_t` to prevent many calls to `malloc`
        private var native_match: NativeMatchArray is lazy do
                native_match_is_init = true
-               return new NativeMatchArray.malloc(native.re_nsub+1)
+               return new NativeMatchArray.malloc(native.as(not null).re_nsub+1)
        end
 
        private var native_match_is_init = false
index c9d1279..8fefb24 100644 (file)
@@ -1360,30 +1360,19 @@ abstract class String
        # Letters that follow a letter are lowercased
        # Letters that follow a non-letter are upcased.
        #
+       # If `keep_upper = true`, already uppercase letters are not lowercased.
+       #
        # SEE : `Char::is_letter` for the definition of letter.
        #
        #     assert "jAVASCRIPT".capitalized == "Javascript"
        #     assert "i am root".capitalized == "I Am Root"
        #     assert "ab_c -ab0c ab\nc".capitalized == "Ab_C -Ab0C Ab\nC"
-       fun capitalized: SELFTYPE do
+       #     assert "preserve my ACRONYMS".capitalized(keep_upper=true) == "Preserve My ACRONYMS"
+       fun capitalized(keep_upper: nullable Bool): SELFTYPE do
                if length == 0 then return self
 
                var buf = new Buffer.with_cap(length)
-
-               var curr = chars[0].to_upper
-               var prev = curr
-               buf[0] = curr
-
-               for i in [1 .. length[ do
-                       prev = curr
-                       curr = self[i]
-                       if prev.is_letter then
-                               buf[i] = curr.to_lower
-                       else
-                               buf[i] = curr.to_upper
-                       end
-               end
-
+               buf.capitalize(keep_upper=keep_upper, src=self)
                return buf.to_s
        end
 end
@@ -1478,6 +1467,13 @@ abstract class Buffer
        # Letters that follow a letter are lowercased
        # Letters that follow a non-letter are upcased.
        #
+       # If `keep_upper = true`, uppercase letters are not lowercased.
+       #
+       # When `src` is specified, this method reads from `src` instead of `self`
+       # but it still writes the result to the beginning of `self`.
+       # This requires `self` to have the capacity to receive all of the
+       # capitalized content of `src`.
+       #
        # SEE: `Char::is_letter` for the definition of a letter.
        #
        #     var b = new FlatBuffer.from("jAVAsCriPt")
@@ -1489,16 +1485,32 @@ abstract class Buffer
        #     b = new FlatBuffer.from("ab_c -ab0c ab\nc")
        #     b.capitalize
        #     assert b == "Ab_C -Ab0C Ab\nC"
-       fun capitalize do
+       #
+       #     b = new FlatBuffer.from("12345")
+       #     b.capitalize(src="foo")
+       #     assert b == "Foo45"
+       #
+       #     b = new FlatBuffer.from("preserve my ACRONYMS")
+       #     b.capitalize(keep_upper=true)
+       #     assert b == "Preserve My ACRONYMS"
+       fun capitalize(keep_upper: nullable Bool, src: nullable Text) do
+               src = src or else self
+               var length = src.length
                if length == 0 then return
-               var c = self[0].to_upper
+               keep_upper = keep_upper or else false
+
+               var c = src[0].to_upper
                self[0] = c
                var prev = c
                for i in [1 .. length[ do
                        prev = c
-                       c = self[i]
+                       c = src[i]
                        if prev.is_letter then
-                               self[i] = c.to_lower
+                               if keep_upper then
+                                       self[i] = c
+                               else
+                                       self[i] = c.to_lower
+                               end
                        else
                                self[i] = c.to_upper
                        end
@@ -2102,7 +2114,12 @@ end
 # see `alpha_comparator`
 private class AlphaComparator
        super Comparator
-       redef fun compare(a, b) do return a.to_s <=> b.to_s
+       redef fun compare(a, b) do
+               if a == b then return 0
+               if a == null then return -1
+               if b == null then return 1
+               return a.to_s <=> b.to_s
+       end
 end
 
 # Stateless comparator that naively use `to_s` to compare things.
@@ -2160,6 +2177,11 @@ redef class NativeString
        # SEE: `abstract_text::Text` for more info on the difference
        # between `Text::bytelen` and `Text::length`.
        fun to_s_full(bytelen, unilen: Int): String is abstract
+
+       # Copies the content of `src` to `self`
+       #
+       # NOTE: `self` must be large enough to withold `self.bytelen` bytes
+       fun fill_from(src: Text) do src.copy_to_native(self, src.bytelen, 0, 0)
 end
 
 redef class NativeArray[E]
index 2fa0bcb..9e70321 100644 (file)
@@ -369,6 +369,10 @@ redef class FlatText
                end
                return res
        end
+
+       redef fun copy_to_native(dst, n, src_off, dst_off) do
+               _items.copy_to(dst, n, first_byte + src_off, dst_off)
+       end
 end
 
 # Immutable strings of characters.
index f370e49..f57b33d 100644 (file)
@@ -221,23 +221,16 @@ private class Concat
        end
 
        redef fun copy_to_native(dest, n, src_offset, dest_offset) do
-               var subs = new RopeSubstrings.from(self, src_offset)
-               var st = src_offset - subs.pos
-               var off = dest_offset
-               while n > 0 do
-                       var it = subs.item
-                       if n > it.length then
-                               var cplen = it.length - st
-                               it._items.copy_to(dest, cplen, st, off)
-                               off += cplen
-                               n -= cplen
-                       else
-                               it._items.copy_to(dest, n, st, off)
-                               n = 0
-                       end
-                       subs.next
-                       st = 0
+               var l = _left
+               if src_offset < l.bytelen then
+                       var lcopy = l.bytelen - src_offset
+                       lcopy = if lcopy > n then n else lcopy
+                       l.copy_to_native(dest, lcopy, src_offset, dest_offset)
+                       dest_offset += lcopy
+                       n -= lcopy
+                       src_offset = 0
                end
+               _right.copy_to_native(dest, n, src_offset, dest_offset)
        end
 
        # Returns a balanced version of `self`
@@ -828,7 +821,7 @@ private class ReverseRopeSubstrings
        redef fun next do
                if pos < 0 then return
                var curr = iter.prev
-               var currit = curr.node
+               var currit = curr.as(not null).node
                while curr != null do
                        currit = curr.node
                        if not currit isa Concat then
@@ -935,14 +928,14 @@ private class RopeSubstrings
        redef fun next do
                pos += str.length
                if pos > max then return
-               var it = iter.prev
+               var it = iter.prev.as(not null)
                var rnod = it.node
                loop
                        if not rnod isa Concat then
                                it.ldone = true
                                it.rdone = true
                                str = rnod.as(FlatString)
-                               iter = it.as(not null)
+                               iter = it
                                break
                        end
                        if not it.ldone then
@@ -954,7 +947,7 @@ private class RopeSubstrings
                                rnod = rnod._right
                                it = new RopeCharIteratorPiece(rnod, false, false, it)
                        else
-                               it = it.prev
+                               it = it.prev.as(not null)
                                rnod = it.node
                                continue
                        end
index 6662877..5f3f017 100644 (file)
@@ -16,3 +16,4 @@
 module crapto
 
 import english_utils
+import xor
diff --git a/lib/crapto/examples/repeating_key_xor_cipher.txt b/lib/crapto/examples/repeating_key_xor_cipher.txt
new file mode 100644 (file)
index 0000000..cecdb81
--- /dev/null
@@ -0,0 +1,64 @@
+HUIfTQsPAh9PE048GmllH0kcDk4TAQsHThsBFkU2AB4BSWQgVB0dQzNTTmVS
+BgBHVBwNRU0HBAxTEjwMHghJGgkRTxRMIRpHKwAFHUdZEQQJAGQmB1MANxYG
+DBoXQR0BUlQwXwAgEwoFR08SSAhFTmU+Fgk4RQYFCBpGB08fWXh+amI2DB0P
+QQ1IBlUaGwAdQnQEHgFJGgkRAlJ6f0kASDoAGhNJGk9FSA8dDVMEOgFSGQEL
+QRMGAEwxX1NiFQYHCQdUCxdBFBZJeTM1CxsBBQ9GB08dTnhOSCdSBAcMRVhI
+CEEATyBUCHQLHRlJAgAOFlwAUjBpZR9JAgJUAAELB04CEFMBJhAVTQIHAh9P
+G054MGk2UgoBCVQGBwlTTgIQUwg7EAYFSQ8PEE87ADpfRyscSWQzT1QCEFMa
+TwUWEXQMBk0PAg4DQ1JMPU4ALwtJDQhOFw0VVB1PDhxFXigLTRkBEgcKVVN4
+Tk9iBgELR1MdDAAAFwoFHww6Ql5NLgFBIg4cSTRWQWI1Bk9HKn47CE8BGwFT
+QjcEBx4MThUcDgYHKxpUKhdJGQZZVCFFVwcDBVMHMUV4LAcKQR0JUlk3TwAm
+HQdJEwATARNFTg5JFwQ5C15NHQYEGk94dzBDADsdHE4UVBUaDE5JTwgHRTkA
+Umc6AUETCgYAN1xGYlUKDxJTEUgsAA0ABwcXOwlSGQELQQcbE0c9GioWGgwc
+AgcHSAtPTgsAABY9C1VNCAINGxgXRHgwaWUfSQcJABkRRU8ZAUkDDTUWF01j
+OgkRTxVJKlZJJwFJHQYADUgRSAsWSR8KIgBSAAxOABoLUlQwW1RiGxpOCEtU
+YiROCk8gUwY1C1IJCAACEU8QRSxORTBSHQYGTlQJC1lOBAAXRTpCUh0FDxhU
+ZXhzLFtHJ1JbTkoNVDEAQU4bARZFOwsXTRAPRlQYE042WwAuGxoaAk5UHAoA
+ZCYdVBZ0ChQLSQMYVAcXQTwaUy1SBQsTAAAAAAAMCggHRSQJExRJGgkGAAdH
+MBoqER1JJ0dDFQZFRhsBAlMMIEUHHUkPDxBPH0EzXwArBkkdCFUaDEVHAQAN
+U29lSEBAWk44G09fDXhxTi0RAk4ITlQbCk0LTx4cCjBFeCsGHEETAB1EeFZV
+IRlFTi4AGAEORU4CEFMXPBwfCBpOAAAdHUMxVVUxUmM9ElARGgZBAg4PAQQz
+DB4EGhoIFwoKUDFbTCsWBg0OTwEbRSonSARTBDpFFwsPCwIATxNOPBpUKhMd
+Th5PAUgGQQBPCxYRdG87TQoPD1QbE0s9GkFiFAUXR0cdGgkADwENUwg1DhdN
+AQsTVBgXVHYaKkg7TgNHTB0DAAA9DgQACjpFX0BJPQAZHB1OeE5PYjYMAg5M
+FQBFKjoHDAEAcxZSAwZOBREBC0k2HQxiKwYbR0MVBkVUHBZJBwp0DRMDDk5r
+NhoGACFVVWUeBU4MRREYRVQcFgAdQnQRHU0OCxVUAgsAK05ZLhdJZChWERpF
+QQALSRwTMRdeTRkcABcbG0M9Gk0jGQwdR1ARGgNFDRtJeSchEVIDBhpBHQlS
+WTdPBzAXSQ9HTBsJA0UcQUl5bw0KB0oFAkETCgYANlVXKhcbC0sAGgdFUAIO
+ChZJdAsdTR0HDBFDUk43GkcrAAUdRyonBwpOTkJEUyo8RR8USSkOEENSSDdX
+RSAdDRdLAA0HEAAeHQYRBDYJC00MDxVUZSFQOV1IJwYdB0dXHRwNAA9PGgMK
+OwtTTSoBDBFPHU54W04mUhoPHgAdHEQAZGU/OjV6RSQMBwcNGA5SaTtfADsX
+GUJHWREYSQAnSARTBjsIGwNOTgkVHRYANFNLJ1IIThVIHQYKAGQmBwcKLAwR
+DB0HDxNPAU94Q083UhoaBkcTDRcAAgYCFkU1RQUEBwFBfjwdAChPTikBSR0T
+TwRIEVIXBgcURTULFk0OBxMYTwFUN0oAIQAQBwkHVGIzQQAGBR8EdCwRCEkH
+ElQcF0w0U05lUggAAwANBxAAHgoGAwkxRRMfDE4DARYbTn8aKmUxCBsURVQf
+DVlOGwEWRTIXFwwCHUEVHRcAMlVDKRsHSUdMHQMAAC0dCAkcdCIeGAxOazkA
+BEk2HQAjHA1OAFIbBxNJAEhJBxctDBwKSRoOVBwbTj8aQS4dBwlHKjUECQAa
+BxscEDMNUhkBC0ETBxdULFUAJQAGARFJGk9FVAYGGlMNMRcXTRoBDxNPeG43
+TQA7HRxJFUVUCQhBFAoNUwctRQYFDE43PT9SUDdJUydcSWRtcwANFVAHAU5T
+FjtFGgwbCkEYBhlFeFsABRcbAwZOVCYEWgdPYyARNRcGAQwKQRYWUlQwXwAg
+ExoLFAAcARFUBwFOUwImCgcDDU5rIAcXUj0dU2IcBk4TUh0YFUkASEkcC3QI
+GwMMQkE9SB8AMk9TNlIOCxNUHQZCAAoAHh1FXjYCDBsFABkOBkk7FgALVQRO
+D0EaDwxOSU8dGgI8EVIBAAUEVA5SRjlUQTYbCk5teRsdRVQcDhkDADBFHwhJ
+AQ8XClJBNl4AC1IdBghVEwARABoHCAdFXjwdGEkDCBMHBgAwW1YnUgAaRyon
+B0VTGgoZUwE7EhxNCAAFVAMXTjwaTSdSEAESUlQNBFJOZU5LXHQMHE0EF0EA
+Bh9FeRp5LQdFTkAZREgMU04CEFMcMQQAQ0lkay0ABwcqXwA1FwgFAk4dBkIA
+CA4aB0l0PD1MSQ8PEE87ADtbTmIGDAILAB0cRSo3ABwBRTYKFhROHUETCgZU
+MVQHYhoGGksABwdJAB0ASTpFNwQcTRoDBBgDUkksGioRHUkKCE5THEVCC08E
+EgF0BBwJSQoOGkgGADpfADETDU5tBzcJEFMLTx0bAHQJCx8ADRJUDRdMN1RH
+YgYGTi5jMURFeQEaSRAEOkURDAUCQRkKUmQ5XgBIKwYbQFIRSBVJGgwBGgtz
+RRNNDwcVWE8BT3hJVCcCSQwGQx9IBE4KTwwdASEXF01jIgQATwZIPRpXKwYK
+BkdEGwsRTxxDSToGMUlSCQZOFRwKUkQ5VEMnUh0BR0MBGgAAZDwGUwY7CBdN
+HB5BFwMdUz0aQSwWSQoITlMcRUILTxoCEDUXF01jNw4BTwVBNlRBYhAIGhNM
+EUgIRU5CRFMkOhwGBAQLTVQOHFkvUkUwF0lkbXkbHUVUBgAcFA0gRQYFCBpB
+PU8FQSsaVycTAkJHYhsRSQAXABxUFzFFFggICkEDHR1OPxoqER1JDQhNEUgK
+TkJPDAUAJhwQAg0XQRUBFgArU04lUh0GDlNUGwpOCU9jeTY1HFJARE4xGA4L
+ACxSQTZSDxsJSw1ICFUdBgpTNjUcXk0OAUEDBxtUPRpCLQtFTgBPVB8NSRoK
+SREKLUUVAklkERgOCwAsUkE2Ug8bCUsNSAhVHQYKUyI7RQUFABoEVA0dWXQa
+Ry1SHgYOVBFIB08XQ0kUCnRvPgwQTgUbGBwAOVREYhAGAQBJEUgETgpPGR8E
+LUUGBQgaQRIaHEshGk03AQANR1QdBAkAFwAcUwE9AFxNY2QxGA4LACxSQTZS
+DxsJSw1ICFUdBgpTJjsIF00GAE1ULB1NPRpPLF5JAgJUVAUAAAYKCAFFXjUe
+DBBOFRwOBgA+T04pC0kDElMdC0VXBgYdFkU2CgtNEAEUVBwTWXhTVG5SGg8e
+AB0cRSo+AwgKRSANExlJCBQaBAsANU9TKxFJL0dMHRwRTAtPBRwQMAAATQcB
+FlRlIkw5QwA2GggaR0YBBg5ZTgIcAAw3SVIaAQcVEU8QTyEaYy0fDE4ITlhI
+Jk8DCkkcC3hFMQIEC0EbAVIqCFZBO1IdBgZUVA4QTgUWSR4QJwwRTWM=
diff --git a/lib/crapto/examples/repeating_key_xor_solve.nit b/lib/crapto/examples/repeating_key_xor_solve.nit
new file mode 100644 (file)
index 0000000..f88e2d9
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+import base64
+import crapto
+
+# Check usage
+if args.length != 1 then
+       print "Usage: repeating_key_xor_solve <cipher_file>"
+       exit 1
+end
+
+# Read the cipher from the file
+var cipher_bytes = args[0].to_path.read_all_bytes.decode_base64
+
+# Create a RepeatingKeyXorCipher object to manipulate your ciphertext
+var xorcipher = new RepeatingKeyXorCipher
+xorcipher.ciphertext = cipher_bytes
+
+# Try to find the best candidate key
+xorcipher.find_key
+
+# Decrypt the cipher according to the found key
+xorcipher.decrypt
+
+# Check the resulting plaintext out...
+print xorcipher.plaintext
diff --git a/lib/crapto/xor.nit b/lib/crapto/xor.nit
new file mode 100644 (file)
index 0000000..8745c1a
--- /dev/null
@@ -0,0 +1,150 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+# Cryptographic attacks and utilities for XOR-based algorithms.
+module xor
+
+import combinations
+import crypto
+import english_utils
+
+redef class SingleByteXorCipher
+       # Tries to find key using frequency analysis on all possible plaintexts.
+       # Populates `self.key`
+       fun find_key do
+
+               # Accumulate best result
+               var max = 0.0
+               var best = 0.to_b
+
+               # Iterate on possible values for a byte
+               var xor_b = new Bytes.with_capacity(1)
+               for b in [0 .. 255] do
+                       # Need `Bytes` to pass to xor
+                       xor_b[0] = b.to_b
+
+                       # Xor and evaluate result
+                       var xored = ciphertext.xorcipher(xor_b)
+                       var result = xored.to_s.english_scoring
+                       if result > max then
+                               max = result
+                               best = b.to_b
+                       end
+               end
+
+               self.key = best
+
+       end
+end
+
+redef class RepeatingKeyXorCipher
+       # Attempts to find the key by using frequency analysis on the resulting plaintexts.
+       # Best key lengths are estimated using the hamming distance of blocks of keylength bytes.
+       # Ciphertext is then transposed in such a way that it can be solved by sequences of
+       # SingleByteXor (one for each offset in the key).
+       #
+       # `min_keylength` and `max_keylength` are limits as to what key lengths are tested.
+       # `considered_keylength_count` is the number of best key lengths that are kept to be
+       # analysed by the SingleByteXor frequency analysis.
+       fun find_key(min_keylength, max_keylength, considered_keylength_count: nullable Int)  do
+
+               # Set default values
+               if min_keylength == null then min_keylength = 2
+               if max_keylength == null then max_keylength = 40
+               if considered_keylength_count == null then considered_keylength_count = 3
+
+               # Find the best candidate key lengths based on the normalized hamming distances
+               var best_sizes = get_normalized_hamming_distances(min_keylength, max_keylength, considered_keylength_count)
+
+               var best = 0.0
+               var best_key: nullable Bytes = null
+               for ks in best_sizes do
+
+                       # Rearrange ciphertext to be in SingleByteXORable blocks
+                       var transposed = transpose_ciphertext(ks)
+
+                       var key = new Bytes.empty
+                       for slot in transposed do
+                               var sbx = new SingleByteXorCipher
+                               sbx.ciphertext = slot
+                               sbx.find_key
+                               key.add sbx.key
+                       end
+
+                       # Evaluate the resulting plaintext based on english frequency analysis
+                       var eng_score = ciphertext.xorcipher(key).to_s.english_scoring
+                       if eng_score > best then
+                               best_key = key
+                               best = eng_score
+                       end
+
+                       assert best_key != null
+                       self.key = best_key
+
+               end
+       end
+
+       # Computes the normalized hamming distances between blocks of ciphertext of length between `min_length` and `max_length`.
+       # The `considered_keylength_count` smallest results are returned
+       private fun get_normalized_hamming_distances(min_keylength, max_keylength, considered_keylength_count: Int): Array[Int] do
+
+               var normalized_distances = new HashMap[Float, Int]
+
+               # Iterate over all given key lengths
+               for ks in [min_keylength .. max_keylength[ do
+
+                       # Initialize the blocks of size `ks`
+                       var blocks = new Array[Bytes]
+                       while (blocks.length + 1) * ks < ciphertext.length do
+                               blocks.add(ciphertext.slice(blocks.length * ks, ks))
+                       end
+
+                       # Compute the normalized hamming distance with all block combinations
+                       var pairs = new CombinationCollection[Bytes](blocks, 2)
+                       var hamming_dists = new Array[Float]
+                       for p in pairs do
+                               hamming_dists.add(p[0].hamming_distance(p[1]).to_f / ks.to_f)
+                       end
+
+                       # Normalize the results based on the number of blocks
+                       var normalized = 0.0
+                       for dist in hamming_dists do normalized += dist
+                       normalized /= hamming_dists.length.to_f
+                       normalized_distances[normalized] = ks
+
+               end
+
+               # Collect the best candidates
+               var distances = normalized_distances.keys.to_a
+               default_comparator.sort(distances)
+               var best_distances = distances.subarray(0, considered_keylength_count)
+               var best_sizes = [for d in best_distances do normalized_distances[d]]
+
+               return best_sizes
+       end
+
+       # Returns a rearranged format of the ciphertext where every byte of a slot will be XORed with the same offset of a key of length `keylength`.
+       private fun transpose_ciphertext(keylength: Int): Array[Bytes] do
+               var transposed = new Array[Bytes]
+               for i in [0 .. keylength[ do
+                       transposed.add(new Bytes.empty)
+               end
+
+               for byte_idx in [0 .. ciphertext.length[ do
+                       transposed[byte_idx % keylength].add(ciphertext[byte_idx])
+               end
+
+               return transposed
+       end
+end
similarity index 91%
rename from lib/crypto.nit
rename to lib/crypto/basic_ciphers.nit
index 2abe5d7..e4a133c 100644 (file)
@@ -12,8 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Mix of all things cryptography-related
-module crypto
+# Basic cryptographic ciphers and utilities.
+module basic_ciphers
 
 redef class Char
        # Rotates self of `x`
@@ -157,20 +157,21 @@ redef class Text
 end
 
 redef class Bytes
-
-       # Returns `self` xored with `key`
-       #
-       # The key is cycled through until the `self` has been completely xored.
-       #
-       #     assert "goodmorning".to_bytes.xorcipher(" ".to_bytes) == "GOODMORNING".bytes
-       fun xorcipher(key: Bytes): Bytes do
-               var xored = new Bytes.with_capacity(self.length)
-
-               for i in self.length.times do
-                       xored.add(self[i] ^ key[i % key.length])
+       # Computes the edit/hamming distance of two sequences of bytes.
+       #
+       #     assert "this is a test".to_bytes.hamming_distance("wokka wokka!!!".bytes) == 37
+       #     assert "this is a test".to_bytes.hamming_distance("this is a test".bytes) == 0
+       #
+       fun hamming_distance(other: SequenceRead[Byte]): Int do
+               var diff = 0
+               for idx in self.length.times do
+                       var res_byte = self[idx] ^ other[idx]
+                       for bit in [0..8[ do
+                               if res_byte & 1u8 == 1u8 then diff += 1
+                               res_byte = res_byte >> 1
+                       end
                end
-
-               return xored
+               return diff
        end
 end
 
diff --git a/lib/crypto/crypto.nit b/lib/crypto/crypto.nit
new file mode 100644 (file)
index 0000000..4709403
--- /dev/null
@@ -0,0 +1,19 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+# Mix of all things cryptography-related
+module crypto
+
+import basic_ciphers
+import xor_ciphers
similarity index 89%
rename from lib/crypto.ini
rename to lib/crypto/package.ini
index e8b4cf1..24f6b14 100644 (file)
@@ -6,6 +6,6 @@ license=Apache-2.0
 [upstream]
 browse=https://github.com/nitlang/nit/tree/master/lib/crypto.nit
 git=https://github.com/nitlang/nit.git
-git.directory=lib/crypto.nit
+git.directory=lib/crypto/crypto.nit
 homepage=http://nitlanguage.org
 issues=https://github.com/nitlang/nit/issues
diff --git a/lib/crypto/xor_ciphers.nit b/lib/crypto/xor_ciphers.nit
new file mode 100644 (file)
index 0000000..1c4807c
--- /dev/null
@@ -0,0 +1,88 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+# XOR oriented cryptographic ciphers and utilities.
+module xor_ciphers
+
+redef class Bytes
+       # Returns `self` xored with `key`
+       #
+       # The key is cycled through until the `self` has been completely xored.
+       #
+       #     assert "goodmorning".to_bytes.xorcipher(" ".to_bytes) == "GOODMORNING".bytes
+       fun xorcipher(key: Bytes): Bytes do
+               var xored = new Bytes.with_capacity(self.length)
+
+               for i in self.length.times do
+                       xored.add(self[i] ^ key[i % key.length])
+               end
+
+               return xored
+       end
+end
+
+# Base class to modelize cryptographic ciphers
+abstract class Cipher
+
+       # Encrypted data
+       var ciphertext = new Bytes.empty is writable
+
+       # Unencrypted data
+       var plaintext = new Bytes.empty is writable
+
+       # Encrypt plaintext and populate `self.ciphertext`
+       fun encrypt is abstract
+
+       # Decrypt ciphertext and populate `self.plaintext`
+       fun decrypt is abstract
+
+end
+
+# Simple XOR cipher where the whole plaintext is XORed with a single byte.
+class SingleByteXorCipher
+       super Cipher
+
+       # Cryptographic key used in encryption and decryption.
+       var key: Byte = 0.to_b
+
+       redef fun encrypt do
+               var key_bytes = new Bytes.with_capacity(1)
+               key_bytes.add(key)
+               ciphertext = plaintext.xorcipher(key_bytes)
+       end
+
+       redef fun decrypt do
+               var key_bytes = new Bytes.with_capacity(1)
+               key_bytes.add(key)
+               plaintext = ciphertext.xorcipher(key_bytes)
+       end
+end
+
+# XOR cipher where the key is repeated to match the length of the message.
+class RepeatingKeyXorCipher
+       super Cipher
+
+       # Cryptographic key used in encryption and decryption.
+       var key = new Bytes.empty
+
+       redef fun encrypt do
+               assert key.length > 0
+               ciphertext = plaintext.xorcipher(key)
+       end
+
+       redef fun decrypt do
+               assert key.length > 0
+               plaintext = ciphertext.xorcipher(key)
+       end
+end
index a1eed2f..245d071 100644 (file)
@@ -162,6 +162,35 @@ class EulerCamera
                yaw = 0.0
                roll = 0.0
        end
+
+       # Convert the position `x, y` on screen, to world coordinates on the plane at `target_z`
+       #
+       # `target_z` defaults to `0.0` and specifies the Z coordinates of the plane
+       # on which to project the screen position `x, y`.
+       #
+       # This method assumes that the camera is looking along the Z axis towards higher values.
+       # Using it in a different orientation can be useful, but won't result in valid
+       # world coordinates.
+       fun camera_to_world(x, y: Numeric, target_z: nullable Float): Point[Float]
+       do
+               # TODO, this method could be tweaked to support projecting the 2D point,
+               # on the near plane (x,y) onto a given distance no matter to orientation
+               # of the camera.
+
+               target_z = target_z or else 0.0
+
+               # Convert from pixel units / window resolution to
+               # units on the near clipping wall to
+               # units on the target wall at Z = 0
+               var near_height = (field_of_view_y/2.0).tan * near
+               var cross_screen_to_near = near_height / (display.height.to_f/2.0)
+               var cross_near_to_target = (position.z - target_z) / near
+               var mod = cross_screen_to_near * cross_near_to_target * 1.72 # FIXME drop the magic number
+
+               var wx = position.x + (x.to_f-display.width.to_f/2.0) * mod
+               var wy = position.y - (y.to_f-display.height.to_f/2.0) * mod
+               return new Point[Float](wx, wy)
+       end
 end
 
 # Orthogonal camera to draw UI objects with services to work with screens of different sizes
index f16609f..896f07f 100644 (file)
@@ -47,7 +47,7 @@ redef class App
        private var frame_count = 0
 
        # Deadline used to compute `current_fps`
-       private var frame_count_deadline = 5
+       private var frame_count_deadline = 5.0
 
        # Check and sleep to maintain a frame-rate bellow `maximum_fps`
        #
@@ -55,12 +55,12 @@ redef class App
        # Is automatically called at the end of `full_frame`.
        fun limit_fps
        do
-               var t = clock.total.sec
+               var t = clock.total
                if t >= frame_count_deadline then
                        var cfps = frame_count.to_f / 5.0
                        self.current_fps = cfps
                        frame_count = 0
-                       frame_count_deadline = t + 5
+                       frame_count_deadline = t + 5.0
                end
                frame_count += 1
 
index 3841bc6..f4abd92 100644 (file)
@@ -44,19 +44,19 @@ redef class App
        private var frame_count = 0
 
        # Deadline used to compute `current_fps`
-       private var frame_count_deadline = 0
+       private var frame_count_deadline = 0.0
 
        # Check and sleep to maitain a frame-rate bellow `maximum_fps`
        # Also periodically uptate `current_fps`
        # Is automatically called at the end of `full_frame`.
        fun limit_fps
        do
-               var t = clock.total.sec
+               var t = clock.total
                if t >= frame_count_deadline then
                        var cfps = frame_count.to_f / 5.0
                        self.current_fps = cfps
                        frame_count = 0
-                       frame_count_deadline = t + 5
+                       frame_count_deadline = t + 5.0
                end
                frame_count += 1
 
index 6bc8b36..2db66b8 100644 (file)
@@ -26,7 +26,8 @@ and the FFI enables calling services in different languages.
 A minimal example follows with a custom `Action` and using `FileServer`.
 
 More general examples are available at `lib/nitcorn/examples/`.
-It includes the configuration of `http://xymus.net/` which merges many other _nitcorn_ applications.
+For an example of a larger project merging many _nitcorn_ applications into one server,
+take a look at the configuration of `http://xymus.net/` at `../contrib/xymus_net/xymus_net.nit`.
 
 Larger projects using _nitcorn_ can be found in the `contrib/` folder:
 * _opportunity_ is a meetup planner heavily based on _nitcorn_.
index d1bdeae..b1bbed9 100644 (file)
@@ -2,10 +2,6 @@ all: bin/restful_annot
        mkdir -p bin/
        ../../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/simple_file_server.nit
 
-xymus.net:
-       mkdir -p bin/
-       ../../../bin/nitc --dir bin/ src/xymus_net.nit
-
 pre-build: src/restful_annot_gen.nit
 src/restful_annot_gen.nit:
        ../../../bin/nitrestful -o $@ src/restful_annot.nit
index 5995558..a9ef5f6 100644 (file)
@@ -74,6 +74,12 @@ class FileServer
        # Show directory listing?
        var show_directory_listing = true is writable
 
+       # Default file returned when no static file matches the requested URI.
+       #
+       # If no `default_file` is provided, the FileServer responds 404 error to
+       # unmatched queries.
+       var default_file: nullable String = null is writable
+
        redef fun answer(request, turi)
        do
                var response
@@ -86,15 +92,14 @@ class FileServer
                # This make sure that the requested file is within the root folder.
                if (local_file + "/").has_prefix(root) then
                        # Does it exists?
-                       if local_file.file_exists then
-                               if local_file.file_stat.is_dir then
+                       var file_stat = local_file.file_stat
+                       if file_stat != null then
+                               if file_stat.is_dir then
                                        # If we target a directory without an ending `/`,
                                        # redirect to the directory ending with `/`.
-                                       if not request.uri.is_empty and
-                                          request.uri.chars.last != '/' then
-                                               response = new HttpResponse(303)
-                                               response.header["Location"] = request.uri + "/"
-                                               return response
+                                       var uri = request.uri
+                                       if not uri.is_empty and uri.chars.last != '/' then
+                                               return answer_redirection(request.uri + "/")
                                        end
 
                                        # Show index file instead of the directory listing
@@ -108,34 +113,94 @@ class FileServer
                                        end
                                end
 
-                               var is_dir = local_file.file_stat.is_dir
-                               if show_directory_listing and is_dir then
-                                       # Show the directory listing
-                                       var title = turi
-                                       var files = local_file.files
+                               file_stat = local_file.file_stat
+                               if show_directory_listing and file_stat != null and file_stat.is_dir then
+                                       response = answer_directory_listing(request, turi, local_file)
+                               else if file_stat != null and not file_stat.is_dir then # It's a single file
+                                       response = answer_file(local_file)
+                               else response = answer_default
+                       else response = answer_default
+               else response = new HttpResponse(403)
 
-                                       alpha_comparator.sort files
+               if response.status_code != 200 then
+                       var tmpl = error_page(response.status_code)
+                       if header != null and tmpl isa ErrorTemplate then tmpl.header = header
+                       response.body = tmpl.to_s
+               end
 
-                                       var links = new Array[String]
-                                       if turi.length > 1 then
-                                               var path = (request.uri + "/..").simplify_path
-                                               links.add "<a href=\"{path}/\">..</a>"
-                                       end
-                                       for file in files do
-                                               var local_path = local_file.join_path(file).simplify_path
-                                               var web_path = file.simplify_path
-                                               if local_path.file_stat.is_dir then web_path = web_path + "/"
-                                               links.add "<a href=\"{web_path}\">{file}</a>"
-                                       end
+               return response
+       end
+
+       # Answer the `default_file` if any.
+       fun answer_default: HttpResponse do
+               var default_file = self.default_file
+               if default_file == null then
+                       return new HttpResponse(404)
+               end
+
+               var local_file = (root / default_file).simplify_path
+               return answer_file(local_file)
+       end
 
-                                       var header = self.header
-                                       var header_code
-                                       if header != null then
-                                               header_code = header.write_to_string
-                                       else header_code = ""
+       # Answer a 303 redirection to `location`.
+       fun answer_redirection(location: String): HttpResponse do
+               var response = new HttpResponse(303)
+               response.header["Location"] = location
+               return response
+       end
+
+       # Build a reponse containing a single `local_file`.
+       #
+       # Returns a 404 error if local_file does not exists.
+       fun answer_file(local_file: String): HttpResponse do
+               if not local_file.file_exists then return new HttpResponse(404)
+
+               var response = new HttpResponse(200)
+               response.files.add local_file
+
+               # Set Content-Type depending on the file extension
+               var ext = local_file.file_extension
+               if ext != null then
+                       var media_type = media_types[ext]
+                       if media_type != null then
+                               response.header["Content-Type"] = media_type
+                       else response.header["Content-Type"] = "application/octet-stream"
+               end
 
-                                       response = new HttpResponse(200)
-                                       response.body = """
+               # Cache control
+               response.header["cache-control"] = cache_control
+               return response
+       end
+
+       # Answer with a directory listing for files within `local_files`.
+       fun answer_directory_listing(request: HttpRequest, turi, local_file: String): HttpResponse do
+               # Show the directory listing
+               var title = turi
+               var files = local_file.files
+
+               alpha_comparator.sort files
+
+               var links = new Array[String]
+               if turi.length > 1 then
+                       var path = (request.uri + "/..").simplify_path
+                       links.add "<a href=\"{path}/\">..</a>"
+               end
+               for file in files do
+                       var local_path = local_file.join_path(file).simplify_path
+                       var web_path = file.simplify_path
+                       var file_stat = local_path.file_stat
+                       if file_stat != null and file_stat.is_dir then web_path = web_path + "/"
+                       links.add "<a href=\"{web_path}\">{file}</a>"
+               end
+
+               var header = self.header
+               var header_code
+               if header != null then
+                       header_code = header.write_to_string
+               else header_code = ""
+
+               var response = new HttpResponse(200)
+               response.body = """
 <!DOCTYPE html>
 <head>
        <meta charset="utf-8">
@@ -157,33 +222,7 @@ class FileServer
 </body>
 </html>"""
 
-                                       response.header["Content-Type"] = media_types["html"].as(not null)
-                               else if not is_dir then
-                                       # It's a single file
-                                       response = new HttpResponse(200)
-                                       response.files.add local_file
-
-                                       var ext = local_file.file_extension
-                                       if ext != null then
-                                               var media_type = media_types[ext]
-                                               if media_type != null then
-                                                       response.header["Content-Type"] = media_type
-                                               else response.header["Content-Type"] = "application/octet-stream"
-                                       end
-
-                                       # Cache control
-                                       response.header["cache-control"] = cache_control
-
-                               else response = new HttpResponse(404)
-                       else response = new HttpResponse(404)
-               else response = new HttpResponse(403)
-
-               if response.status_code != 200 then
-                       var tmpl = error_page(response.status_code)
-                       if header != null and tmpl isa ErrorTemplate then tmpl.header = header
-                       response.body = tmpl.to_s
-               end
-
+               response.header["Content-Type"] = media_types["html"].as(not null)
                return response
        end
 end
index ba09bc1..cf4d0fa 100644 (file)
@@ -359,3 +359,72 @@ redef universal Int
                return self & 0x3FFF_FFFF
        end
 end
+
+redef universal Float
+       # Smoothened `self`, used by `ImprovedNoise`
+       private fun fade: Float do return self*self*self*(self*(self*6.0-15.0)+10.0)
+end
+
+# Direct translation of Ken Perlin's improved noise Java implementation
+#
+# This implementation differs from `PerlinNoise` on two main points.
+# This noise is calculated for a 3D point, vs 2D in `PerlinNoise`.
+# `PerlinNoise` is based off a customizable seed, while this noise has a static data source.
+class ImprovedNoise
+
+       # Permutations
+       private var p: Array[Int] = [151,160,137,91,90,15,
+               131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
+               190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
+               88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
+               77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
+               102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
+               135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
+               5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
+               223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
+               129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
+               251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
+               49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
+               138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180] * 2
+
+       # Noise value in [-1..1] at 3D coordinates `x, y, z`
+       fun noise(x, y, z: Float): Float
+       do
+               var xx = x.floor.to_i & 255
+               var yy = y.floor.to_i & 255
+               var zz = z.floor.to_i & 255
+
+               x -= x.floor
+               y -= y.floor
+               z -= z.floor
+
+               var u = x.fade
+               var v = y.fade
+               var w = z.fade
+
+               var a  = p[xx  ] + yy
+               var aa = p[a   ] + zz
+               var ab = p[a+1 ] + zz
+               var b  = p[xx+1] + yy
+               var ba = p[b   ] + zz
+               var bb = p[b+1 ] + zz
+
+               return w.lerp(v.lerp(u.lerp(grad(p[aa  ], x,     y,     z    ),
+                                           grad(p[ba  ], x-1.0, y,     z    )),
+                                    u.lerp(grad(p[ab  ], x,     y-1.0, z    ),
+                                           grad(p[bb  ], x-1.0, y-1.0, z    ))),
+                      v.lerp(u.lerp(grad(p[aa+1], x,     y,     z-1.0),
+                                           grad(p[ba+1], x-1.0, y,     z-1.0)),
+                                    u.lerp(grad(p[ab+1], x,     y-1.0, z-1.0),
+                                           grad(p[bb+1], x-1.0, y-1.0, z-1.0))))
+       end
+
+       # Value at a corner of the grid
+       private fun grad(hash: Int, x, y, z: Float): Float
+       do
+               var h = hash & 15
+               var u = if h < 8 then x else y
+               var v = if h < 4 then y else if h == 12 or h == 14 then x else z
+               return (if h.is_even then u else -u) + (if h & 2 == 0 then v else -v)
+       end
+end
index 4763061..b9e436a 100644 (file)
@@ -122,11 +122,8 @@ class PerfEntry
        # Total execution time of this event
        var sum = 0.0
 
-       # Register a new event execution time with a `Timespec`
-       fun add(lapse: Timespec) do add_float lapse.to_f
-
-       # Register a new event execution time in seconds using a `Float`
-       fun add_float(time: Float)
+       # Register a new event execution time in seconds
+       fun add(time: Float)
        do
                if time.to_f < min.to_f or count == 0 then min = time
                if time.to_f > max.to_f then max = time
index 08f7c9a..2bb6646 100644 (file)
@@ -175,6 +175,17 @@ directory from where you launch your app.
 If you run the app from another directory, it’s safer to use the absolute path of
 the directory that you want to serve.
 
+In some cases, you can want to redirect request to static files to a default file
+instead of returning a 404 error.
+This can be achieved by specifying a default file in the StaticHandler:
+
+~~~
+app.use("/static/", new StaticHandler("public/", "default.html"))
+~~~
+
+This way all non-matched queries to the StaticHandler will be answered with the
+`default.html` file.
+
 ## Advanced Routing
 
 **Routing** refers to the definition of application end points (URIs) and how
@@ -428,6 +439,23 @@ receive a `404 Not found` error.
 * `res.send()` Send a response of various types.
 * `res.error()` Set the response status code and send its message as the response body.
 
+## Response cycle
+
+When the popcorn `App` receives a request, the response cycle is the following:
+
+1. `pre-middlewares` lookup matching middlewares registered with `use_before(pre_middleware)`:
+       1. execute matching middleware by registration order
+       2. if a middleware send a response then let the `pre-middlewares` loop continue
+          with the next middleware
+2. `response-handlers` lookup matching handlers registered with `use(handler)`:
+       1. execute matching middleware by registration order
+       2. if a middleware send a response then stop the `response-handlers` loop
+       3. if no hander matches or sends a response, generate a 404 response
+3. `post-middlewares` lookup matching handlers registered with `use_after(post_handler)`:
+       1. execute matching middleware by registration order
+       2. if a middleware send a response then let the `post-middlewares` loop continue
+          with the next middleware
+
 ## Middlewares
 
 ### Overview
@@ -465,7 +493,7 @@ end
 
 
 var app = new App
-app.use("/*", new MyLogger)
+app.use_before("/*", new MyLogger)
 app.use("/", new HelloHandler)
 app.listen("localhost", 3000)
 ~~~
@@ -474,8 +502,9 @@ By using the `MyLogger` handler to the route `/*` we ensure that every requests
 (even 404 ones) pass through the middleware handler.
 This handler just prints “Request Logged!” when a request is received.
 
-The order of middleware loading is important: middleware functions that are loaded first are also executed first.
-In the above example, `MyLogger` will be executed before `HelloHandler`.
+Be default, the order of middleware execution is that are loaded first are also executed first.
+To ensure our middleware `MyLogger` will be executed before all the other, we add it
+with the `use_before` method.
 
 ### Ultra cool, more advanced logger example
 
@@ -505,7 +534,7 @@ class LogHandler
        redef fun all(req, res) do
                var timer = req.timer
                if timer != null then
-                       print "{req.method} {req.uri} {res.color_status} ({timer.total})"
+                       print "{req.method} {req.uri} {res.color_status} ({timer.total}s)"
                else
                        print "{req.method} {req.uri} {res.color_status}"
                end
@@ -519,9 +548,9 @@ class HelloHandler
 end
 
 var app = new App
-app.use("/*", new RequestTimeHandler)
+app.use_before("/*", new RequestTimeHandler)
 app.use("/", new HelloHandler)
-app.use("/*", new LogHandler)
+app.use_after("/*", new LogHandler)
 app.listen("localhost", 3000)
 ~~~
 
@@ -530,9 +559,15 @@ Doing so we can access our data from all handlers that import our module, direct
 from the `req` parameter.
 
 We use the new middleware called `RequestTimeHandler` to initialize the request timer.
+Because of the `use_before` method, the `RequestTimeHandler` middleware will be executed
+before all the others.
+
+We then let the `HelloHandler` produce the response.
 
 Finally, our `LogHandler` will display a bunch of data and use the request `timer`
 to display the time it took to process the request.
+Because of the `use_after` method, the `LogHandler` middleware will be executed after
+all the others.
 
 The app now uses the `RequestTimeHandler` middleware for every requests received
 by the Popcorn app.
@@ -808,8 +843,13 @@ to your angular controller:
 import popcorn
 
 var app = new App
-app.use("/*", new StaticHandler("my-ng-app/"))
+app.use("/*", new StaticHandler("my-ng-app/", "index.html"))
 app.listen("localhost", 3000)
 ~~~
 
+Because the StaticHandler will not find the angular routes as static files,
+you must specify the path to the default angular controller.
+In this example, the StaticHandler will redirect any unknown requests to the `index.html`
+angular controller.
+
 See the examples for a more detailed use case working with a JSON API.
index ba7f4f2..9212a8e 100644 (file)
@@ -40,5 +40,5 @@ end
 
 var app = new App
 app.use("/counter", new CounterAPI)
-app.use("/*", new StaticHandler("www/"))
+app.use("/*", new StaticHandler("www/", "index.html"))
 app.listen("localhost", 3000)
index b97d10c..4ca9120 100644 (file)
@@ -34,7 +34,7 @@ class LogHandler
        redef fun all(req, res) do
                var timer = req.timer
                if timer != null then
-                       print "{req.method} {req.uri} {res.color_status} ({timer.total})"
+                       print "{req.method} {req.uri} {res.color_status} ({timer.total}s)"
                else
                        print "{req.method} {req.uri} {res.color_status}"
                end
@@ -48,7 +48,7 @@ class HelloHandler
 end
 
 var app = new App
-app.use("/*", new RequestTimeHandler)
+app.use_before("/*", new RequestTimeHandler)
 app.use("/", new HelloHandler)
-app.use("/*", new LogHandler)
+app.use_after("/*", new LogHandler)
 app.listen("localhost", 3000)
index 98be552..4052169 100644 (file)
@@ -30,6 +30,6 @@ end
 
 
 var app = new App
-app.use("/*", new LogHandler)
+app.use_before("/*", new LogHandler)
 app.use("/", new HelloHandler)
 app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/static_files/example_static_default.nit b/lib/popcorn/examples/static_files/example_static_default.nit
new file mode 100644 (file)
index 0000000..6aa5b34
--- /dev/null
@@ -0,0 +1,21 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# 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.
+
+import popcorn
+
+var app = new App
+app.use("/", new StaticHandler("public/", "default.html"))
+app.listen("localhost", 3000)
diff --git a/lib/popcorn/examples/static_files/public/default.html b/lib/popcorn/examples/static_files/public/default.html
new file mode 100644 (file)
index 0000000..2abb789
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Default Page</h1>
+       </body>
+</html>
index 2c85e15..5d84961 100644 (file)
@@ -235,10 +235,16 @@ class StaticHandler
        # Static files directory to serve.
        var static_dir: String
 
+       # Default file to serve if nothing matches the request.
+       #
+       # `null` for no default file.
+       var default_file: nullable String
+
        # Internal file server used to lookup and render files.
        var file_server: FileServer is lazy do
                var srv = new FileServer(static_dir)
                srv.show_directory_listing = false
+               srv.default_file = default_file
                return srv
        end
 
@@ -307,28 +313,71 @@ class Router
        # List of handlers to match with requests.
        private var handlers = new Map[AppRoute, Handler]
 
+       # List of handlers to match before every other.
+       private var pre_handlers = new Map[AppRoute, Handler]
+
+       # List of handlers to match after every other.
+       private var post_handlers = new Map[AppRoute, Handler]
+
        # Register a `handler` for a route `path`.
        #
        # Route paths are matched in registration order.
        fun use(path: String, handler: Handler) do
-               var route
-               if handler isa Router or handler isa StaticHandler then
-                       route = new AppGlobRoute(path)
-               else if path.has_suffix("*") then
-                       route = new AppGlobRoute(path)
-               else
-                       route = new AppParamRoute(path)
-               end
+               var route = build_route(handler, path)
                handlers[route] = handler
        end
 
+       # Register a pre-handler for a route `path`.
+       #
+       # Prehandlers are matched before every other handlers in registrastion order.
+       fun use_before(path: String, handler: Handler) do
+               var route = build_route(handler, path)
+               pre_handlers[route] = handler
+       end
+
+       # Register a post-handler for a route `path`.
+       #
+       # Posthandlers are matched after every other handlers in registrastion order.
+       fun use_after(path: String, handler: Handler) do
+               var route = build_route(handler, path)
+               post_handlers[route] = handler
+       end
+
        redef fun handle(route, uri, req, res) do
                if not route.match(uri) then return
+               handle_pre(route, uri, req, res)
+               handle_in(route, uri, req, res)
+               handle_post(route, uri, req, res)
+       end
+
+       private fun handle_pre(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+               for hroute, handler in pre_handlers do
+                       handler.handle(hroute, route.uri_root(uri), req, res)
+               end
+       end
+
+       private fun handle_in(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
                for hroute, handler in handlers do
                        handler.handle(hroute, route.uri_root(uri), req, res)
                        if res.sent then break
                end
        end
+
+       private fun handle_post(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do
+               for hroute, handler in post_handlers do
+                       handler.handle(hroute, route.uri_root(uri), req, res)
+               end
+       end
+
+       private fun build_route(handler: Handler, path: String): AppRoute do
+               if handler isa Router or handler isa StaticHandler then
+                       return new AppGlobRoute(path)
+               else if path.has_suffix("*") then
+                       return new AppGlobRoute(path)
+               else
+                       return new AppParamRoute(path)
+               end
+       end
 end
 
 # Popcorn application.
index 38e4bd9..518d9dc 100644 (file)
@@ -48,7 +48,7 @@ class ConsoleLog
        redef fun all(req, res) do
                var clock = req.clock
                if clock != null then
-                       print "{req.method} {req.uri} {status(res)} ({clock.total})"
+                       print "{req.method} {req.uri} {status(res)} ({clock.total}s)"
                else
                        print "{req.method} {req.uri} {status(res)}"
                end
index 887c122..64e4459 100644 (file)
@@ -48,12 +48,19 @@ redef class App
        redef fun answer(req, uri) do
                uri = uri.simplify_path
                var res = new HttpResponse(404)
+               for route, handler in pre_handlers do
+                       handler.handle(route, uri, req, res)
+               end
                for route, handler in handlers do
                        handler.handle(route, uri, req, res)
+                       if res.sent then break
                end
                if not res.sent then
                        res.send(error_tpl(res.status_code, res.status_message), 404)
                end
+               for route, handler in post_handlers do
+                       handler.handle(route, uri, req, res)
+               end
                res.session = req.session
                return res
        end
index c07813f..bb94961 100644 (file)
@@ -4,4 +4,19 @@
 [Client] curl -s localhost:*****/counter -X POST
 {"label":"Visitors","value":1}
 [Client] curl -s localhost:*****/counter
-{"label":"Visitors","value":1}
\ No newline at end of file
+{"label":"Visitors","value":1}
+[Client] curl -s localhost:*****/not_found
+<!DOCTYPE html>
+<html lang='en' ng-app='ng-example'>
+       <head>
+               <base href='/'>
+               <title>ng-example</title>
+       </head>
+       <body>
+               <div ng-view></div>
+
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js'></script>
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular-route.js'></script>
+               <script src='/javascripts/ng-example.js'></script>
+       </body>
+</html>
diff --git a/lib/popcorn/tests/res/test_example_static_default.res b/lib/popcorn/tests/res/test_example_static_default.res
new file mode 100644 (file)
index 0000000..59392f6
--- /dev/null
@@ -0,0 +1,88 @@
+
+[Client] curl -s localhost:*****/css/style.css
+body {
+       color: blue;
+       padding: 20px;
+}
+
+[Client] curl -s localhost:*****/js/app.js
+alert("Hello World!");
+
+[Client] curl -s localhost:*****/hello.html
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Hello Popcorn!</h1>
+
+               <img src="/images/trollface.jpg" alt="maybe it's a kitten?" />
+
+               <script src="/js/app.js"></script>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Default Page</h1>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/css/not_found.nit
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Default Page</h1>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/static/css/not_found.nit
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Default Page</h1>
+       </body>
+</html>
+
+[Client] curl -s localhost:*****/not_found.nit
+<!DOCTYPE html>
+<html>
+       <head>
+               <meta charset="UTF-8">
+
+               <title>Some Popcorn love</title>
+
+               <link rel="stylesheet" type="text/css" href="/css/style.css">
+       </head>
+       <body>
+               <h1>Default Page</h1>
+       </body>
+</html>
index 1b14f14..3a6692f 100644 (file)
@@ -28,13 +28,6 @@ alert("Hello World!");
 </html>
 
 [Client] curl -s localhost:*****/
-Warning: Headers already sent!
-<!DOCTYPE html>
-<html>
-       <body>
-               <h1>Another Index</h1>
-       </body>
-</html>
 <!DOCTYPE html>
 <html>
        <body>
index 41193d2..29b3fc2 100644 (file)
@@ -28,9 +28,9 @@ class TestClient
 end
 
 var app = new App
-app.use("/*", new RequestTimeHandler)
+app.use_before("/*", new RequestTimeHandler)
 app.use("/", new HelloHandler)
-app.use("/*", new LogHandler)
+app.use_after("/*", new LogHandler)
 
 var host = test_host
 var port = test_port
index d1ab509..821aa32 100644 (file)
@@ -24,13 +24,14 @@ class TestClient
                system "curl -s {host}:{port}/counter"
                system "curl -s {host}:{port}/counter -X POST"
                system "curl -s {host}:{port}/counter"
+               system "curl -s {host}:{port}/not_found" # handled by angular controller
                return null
        end
 end
 
 var app = new App
 app.use("/counter", new CounterAPI)
-app.use("/*", new StaticHandler("www/"))
+app.use("/*", new StaticHandler("../examples/angular/www/", "index.html"))
 
 var host = test_host
 var port = test_port
index 50f6af9..e8aab41 100644 (file)
@@ -28,7 +28,7 @@ class TestClient
 end
 
 var app = new App
-app.use("/*", new LogHandler)
+app.use_before("/*", new LogHandler)
 app.use("/", new HelloHandler)
 
 var host = test_host
diff --git a/lib/popcorn/tests/test_example_static_default.nit b/lib/popcorn/tests/test_example_static_default.nit
new file mode 100644 (file)
index 0000000..ced04e2
--- /dev/null
@@ -0,0 +1,52 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>
+#
+# 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.
+
+import base_tests
+import example_static_default
+
+class TestClient
+       super ClientThread
+
+       redef fun main do
+               system "curl -s {host}:{port}/css/style.css"
+               system "curl -s {host}:{port}/js/app.js"
+               system "curl -s {host}:{port}/hello.html"
+               system "curl -s {host}:{port}/"
+
+               system "curl -s {host}:{port}/css/not_found.nit"
+               system "curl -s {host}:{port}/static/css/not_found.nit"
+               system "curl -s {host}:{port}/not_found.nit"
+
+               return null
+       end
+end
+
+var app = new App
+app.use("/", new StaticHandler("../examples/static_files/public/", "default.html"))
+
+var host = test_host
+var port = test_port
+
+var server = new AppThread(host, port, app)
+server.start
+0.1.sleep
+
+var client = new TestClient(host, port)
+client.start
+client.join
+0.1.sleep
+
+exit 0
index 4d22201..d32c908 100644 (file)
@@ -14,6 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# A native wrapper ove the postgres c api
 module native_postgres is pkgconfig("libpq")
 
 in "C header" `{
@@ -30,7 +31,9 @@ extern class ExecStatusType `{int`}
   new nonfatal_error  `{ return PGRES_NONFATAL_ERROR; `}
   new fatal_error     `{ return PGRES_FATAL_ERROR; `}
 
-  fun is_ok: Bool `{return self == PGRES_TUPLES_OK || self == PGRES_COMMAND_OK; `}
+  fun is_ok: Bool `{
+    return !(self == PGRES_BAD_RESPONSE || self == PGRES_NONFATAL_ERROR || self == PGRES_FATAL_ERROR);
+  `}
 
   redef fun to_s import NativeString.to_s `{
     char * err = PQresStatus(self);
@@ -46,7 +49,7 @@ extern class ConnStatusType `{int`}
   fun is_ok: Bool `{return self == CONNECTION_OK; `}
 end
 
-extern class PGResult `{PGresult *`}
+extern class NativePGResult `{PGresult *`}
   # Frees the memory block associated with the result
   fun clear `{PQclear(self); `}
 
@@ -83,27 +86,27 @@ end
 extern class NativePostgres `{PGconn *`}
 
   # Connect to a new database using the conninfo string as a parameter
-  new connectdb(conninfo: String) import String.to_cstring `{
+  new connectdb(conninfo: Text) import Text.to_cstring `{
     PGconn * self = NULL;
-    self = PQconnectdb(String_to_cstring(conninfo));
+    self = PQconnectdb(Text_to_cstring(conninfo));
     return self;
   `}
 
   # Submits a query to the server and waits for the result returns the ExecStatustype of the query
-  fun exec(query: String): PGResult import String.to_cstring `{
-    PGresult *res = PQexec(self, String_to_cstring(query));
+  fun exec(query: Text): NativePGResult import Text.to_cstring `{
+    PGresult *res = PQexec(self, Text_to_cstring(query));
     return res;
   `}
 
   # Prepares a statement with the given parameters
-  fun prepare(stmt: String, query: String, nParams: Int):PGResult import String.to_cstring `{
+  fun prepare(stmt: String, query: String, nParams: Int): NativePGResult import String.to_cstring `{
     const char * stmtName = String_to_cstring(stmt);
     const char * queryStr = String_to_cstring(query);
     PGresult * res = PQprepare(self, stmtName, queryStr, nParams, NULL);
     return res;
   `}
 
-  fun exec_prepared(stmt: String, nParams: Int, values: Array[String], pLengths: Array[Int], pFormats: Array[Int], resultFormat: Int):PGResult import String.to_cstring, Array[String].[], Array[Int].[] `{
+  fun exec_prepared(stmt: String, nParams: Int, values: Array[String], pLengths: Array[Int], pFormats: Array[Int], resultFormat: Int): NativePGResult import String.to_cstring, Array[String].[], Array[Int].[] `{
     const char * stmtName = String_to_cstring(stmt);
     const char * paramValues[nParams];
     int paramLengths[nParams];
diff --git a/lib/postgresql/postgres.nit b/lib/postgresql/postgres.nit
new file mode 100644 (file)
index 0000000..22e498f
--- /dev/null
@@ -0,0 +1,144 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Guilherme Mansur<guilhermerpmansur@gmail.com>
+#
+# 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.
+
+# Services to manipulate a Postgres database
+#
+# For more information, refer to the documentation of http://www.postgresql.org/docs/manuals/
+#
+# ### Usage example
+#
+# ~~~
+# class Animal
+#   var name: String
+#   var kind: String
+#   var age: Int
+# end
+#
+# var animals = new Array[Animal]
+# var dog = new Animal("Lassy", "dog", 10)
+# var cat = new Animal("Garfield", "cat", 3)
+# var turtle = new Animal("George", "turtle", 123)
+#
+# animals.add(dog)
+# animals.add(cat)
+# animals.add(turtle)
+#
+# var db = new Postgres.open("dbname=postgres")
+#
+# assert db_is_open: not db.is_closed
+# assert create_table: db.create_table("IF NOT EXISTS animals (aname TEXT PRIMARY KEY, kind TEXT NOT NULL, age INT NOT NULL)") else print db.error
+#
+# for animal in animals do
+#   assert insert: db.insert("INTO animals VALUES('{animal.name}', '{animal.kind}', {animal.age})") else print db.error
+# end
+#
+# var result = db.raw_execute("SELECT * FROM animals")
+# assert  result.is_ok
+# assert drop_table: db.execute("DROP TABLE animals")
+# db.finish
+# assert db_is_closed: db.is_closed
+# ~~~
+module postgres
+
+private import native_postgres
+
+# A connection to a Postgres database
+class Postgres
+  private var native_connection: NativePostgres
+
+  var is_closed = true
+
+  # Open the connnection with the database using the `conninfo`
+  init open(conninfo: Text)
+  do
+    init(new NativePostgres.connectdb(conninfo))
+    if native_connection.status.is_ok then is_closed = false
+  end
+
+  # Close this connection with the database
+  fun finish
+  do
+    if is_closed then return
+
+    is_closed = true
+
+    native_connection.finish
+  end
+
+  fun prepare(stmt_name:String, query:String, num_params: Int):PGResult do return new PGResult(native_connection.prepare(stmt_name, query, num_params))
+
+  fun exec_prepared(stmt_name: String, num_params: Int, values: Array[String], param_lengths: Array[Int], param_formats: Array[Int], result_format: Int):PGResult do
+    return new PGResult(native_connection.exec_prepared(stmt_name, num_params, values, param_lengths, param_formats, result_format))
+  end
+
+  # Executes a `query` and returns the raw `PGResult`
+  fun raw_execute(query: Text): PGResult do return new PGResult(native_connection.exec(query))
+
+  # Execute the `sql` statement and returns `true` on success
+  fun execute(query: Text): Bool do return native_connection.exec(query).status.is_ok
+
+  # Create a table on the DB with a statement beginning with "CREATE TABLE ", followed by `rest`
+  #
+  # This method does not escape special characters.
+  fun create_table(rest: Text): Bool do return execute("CREATE TABLE " + rest)
+
+  # Insert in the DB with a statement beginning with "INSERT ", followed by `rest`
+  #
+  # This method does not escape special characters.
+  fun insert(rest: Text): Bool do return execute("INSERT " + rest)
+
+  # Replace in the DB with a statement beginning with "REPLACE", followed by `rest`
+  #
+  # This method does not escape special characters.
+  fun replace(rest: Text): Bool do return execute("REPLACE " + rest)
+
+  # The latest error message on the connection an empty string if none
+  fun error: String do return native_connection.error
+
+  # The status of this connection
+  fun is_valid: Bool do return native_connection.status.is_ok
+
+  # Resets the connection to the database
+  fun reset do native_connection.reset
+end
+
+# The result of a given query
+class PGResult
+  private var pg_result: NativePGResult
+
+  fun clear do pg_result.clear
+
+  # Returns the number of rows in the query result
+  fun ntuples:Int do return pg_result.ntuples
+
+  # Returns the number of columns in each row of the query result
+  fun nfields:Int do return pg_result.nfields
+
+  # Returns the ExecStatusType of a result
+  fun is_ok:Bool do return pg_result.status.is_ok
+
+  # Returns the field name of a given `column_number`
+  fun fname(column_number:Int):String do return pg_result.fname(column_number)
+
+  # Returns the column number associated with the `column_name`
+  fun fnumber(column_name:String):Int do return pg_result.fnumber(column_name)
+
+  # Returns a single field value of one row of the result at `row_number`, `column_number`
+  fun value(row_number:Int, column_number:Int):String  do return pg_result.value(row_number, column_number)
+
+  # Tests wether a field specified by the `row_number` and `column_number` is null.
+  fun is_null(row_number:Int, column_number: Int): Bool do return pg_result.is_null(row_number, column_number)
+end
diff --git a/lib/readline.ini b/lib/readline.ini
new file mode 100644 (file)
index 0000000..c7e2ee9
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=readline
+tags=lib
+maintainer=Frédéric Vachon <fredvac@gmail.com>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/readline.nit
+git=https://github.com/nitlang/nit.git
+git.directory=lib/readline.nit
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
diff --git a/lib/readline.nit b/lib/readline.nit
new file mode 100644 (file)
index 0000000..783e4e4
--- /dev/null
@@ -0,0 +1,58 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2016 Frédéric Vachon <fredvac@gmail.com>
+#
+# 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.
+
+# GNU readline library wrapper
+module readline is ldflags "-lreadline"
+
+in "C" `{
+       #include <readline/readline.h>
+       #include <readline/history.h>
+`}
+
+private fun native_readline(prompt: NativeString): NativeString `{
+       return readline(prompt);
+`}
+
+private fun native_add_history(data: NativeString) `{
+       if (data == NULL) return;
+       add_history(data);
+`}
+
+# Set emacs keybindings mode
+fun set_vi_mode `{ rl_editing_mode = 0; `}
+
+# Set emacs keybindings mode
+fun set_emacs_mode `{ rl_editing_mode = 1; `}
+
+# Use the GNU Library readline function
+# Returns `null` if EOF is read
+# If `with_history` is true, it will save all commands in the history except
+# empty strings and white characters strings
+fun readline(message: String, with_history: nullable Bool): nullable String do
+       var line = native_readline(message.to_cstring)
+       if line.address_is_null then return null
+
+       var nit_str = line.to_s
+
+       if with_history != null and with_history then
+               if nit_str.trim != "" then native_add_history(line)
+       end
+
+       return nit_str
+end
+
+# Adds the data String to the history no matter what it contains
+fun add_history(data: String) do native_add_history data.to_cstring
index 8cfa4da..243b5d8 100644 (file)
@@ -73,13 +73,16 @@ extern class Timespec `{struct timespec*`}
                clock_gettime(CLOCK_MONOTONIC, self);
        `}
 
-       # Substract a Timespec from `self`.
-       fun - ( o : Timespec ) : Timespec
+       # Subtract `other` from `self`
+       fun -(other: Timespec): Timespec
        do
-               var s = sec - o.sec
-               var ns = nanosec - o.nanosec
-               if ns > nanosec then s += 1
-               return new Timespec( s, ns )
+               var s = sec - other.sec
+               var ns = nanosec - other.nanosec
+               if ns < 0 then
+                       s -= 1
+                       ns += 1000000000
+               end
+               return new Timespec(s, ns)
        end
 
        # Number of whole seconds of elapsed time.
@@ -117,15 +120,33 @@ extern class Timespec `{struct timespec*`}
 end
 
 # Keeps track of real time
+#
+# ~~~
+# var clock = new Clock
+#
+# # sleeping at least 1s
+# 1.0.sleep
+# assert clock.total >= 1.0
+# assert clock.lapse >= 1.0
+#
+# # sleeping at least 5ms
+# 0.005.sleep
+# assert clock.total >= 1.005
+# assert clock.lapse >= 0.005
+# ~~~
 class Clock
-       # Time at instanciation
+       super FinalizableOnce
+
+       # TODO use less mallocs
+
+       # Time at creation
        protected var time_at_beginning = new Timespec.monotonic_now
 
        # Time at last time a lapse method was called
        protected var time_at_last_lapse = new Timespec.monotonic_now
 
        # Smallest time frame reported by clock
-       fun resolution : Timespec `{
+       fun resolution: Timespec `{
                struct timespec* tv = malloc( sizeof(struct timespec) );
 #ifdef __MACH__
                clock_serv_t cclock;
@@ -142,18 +163,43 @@ class Clock
                return tv;
        `}
 
-       # Return timelapse since instanciation of this instance
-       fun total : Timespec
+       # Seconds since the creation of this instance
+       fun total: Float
        do
-               return new Timespec.monotonic_now - time_at_beginning
+               var now = new Timespec.monotonic_now
+               var diff = now - time_at_beginning
+               var r = diff.to_f
+               diff.free
+               now.free
+               return r
        end
 
-       # Return timelapse since last call to lapse
-       fun lapse : Timespec
+       # Seconds since the last call to `lapse`
+       fun lapse: Float
        do
                var nt = new Timespec.monotonic_now
                var dt = nt - time_at_last_lapse
+               var r = dt.to_f
+               dt.free
+               time_at_last_lapse.free
                time_at_last_lapse = nt
-               return dt
+               return r
+       end
+
+       # Seconds since the last call to `lapse`, without resetting the lapse counter
+       fun peek_lapse: Float
+       do
+               var nt = new Timespec.monotonic_now
+               var dt = nt - time_at_last_lapse
+               var r = dt.to_f
+               nt.free
+               dt.free
+               return r
+       end
+
+       redef fun finalize_once
+       do
+               time_at_beginning.free
+               time_at_last_lapse.free
        end
 end
index 4541428..3554d22 100644 (file)
@@ -55,7 +55,7 @@ end
 # then using a reference.
 class SerializerCache
        # Map of already serialized objects to the reference id
-       private var sent: Map[Serializable, Int] = new StrictHashMap[Serializable, Int]
+       protected var sent: Map[Serializable, Int] = new StrictHashMap[Serializable, Int]
 
        # Is `object` known?
        fun has_object(object: Serializable): Bool do return sent.keys.has(object)
@@ -88,7 +88,7 @@ end
 # Used by `Deserializer` to find already deserialized objects by their reference.
 class DeserializerCache
        # Map of references to already deserialized objects.
-       private var received: Map[Int, Object] = new StrictHashMap[Int, Object]
+       protected var received: Map[Int, Object] = new StrictHashMap[Int, Object]
 
        # Is there an object associated to `id`?
        fun has_id(id: Int): Bool do return received.keys.has(id)
index 2927a24..a316782 100644 (file)
@@ -386,7 +386,7 @@ Put primitive attributes in a box instead of an union.
 ### `--no-shortcut-equal`
 Always call == in a polymorphic way.
 
-### `--no-tag-primitive`
+### `--no-tag-primitives`
 Use only boxes for primitive types.
 
 The separate compiler uses tagged values to encode common primitive types like Int, Bool and Char.
@@ -410,9 +410,6 @@ Use an indirection when calling.
 
 Just add the trampolines of `--substitute-monomorph` without doing any additionnal optimizations.
 
-### `--no-tag-primitives`
-Use only boxes for primitive types.
-
 ## INTERNAL OPTIONS
 
 These options can be used to control the fine behavior of the tool.
index cd5472a..0b42d2f 100644 (file)
@@ -66,8 +66,8 @@ Also generate private API.
 
 ## CUSTOMIZATION
 
-### `--sharedir`
-Directory containing nitdoc assets.
+### `--share-dir`
+Directory containing tools assets.
 
 By default `$NIT_DIR/share/nitdoc/` is used.
 
index 0576201..6f09402 100644 (file)
@@ -268,10 +268,13 @@ Output name (default is 'nitunit.xml').
 `nitunit` produces a XML file compatible with JUnit.
 
 ### `--dir`
-Working directory (default is '.nitunit').
+Working directory (default is 'nitunit.out').
 
 In order to execute the tests, nit files are generated then compiled and executed in the giver working directory.
 
+In case of success, the directory is removed.
+In case of failure, it is kept as is so files can be investigated.
+
 ### `--nitc`
 nitc compiler to use.
 
@@ -312,6 +315,29 @@ Only display the skeleton, do not write any file.
 
 Indicate the specific Nit compiler executable to use. See `--nitc`.
 
+### `NIT_TESTING`
+
+The environment variable `NIT_TESTING` is set to `true` during the execution of program tests.
+Some libraries of programs can use it to produce specific reproducible results; or just to exit their executions.
+
+Unit-tests may unset this environment variable to retrieve the original behavior of such piece of software.
+
+### `SRAND`
+
+In order to maximize reproducibility, `SRAND` is set to 0.
+This make the pseudo-random generator no random at all.
+See `Sys::srand` for details.
+
+To retrieve the randomness, unit-tests may unset this environment variable then call `srand`.
+
+### `NIT_TESTING_ID`
+
+Parallel executions can cause some race collisions on named resources (e.g. DB table names).
+To solve this issue, `NIT_TESTING_ID` is initialized with a distinct integer identifier that can be used to give unique names to resources.
+
+Note: `rand` is not a recommended way to get a distinct identifier because its randomness is disabled by default. See `SRAND`.
+
+
 # SEE ALSO
 
 The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/nitweb/directives/entity/card.html b/share/nitweb/directives/entity/card.html
new file mode 100644 (file)
index 0000000..aa9ccb3
--- /dev/null
@@ -0,0 +1,11 @@
+<div class='card'>
+       <div class='card-left text-center'>
+               <entity-tag mentity='mentity' />
+       </div>
+       <div class='card-body'>
+               <h5 class='card-heading'>
+                       <entity-signature mentity='mentity'/>
+               </h5>
+               <span class='synopsis' ng-bind-html='mentity.mdoc.html_synopsis' />
+       </div>
+</div>
diff --git a/share/nitweb/directives/entity/doc.html b/share/nitweb/directives/entity/doc.html
new file mode 100644 (file)
index 0000000..9d0ffb0
--- /dev/null
@@ -0,0 +1,5 @@
+<div class='card' ng-if='mentity.mdoc'>
+       <div class='card-body'>
+               <div ng-bind-html='mentity.mdoc.html_documentation'></div>
+       </div>
+</div>
diff --git a/share/nitweb/directives/entity/link.html b/share/nitweb/directives/entity/link.html
new file mode 100644 (file)
index 0000000..740ea8b
--- /dev/null
@@ -0,0 +1,3 @@
+<span>
+       <a ng-href='{{mentity.web_url}}'>{{mentity.name}}</a>
+</span>
diff --git a/share/nitweb/directives/entity/list.html b/share/nitweb/directives/entity/list.html
new file mode 100644 (file)
index 0000000..cc026a4
--- /dev/null
@@ -0,0 +1,19 @@
+<div class='entity-list'
+       ng-if='(listEntities | filter:listObjectFilter).length > 0'>
+       <h3 id={{listId}}>
+               <span>{{listTitle}}</span>
+               <button class='btn btn-link btn-xs pull-right btn-filter' ng-click='toggleFilters()'>
+                       <span class='glyphicon glyphicon-filter text-muted' />
+               </button>
+       </h3>
+               <div ng-if='showFilters'>
+                       <ui-filter-form
+                               search-filter='listObjectFilter'
+                               visibility-filter='visibilityFilter'>
+               </div>
+               <div class='card-list'>
+                       <entity-card mentity='mentity'
+                               ng-repeat='mentity in listEntities | filter:listObjectFilter | visibility:visibilityFilter' />
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/directives/entity/location.html b/share/nitweb/directives/entity/location.html
new file mode 100644 (file)
index 0000000..7e817c6
--- /dev/null
@@ -0,0 +1,5 @@
+<span ng-if='mentity.location'>
+       <a ng-href="{{mentity.web_url}}">{{mentity.location.file}}
+               <span ng-if='mentity.location.line_start'>:{{mentity.location.line_start}}</span>
+       </a>
+</span>
diff --git a/share/nitweb/directives/entity/signature.html b/share/nitweb/directives/entity/signature.html
new file mode 100644 (file)
index 0000000..f3f2dc6
--- /dev/null
@@ -0,0 +1,51 @@
+<span class='signature'>
+       <span ng-repeat='modifier in mentity.modifiers'>
+               <span ng-if='modifier != "public"' class='modifier'>{{modifier}}</span>
+       </span>
+       <span class='name'>
+               <entity-link mentity='mentity' />
+       </span>
+       <span ng-if='mentity.mparameters'>
+               <span ng-if='mentity.mparameters.length > 0'>
+                       <span>[</span>
+                       <span ng-repeat='mparam in mentity.mparameters'>
+                               <span>
+                                       <span>{{mparam.name}}</span>
+                                       <span>: </span>
+                                       <entity-signature mentity='mparam.mtype' />
+                               </span>
+                               <span ng-if='$middle'>, </span>
+                       </span>
+                       <span>]</span>
+               </span>
+       </span>
+       <span ng-if='mentity.msignature'>
+               <span ng-if='mentity.msignature.arity > 0'>
+                       <span>(</span>
+                       <span ng-repeat='mparam in mentity.msignature.mparams'>
+                               <span>
+                                       <span>{{mparam.name}}</span>
+                                       <span ng-if='mentity.is_intro !== false'>
+                                               <span>: </span>
+                                               <entity-signature mentity='mparam.mtype' />
+                                       </span>
+                                       <span ng-if='mparam.is_vararg'>...</span>
+                               </span>
+                               <span ng-if='!first && !$last'>, </span>
+                       </span>
+                       <span>)</span>
+               </span>
+               <span ng-if='mentity.is_intro !== false && mentity.msignature.return_mtype'>
+                       <span>: </span>
+                       <entity-signature mentity='mentity.msignature.return_mtype' />
+               </span>
+       </span>
+       <span ng-if='mentity.is_intro !== false && mentity.static_mtype'>
+               <span>: </span>
+               <entity-signature mentity='mentity.static_mtype' />
+       </span>
+       <span ng-if='mentity.bound'>
+               <span>: </span>
+               <entity-signature mentity='mentity.bound' />
+       </span>
+</span>
diff --git a/share/nitweb/directives/entity/tag.html b/share/nitweb/directives/entity/tag.html
new file mode 100644 (file)
index 0000000..7c69f1e
--- /dev/null
@@ -0,0 +1,5 @@
+<span class="glyphicon glyphicon-tag" ng-class='{
+               "text-success": mentity.visibility == "public",
+               "text-warning": mentity.visibility == "protected",
+               "text-danger": mentity.visibility == "private",
+}' />
diff --git a/share/nitweb/directives/group-block.html b/share/nitweb/directives/group-block.html
new file mode 100644 (file)
index 0000000..ad7f00e
--- /dev/null
@@ -0,0 +1,21 @@
+<div class='media'>
+       <div class='media-left text-center' ng-if='mentity.visibility' ng-class='{
+                       "text-success": mentity.visibility == "public",
+                       "text-warning": mentity.visibility == "protected",
+                       "text-danger": mentity.visibility == "private",
+       }'>
+               <span class="glyphicon glyphicon-tag"></span>
+       </div>
+       <div class='media-body'>
+               <h5 class='media-heading'>
+                       <entity-signature mentity='mentity'/>
+               </h5>
+               <span class='synopsis'>{{mentity.mdoc.synopsis}}</span>
+               <div ng-if='recursive && mentity.mgroups'>
+                       <group-block mentity-id='mgroup' ng-repeat='mgroup in mentity.mgroups' />
+               </div>
+               <div ng-if='recursive && mentity.mmodules'>
+                       <module-block mentity-id='mmodule' ng-repeat='mmodule in mentity.mmodules' />
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/directives/ui-filter-button-vis.html b/share/nitweb/directives/ui-filter-button-vis.html
new file mode 100644 (file)
index 0000000..8d4f8d4
--- /dev/null
@@ -0,0 +1,6 @@
+<button
+       class='btn btn-link btn-xs'
+       ng-click='toggle()'>
+       <span ng-if='property' ng-class='classesOn'/>
+       <span ng-if='!property' ng-class='classesOff'/>
+</button>
diff --git a/share/nitweb/directives/ui-filter-field.html b/share/nitweb/directives/ui-filter-field.html
new file mode 100644 (file)
index 0000000..05c51e4
--- /dev/null
@@ -0,0 +1,4 @@
+<div class='form-group has-icon'>
+       <input type='text' class='form-control' ng-model='property' placeholder='Filter...'>
+       <span class='glyphicon glyphicon-search form-control-icon text-muted'></span>
+</div>
diff --git a/share/nitweb/directives/ui-filter-form.html b/share/nitweb/directives/ui-filter-form.html
new file mode 100644 (file)
index 0000000..9782af8
--- /dev/null
@@ -0,0 +1,6 @@
+<form class='form-inline'>
+       <ui-filter-field property='searchFilter.$' />
+       <div class='pull-right'>
+               <ui-filter-group-vis property='visibilityFilter' />
+       </div>
+</form>
diff --git a/share/nitweb/directives/ui-filter-group-vis.html b/share/nitweb/directives/ui-filter-group-vis.html
new file mode 100644 (file)
index 0000000..7fcd475
--- /dev/null
@@ -0,0 +1,14 @@
+<div class='form-group'>
+       <ui-filter-button-vis property='property.public'
+                       classes-on='"glyphicon glyphicon-eye-open text-success"'
+                       classes-off='"glyphicon glyphicon-eye-close text-success"'
+                       title='Toggle public' />
+       <ui-filter-button-vis property='property.protected'
+                       classes-on='"glyphicon glyphicon-eye-open text-warning"'
+                       classes-off='"glyphicon glyphicon-eye-close text-warning"'
+                       title='Toggle protected' />
+       <ui-filter-button-vis property='property.private'
+                       classes-on='"glyphicon glyphicon-eye-open text-danger"'
+                       classes-off='"glyphicon glyphicon-eye-close text-danger"'
+                       title='Toggle private' />
+</div>
diff --git a/share/nitweb/index.html b/share/nitweb/index.html
new file mode 100644 (file)
index 0000000..f2a54c5
--- /dev/null
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang='en' ng-app='nitweb'>
+       <head>
+               <base href='/'>
+               <meta charset='utf-8'>
+               <meta http-equiv='X-UA-Compatible' content='IE=edge'>
+               <meta name='viewport' content='width=device-width, initial-scale=1'>
+               <title>ng-doc</title>
+
+               <link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'
+                       integrity='sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7'
+                       crossorigin='anonymous' rel='stylesheet'>
+
+               <link href='/stylesheets/nitweb_bootstrap.css' rel='stylesheet'>
+               <link href='/stylesheets/nitweb.css' rel='stylesheet'>
+       </head>
+       <body>
+               <nav class='navbar navbar-default navbar-fixed-top'>
+                       <div class='container-fluid'>
+                               <div class='col-xs-3 navbar-header'>
+                                       <a class='navbar-brand' ng-href='/'>Nitdoc</a>
+                               </div>
+                       </div>
+               </nav>
+               <div ng-view></div>
+
+               <script src='https://code.jquery.com/jquery-1.12.4.min.js'
+                       integrity='sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ='
+                       crossorigin='anonymous''></script>
+               <script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js'
+                       integrity='sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS'
+                       crossorigin='anonymous'></script>
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular.min.js'>
+               </script>
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular-route.js'>
+               </script>
+               <script src='https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.5/angular-sanitize.js'>
+               </script>
+
+               <script src='/javascripts/nitweb.js'></script>
+               <script src='/javascripts/model.js'></script>
+               <script src='/javascripts/entities.js'></script>
+               <script src='/javascripts/ui.js'></script>
+       </body>
+</html>
diff --git a/share/nitweb/javascripts/entities.js b/share/nitweb/javascripts/entities.js
new file mode 100644 (file)
index 0000000..255602c
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * 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.
+ */
+
+(function() {
+       angular
+               .module('entities', ['ui', 'model'])
+
+               .controller('EntityCtrl', ['Model', '$routeParams', '$scope', function(Model, $routeParams, $scope) {
+                       Model.loadEntity($routeParams.id,
+                               function(data) {
+                                       $scope.mentity = data;
+                               }, function(err) {
+                                       $scope.error = err;
+                               });
+               }])
+
+               .directive('entityLink', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               templateUrl: '/directives/entity/link.html'
+                       };
+               })
+
+               .directive('entityDoc', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               templateUrl: '/directives/entity/doc.html'
+                       };
+               })
+
+               .directive('entitySignature', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               templateUrl: '/directives/entity/signature.html'
+                       };
+               })
+
+               .directive('entityTag', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/entity/tag.html'
+                       };
+               })
+
+               .directive('entityLocation', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               templateUrl: '/directives/entity/location.html'
+                       };
+               })
+
+               .directive('entityCard', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       mentity: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/entity/card.html'
+                       };
+               })
+
+               .directive('entityList', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       listEntities: '=',
+                                       listTitle: '@',
+                                       listObjectFilter: '=',
+                               },
+                               templateUrl: '/directives/entity/list.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.showFilters = false;
+                                       if(!$scope.listObjectFilter) {
+                                               $scope.listObjectFilter = {};
+                                       }
+                                       if(!$scope.visibilityFilter) {
+                                               $scope.visibilityFilter = {
+                                                       public: true,
+                                                       protected: true,
+                                                       private: false
+                                               };
+                                       }
+                                       $scope.toggleFilters = function() {
+                                               $scope.showFilters = !$scope.showFilters;
+                                       };
+                               }
+                       };
+               })
+})();
diff --git a/share/nitweb/javascripts/model.js b/share/nitweb/javascripts/model.js
new file mode 100644 (file)
index 0000000..b5e8bbf
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * 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.
+ */
+
+(function() {
+       var apiUrl = '/api';
+
+       angular
+               .module('model', [])
+
+               .factory('Model', [ '$http', function($http) {
+                       return {
+                               loadEntity: function(id, cb, cbErr) {
+                                       $http.get(apiUrl + '/entity/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               }
+
+                       };
+               }])
+})();
diff --git a/share/nitweb/javascripts/nitweb.js b/share/nitweb/javascripts/nitweb.js
new file mode 100644 (file)
index 0000000..28291fb
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * 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.
+ */
+
+(function() {
+       angular.module('nitweb', ['ngRoute', 'ngSanitize', 'entities'])
+
+       .config(function($routeProvider, $locationProvider) {
+               $routeProvider
+                       .when('/', {
+                               templateUrl: 'views/index.html'
+                       })
+                       .when('/package/:id', {
+                               templateUrl: 'views/package.html',
+                               controller: 'EntityCtrl',
+                               controllerAs: 'entityCtrl'
+                       })
+                       .when('/group/:id', {
+                               templateUrl: 'views/group.html',
+                               controller: 'EntityCtrl',
+                               controllerAs: 'entityCtrl'
+                       })
+                       .when('/module/:id', {
+                               templateUrl: 'views/module.html',
+                               controller: 'EntityCtrl',
+                               controllerAs: 'entityCtrl'
+                       })
+                       .when('/class/:id', {
+                               templateUrl: 'views/class.html',
+                               controller: 'EntityCtrl',
+                               controllerAs: 'entityCtrl'
+                       })
+                       .when('/classdef/:id', {
+                               templateUrl: 'views/classdef.html',
+                               controller: 'EntityCtrl',
+                               controllerAs: 'entityCtrl'
+                       })
+                       .when('/property/:id', {
+                               templateUrl: 'views/property.html',
+                               controller: 'EntityCtrl',
+                               controllerAs: 'entityCtrl'
+                       })
+                       .when('/propdef/:id', {
+                               templateUrl: 'views/propdef.html',
+                               controller: 'EntityCtrl',
+                               controllerAs: 'entityCtrl'
+                       })
+                       .otherwise({
+                               redirectTo: '/'
+                       });
+               $locationProvider.html5Mode(true);
+       });
+})();
diff --git a/share/nitweb/javascripts/ui.js b/share/nitweb/javascripts/ui.js
new file mode 100644 (file)
index 0000000..aae0234
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * 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.
+ */
+
+(function() {
+       angular
+               .module('ui', [ 'model' ])
+
+               .directive('uiFilters', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       property: '=',
+                                       classesOn: '=',
+                                       classesOff: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/ui-filter-button-vis.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.toggle = function() {
+                                               $scope.property = !$scope.property;
+                                       }
+                               }
+                       };
+               })
+
+               .filter('visibility', function() {
+                       return function(input, visibilityFilter) {
+                               var res = [];
+                               input.forEach(function(entry) {
+                                       if(visibilityFilter.public == false && entry.visibility == "public") {
+                                               return;
+                                       }
+                                       if(visibilityFilter.protected == false && entry.visibility == "protected") {
+                                               return;
+                                       }
+                                       if(visibilityFilter.private == false && entry.visibility == "private") {
+                                               return;
+                                       }
+                                       res.push(entry);
+                               });
+                               return res;
+                       };
+               })
+
+               .directive('uiFilterForm', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       searchFilter: '=',
+                                       visibilityFilter: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/ui-filter-form.html'
+                       };
+               })
+
+               .directive('uiFilterField', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       property: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/ui-filter-field.html'
+                       };
+               })
+
+               .directive('uiFilterGroupVis', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       property: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/ui-filter-group-vis.html'
+                       };
+               })
+
+               .directive('uiFilterButtonVis', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       property: '=',
+                                       classesOn: '=',
+                                       classesOff: '='
+                               },
+                               replace: true,
+                               templateUrl: '/directives/ui-filter-button-vis.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.toggle = function() {
+                                               $scope.property = !$scope.property;
+                                       }
+                               }
+                       };
+               })
+})();
diff --git a/share/nitweb/stylesheets/nitweb.css b/share/nitweb/stylesheets/nitweb.css
new file mode 100644 (file)
index 0000000..eca9c48
--- /dev/null
@@ -0,0 +1,179 @@
+/* Body */
+
+body {
+       background: #f2f2f2;
+       margin-top: 70px;
+       margin-bottom: 70px;
+}
+
+h1, h2, h3, h4, h5, h6 {
+       color: #666;
+}
+
+a {
+       cursor: pointer;
+}
+
+.nitdoc h1, .nitdoc h2, .nitdoc h3, .nitdoc h4, .nitdoc h5, .nitdoc h6 {
+       color: #333;
+}
+
+.page-header {
+    margin-top: 0;
+    border: none;
+}
+
+/* cards */
+
+.card.active {
+       border: 1px solid #1E9431;
+}
+
+.card, .card-body { overflow: hidden; }
+
+.card-heading {
+    margin-top: 0;
+    margin-bottom: 5px;
+}
+
+.card {
+       background: #fff;
+       border: 1px solid #ccc;
+       margin-top: 10px;
+       box-shadow: 0 -1px 0 #e5e5e5,0 0 2px rgba(0,0,0,.12),0 2px 4px rgba(0,0,0,.24);
+}
+
+.card-body {
+    padding: 15px;
+    width: 10000px;
+}
+
+.card-body, .card-right, .card-left {
+    display: table-cell;
+    vertical-align: top;
+}
+
+.card-left, .card>.pull-left {
+    padding: 15px;
+       padding-right: 0px;
+}
+.card-right, .card>.pull-right {
+    padding: 15px;
+       padding-left: 0px;
+}
+
+.card-list {
+       margin-top: 10px;
+}
+
+.card-list > .card:first-child {
+       border-top: 1px solid #ccc;
+}
+
+.card-list > .card {
+       margin-top: 0;
+       border-top: none;
+}
+
+/* ui */
+
+entity-list .btn-filter {
+       visibility: hidden;
+}
+
+entity-list:hover .btn-filter {
+       visibility: visible;
+}
+
+/* doc */
+
+.nitdoc .synopsys {
+       font-size: 2em;
+}
+
+.signature {
+       color: #666;
+       font-family: monospace;
+}
+
+.signature .name {
+       font-weight: bold;
+}
+
+.page-header .signature .name, .signature .signature .name {
+       font-weight: normal;
+}
+
+.signature .signature a {
+       color: #666;
+       font-family: monospace;
+}
+
+/* tabs */
+
+.nav-tabs li { cursor: pointer; }
+
+.nav>li.warning>a {
+    color: #fff;
+    background-color: #f0ad4e;
+}
+
+.nav>li.warning>a:focus, .nav>li.warning>a:hover {
+    background-color: #ff9c0f;
+}
+
+/* forms */
+
+.has-icon {
+    position: relative;
+}
+
+.has-icon .form-control {
+       padding-left: 35px;
+}
+
+.form-control-icon {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 2;
+    display: block;
+    width: 34px;
+    height: 34px;
+    line-height: 34px;
+    text-align: center;
+    pointer-events: none;
+}
+
+/*
+ * Code Highlighting
+ */
+
+.nitcode a { color: inherit; text-decoration: inherit; } /* hide links */
+.nitcode a:hover { text-decoration: underline; } /* underline links */
+.nitcode span[title]:hover { text-decoration: underline; } /* underline titles */
+/* lexical raw tokens. independent of usage or semantic: */
+.nitcode .nc_c { color: gray; font-style: italic; } /* comment */
+.nitcode .nc_d { color: #3D8127; font-style: italic; } /* documentation comments */
+.nitcode .nc_k { font-weight: bold; } /* keyword */
+.nitcode .nc_o {} /* operator */
+.nitcode .nc_i {} /* standard identifier */
+.nitcode .nc_t { color: #445588; font-weight: bold; } /* type/class identifier */
+.nitcode .nc_a { color: #445588; font-style: italic; } /* old style attribute identifier */
+.nitcode .nc_l { color: #009999; } /* char and number literal */
+.nitcode .nc_s { color: #8F1546; } /* string literal */
+/* syntactic token usage. added because of their position in the AST */
+.nitcode .nc_ast { color: blue; } /* assert label */
+.nitcode .nc_la { color: blue; } /* break/continue label */
+.nitcode .nc_m { color: #445588; } /* module name */
+/* syntactic groups */
+.nitcode .nc_def { font-weight: bold; color: blue; } /* name used in a definition */
+.nitcode .nc_def.nc_a { color: blue; } /* name used in a attribute definition */
+.nitcode .nc_def.nc_t { color: blue; } /* name used in a class or vt definition */
+.nitcode .nc_ss { color: #9E6BEB; } /* superstrings */
+.nitcode .nc_cdef {} /* A whole class definition */
+.nitcode .nc_pdef {} /* A whole property definition */
+/* semantic token usage */
+.nitcode .nc_v { font-style: italic; } /* local variable or parameter */
+.nitcode .nc_vt { font-style: italic; } /* virtual type or formal type */
+.nitcode .nc_error { border: 1px red solid;} /* not used */
diff --git a/share/nitweb/stylesheets/nitweb_bootstrap.css b/share/nitweb/stylesheets/nitweb_bootstrap.css
new file mode 100644 (file)
index 0000000..e82732b
--- /dev/null
@@ -0,0 +1,5769 @@
+/*! normalize.css v3.0.0 | MIT License | git.io/normalize */\r
+html {\r
+  font-family: sans-serif;\r
+  -ms-text-size-adjust: 100%;\r
+  -webkit-text-size-adjust: 100%;\r
+}\r
+body {\r
+  margin: 0;\r
+}\r
+article,\r
+aside,\r
+details,\r
+figcaption,\r
+figure,\r
+footer,\r
+header,\r
+hgroup,\r
+main,\r
+nav,\r
+section,\r
+summary {\r
+  display: block;\r
+}\r
+audio,\r
+canvas,\r
+progress,\r
+video {\r
+  display: inline-block;\r
+  vertical-align: baseline;\r
+}\r
+audio:not([controls]) {\r
+  display: none;\r
+  height: 0;\r
+}\r
+[hidden],\r
+template {\r
+  display: none;\r
+}\r
+a {\r
+  background: transparent;\r
+}\r
+a:active,\r
+a:hover {\r
+  outline: 0;\r
+}\r
+abbr[title] {\r
+  border-bottom: 1px dotted;\r
+}\r
+b,\r
+strong {\r
+  font-weight: bold;\r
+}\r
+dfn {\r
+  font-style: italic;\r
+}\r
+h1 {\r
+  font-size: 2em;\r
+  margin: 0.67em 0;\r
+}\r
+mark {\r
+  background: #ff0;\r
+  color: #000;\r
+}\r
+small {\r
+  font-size: 80%;\r
+}\r
+sub,\r
+sup {\r
+  font-size: 75%;\r
+  line-height: 0;\r
+  position: relative;\r
+  vertical-align: baseline;\r
+}\r
+sup {\r
+  top: -0.5em;\r
+}\r
+sub {\r
+  bottom: -0.25em;\r
+}\r
+img {\r
+  border: 0;\r
+}\r
+svg:not(:root) {\r
+  overflow: hidden;\r
+}\r
+figure {\r
+  margin: 1em 40px;\r
+}\r
+hr {\r
+  -moz-box-sizing: content-box;\r
+  box-sizing: content-box;\r
+  height: 0;\r
+}\r
+pre {\r
+  overflow: auto;\r
+}\r
+code,\r
+kbd,\r
+pre,\r
+samp {\r
+  font-family: monospace, monospace;\r
+  font-size: 1em;\r
+}\r
+button,\r
+input,\r
+optgroup,\r
+select,\r
+textarea {\r
+  color: inherit;\r
+  font: inherit;\r
+  margin: 0;\r
+}\r
+button {\r
+  overflow: visible;\r
+}\r
+button,\r
+select {\r
+  text-transform: none;\r
+}\r
+button,\r
+html input[type="button"],\r
+input[type="reset"],\r
+input[type="submit"] {\r
+  -webkit-appearance: button;\r
+  cursor: pointer;\r
+}\r
+button[disabled],\r
+html input[disabled] {\r
+  cursor: default;\r
+}\r
+button::-moz-focus-inner,\r
+input::-moz-focus-inner {\r
+  border: 0;\r
+  padding: 0;\r
+}\r
+input {\r
+  line-height: normal;\r
+}\r
+input[type="checkbox"],\r
+input[type="radio"] {\r
+  box-sizing: border-box;\r
+  padding: 0;\r
+}\r
+input[type="number"]::-webkit-inner-spin-button,\r
+input[type="number"]::-webkit-outer-spin-button {\r
+  height: auto;\r
+}\r
+input[type="search"] {\r
+  -webkit-appearance: textfield;\r
+  -moz-box-sizing: content-box;\r
+  -webkit-box-sizing: content-box;\r
+  box-sizing: content-box;\r
+}\r
+input[type="search"]::-webkit-search-cancel-button,\r
+input[type="search"]::-webkit-search-decoration {\r
+  -webkit-appearance: none;\r
+}\r
+fieldset {\r
+  border: 1px solid #c0c0c0;\r
+  margin: 0 2px;\r
+  padding: 0.35em 0.625em 0.75em;\r
+}\r
+legend {\r
+  border: 0;\r
+  padding: 0;\r
+}\r
+textarea {\r
+  overflow: auto;\r
+}\r
+optgroup {\r
+  font-weight: bold;\r
+}\r
+table {\r
+  border-collapse: collapse;\r
+  border-spacing: 0;\r
+}\r
+td,\r
+th {\r
+  padding: 0;\r
+}\r
+@media print {\r
+  * {\r
+    text-shadow: none !important;\r
+    color: #000 !important;\r
+    background: transparent !important;\r
+    box-shadow: none !important;\r
+  }\r
+  a,\r
+  a:visited {\r
+    text-decoration: underline;\r
+  }\r
+  a[href]:after {\r
+    content: " (" attr(href) ")";\r
+  }\r
+  abbr[title]:after {\r
+    content: " (" attr(title) ")";\r
+  }\r
+  a[href^="javascript:"]:after,\r
+  a[href^="#"]:after {\r
+    content: "";\r
+  }\r
+  pre,\r
+  blockquote {\r
+    border: 1px solid #999;\r
+    page-break-inside: avoid;\r
+  }\r
+  thead {\r
+    display: table-header-group;\r
+  }\r
+  tr,\r
+  img {\r
+    page-break-inside: avoid;\r
+  }\r
+  img {\r
+    max-width: 100% !important;\r
+  }\r
+  p,\r
+  h2,\r
+  h3 {\r
+    orphans: 3;\r
+    widows: 3;\r
+  }\r
+  h2,\r
+  h3 {\r
+    page-break-after: avoid;\r
+  }\r
+  select {\r
+    background: #fff !important;\r
+  }\r
+  .navbar {\r
+    display: none;\r
+  }\r
+  .table td,\r
+  .table th {\r
+    background-color: #fff !important;\r
+  }\r
+  .btn > .caret,\r
+  .dropup > .btn > .caret {\r
+    border-top-color: #000 !important;\r
+  }\r
+  .label {\r
+    border: 1px solid #000;\r
+  }\r
+  .table {\r
+    border-collapse: collapse !important;\r
+  }\r
+  .table-bordered th,\r
+  .table-bordered td {\r
+    border: 1px solid #ddd !important;\r
+  }\r
+}\r
+* {\r
+  -webkit-box-sizing: border-box;\r
+  -moz-box-sizing: border-box;\r
+  box-sizing: border-box;\r
+}\r
+*:before,\r
+*:after {\r
+  -webkit-box-sizing: border-box;\r
+  -moz-box-sizing: border-box;\r
+  box-sizing: border-box;\r
+}\r
+html {\r
+  font-size: 62.5%;\r
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\r
+}\r
+body {\r
+  font-family: sans-serif;\r
+  font-size: 14px;\r
+  line-height: 1.428571429;\r
+  color: #333333;\r
+  background-color: #f2f2f2;\r
+}\r
+input,\r
+button,\r
+select,\r
+textarea {\r
+  font-family: inherit;\r
+  font-size: inherit;\r
+  line-height: inherit;\r
+}\r
+a {\r
+  color: #0d8921;\r
+  text-decoration: none;\r
+}\r
+a:hover,\r
+a:focus {\r
+  color: #064310;\r
+  text-decoration: underline;\r
+}\r
+a:focus {\r
+  outline: thin dotted;\r
+  outline: 5px auto -webkit-focus-ring-color;\r
+  outline-offset: -2px;\r
+}\r
+figure {\r
+  margin: 0;\r
+}\r
+img {\r
+  vertical-align: middle;\r
+}\r
+.img-responsive,\r
+.thumbnail > img,\r
+.thumbnail a > img,\r
+.carousel-inner > .item > img,\r
+.carousel-inner > .item > a > img {\r
+  display: block;\r
+  max-width: 100%;\r
+  height: auto;\r
+}\r
+.img-rounded {\r
+  border-radius: 0px;\r
+}\r
+.img-thumbnail {\r
+  padding: 4px;\r
+  line-height: 1.428571429;\r
+  background-color: #f2f2f2;\r
+  border: 1px solid #dddddd;\r
+  border-radius: 0px;\r
+  -webkit-transition: all 0.2s ease-in-out;\r
+  transition: all 0.2s ease-in-out;\r
+  display: inline-block;\r
+  max-width: 100%;\r
+  height: auto;\r
+}\r
+.img-circle {\r
+  border-radius: 50%;\r
+}\r
+hr {\r
+  margin-top: 20px;\r
+  margin-bottom: 20px;\r
+  border: 0;\r
+  border-top: 1px solid #eeeeee;\r
+}\r
+.sr-only {\r
+  position: absolute;\r
+  width: 1px;\r
+  height: 1px;\r
+  margin: -1px;\r
+  padding: 0;\r
+  overflow: hidden;\r
+  clip: rect(0, 0, 0, 0);\r
+  border: 0;\r
+}\r
+h1,\r
+h2,\r
+h3,\r
+h4,\r
+h5,\r
+h6,\r
+.h1,\r
+.h2,\r
+.h3,\r
+.h4,\r
+.h5,\r
+.h6 {\r
+  font-family: sans-serif;\r
+  font-weight: 500;\r
+  line-height: 1.1;\r
+  color: inherit;\r
+}\r
+h1 small,\r
+h2 small,\r
+h3 small,\r
+h4 small,\r
+h5 small,\r
+h6 small,\r
+.h1 small,\r
+.h2 small,\r
+.h3 small,\r
+.h4 small,\r
+.h5 small,\r
+.h6 small,\r
+h1 .small,\r
+h2 .small,\r
+h3 .small,\r
+h4 .small,\r
+h5 .small,\r
+h6 .small,\r
+.h1 .small,\r
+.h2 .small,\r
+.h3 .small,\r
+.h4 .small,\r
+.h5 .small,\r
+.h6 .small {\r
+  font-weight: normal;\r
+  line-height: 1;\r
+  color: #999999;\r
+}\r
+h1,\r
+.h1,\r
+h2,\r
+.h2,\r
+h3,\r
+.h3 {\r
+  margin-top: 20px;\r
+  margin-bottom: 10px;\r
+}\r
+h1 small,\r
+.h1 small,\r
+h2 small,\r
+.h2 small,\r
+h3 small,\r
+.h3 small,\r
+h1 .small,\r
+.h1 .small,\r
+h2 .small,\r
+.h2 .small,\r
+h3 .small,\r
+.h3 .small {\r
+  font-size: 65%;\r
+}\r
+h4,\r
+.h4,\r
+h5,\r
+.h5,\r
+h6,\r
+.h6 {\r
+  margin-top: 10px;\r
+  margin-bottom: 10px;\r
+}\r
+h4 small,\r
+.h4 small,\r
+h5 small,\r
+.h5 small,\r
+h6 small,\r
+.h6 small,\r
+h4 .small,\r
+.h4 .small,\r
+h5 .small,\r
+.h5 .small,\r
+h6 .small,\r
+.h6 .small {\r
+  font-size: 75%;\r
+}\r
+h1,\r
+.h1 {\r
+  font-size: 36px;\r
+}\r
+h2,\r
+.h2 {\r
+  font-size: 30px;\r
+}\r
+h3,\r
+.h3 {\r
+  font-size: 23px;\r
+}\r
+h4,\r
+.h4 {\r
+  font-size: 17px;\r
+}\r
+h5,\r
+.h5 {\r
+  font-size: 14px;\r
+}\r
+h6,\r
+.h6 {\r
+  font-size: 11px;\r
+}\r
+p {\r
+  margin: 0 0 10px;\r
+}\r
+.lead {\r
+  margin-bottom: 20px;\r
+  font-size: 16px;\r
+  font-weight: 200;\r
+  line-height: 1.4;\r
+}\r
+@media (min-width: 768px) {\r
+  .lead {\r
+    font-size: 21px;\r
+  }\r
+}\r
+small,\r
+.small {\r
+  font-size: 85%;\r
+}\r
+cite {\r
+  font-style: normal;\r
+}\r
+.text-left {\r
+  text-align: left;\r
+}\r
+.text-right {\r
+  text-align: right;\r
+}\r
+.text-center {\r
+  text-align: center;\r
+}\r
+.text-justify {\r
+  text-align: justify;\r
+}\r
+.text-muted {\r
+  color: #999999;\r
+}\r
+.text-primary {\r
+  color: #0d8921;\r
+}\r
+a.text-primary:hover {\r
+  color: #095a16;\r
+}\r
+.text-success {\r
+  color: #5cb85c;\r
+}\r
+a.text-success:hover {\r
+  color: #449d44;\r
+}\r
+.text-info {\r
+  color: #5bc0de;\r
+}\r
+a.text-info:hover {\r
+  color: #31b0d5;\r
+}\r
+.text-warning {\r
+  color: #f0ad4e;\r
+}\r
+a.text-warning:hover {\r
+  color: #ec971f;\r
+}\r
+.text-danger {\r
+  color: #d9534f;\r
+}\r
+a.text-danger:hover {\r
+  color: #c9302c;\r
+}\r
+.bg-primary {\r
+  color: #fff;\r
+  background-color: #0d8921;\r
+}\r
+a.bg-primary:hover {\r
+  background-color: #095a16;\r
+}\r
+.bg-success {\r
+  background-color: #dff0d8;\r
+}\r
+a.bg-success:hover {\r
+  background-color: #c1e2b3;\r
+}\r
+.bg-info {\r
+  background-color: #d9edf7;\r
+}\r
+a.bg-info:hover {\r
+  background-color: #afd9ee;\r
+}\r
+.bg-warning {\r
+  background-color: #fcf8e3;\r
+}\r
+a.bg-warning:hover {\r
+  background-color: #f7ecb5;\r
+}\r
+.bg-danger {\r
+  background-color: #f2dede;\r
+}\r
+a.bg-danger:hover {\r
+  background-color: #e4b9b9;\r
+}\r
+.page-header {\r
+  padding-bottom: 9px;\r
+  margin: 40px 0 20px;\r
+  border-bottom: 1px solid #eeeeee;\r
+}\r
+ul,\r
+ol {\r
+  margin-top: 0;\r
+  margin-bottom: 10px;\r
+}\r
+ul ul,\r
+ol ul,\r
+ul ol,\r
+ol ol {\r
+  margin-bottom: 0;\r
+}\r
+.list-unstyled {\r
+  padding-left: 0;\r
+  list-style: none;\r
+}\r
+.list-inline {\r
+  padding-left: 0;\r
+  list-style: none;\r
+}\r
+.list-inline > li {\r
+  display: inline-block;\r
+  padding-left: 5px;\r
+  padding-right: 5px;\r
+}\r
+.list-inline > li:first-child {\r
+  padding-left: 0;\r
+}\r
+dl {\r
+  margin-top: 0;\r
+  margin-bottom: 20px;\r
+}\r
+dt,\r
+dd {\r
+  line-height: 1.428571429;\r
+}\r
+dt {\r
+  font-weight: bold;\r
+}\r
+dd {\r
+  margin-left: 0;\r
+}\r
+@media (min-width: 768px) {\r
+  .dl-horizontal dt {\r
+    float: left;\r
+    width: 160px;\r
+    clear: left;\r
+    text-align: right;\r
+    overflow: hidden;\r
+    text-overflow: ellipsis;\r
+    white-space: nowrap;\r
+  }\r
+  .dl-horizontal dd {\r
+    margin-left: 180px;\r
+  }\r
+}\r
+abbr[title],\r
+abbr[data-original-title] {\r
+  cursor: help;\r
+  border-bottom: 1px dotted #999999;\r
+}\r
+.initialism {\r
+  font-size: 90%;\r
+  text-transform: uppercase;\r
+}\r
+blockquote {\r
+  padding: 10px 20px;\r
+  margin: 0 0 20px;\r
+  font-size: 17.5px;\r
+  border-left: 5px solid #eeeeee;\r
+}\r
+blockquote p:last-child,\r
+blockquote ul:last-child,\r
+blockquote ol:last-child {\r
+  margin-bottom: 0;\r
+}\r
+blockquote footer,\r
+blockquote small,\r
+blockquote .small {\r
+  display: block;\r
+  font-size: 80%;\r
+  line-height: 1.428571429;\r
+  color: #999999;\r
+}\r
+blockquote footer:before,\r
+blockquote small:before,\r
+blockquote .small:before {\r
+  content: '\2014 \00A0';\r
+}\r
+.blockquote-reverse,\r
+blockquote.pull-right {\r
+  padding-right: 15px;\r
+  padding-left: 0;\r
+  border-right: 5px solid #eeeeee;\r
+  border-left: 0;\r
+  text-align: right;\r
+}\r
+.blockquote-reverse footer:before,\r
+blockquote.pull-right footer:before,\r
+.blockquote-reverse small:before,\r
+blockquote.pull-right small:before,\r
+.blockquote-reverse .small:before,\r
+blockquote.pull-right .small:before {\r
+  content: '';\r
+}\r
+.blockquote-reverse footer:after,\r
+blockquote.pull-right footer:after,\r
+.blockquote-reverse small:after,\r
+blockquote.pull-right small:after,\r
+.blockquote-reverse .small:after,\r
+blockquote.pull-right .small:after {\r
+  content: '\00A0 \2014';\r
+}\r
+blockquote:before,\r
+blockquote:after {\r
+  content: "";\r
+}\r
+address {\r
+  margin-bottom: 20px;\r
+  font-style: normal;\r
+  line-height: 1.428571429;\r
+}\r
+code,\r
+kbd,\r
+pre,\r
+samp {\r
+  font-family: monospace;\r
+}\r
+code {\r
+  padding: 2px 4px;\r
+  font-size: 90%;\r
+  color: #c7254e;\r
+  background-color: #f9f2f4;\r
+  white-space: nowrap;\r
+  border-radius: 0px;\r
+}\r
+kbd {\r
+  padding: 2px 4px;\r
+  font-size: 90%;\r
+  color: #ffffff;\r
+  background-color: #333333;\r
+  border-radius: 0px;\r
+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);\r
+}\r
+pre {\r
+  display: block;\r
+  padding: 9.5px;\r
+  margin: 0 0 10px;\r
+  font-size: 13px;\r
+  line-height: 1.428571429;\r
+  word-break: break-all;\r
+  word-wrap: break-word;\r
+  color: #333333;\r
+  background-color: #f5f5f5;\r
+  border: 1px solid #cccccc;\r
+  border-radius: 0px;\r
+}\r
+pre code {\r
+  padding: 0;\r
+  font-size: inherit;\r
+  color: inherit;\r
+  white-space: pre-wrap;\r
+  background-color: transparent;\r
+  border-radius: 0;\r
+}\r
+.pre-scrollable {\r
+  max-height: 340px;\r
+  overflow-y: scroll;\r
+}\r
+.container {\r
+  margin-right: auto;\r
+  margin-left: auto;\r
+  padding-left: 15px;\r
+  padding-right: 15px;\r
+}\r
+@media (min-width: 768px) {\r
+  .container {\r
+    width: 750px;\r
+  }\r
+}\r
+@media (min-width: 992px) {\r
+  .container {\r
+    width: 970px;\r
+  }\r
+}\r
+@media (min-width: 1200px) {\r
+  .container {\r
+    width: 1170px;\r
+  }\r
+}\r
+.container-fluid {\r
+  margin-right: auto;\r
+  margin-left: auto;\r
+  padding-left: 15px;\r
+  padding-right: 15px;\r
+}\r
+.row {\r
+  margin-left: -15px;\r
+  margin-right: -15px;\r
+}\r
+.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {\r
+  position: relative;\r
+  min-height: 1px;\r
+  padding-left: 15px;\r
+  padding-right: 15px;\r
+}\r
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {\r
+  float: left;\r
+}\r
+.col-xs-12 {\r
+  width: 100%;\r
+}\r
+.col-xs-11 {\r
+  width: 91.66666666666666%;\r
+}\r
+.col-xs-10 {\r
+  width: 83.33333333333334%;\r
+}\r
+.col-xs-9 {\r
+  width: 75%;\r
+}\r
+.col-xs-8 {\r
+  width: 66.66666666666666%;\r
+}\r
+.col-xs-7 {\r
+  width: 58.333333333333336%;\r
+}\r
+.col-xs-6 {\r
+  width: 50%;\r
+}\r
+.col-xs-5 {\r
+  width: 41.66666666666667%;\r
+}\r
+.col-xs-4 {\r
+  width: 33.33333333333333%;\r
+}\r
+.col-xs-3 {\r
+  width: 25%;\r
+}\r
+.col-xs-2 {\r
+  width: 16.666666666666664%;\r
+}\r
+.col-xs-1 {\r
+  width: 8.333333333333332%;\r
+}\r
+.col-xs-pull-12 {\r
+  right: 100%;\r
+}\r
+.col-xs-pull-11 {\r
+  right: 91.66666666666666%;\r
+}\r
+.col-xs-pull-10 {\r
+  right: 83.33333333333334%;\r
+}\r
+.col-xs-pull-9 {\r
+  right: 75%;\r
+}\r
+.col-xs-pull-8 {\r
+  right: 66.66666666666666%;\r
+}\r
+.col-xs-pull-7 {\r
+  right: 58.333333333333336%;\r
+}\r
+.col-xs-pull-6 {\r
+  right: 50%;\r
+}\r
+.col-xs-pull-5 {\r
+  right: 41.66666666666667%;\r
+}\r
+.col-xs-pull-4 {\r
+  right: 33.33333333333333%;\r
+}\r
+.col-xs-pull-3 {\r
+  right: 25%;\r
+}\r
+.col-xs-pull-2 {\r
+  right: 16.666666666666664%;\r
+}\r
+.col-xs-pull-1 {\r
+  right: 8.333333333333332%;\r
+}\r
+.col-xs-pull-0 {\r
+  right: 0%;\r
+}\r
+.col-xs-push-12 {\r
+  left: 100%;\r
+}\r
+.col-xs-push-11 {\r
+  left: 91.66666666666666%;\r
+}\r
+.col-xs-push-10 {\r
+  left: 83.33333333333334%;\r
+}\r
+.col-xs-push-9 {\r
+  left: 75%;\r
+}\r
+.col-xs-push-8 {\r
+  left: 66.66666666666666%;\r
+}\r
+.col-xs-push-7 {\r
+  left: 58.333333333333336%;\r
+}\r
+.col-xs-push-6 {\r
+  left: 50%;\r
+}\r
+.col-xs-push-5 {\r
+  left: 41.66666666666667%;\r
+}\r
+.col-xs-push-4 {\r
+  left: 33.33333333333333%;\r
+}\r
+.col-xs-push-3 {\r
+  left: 25%;\r
+}\r
+.col-xs-push-2 {\r
+  left: 16.666666666666664%;\r
+}\r
+.col-xs-push-1 {\r
+  left: 8.333333333333332%;\r
+}\r
+.col-xs-push-0 {\r
+  left: 0%;\r
+}\r
+.col-xs-offset-12 {\r
+  margin-left: 100%;\r
+}\r
+.col-xs-offset-11 {\r
+  margin-left: 91.66666666666666%;\r
+}\r
+.col-xs-offset-10 {\r
+  margin-left: 83.33333333333334%;\r
+}\r
+.col-xs-offset-9 {\r
+  margin-left: 75%;\r
+}\r
+.col-xs-offset-8 {\r
+  margin-left: 66.66666666666666%;\r
+}\r
+.col-xs-offset-7 {\r
+  margin-left: 58.333333333333336%;\r
+}\r
+.col-xs-offset-6 {\r
+  margin-left: 50%;\r
+}\r
+.col-xs-offset-5 {\r
+  margin-left: 41.66666666666667%;\r
+}\r
+.col-xs-offset-4 {\r
+  margin-left: 33.33333333333333%;\r
+}\r
+.col-xs-offset-3 {\r
+  margin-left: 25%;\r
+}\r
+.col-xs-offset-2 {\r
+  margin-left: 16.666666666666664%;\r
+}\r
+.col-xs-offset-1 {\r
+  margin-left: 8.333333333333332%;\r
+}\r
+.col-xs-offset-0 {\r
+  margin-left: 0%;\r
+}\r
+@media (min-width: 768px) {\r
+  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {\r
+    float: left;\r
+  }\r
+  .col-sm-12 {\r
+    width: 100%;\r
+  }\r
+  .col-sm-11 {\r
+    width: 91.66666666666666%;\r
+  }\r
+  .col-sm-10 {\r
+    width: 83.33333333333334%;\r
+  }\r
+  .col-sm-9 {\r
+    width: 75%;\r
+  }\r
+  .col-sm-8 {\r
+    width: 66.66666666666666%;\r
+  }\r
+  .col-sm-7 {\r
+    width: 58.333333333333336%;\r
+  }\r
+  .col-sm-6 {\r
+    width: 50%;\r
+  }\r
+  .col-sm-5 {\r
+    width: 41.66666666666667%;\r
+  }\r
+  .col-sm-4 {\r
+    width: 33.33333333333333%;\r
+  }\r
+  .col-sm-3 {\r
+    width: 25%;\r
+  }\r
+  .col-sm-2 {\r
+    width: 16.666666666666664%;\r
+  }\r
+  .col-sm-1 {\r
+    width: 8.333333333333332%;\r
+  }\r
+  .col-sm-pull-12 {\r
+    right: 100%;\r
+  }\r
+  .col-sm-pull-11 {\r
+    right: 91.66666666666666%;\r
+  }\r
+  .col-sm-pull-10 {\r
+    right: 83.33333333333334%;\r
+  }\r
+  .col-sm-pull-9 {\r
+    right: 75%;\r
+  }\r
+  .col-sm-pull-8 {\r
+    right: 66.66666666666666%;\r
+  }\r
+  .col-sm-pull-7 {\r
+    right: 58.333333333333336%;\r
+  }\r
+  .col-sm-pull-6 {\r
+    right: 50%;\r
+  }\r
+  .col-sm-pull-5 {\r
+    right: 41.66666666666667%;\r
+  }\r
+  .col-sm-pull-4 {\r
+    right: 33.33333333333333%;\r
+  }\r
+  .col-sm-pull-3 {\r
+    right: 25%;\r
+  }\r
+  .col-sm-pull-2 {\r
+    right: 16.666666666666664%;\r
+  }\r
+  .col-sm-pull-1 {\r
+    right: 8.333333333333332%;\r
+  }\r
+  .col-sm-pull-0 {\r
+    right: 0%;\r
+  }\r
+  .col-sm-push-12 {\r
+    left: 100%;\r
+  }\r
+  .col-sm-push-11 {\r
+    left: 91.66666666666666%;\r
+  }\r
+  .col-sm-push-10 {\r
+    left: 83.33333333333334%;\r
+  }\r
+  .col-sm-push-9 {\r
+    left: 75%;\r
+  }\r
+  .col-sm-push-8 {\r
+    left: 66.66666666666666%;\r
+  }\r
+  .col-sm-push-7 {\r
+    left: 58.333333333333336%;\r
+  }\r
+  .col-sm-push-6 {\r
+    left: 50%;\r
+  }\r
+  .col-sm-push-5 {\r
+    left: 41.66666666666667%;\r
+  }\r
+  .col-sm-push-4 {\r
+    left: 33.33333333333333%;\r
+  }\r
+  .col-sm-push-3 {\r
+    left: 25%;\r
+  }\r
+  .col-sm-push-2 {\r
+    left: 16.666666666666664%;\r
+  }\r
+  .col-sm-push-1 {\r
+    left: 8.333333333333332%;\r
+  }\r
+  .col-sm-push-0 {\r
+    left: 0%;\r
+  }\r
+  .col-sm-offset-12 {\r
+    margin-left: 100%;\r
+  }\r
+  .col-sm-offset-11 {\r
+    margin-left: 91.66666666666666%;\r
+  }\r
+  .col-sm-offset-10 {\r
+    margin-left: 83.33333333333334%;\r
+  }\r
+  .col-sm-offset-9 {\r
+    margin-left: 75%;\r
+  }\r
+  .col-sm-offset-8 {\r
+    margin-left: 66.66666666666666%;\r
+  }\r
+  .col-sm-offset-7 {\r
+    margin-left: 58.333333333333336%;\r
+  }\r
+  .col-sm-offset-6 {\r
+    margin-left: 50%;\r
+  }\r
+  .col-sm-offset-5 {\r
+    margin-left: 41.66666666666667%;\r
+  }\r
+  .col-sm-offset-4 {\r
+    margin-left: 33.33333333333333%;\r
+  }\r
+  .col-sm-offset-3 {\r
+    margin-left: 25%;\r
+  }\r
+  .col-sm-offset-2 {\r
+    margin-left: 16.666666666666664%;\r
+  }\r
+  .col-sm-offset-1 {\r
+    margin-left: 8.333333333333332%;\r
+  }\r
+  .col-sm-offset-0 {\r
+    margin-left: 0%;\r
+  }\r
+}\r
+@media (min-width: 992px) {\r
+  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {\r
+    float: left;\r
+  }\r
+  .col-md-12 {\r
+    width: 100%;\r
+  }\r
+  .col-md-11 {\r
+    width: 91.66666666666666%;\r
+  }\r
+  .col-md-10 {\r
+    width: 83.33333333333334%;\r
+  }\r
+  .col-md-9 {\r
+    width: 75%;\r
+  }\r
+  .col-md-8 {\r
+    width: 66.66666666666666%;\r
+  }\r
+  .col-md-7 {\r
+    width: 58.333333333333336%;\r
+  }\r
+  .col-md-6 {\r
+    width: 50%;\r
+  }\r
+  .col-md-5 {\r
+    width: 41.66666666666667%;\r
+  }\r
+  .col-md-4 {\r
+    width: 33.33333333333333%;\r
+  }\r
+  .col-md-3 {\r
+    width: 25%;\r
+  }\r
+  .col-md-2 {\r
+    width: 16.666666666666664%;\r
+  }\r
+  .col-md-1 {\r
+    width: 8.333333333333332%;\r
+  }\r
+  .col-md-pull-12 {\r
+    right: 100%;\r
+  }\r
+  .col-md-pull-11 {\r
+    right: 91.66666666666666%;\r
+  }\r
+  .col-md-pull-10 {\r
+    right: 83.33333333333334%;\r
+  }\r
+  .col-md-pull-9 {\r
+    right: 75%;\r
+  }\r
+  .col-md-pull-8 {\r
+    right: 66.66666666666666%;\r
+  }\r
+  .col-md-pull-7 {\r
+    right: 58.333333333333336%;\r
+  }\r
+  .col-md-pull-6 {\r
+    right: 50%;\r
+  }\r
+  .col-md-pull-5 {\r
+    right: 41.66666666666667%;\r
+  }\r
+  .col-md-pull-4 {\r
+    right: 33.33333333333333%;\r
+  }\r
+  .col-md-pull-3 {\r
+    right: 25%;\r
+  }\r
+  .col-md-pull-2 {\r
+    right: 16.666666666666664%;\r
+  }\r
+  .col-md-pull-1 {\r
+    right: 8.333333333333332%;\r
+  }\r
+  .col-md-pull-0 {\r
+    right: 0%;\r
+  }\r
+  .col-md-push-12 {\r
+    left: 100%;\r
+  }\r
+  .col-md-push-11 {\r
+    left: 91.66666666666666%;\r
+  }\r
+  .col-md-push-10 {\r
+    left: 83.33333333333334%;\r
+  }\r
+  .col-md-push-9 {\r
+    left: 75%;\r
+  }\r
+  .col-md-push-8 {\r
+    left: 66.66666666666666%;\r
+  }\r
+  .col-md-push-7 {\r
+    left: 58.333333333333336%;\r
+  }\r
+  .col-md-push-6 {\r
+    left: 50%;\r
+  }\r
+  .col-md-push-5 {\r
+    left: 41.66666666666667%;\r
+  }\r
+  .col-md-push-4 {\r
+    left: 33.33333333333333%;\r
+  }\r
+  .col-md-push-3 {\r
+    left: 25%;\r
+  }\r
+  .col-md-push-2 {\r
+    left: 16.666666666666664%;\r
+  }\r
+  .col-md-push-1 {\r
+    left: 8.333333333333332%;\r
+  }\r
+  .col-md-push-0 {\r
+    left: 0%;\r
+  }\r
+  .col-md-offset-12 {\r
+    margin-left: 100%;\r
+  }\r
+  .col-md-offset-11 {\r
+    margin-left: 91.66666666666666%;\r
+  }\r
+  .col-md-offset-10 {\r
+    margin-left: 83.33333333333334%;\r
+  }\r
+  .col-md-offset-9 {\r
+    margin-left: 75%;\r
+  }\r
+  .col-md-offset-8 {\r
+    margin-left: 66.66666666666666%;\r
+  }\r
+  .col-md-offset-7 {\r
+    margin-left: 58.333333333333336%;\r
+  }\r
+  .col-md-offset-6 {\r
+    margin-left: 50%;\r
+  }\r
+  .col-md-offset-5 {\r
+    margin-left: 41.66666666666667%;\r
+  }\r
+  .col-md-offset-4 {\r
+    margin-left: 33.33333333333333%;\r
+  }\r
+  .col-md-offset-3 {\r
+    margin-left: 25%;\r
+  }\r
+  .col-md-offset-2 {\r
+    margin-left: 16.666666666666664%;\r
+  }\r
+  .col-md-offset-1 {\r
+    margin-left: 8.333333333333332%;\r
+  }\r
+  .col-md-offset-0 {\r
+    margin-left: 0%;\r
+  }\r
+}\r
+@media (min-width: 1200px) {\r
+  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {\r
+    float: left;\r
+  }\r
+  .col-lg-12 {\r
+    width: 100%;\r
+  }\r
+  .col-lg-11 {\r
+    width: 91.66666666666666%;\r
+  }\r
+  .col-lg-10 {\r
+    width: 83.33333333333334%;\r
+  }\r
+  .col-lg-9 {\r
+    width: 75%;\r
+  }\r
+  .col-lg-8 {\r
+    width: 66.66666666666666%;\r
+  }\r
+  .col-lg-7 {\r
+    width: 58.333333333333336%;\r
+  }\r
+  .col-lg-6 {\r
+    width: 50%;\r
+  }\r
+  .col-lg-5 {\r
+    width: 41.66666666666667%;\r
+  }\r
+  .col-lg-4 {\r
+    width: 33.33333333333333%;\r
+  }\r
+  .col-lg-3 {\r
+    width: 25%;\r
+  }\r
+  .col-lg-2 {\r
+    width: 16.666666666666664%;\r
+  }\r
+  .col-lg-1 {\r
+    width: 8.333333333333332%;\r
+  }\r
+  .col-lg-pull-12 {\r
+    right: 100%;\r
+  }\r
+  .col-lg-pull-11 {\r
+    right: 91.66666666666666%;\r
+  }\r
+  .col-lg-pull-10 {\r
+    right: 83.33333333333334%;\r
+  }\r
+  .col-lg-pull-9 {\r
+    right: 75%;\r
+  }\r
+  .col-lg-pull-8 {\r
+    right: 66.66666666666666%;\r
+  }\r
+  .col-lg-pull-7 {\r
+    right: 58.333333333333336%;\r
+  }\r
+  .col-lg-pull-6 {\r
+    right: 50%;\r
+  }\r
+  .col-lg-pull-5 {\r
+    right: 41.66666666666667%;\r
+  }\r
+  .col-lg-pull-4 {\r
+    right: 33.33333333333333%;\r
+  }\r
+  .col-lg-pull-3 {\r
+    right: 25%;\r
+  }\r
+  .col-lg-pull-2 {\r
+    right: 16.666666666666664%;\r
+  }\r
+  .col-lg-pull-1 {\r
+    right: 8.333333333333332%;\r
+  }\r
+  .col-lg-pull-0 {\r
+    right: 0%;\r
+  }\r
+  .col-lg-push-12 {\r
+    left: 100%;\r
+  }\r
+  .col-lg-push-11 {\r
+    left: 91.66666666666666%;\r
+  }\r
+  .col-lg-push-10 {\r
+    left: 83.33333333333334%;\r
+  }\r
+  .col-lg-push-9 {\r
+    left: 75%;\r
+  }\r
+  .col-lg-push-8 {\r
+    left: 66.66666666666666%;\r
+  }\r
+  .col-lg-push-7 {\r
+    left: 58.333333333333336%;\r
+  }\r
+  .col-lg-push-6 {\r
+    left: 50%;\r
+  }\r
+  .col-lg-push-5 {\r
+    left: 41.66666666666667%;\r
+  }\r
+  .col-lg-push-4 {\r
+    left: 33.33333333333333%;\r
+  }\r
+  .col-lg-push-3 {\r
+    left: 25%;\r
+  }\r
+  .col-lg-push-2 {\r
+    left: 16.666666666666664%;\r
+  }\r
+  .col-lg-push-1 {\r
+    left: 8.333333333333332%;\r
+  }\r
+  .col-lg-push-0 {\r
+    left: 0%;\r
+  }\r
+  .col-lg-offset-12 {\r
+    margin-left: 100%;\r
+  }\r
+  .col-lg-offset-11 {\r
+    margin-left: 91.66666666666666%;\r
+  }\r
+  .col-lg-offset-10 {\r
+    margin-left: 83.33333333333334%;\r
+  }\r
+  .col-lg-offset-9 {\r
+    margin-left: 75%;\r
+  }\r
+  .col-lg-offset-8 {\r
+    margin-left: 66.66666666666666%;\r
+  }\r
+  .col-lg-offset-7 {\r
+    margin-left: 58.333333333333336%;\r
+  }\r
+  .col-lg-offset-6 {\r
+    margin-left: 50%;\r
+  }\r
+  .col-lg-offset-5 {\r
+    margin-left: 41.66666666666667%;\r
+  }\r
+  .col-lg-offset-4 {\r
+    margin-left: 33.33333333333333%;\r
+  }\r
+  .col-lg-offset-3 {\r
+    margin-left: 25%;\r
+  }\r
+  .col-lg-offset-2 {\r
+    margin-left: 16.666666666666664%;\r
+  }\r
+  .col-lg-offset-1 {\r
+    margin-left: 8.333333333333332%;\r
+  }\r
+  .col-lg-offset-0 {\r
+    margin-left: 0%;\r
+  }\r
+}\r
+table {\r
+  max-width: 100%;\r
+  background-color: transparent;\r
+}\r
+th {\r
+  text-align: left;\r
+}\r
+.table {\r
+  width: 100%;\r
+  margin-bottom: 20px;\r
+}\r
+.table > thead > tr > th,\r
+.table > tbody > tr > th,\r
+.table > tfoot > tr > th,\r
+.table > thead > tr > td,\r
+.table > tbody > tr > td,\r
+.table > tfoot > tr > td {\r
+  padding: 8px;\r
+  line-height: 1.428571429;\r
+  vertical-align: top;\r
+  border-top: 1px solid #dddddd;\r
+}\r
+.table > thead > tr > th {\r
+  vertical-align: bottom;\r
+  border-bottom: 2px solid #dddddd;\r
+}\r
+.table > caption + thead > tr:first-child > th,\r
+.table > colgroup + thead > tr:first-child > th,\r
+.table > thead:first-child > tr:first-child > th,\r
+.table > caption + thead > tr:first-child > td,\r
+.table > colgroup + thead > tr:first-child > td,\r
+.table > thead:first-child > tr:first-child > td {\r
+  border-top: 0;\r
+}\r
+.table > tbody + tbody {\r
+  border-top: 2px solid #dddddd;\r
+}\r
+.table .table {\r
+  background-color: #f2f2f2;\r
+}\r
+.table-condensed > thead > tr > th,\r
+.table-condensed > tbody > tr > th,\r
+.table-condensed > tfoot > tr > th,\r
+.table-condensed > thead > tr > td,\r
+.table-condensed > tbody > tr > td,\r
+.table-condensed > tfoot > tr > td {\r
+  padding: 5px;\r
+}\r
+.table-bordered {\r
+  border: 1px solid #dddddd;\r
+}\r
+.table-bordered > thead > tr > th,\r
+.table-bordered > tbody > tr > th,\r
+.table-bordered > tfoot > tr > th,\r
+.table-bordered > thead > tr > td,\r
+.table-bordered > tbody > tr > td,\r
+.table-bordered > tfoot > tr > td {\r
+  border: 1px solid #dddddd;\r
+}\r
+.table-bordered > thead > tr > th,\r
+.table-bordered > thead > tr > td {\r
+  border-bottom-width: 2px;\r
+}\r
+.table-striped > tbody > tr:nth-child(odd) > td,\r
+.table-striped > tbody > tr:nth-child(odd) > th {\r
+  background-color: #f9f9f9;\r
+}\r
+.table-hover > tbody > tr:hover > td,\r
+.table-hover > tbody > tr:hover > th {\r
+  background-color: #dbdbdb;\r
+}\r
+table col[class*="col-"] {\r
+  position: static;\r
+  float: none;\r
+  display: table-column;\r
+}\r
+table td[class*="col-"],\r
+table th[class*="col-"] {\r
+  position: static;\r
+  float: none;\r
+  display: table-cell;\r
+}\r
+.table > thead > tr > td.active,\r
+.table > tbody > tr > td.active,\r
+.table > tfoot > tr > td.active,\r
+.table > thead > tr > th.active,\r
+.table > tbody > tr > th.active,\r
+.table > tfoot > tr > th.active,\r
+.table > thead > tr.active > td,\r
+.table > tbody > tr.active > td,\r
+.table > tfoot > tr.active > td,\r
+.table > thead > tr.active > th,\r
+.table > tbody > tr.active > th,\r
+.table > tfoot > tr.active > th {\r
+  background-color: #dbdbdb;\r
+}\r
+.table-hover > tbody > tr > td.active:hover,\r
+.table-hover > tbody > tr > th.active:hover,\r
+.table-hover > tbody > tr.active:hover > td,\r
+.table-hover > tbody > tr.active:hover > th {\r
+  background-color: #cecece;\r
+}\r
+.table > thead > tr > td.success,\r
+.table > tbody > tr > td.success,\r
+.table > tfoot > tr > td.success,\r
+.table > thead > tr > th.success,\r
+.table > tbody > tr > th.success,\r
+.table > tfoot > tr > th.success,\r
+.table > thead > tr.success > td,\r
+.table > tbody > tr.success > td,\r
+.table > tfoot > tr.success > td,\r
+.table > thead > tr.success > th,\r
+.table > tbody > tr.success > th,\r
+.table > tfoot > tr.success > th {\r
+  background-color: #dff0d8;\r
+}\r
+.table-hover > tbody > tr > td.success:hover,\r
+.table-hover > tbody > tr > th.success:hover,\r
+.table-hover > tbody > tr.success:hover > td,\r
+.table-hover > tbody > tr.success:hover > th {\r
+  background-color: #d0e9c6;\r
+}\r
+.table > thead > tr > td.info,\r
+.table > tbody > tr > td.info,\r
+.table > tfoot > tr > td.info,\r
+.table > thead > tr > th.info,\r
+.table > tbody > tr > th.info,\r
+.table > tfoot > tr > th.info,\r
+.table > thead > tr.info > td,\r
+.table > tbody > tr.info > td,\r
+.table > tfoot > tr.info > td,\r
+.table > thead > tr.info > th,\r
+.table > tbody > tr.info > th,\r
+.table > tfoot > tr.info > th {\r
+  background-color: #d9edf7;\r
+}\r
+.table-hover > tbody > tr > td.info:hover,\r
+.table-hover > tbody > tr > th.info:hover,\r
+.table-hover > tbody > tr.info:hover > td,\r
+.table-hover > tbody > tr.info:hover > th {\r
+  background-color: #c4e3f3;\r
+}\r
+.table > thead > tr > td.warning,\r
+.table > tbody > tr > td.warning,\r
+.table > tfoot > tr > td.warning,\r
+.table > thead > tr > th.warning,\r
+.table > tbody > tr > th.warning,\r
+.table > tfoot > tr > th.warning,\r
+.table > thead > tr.warning > td,\r
+.table > tbody > tr.warning > td,\r
+.table > tfoot > tr.warning > td,\r
+.table > thead > tr.warning > th,\r
+.table > tbody > tr.warning > th,\r
+.table > tfoot > tr.warning > th {\r
+  background-color: #fcf8e3;\r
+}\r
+.table-hover > tbody > tr > td.warning:hover,\r
+.table-hover > tbody > tr > th.warning:hover,\r
+.table-hover > tbody > tr.warning:hover > td,\r
+.table-hover > tbody > tr.warning:hover > th {\r
+  background-color: #faf2cc;\r
+}\r
+.table > thead > tr > td.danger,\r
+.table > tbody > tr > td.danger,\r
+.table > tfoot > tr > td.danger,\r
+.table > thead > tr > th.danger,\r
+.table > tbody > tr > th.danger,\r
+.table > tfoot > tr > th.danger,\r
+.table > thead > tr.danger > td,\r
+.table > tbody > tr.danger > td,\r
+.table > tfoot > tr.danger > td,\r
+.table > thead > tr.danger > th,\r
+.table > tbody > tr.danger > th,\r
+.table > tfoot > tr.danger > th {\r
+  background-color: #f2dede;\r
+}\r
+.table-hover > tbody > tr > td.danger:hover,\r
+.table-hover > tbody > tr > th.danger:hover,\r
+.table-hover > tbody > tr.danger:hover > td,\r
+.table-hover > tbody > tr.danger:hover > th {\r
+  background-color: #ebcccc;\r
+}\r
+@media (max-width: 767px) {\r
+  .table-responsive {\r
+    width: 100%;\r
+    margin-bottom: 15px;\r
+    overflow-y: hidden;\r
+    overflow-x: scroll;\r
+    -ms-overflow-style: -ms-autohiding-scrollbar;\r
+    border: 1px solid #dddddd;\r
+    -webkit-overflow-scrolling: touch;\r
+  }\r
+  .table-responsive > .table {\r
+    margin-bottom: 0;\r
+  }\r
+  .table-responsive > .table > thead > tr > th,\r
+  .table-responsive > .table > tbody > tr > th,\r
+  .table-responsive > .table > tfoot > tr > th,\r
+  .table-responsive > .table > thead > tr > td,\r
+  .table-responsive > .table > tbody > tr > td,\r
+  .table-responsive > .table > tfoot > tr > td {\r
+    white-space: nowrap;\r
+  }\r
+  .table-responsive > .table-bordered {\r
+    border: 0;\r
+  }\r
+  .table-responsive > .table-bordered > thead > tr > th:first-child,\r
+  .table-responsive > .table-bordered > tbody > tr > th:first-child,\r
+  .table-responsive > .table-bordered > tfoot > tr > th:first-child,\r
+  .table-responsive > .table-bordered > thead > tr > td:first-child,\r
+  .table-responsive > .table-bordered > tbody > tr > td:first-child,\r
+  .table-responsive > .table-bordered > tfoot > tr > td:first-child {\r
+    border-left: 0;\r
+  }\r
+  .table-responsive > .table-bordered > thead > tr > th:last-child,\r
+  .table-responsive > .table-bordered > tbody > tr > th:last-child,\r
+  .table-responsive > .table-bordered > tfoot > tr > th:last-child,\r
+  .table-responsive > .table-bordered > thead > tr > td:last-child,\r
+  .table-responsive > .table-bordered > tbody > tr > td:last-child,\r
+  .table-responsive > .table-bordered > tfoot > tr > td:last-child {\r
+    border-right: 0;\r
+  }\r
+  .table-responsive > .table-bordered > tbody > tr:last-child > th,\r
+  .table-responsive > .table-bordered > tfoot > tr:last-child > th,\r
+  .table-responsive > .table-bordered > tbody > tr:last-child > td,\r
+  .table-responsive > .table-bordered > tfoot > tr:last-child > td {\r
+    border-bottom: 0;\r
+  }\r
+}\r
+fieldset {\r
+  padding: 0;\r
+  margin: 0;\r
+  border: 0;\r
+  min-width: 0;\r
+}\r
+legend {\r
+  display: block;\r
+  width: 100%;\r
+  padding: 0;\r
+  margin-bottom: 20px;\r
+  font-size: 21px;\r
+  line-height: inherit;\r
+  color: #333333;\r
+  border: 0;\r
+  border-bottom: 1px solid #e5e5e5;\r
+}\r
+label {\r
+  display: inline-block;\r
+  margin-bottom: 5px;\r
+  font-weight: bold;\r
+}\r
+input[type="search"] {\r
+  -webkit-box-sizing: border-box;\r
+  -moz-box-sizing: border-box;\r
+  box-sizing: border-box;\r
+}\r
+input[type="radio"],\r
+input[type="checkbox"] {\r
+  margin: 4px 0 0;\r
+  margin-top: 1px \9;\r
+  /* IE8-9 */\r
+\r
+  line-height: normal;\r
+}\r
+input[type="file"] {\r
+  display: block;\r
+}\r
+input[type="range"] {\r
+  display: block;\r
+  width: 100%;\r
+}\r
+select[multiple],\r
+select[size] {\r
+  height: auto;\r
+}\r
+input[type="file"]:focus,\r
+input[type="radio"]:focus,\r
+input[type="checkbox"]:focus {\r
+  outline: thin dotted;\r
+  outline: 5px auto -webkit-focus-ring-color;\r
+  outline-offset: -2px;\r
+}\r
+output {\r
+  display: block;\r
+  padding-top: 7px;\r
+  font-size: 14px;\r
+  line-height: 1.428571429;\r
+  color: #555555;\r
+}\r
+.form-control {\r
+  display: block;\r
+  width: 100%;\r
+  height: 34px;\r
+  padding: 6px 12px;\r
+  font-size: 14px;\r
+  line-height: 1.428571429;\r
+  color: #555555;\r
+  background-color: #ffffff;\r
+  background-image: none;\r
+  border: 1px solid #cccccc;\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+  -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\r
+  transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;\r
+}\r
+.form-control:focus {\r
+  border-color: #66afe9;\r
+  outline: 0;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\r
+  box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);\r
+}\r
+.form-control::-moz-placeholder {\r
+  color: #999999;\r
+  opacity: 1;\r
+}\r
+.form-control:-ms-input-placeholder {\r
+  color: #999999;\r
+}\r
+.form-control::-webkit-input-placeholder {\r
+  color: #999999;\r
+}\r
+.form-control[disabled],\r
+.form-control[readonly],\r
+fieldset[disabled] .form-control {\r
+  cursor: not-allowed;\r
+  background-color: #eeeeee;\r
+  opacity: 1;\r
+}\r
+textarea.form-control {\r
+  height: auto;\r
+}\r
+input[type="search"] {\r
+  -webkit-appearance: none;\r
+}\r
+input[type="date"] {\r
+  line-height: 34px;\r
+}\r
+.form-group {\r
+  margin-bottom: 15px;\r
+}\r
+.radio,\r
+.checkbox {\r
+  display: block;\r
+  min-height: 20px;\r
+  margin-top: 10px;\r
+  margin-bottom: 10px;\r
+  padding-left: 20px;\r
+}\r
+.radio label,\r
+.checkbox label {\r
+  display: inline;\r
+  font-weight: normal;\r
+  cursor: pointer;\r
+}\r
+.radio input[type="radio"],\r
+.radio-inline input[type="radio"],\r
+.checkbox input[type="checkbox"],\r
+.checkbox-inline input[type="checkbox"] {\r
+  float: left;\r
+  margin-left: -20px;\r
+}\r
+.radio + .radio,\r
+.checkbox + .checkbox {\r
+  margin-top: -5px;\r
+}\r
+.radio-inline,\r
+.checkbox-inline {\r
+  display: inline-block;\r
+  padding-left: 20px;\r
+  margin-bottom: 0;\r
+  vertical-align: middle;\r
+  font-weight: normal;\r
+  cursor: pointer;\r
+}\r
+.radio-inline + .radio-inline,\r
+.checkbox-inline + .checkbox-inline {\r
+  margin-top: 0;\r
+  margin-left: 10px;\r
+}\r
+input[type="radio"][disabled],\r
+input[type="checkbox"][disabled],\r
+.radio[disabled],\r
+.radio-inline[disabled],\r
+.checkbox[disabled],\r
+.checkbox-inline[disabled],\r
+fieldset[disabled] input[type="radio"],\r
+fieldset[disabled] input[type="checkbox"],\r
+fieldset[disabled] .radio,\r
+fieldset[disabled] .radio-inline,\r
+fieldset[disabled] .checkbox,\r
+fieldset[disabled] .checkbox-inline {\r
+  cursor: not-allowed;\r
+}\r
+.input-sm {\r
+  height: 30px;\r
+  padding: 5px 10px;\r
+  font-size: 12px;\r
+  line-height: 1.5;\r
+  border-radius: 0px;\r
+}\r
+select.input-sm {\r
+  height: 30px;\r
+  line-height: 30px;\r
+}\r
+textarea.input-sm,\r
+select[multiple].input-sm {\r
+  height: auto;\r
+}\r
+.input-lg {\r
+  height: 45px;\r
+  padding: 10px 16px;\r
+  font-size: 18px;\r
+  line-height: 1.33;\r
+  border-radius: 0px;\r
+}\r
+select.input-lg {\r
+  height: 45px;\r
+  line-height: 45px;\r
+}\r
+textarea.input-lg,\r
+select[multiple].input-lg {\r
+  height: auto;\r
+}\r
+.has-feedback {\r
+  position: relative;\r
+}\r
+.has-feedback .form-control {\r
+  padding-right: 42.5px;\r
+}\r
+.has-feedback .form-control-feedback {\r
+  position: absolute;\r
+  top: 25px;\r
+  right: 0;\r
+  display: block;\r
+  width: 34px;\r
+  height: 34px;\r
+  line-height: 34px;\r
+  text-align: center;\r
+}\r
+.has-success .help-block,\r
+.has-success .control-label,\r
+.has-success .radio,\r
+.has-success .checkbox,\r
+.has-success .radio-inline,\r
+.has-success .checkbox-inline {\r
+  color: #5cb85c;\r
+}\r
+.has-success .form-control {\r
+  border-color: #5cb85c;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+}\r
+.has-success .form-control:focus {\r
+  border-color: #449d44;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #a3d7a3;\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #a3d7a3;\r
+}\r
+.has-success .input-group-addon {\r
+  color: #5cb85c;\r
+  border-color: #5cb85c;\r
+  background-color: #dff0d8;\r
+}\r
+.has-success .form-control-feedback {\r
+  color: #5cb85c;\r
+}\r
+.has-warning .help-block,\r
+.has-warning .control-label,\r
+.has-warning .radio,\r
+.has-warning .checkbox,\r
+.has-warning .radio-inline,\r
+.has-warning .checkbox-inline {\r
+  color: #f0ad4e;\r
+}\r
+.has-warning .form-control {\r
+  border-color: #f0ad4e;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+}\r
+.has-warning .form-control:focus {\r
+  border-color: #ec971f;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #f8d9ac;\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #f8d9ac;\r
+}\r
+.has-warning .input-group-addon {\r
+  color: #f0ad4e;\r
+  border-color: #f0ad4e;\r
+  background-color: #fcf8e3;\r
+}\r
+.has-warning .form-control-feedback {\r
+  color: #f0ad4e;\r
+}\r
+.has-error .help-block,\r
+.has-error .control-label,\r
+.has-error .radio,\r
+.has-error .checkbox,\r
+.has-error .radio-inline,\r
+.has-error .checkbox-inline {\r
+  color: #d9534f;\r
+}\r
+.has-error .form-control {\r
+  border-color: #d9534f;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);\r
+}\r
+.has-error .form-control:focus {\r
+  border-color: #c9302c;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #eba5a3;\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #eba5a3;\r
+}\r
+.has-error .input-group-addon {\r
+  color: #d9534f;\r
+  border-color: #d9534f;\r
+  background-color: #f2dede;\r
+}\r
+.has-error .form-control-feedback {\r
+  color: #d9534f;\r
+}\r
+.form-control-static {\r
+  margin-bottom: 0;\r
+}\r
+.help-block {\r
+  display: block;\r
+  margin-top: 5px;\r
+  margin-bottom: 10px;\r
+  color: #737373;\r
+}\r
+@media (min-width: 768px) {\r
+  .form-inline .form-group {\r
+    display: inline-block;\r
+    margin-bottom: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .form-inline .form-control {\r
+    display: inline-block;\r
+    width: auto;\r
+    vertical-align: middle;\r
+  }\r
+  .form-inline .input-group > .form-control {\r
+    width: 100%;\r
+  }\r
+  .form-inline .control-label {\r
+    margin-bottom: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .form-inline .radio,\r
+  .form-inline .checkbox {\r
+    display: inline-block;\r
+    margin-top: 0;\r
+    margin-bottom: 0;\r
+    padding-left: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .form-inline .radio input[type="radio"],\r
+  .form-inline .checkbox input[type="checkbox"] {\r
+    float: none;\r
+    margin-left: 0;\r
+  }\r
+  .form-inline .has-feedback .form-control-feedback {\r
+    top: 0;\r
+  }\r
+}\r
+.form-horizontal .control-label,\r
+.form-horizontal .radio,\r
+.form-horizontal .checkbox,\r
+.form-horizontal .radio-inline,\r
+.form-horizontal .checkbox-inline {\r
+  margin-top: 0;\r
+  margin-bottom: 0;\r
+  padding-top: 7px;\r
+}\r
+.form-horizontal .radio,\r
+.form-horizontal .checkbox {\r
+  min-height: 27px;\r
+}\r
+.form-horizontal .form-group {\r
+  margin-left: -15px;\r
+  margin-right: -15px;\r
+}\r
+.form-horizontal .form-control-static {\r
+  padding-top: 7px;\r
+}\r
+@media (min-width: 768px) {\r
+  .form-horizontal .control-label {\r
+    text-align: right;\r
+  }\r
+}\r
+.form-horizontal .has-feedback .form-control-feedback {\r
+  top: 0;\r
+  right: 15px;\r
+}\r
+.btn {\r
+  display: inline-block;\r
+  margin-bottom: 0;\r
+  font-weight: normal;\r
+  text-align: center;\r
+  vertical-align: middle;\r
+  cursor: pointer;\r
+  background-image: none;\r
+  border: 1px solid transparent;\r
+  white-space: nowrap;\r
+  padding: 6px 12px;\r
+  font-size: 14px;\r
+  line-height: 1.428571429;\r
+  border-radius: 0px;\r
+  -webkit-user-select: none;\r
+  -moz-user-select: none;\r
+  -ms-user-select: none;\r
+  -o-user-select: none;\r
+  user-select: none;\r
+}\r
+.btn:focus {\r
+  outline: thin dotted;\r
+  outline: 5px auto -webkit-focus-ring-color;\r
+  outline-offset: -2px;\r
+}\r
+.btn:hover,\r
+.btn:focus {\r
+  color: #333333;\r
+  text-decoration: none;\r
+}\r
+.btn:active,\r
+.btn.active {\r
+  outline: 0;\r
+  background-image: none;\r
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\r
+  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\r
+}\r
+.btn.disabled,\r
+.btn[disabled],\r
+fieldset[disabled] .btn {\r
+  cursor: not-allowed;\r
+  pointer-events: none;\r
+  opacity: 0.65;\r
+  filter: alpha(opacity=65);\r
+  -webkit-box-shadow: none;\r
+  box-shadow: none;\r
+}\r
+.btn-default {\r
+  color: #333333;\r
+  background-color: #ffffff;\r
+  border-color: #cccccc;\r
+}\r
+.btn-default:hover,\r
+.btn-default:focus,\r
+.btn-default:active,\r
+.btn-default.active,\r
+.open .dropdown-toggle.btn-default {\r
+  color: #333333;\r
+  background-color: #ebebeb;\r
+  border-color: #adadad;\r
+}\r
+.btn-default:active,\r
+.btn-default.active,\r
+.open .dropdown-toggle.btn-default {\r
+  background-image: none;\r
+}\r
+.btn-default.disabled,\r
+.btn-default[disabled],\r
+fieldset[disabled] .btn-default,\r
+.btn-default.disabled:hover,\r
+.btn-default[disabled]:hover,\r
+fieldset[disabled] .btn-default:hover,\r
+.btn-default.disabled:focus,\r
+.btn-default[disabled]:focus,\r
+fieldset[disabled] .btn-default:focus,\r
+.btn-default.disabled:active,\r
+.btn-default[disabled]:active,\r
+fieldset[disabled] .btn-default:active,\r
+.btn-default.disabled.active,\r
+.btn-default[disabled].active,\r
+fieldset[disabled] .btn-default.active {\r
+  background-color: #ffffff;\r
+  border-color: #cccccc;\r
+}\r
+.btn-default .badge {\r
+  color: #ffffff;\r
+  background-color: #333333;\r
+}\r
+.btn-primary {\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+  border-color: #0b721b;\r
+}\r
+.btn-primary:hover,\r
+.btn-primary:focus,\r
+.btn-primary:active,\r
+.btn-primary.active,\r
+.open .dropdown-toggle.btn-primary {\r
+  color: #ffffff;\r
+  background-color: #096418;\r
+  border-color: #053a0e;\r
+}\r
+.btn-primary:active,\r
+.btn-primary.active,\r
+.open .dropdown-toggle.btn-primary {\r
+  background-image: none;\r
+}\r
+.btn-primary.disabled,\r
+.btn-primary[disabled],\r
+fieldset[disabled] .btn-primary,\r
+.btn-primary.disabled:hover,\r
+.btn-primary[disabled]:hover,\r
+fieldset[disabled] .btn-primary:hover,\r
+.btn-primary.disabled:focus,\r
+.btn-primary[disabled]:focus,\r
+fieldset[disabled] .btn-primary:focus,\r
+.btn-primary.disabled:active,\r
+.btn-primary[disabled]:active,\r
+fieldset[disabled] .btn-primary:active,\r
+.btn-primary.disabled.active,\r
+.btn-primary[disabled].active,\r
+fieldset[disabled] .btn-primary.active {\r
+  background-color: #0d8921;\r
+  border-color: #0b721b;\r
+}\r
+.btn-primary .badge {\r
+  color: #0d8921;\r
+  background-color: #ffffff;\r
+}\r
+.btn-success {\r
+  color: #ffffff;\r
+  background-color: #5cb85c;\r
+  border-color: #4cae4c;\r
+}\r
+.btn-success:hover,\r
+.btn-success:focus,\r
+.btn-success:active,\r
+.btn-success.active,\r
+.open .dropdown-toggle.btn-success {\r
+  color: #ffffff;\r
+  background-color: #47a447;\r
+  border-color: #398439;\r
+}\r
+.btn-success:active,\r
+.btn-success.active,\r
+.open .dropdown-toggle.btn-success {\r
+  background-image: none;\r
+}\r
+.btn-success.disabled,\r
+.btn-success[disabled],\r
+fieldset[disabled] .btn-success,\r
+.btn-success.disabled:hover,\r
+.btn-success[disabled]:hover,\r
+fieldset[disabled] .btn-success:hover,\r
+.btn-success.disabled:focus,\r
+.btn-success[disabled]:focus,\r
+fieldset[disabled] .btn-success:focus,\r
+.btn-success.disabled:active,\r
+.btn-success[disabled]:active,\r
+fieldset[disabled] .btn-success:active,\r
+.btn-success.disabled.active,\r
+.btn-success[disabled].active,\r
+fieldset[disabled] .btn-success.active {\r
+  background-color: #5cb85c;\r
+  border-color: #4cae4c;\r
+}\r
+.btn-success .badge {\r
+  color: #5cb85c;\r
+  background-color: #ffffff;\r
+}\r
+.btn-info {\r
+  color: #ffffff;\r
+  background-color: #5bc0de;\r
+  border-color: #46b8da;\r
+}\r
+.btn-info:hover,\r
+.btn-info:focus,\r
+.btn-info:active,\r
+.btn-info.active,\r
+.open .dropdown-toggle.btn-info {\r
+  color: #ffffff;\r
+  background-color: #39b3d7;\r
+  border-color: #269abc;\r
+}\r
+.btn-info:active,\r
+.btn-info.active,\r
+.open .dropdown-toggle.btn-info {\r
+  background-image: none;\r
+}\r
+.btn-info.disabled,\r
+.btn-info[disabled],\r
+fieldset[disabled] .btn-info,\r
+.btn-info.disabled:hover,\r
+.btn-info[disabled]:hover,\r
+fieldset[disabled] .btn-info:hover,\r
+.btn-info.disabled:focus,\r
+.btn-info[disabled]:focus,\r
+fieldset[disabled] .btn-info:focus,\r
+.btn-info.disabled:active,\r
+.btn-info[disabled]:active,\r
+fieldset[disabled] .btn-info:active,\r
+.btn-info.disabled.active,\r
+.btn-info[disabled].active,\r
+fieldset[disabled] .btn-info.active {\r
+  background-color: #5bc0de;\r
+  border-color: #46b8da;\r
+}\r
+.btn-info .badge {\r
+  color: #5bc0de;\r
+  background-color: #ffffff;\r
+}\r
+.btn-warning {\r
+  color: #ffffff;\r
+  background-color: #f0ad4e;\r
+  border-color: #eea236;\r
+}\r
+.btn-warning:hover,\r
+.btn-warning:focus,\r
+.btn-warning:active,\r
+.btn-warning.active,\r
+.open .dropdown-toggle.btn-warning {\r
+  color: #ffffff;\r
+  background-color: #ed9c28;\r
+  border-color: #d58512;\r
+}\r
+.btn-warning:active,\r
+.btn-warning.active,\r
+.open .dropdown-toggle.btn-warning {\r
+  background-image: none;\r
+}\r
+.btn-warning.disabled,\r
+.btn-warning[disabled],\r
+fieldset[disabled] .btn-warning,\r
+.btn-warning.disabled:hover,\r
+.btn-warning[disabled]:hover,\r
+fieldset[disabled] .btn-warning:hover,\r
+.btn-warning.disabled:focus,\r
+.btn-warning[disabled]:focus,\r
+fieldset[disabled] .btn-warning:focus,\r
+.btn-warning.disabled:active,\r
+.btn-warning[disabled]:active,\r
+fieldset[disabled] .btn-warning:active,\r
+.btn-warning.disabled.active,\r
+.btn-warning[disabled].active,\r
+fieldset[disabled] .btn-warning.active {\r
+  background-color: #f0ad4e;\r
+  border-color: #eea236;\r
+}\r
+.btn-warning .badge {\r
+  color: #f0ad4e;\r
+  background-color: #ffffff;\r
+}\r
+.btn-danger {\r
+  color: #ffffff;\r
+  background-color: #d9534f;\r
+  border-color: #d43f3a;\r
+}\r
+.btn-danger:hover,\r
+.btn-danger:focus,\r
+.btn-danger:active,\r
+.btn-danger.active,\r
+.open .dropdown-toggle.btn-danger {\r
+  color: #ffffff;\r
+  background-color: #d2322d;\r
+  border-color: #ac2925;\r
+}\r
+.btn-danger:active,\r
+.btn-danger.active,\r
+.open .dropdown-toggle.btn-danger {\r
+  background-image: none;\r
+}\r
+.btn-danger.disabled,\r
+.btn-danger[disabled],\r
+fieldset[disabled] .btn-danger,\r
+.btn-danger.disabled:hover,\r
+.btn-danger[disabled]:hover,\r
+fieldset[disabled] .btn-danger:hover,\r
+.btn-danger.disabled:focus,\r
+.btn-danger[disabled]:focus,\r
+fieldset[disabled] .btn-danger:focus,\r
+.btn-danger.disabled:active,\r
+.btn-danger[disabled]:active,\r
+fieldset[disabled] .btn-danger:active,\r
+.btn-danger.disabled.active,\r
+.btn-danger[disabled].active,\r
+fieldset[disabled] .btn-danger.active {\r
+  background-color: #d9534f;\r
+  border-color: #d43f3a;\r
+}\r
+.btn-danger .badge {\r
+  color: #d9534f;\r
+  background-color: #ffffff;\r
+}\r
+.btn-link {\r
+  color: #0d8921;\r
+  font-weight: normal;\r
+  cursor: pointer;\r
+  border-radius: 0;\r
+}\r
+.btn-link,\r
+.btn-link:active,\r
+.btn-link[disabled],\r
+fieldset[disabled] .btn-link {\r
+  background-color: transparent;\r
+  -webkit-box-shadow: none;\r
+  box-shadow: none;\r
+}\r
+.btn-link,\r
+.btn-link:hover,\r
+.btn-link:focus,\r
+.btn-link:active {\r
+  border-color: transparent;\r
+}\r
+.btn-link:hover,\r
+.btn-link:focus {\r
+  color: #064310;\r
+  text-decoration: underline;\r
+  background-color: transparent;\r
+}\r
+.btn-link[disabled]:hover,\r
+fieldset[disabled] .btn-link:hover,\r
+.btn-link[disabled]:focus,\r
+fieldset[disabled] .btn-link:focus {\r
+  color: #999999;\r
+  text-decoration: none;\r
+}\r
+.btn-lg,\r
+.btn-group-lg > .btn {\r
+  padding: 10px 16px;\r
+  font-size: 18px;\r
+  line-height: 1.33;\r
+  border-radius: 0px;\r
+}\r
+.btn-sm,\r
+.btn-group-sm > .btn {\r
+  padding: 5px 10px;\r
+  font-size: 12px;\r
+  line-height: 1.5;\r
+  border-radius: 0px;\r
+}\r
+.btn-xs,\r
+.btn-group-xs > .btn {\r
+  padding: 1px 5px;\r
+  font-size: 12px;\r
+  line-height: 1.5;\r
+  border-radius: 0px;\r
+}\r
+.btn-block {\r
+  display: block;\r
+  width: 100%;\r
+  padding-left: 0;\r
+  padding-right: 0;\r
+}\r
+.btn-block + .btn-block {\r
+  margin-top: 5px;\r
+}\r
+input[type="submit"].btn-block,\r
+input[type="reset"].btn-block,\r
+input[type="button"].btn-block {\r
+  width: 100%;\r
+}\r
+.fade {\r
+  opacity: 0;\r
+  -webkit-transition: opacity 0.15s linear;\r
+  transition: opacity 0.15s linear;\r
+}\r
+.fade.in {\r
+  opacity: 1;\r
+}\r
+.collapse {\r
+  display: none;\r
+}\r
+.collapse.in {\r
+  display: block;\r
+}\r
+.collapsing {\r
+  position: relative;\r
+  height: 0;\r
+  overflow: hidden;\r
+  -webkit-transition: height 0.35s ease;\r
+  transition: height 0.35s ease;\r
+}\r
+@font-face {\r
+  font-family: 'Glyphicons Halflings';\r
+  src: url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.eot');\r
+  src: url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.woff') format('woff'), url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');\r
+}\r
+.glyphicon {\r
+  position: relative;\r
+  top: 1px;\r
+  display: inline-block;\r
+  font-family: 'Glyphicons Halflings';\r
+  font-style: normal;\r
+  font-weight: normal;\r
+  line-height: 1;\r
+  -webkit-font-smoothing: antialiased;\r
+  -moz-osx-font-smoothing: grayscale;\r
+}\r
+.glyphicon-asterisk:before {\r
+  content: "\2a";\r
+}\r
+.glyphicon-plus:before {\r
+  content: "\2b";\r
+}\r
+.glyphicon-euro:before {\r
+  content: "\20ac";\r
+}\r
+.glyphicon-minus:before {\r
+  content: "\2212";\r
+}\r
+.glyphicon-cloud:before {\r
+  content: "\2601";\r
+}\r
+.glyphicon-envelope:before {\r
+  content: "\2709";\r
+}\r
+.glyphicon-pencil:before {\r
+  content: "\270f";\r
+}\r
+.glyphicon-glass:before {\r
+  content: "\e001";\r
+}\r
+.glyphicon-music:before {\r
+  content: "\e002";\r
+}\r
+.glyphicon-search:before {\r
+  content: "\e003";\r
+}\r
+.glyphicon-heart:before {\r
+  content: "\e005";\r
+}\r
+.glyphicon-star:before {\r
+  content: "\e006";\r
+}\r
+.glyphicon-star-empty:before {\r
+  content: "\e007";\r
+}\r
+.glyphicon-user:before {\r
+  content: "\e008";\r
+}\r
+.glyphicon-film:before {\r
+  content: "\e009";\r
+}\r
+.glyphicon-th-large:before {\r
+  content: "\e010";\r
+}\r
+.glyphicon-th:before {\r
+  content: "\e011";\r
+}\r
+.glyphicon-th-list:before {\r
+  content: "\e012";\r
+}\r
+.glyphicon-ok:before {\r
+  content: "\e013";\r
+}\r
+.glyphicon-remove:before {\r
+  content: "\e014";\r
+}\r
+.glyphicon-zoom-in:before {\r
+  content: "\e015";\r
+}\r
+.glyphicon-zoom-out:before {\r
+  content: "\e016";\r
+}\r
+.glyphicon-off:before {\r
+  content: "\e017";\r
+}\r
+.glyphicon-signal:before {\r
+  content: "\e018";\r
+}\r
+.glyphicon-cog:before {\r
+  content: "\e019";\r
+}\r
+.glyphicon-trash:before {\r
+  content: "\e020";\r
+}\r
+.glyphicon-home:before {\r
+  content: "\e021";\r
+}\r
+.glyphicon-file:before {\r
+  content: "\e022";\r
+}\r
+.glyphicon-time:before {\r
+  content: "\e023";\r
+}\r
+.glyphicon-road:before {\r
+  content: "\e024";\r
+}\r
+.glyphicon-download-alt:before {\r
+  content: "\e025";\r
+}\r
+.glyphicon-download:before {\r
+  content: "\e026";\r
+}\r
+.glyphicon-upload:before {\r
+  content: "\e027";\r
+}\r
+.glyphicon-inbox:before {\r
+  content: "\e028";\r
+}\r
+.glyphicon-play-circle:before {\r
+  content: "\e029";\r
+}\r
+.glyphicon-repeat:before {\r
+  content: "\e030";\r
+}\r
+.glyphicon-refresh:before {\r
+  content: "\e031";\r
+}\r
+.glyphicon-list-alt:before {\r
+  content: "\e032";\r
+}\r
+.glyphicon-lock:before {\r
+  content: "\e033";\r
+}\r
+.glyphicon-flag:before {\r
+  content: "\e034";\r
+}\r
+.glyphicon-headphones:before {\r
+  content: "\e035";\r
+}\r
+.glyphicon-volume-off:before {\r
+  content: "\e036";\r
+}\r
+.glyphicon-volume-down:before {\r
+  content: "\e037";\r
+}\r
+.glyphicon-volume-up:before {\r
+  content: "\e038";\r
+}\r
+.glyphicon-qrcode:before {\r
+  content: "\e039";\r
+}\r
+.glyphicon-barcode:before {\r
+  content: "\e040";\r
+}\r
+.glyphicon-tag:before {\r
+  content: "\e041";\r
+}\r
+.glyphicon-tags:before {\r
+  content: "\e042";\r
+}\r
+.glyphicon-book:before {\r
+  content: "\e043";\r
+}\r
+.glyphicon-bookmark:before {\r
+  content: "\e044";\r
+}\r
+.glyphicon-print:before {\r
+  content: "\e045";\r
+}\r
+.glyphicon-camera:before {\r
+  content: "\e046";\r
+}\r
+.glyphicon-font:before {\r
+  content: "\e047";\r
+}\r
+.glyphicon-bold:before {\r
+  content: "\e048";\r
+}\r
+.glyphicon-italic:before {\r
+  content: "\e049";\r
+}\r
+.glyphicon-text-height:before {\r
+  content: "\e050";\r
+}\r
+.glyphicon-text-width:before {\r
+  content: "\e051";\r
+}\r
+.glyphicon-align-left:before {\r
+  content: "\e052";\r
+}\r
+.glyphicon-align-center:before {\r
+  content: "\e053";\r
+}\r
+.glyphicon-align-right:before {\r
+  content: "\e054";\r
+}\r
+.glyphicon-align-justify:before {\r
+  content: "\e055";\r
+}\r
+.glyphicon-list:before {\r
+  content: "\e056";\r
+}\r
+.glyphicon-indent-left:before {\r
+  content: "\e057";\r
+}\r
+.glyphicon-indent-right:before {\r
+  content: "\e058";\r
+}\r
+.glyphicon-facetime-video:before {\r
+  content: "\e059";\r
+}\r
+.glyphicon-picture:before {\r
+  content: "\e060";\r
+}\r
+.glyphicon-map-marker:before {\r
+  content: "\e062";\r
+}\r
+.glyphicon-adjust:before {\r
+  content: "\e063";\r
+}\r
+.glyphicon-tint:before {\r
+  content: "\e064";\r
+}\r
+.glyphicon-edit:before {\r
+  content: "\e065";\r
+}\r
+.glyphicon-share:before {\r
+  content: "\e066";\r
+}\r
+.glyphicon-check:before {\r
+  content: "\e067";\r
+}\r
+.glyphicon-move:before {\r
+  content: "\e068";\r
+}\r
+.glyphicon-step-backward:before {\r
+  content: "\e069";\r
+}\r
+.glyphicon-fast-backward:before {\r
+  content: "\e070";\r
+}\r
+.glyphicon-backward:before {\r
+  content: "\e071";\r
+}\r
+.glyphicon-play:before {\r
+  content: "\e072";\r
+}\r
+.glyphicon-pause:before {\r
+  content: "\e073";\r
+}\r
+.glyphicon-stop:before {\r
+  content: "\e074";\r
+}\r
+.glyphicon-forward:before {\r
+  content: "\e075";\r
+}\r
+.glyphicon-fast-forward:before {\r
+  content: "\e076";\r
+}\r
+.glyphicon-step-forward:before {\r
+  content: "\e077";\r
+}\r
+.glyphicon-eject:before {\r
+  content: "\e078";\r
+}\r
+.glyphicon-chevron-left:before {\r
+  content: "\e079";\r
+}\r
+.glyphicon-chevron-right:before {\r
+  content: "\e080";\r
+}\r
+.glyphicon-plus-sign:before {\r
+  content: "\e081";\r
+}\r
+.glyphicon-minus-sign:before {\r
+  content: "\e082";\r
+}\r
+.glyphicon-remove-sign:before {\r
+  content: "\e083";\r
+}\r
+.glyphicon-ok-sign:before {\r
+  content: "\e084";\r
+}\r
+.glyphicon-question-sign:before {\r
+  content: "\e085";\r
+}\r
+.glyphicon-info-sign:before {\r
+  content: "\e086";\r
+}\r
+.glyphicon-screenshot:before {\r
+  content: "\e087";\r
+}\r
+.glyphicon-remove-circle:before {\r
+  content: "\e088";\r
+}\r
+.glyphicon-ok-circle:before {\r
+  content: "\e089";\r
+}\r
+.glyphicon-ban-circle:before {\r
+  content: "\e090";\r
+}\r
+.glyphicon-arrow-left:before {\r
+  content: "\e091";\r
+}\r
+.glyphicon-arrow-right:before {\r
+  content: "\e092";\r
+}\r
+.glyphicon-arrow-up:before {\r
+  content: "\e093";\r
+}\r
+.glyphicon-arrow-down:before {\r
+  content: "\e094";\r
+}\r
+.glyphicon-share-alt:before {\r
+  content: "\e095";\r
+}\r
+.glyphicon-resize-full:before {\r
+  content: "\e096";\r
+}\r
+.glyphicon-resize-small:before {\r
+  content: "\e097";\r
+}\r
+.glyphicon-exclamation-sign:before {\r
+  content: "\e101";\r
+}\r
+.glyphicon-gift:before {\r
+  content: "\e102";\r
+}\r
+.glyphicon-leaf:before {\r
+  content: "\e103";\r
+}\r
+.glyphicon-fire:before {\r
+  content: "\e104";\r
+}\r
+.glyphicon-eye-open:before {\r
+  content: "\e105";\r
+}\r
+.glyphicon-eye-close:before {\r
+  content: "\e106";\r
+}\r
+.glyphicon-warning-sign:before {\r
+  content: "\e107";\r
+}\r
+.glyphicon-plane:before {\r
+  content: "\e108";\r
+}\r
+.glyphicon-calendar:before {\r
+  content: "\e109";\r
+}\r
+.glyphicon-random:before {\r
+  content: "\e110";\r
+}\r
+.glyphicon-comment:before {\r
+  content: "\e111";\r
+}\r
+.glyphicon-magnet:before {\r
+  content: "\e112";\r
+}\r
+.glyphicon-chevron-up:before {\r
+  content: "\e113";\r
+}\r
+.glyphicon-chevron-down:before {\r
+  content: "\e114";\r
+}\r
+.glyphicon-retweet:before {\r
+  content: "\e115";\r
+}\r
+.glyphicon-shopping-cart:before {\r
+  content: "\e116";\r
+}\r
+.glyphicon-folder-close:before {\r
+  content: "\e117";\r
+}\r
+.glyphicon-folder-open:before {\r
+  content: "\e118";\r
+}\r
+.glyphicon-resize-vertical:before {\r
+  content: "\e119";\r
+}\r
+.glyphicon-resize-horizontal:before {\r
+  content: "\e120";\r
+}\r
+.glyphicon-hdd:before {\r
+  content: "\e121";\r
+}\r
+.glyphicon-bullhorn:before {\r
+  content: "\e122";\r
+}\r
+.glyphicon-bell:before {\r
+  content: "\e123";\r
+}\r
+.glyphicon-certificate:before {\r
+  content: "\e124";\r
+}\r
+.glyphicon-thumbs-up:before {\r
+  content: "\e125";\r
+}\r
+.glyphicon-thumbs-down:before {\r
+  content: "\e126";\r
+}\r
+.glyphicon-hand-right:before {\r
+  content: "\e127";\r
+}\r
+.glyphicon-hand-left:before {\r
+  content: "\e128";\r
+}\r
+.glyphicon-hand-up:before {\r
+  content: "\e129";\r
+}\r
+.glyphicon-hand-down:before {\r
+  content: "\e130";\r
+}\r
+.glyphicon-circle-arrow-right:before {\r
+  content: "\e131";\r
+}\r
+.glyphicon-circle-arrow-left:before {\r
+  content: "\e132";\r
+}\r
+.glyphicon-circle-arrow-up:before {\r
+  content: "\e133";\r
+}\r
+.glyphicon-circle-arrow-down:before {\r
+  content: "\e134";\r
+}\r
+.glyphicon-globe:before {\r
+  content: "\e135";\r
+}\r
+.glyphicon-wrench:before {\r
+  content: "\e136";\r
+}\r
+.glyphicon-tasks:before {\r
+  content: "\e137";\r
+}\r
+.glyphicon-filter:before {\r
+  content: "\e138";\r
+}\r
+.glyphicon-briefcase:before {\r
+  content: "\e139";\r
+}\r
+.glyphicon-fullscreen:before {\r
+  content: "\e140";\r
+}\r
+.glyphicon-dashboard:before {\r
+  content: "\e141";\r
+}\r
+.glyphicon-paperclip:before {\r
+  content: "\e142";\r
+}\r
+.glyphicon-heart-empty:before {\r
+  content: "\e143";\r
+}\r
+.glyphicon-link:before {\r
+  content: "\e144";\r
+}\r
+.glyphicon-phone:before {\r
+  content: "\e145";\r
+}\r
+.glyphicon-pushpin:before {\r
+  content: "\e146";\r
+}\r
+.glyphicon-usd:before {\r
+  content: "\e148";\r
+}\r
+.glyphicon-gbp:before {\r
+  content: "\e149";\r
+}\r
+.glyphicon-sort:before {\r
+  content: "\e150";\r
+}\r
+.glyphicon-sort-by-alphabet:before {\r
+  content: "\e151";\r
+}\r
+.glyphicon-sort-by-alphabet-alt:before {\r
+  content: "\e152";\r
+}\r
+.glyphicon-sort-by-order:before {\r
+  content: "\e153";\r
+}\r
+.glyphicon-sort-by-order-alt:before {\r
+  content: "\e154";\r
+}\r
+.glyphicon-sort-by-attributes:before {\r
+  content: "\e155";\r
+}\r
+.glyphicon-sort-by-attributes-alt:before {\r
+  content: "\e156";\r
+}\r
+.glyphicon-unchecked:before {\r
+  content: "\e157";\r
+}\r
+.glyphicon-expand:before {\r
+  content: "\e158";\r
+}\r
+.glyphicon-collapse-down:before {\r
+  content: "\e159";\r
+}\r
+.glyphicon-collapse-up:before {\r
+  content: "\e160";\r
+}\r
+.glyphicon-log-in:before {\r
+  content: "\e161";\r
+}\r
+.glyphicon-flash:before {\r
+  content: "\e162";\r
+}\r
+.glyphicon-log-out:before {\r
+  content: "\e163";\r
+}\r
+.glyphicon-new-window:before {\r
+  content: "\e164";\r
+}\r
+.glyphicon-record:before {\r
+  content: "\e165";\r
+}\r
+.glyphicon-save:before {\r
+  content: "\e166";\r
+}\r
+.glyphicon-open:before {\r
+  content: "\e167";\r
+}\r
+.glyphicon-saved:before {\r
+  content: "\e168";\r
+}\r
+.glyphicon-import:before {\r
+  content: "\e169";\r
+}\r
+.glyphicon-export:before {\r
+  content: "\e170";\r
+}\r
+.glyphicon-send:before {\r
+  content: "\e171";\r
+}\r
+.glyphicon-floppy-disk:before {\r
+  content: "\e172";\r
+}\r
+.glyphicon-floppy-saved:before {\r
+  content: "\e173";\r
+}\r
+.glyphicon-floppy-remove:before {\r
+  content: "\e174";\r
+}\r
+.glyphicon-floppy-save:before {\r
+  content: "\e175";\r
+}\r
+.glyphicon-floppy-open:before {\r
+  content: "\e176";\r
+}\r
+.glyphicon-credit-card:before {\r
+  content: "\e177";\r
+}\r
+.glyphicon-transfer:before {\r
+  content: "\e178";\r
+}\r
+.glyphicon-cutlery:before {\r
+  content: "\e179";\r
+}\r
+.glyphicon-header:before {\r
+  content: "\e180";\r
+}\r
+.glyphicon-compressed:before {\r
+  content: "\e181";\r
+}\r
+.glyphicon-earphone:before {\r
+  content: "\e182";\r
+}\r
+.glyphicon-phone-alt:before {\r
+  content: "\e183";\r
+}\r
+.glyphicon-tower:before {\r
+  content: "\e184";\r
+}\r
+.glyphicon-stats:before {\r
+  content: "\e185";\r
+}\r
+.glyphicon-sd-video:before {\r
+  content: "\e186";\r
+}\r
+.glyphicon-hd-video:before {\r
+  content: "\e187";\r
+}\r
+.glyphicon-subtitles:before {\r
+  content: "\e188";\r
+}\r
+.glyphicon-sound-stereo:before {\r
+  content: "\e189";\r
+}\r
+.glyphicon-sound-dolby:before {\r
+  content: "\e190";\r
+}\r
+.glyphicon-sound-5-1:before {\r
+  content: "\e191";\r
+}\r
+.glyphicon-sound-6-1:before {\r
+  content: "\e192";\r
+}\r
+.glyphicon-sound-7-1:before {\r
+  content: "\e193";\r
+}\r
+.glyphicon-copyright-mark:before {\r
+  content: "\e194";\r
+}\r
+.glyphicon-registration-mark:before {\r
+  content: "\e195";\r
+}\r
+.glyphicon-cloud-download:before {\r
+  content: "\e197";\r
+}\r
+.glyphicon-cloud-upload:before {\r
+  content: "\e198";\r
+}\r
+.glyphicon-tree-conifer:before {\r
+  content: "\e199";\r
+}\r
+.glyphicon-tree-deciduous:before {\r
+  content: "\e200";\r
+}\r
+.caret {\r
+  display: inline-block;\r
+  width: 0;\r
+  height: 0;\r
+  margin-left: 2px;\r
+  vertical-align: middle;\r
+  border-top: 4px solid;\r
+  border-right: 4px solid transparent;\r
+  border-left: 4px solid transparent;\r
+}\r
+.dropdown {\r
+  position: relative;\r
+}\r
+.dropdown-toggle:focus {\r
+  outline: 0;\r
+}\r
+.dropdown-menu {\r
+  position: absolute;\r
+  top: 100%;\r
+  left: 0;\r
+  z-index: 1000;\r
+  display: none;\r
+  float: left;\r
+  min-width: 160px;\r
+  padding: 5px 0;\r
+  margin: 2px 0 0;\r
+  list-style: none;\r
+  font-size: 14px;\r
+  background-color: #ffffff;\r
+  border: 1px solid #cccccc;\r
+  border: 1px solid rgba(0, 0, 0, 0.15);\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\r
+  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);\r
+  background-clip: padding-box;\r
+}\r
+.dropdown-menu.pull-right {\r
+  right: 0;\r
+  left: auto;\r
+}\r
+.dropdown-menu .divider {\r
+  height: 1px;\r
+  margin: 9px 0;\r
+  overflow: hidden;\r
+  background-color: #e5e5e5;\r
+}\r
+.dropdown-menu > li > a {\r
+  display: block;\r
+  padding: 3px 20px;\r
+  clear: both;\r
+  font-weight: normal;\r
+  line-height: 1.428571429;\r
+  color: #333333;\r
+  white-space: nowrap;\r
+}\r
+.dropdown-menu > li > a:hover,\r
+.dropdown-menu > li > a:focus {\r
+  text-decoration: none;\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+}\r
+.dropdown-menu > .active > a,\r
+.dropdown-menu > .active > a:hover,\r
+.dropdown-menu > .active > a:focus {\r
+  color: #ffffff;\r
+  text-decoration: none;\r
+  outline: 0;\r
+  background-color: #0d8921;\r
+}\r
+.dropdown-menu > .disabled > a,\r
+.dropdown-menu > .disabled > a:hover,\r
+.dropdown-menu > .disabled > a:focus {\r
+  color: #999999;\r
+}\r
+.dropdown-menu > .disabled > a:hover,\r
+.dropdown-menu > .disabled > a:focus {\r
+  text-decoration: none;\r
+  background-color: transparent;\r
+  background-image: none;\r
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);\r
+  cursor: not-allowed;\r
+}\r
+.open > .dropdown-menu {\r
+  display: block;\r
+}\r
+.open > a {\r
+  outline: 0;\r
+}\r
+.dropdown-menu-right {\r
+  left: auto;\r
+  right: 0;\r
+}\r
+.dropdown-menu-left {\r
+  left: 0;\r
+  right: auto;\r
+}\r
+.dropdown-header {\r
+  display: block;\r
+  padding: 3px 20px;\r
+  font-size: 12px;\r
+  line-height: 1.428571429;\r
+  color: #999999;\r
+}\r
+.dropdown-backdrop {\r
+  position: fixed;\r
+  left: 0;\r
+  right: 0;\r
+  bottom: 0;\r
+  top: 0;\r
+  z-index: 990;\r
+}\r
+.pull-right > .dropdown-menu {\r
+  right: 0;\r
+  left: auto;\r
+}\r
+.dropup .caret,\r
+.navbar-fixed-bottom .dropdown .caret {\r
+  border-top: 0;\r
+  border-bottom: 4px solid;\r
+  content: "";\r
+}\r
+.dropup .dropdown-menu,\r
+.navbar-fixed-bottom .dropdown .dropdown-menu {\r
+  top: auto;\r
+  bottom: 100%;\r
+  margin-bottom: 1px;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-right .dropdown-menu {\r
+    left: auto;\r
+    right: 0;\r
+  }\r
+  .navbar-right .dropdown-menu-left {\r
+    left: 0;\r
+    right: auto;\r
+  }\r
+}\r
+.btn-group,\r
+.btn-group-vertical {\r
+  position: relative;\r
+  display: inline-block;\r
+  vertical-align: middle;\r
+}\r
+.btn-group > .btn,\r
+.btn-group-vertical > .btn {\r
+  position: relative;\r
+  float: left;\r
+}\r
+.btn-group > .btn:hover,\r
+.btn-group-vertical > .btn:hover,\r
+.btn-group > .btn:focus,\r
+.btn-group-vertical > .btn:focus,\r
+.btn-group > .btn:active,\r
+.btn-group-vertical > .btn:active,\r
+.btn-group > .btn.active,\r
+.btn-group-vertical > .btn.active {\r
+  z-index: 2;\r
+}\r
+.btn-group > .btn:focus,\r
+.btn-group-vertical > .btn:focus {\r
+  outline: none;\r
+}\r
+.btn-group .btn + .btn,\r
+.btn-group .btn + .btn-group,\r
+.btn-group .btn-group + .btn,\r
+.btn-group .btn-group + .btn-group {\r
+  margin-left: -1px;\r
+}\r
+.btn-toolbar {\r
+  margin-left: -5px;\r
+}\r
+.btn-toolbar .btn-group,\r
+.btn-toolbar .input-group {\r
+  float: left;\r
+}\r
+.btn-toolbar > .btn,\r
+.btn-toolbar > .btn-group,\r
+.btn-toolbar > .input-group {\r
+  margin-left: 5px;\r
+}\r
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\r
+  border-radius: 0;\r
+}\r
+.btn-group > .btn:first-child {\r
+  margin-left: 0;\r
+}\r
+.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {\r
+  border-bottom-right-radius: 0;\r
+  border-top-right-radius: 0;\r
+}\r
+.btn-group > .btn:last-child:not(:first-child),\r
+.btn-group > .dropdown-toggle:not(:first-child) {\r
+  border-bottom-left-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.btn-group > .btn-group {\r
+  float: left;\r
+}\r
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\r
+  border-radius: 0;\r
+}\r
+.btn-group > .btn-group:first-child > .btn:last-child,\r
+.btn-group > .btn-group:first-child > .dropdown-toggle {\r
+  border-bottom-right-radius: 0;\r
+  border-top-right-radius: 0;\r
+}\r
+.btn-group > .btn-group:last-child > .btn:first-child {\r
+  border-bottom-left-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.btn-group .dropdown-toggle:active,\r
+.btn-group.open .dropdown-toggle {\r
+  outline: 0;\r
+}\r
+.btn-group > .btn + .dropdown-toggle {\r
+  padding-left: 8px;\r
+  padding-right: 8px;\r
+}\r
+.btn-group > .btn-lg + .dropdown-toggle {\r
+  padding-left: 12px;\r
+  padding-right: 12px;\r
+}\r
+.btn-group.open .dropdown-toggle {\r
+  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\r
+  box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\r
+}\r
+.btn-group.open .dropdown-toggle.btn-link {\r
+  -webkit-box-shadow: none;\r
+  box-shadow: none;\r
+}\r
+.btn .caret {\r
+  margin-left: 0;\r
+}\r
+.btn-lg .caret {\r
+  border-width: 5px 5px 0;\r
+  border-bottom-width: 0;\r
+}\r
+.dropup .btn-lg .caret {\r
+  border-width: 0 5px 5px;\r
+}\r
+.btn-group-vertical > .btn,\r
+.btn-group-vertical > .btn-group,\r
+.btn-group-vertical > .btn-group > .btn {\r
+  display: block;\r
+  float: none;\r
+  width: 100%;\r
+  max-width: 100%;\r
+}\r
+.btn-group-vertical > .btn-group > .btn {\r
+  float: none;\r
+}\r
+.btn-group-vertical > .btn + .btn,\r
+.btn-group-vertical > .btn + .btn-group,\r
+.btn-group-vertical > .btn-group + .btn,\r
+.btn-group-vertical > .btn-group + .btn-group {\r
+  margin-top: -1px;\r
+  margin-left: 0;\r
+}\r
+.btn-group-vertical > .btn:not(:first-child):not(:last-child) {\r
+  border-radius: 0;\r
+}\r
+.btn-group-vertical > .btn:first-child:not(:last-child) {\r
+  border-top-right-radius: 0px;\r
+  border-bottom-right-radius: 0;\r
+  border-bottom-left-radius: 0;\r
+}\r
+.btn-group-vertical > .btn:last-child:not(:first-child) {\r
+  border-bottom-left-radius: 0px;\r
+  border-top-right-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\r
+  border-radius: 0;\r
+}\r
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,\r
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {\r
+  border-bottom-right-radius: 0;\r
+  border-bottom-left-radius: 0;\r
+}\r
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\r
+  border-top-right-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.btn-group-justified {\r
+  display: table;\r
+  width: 100%;\r
+  table-layout: fixed;\r
+  border-collapse: separate;\r
+}\r
+.btn-group-justified > .btn,\r
+.btn-group-justified > .btn-group {\r
+  float: none;\r
+  display: table-cell;\r
+  width: 1%;\r
+}\r
+.btn-group-justified > .btn-group .btn {\r
+  width: 100%;\r
+}\r
+[data-toggle="buttons"] > .btn > input[type="radio"],\r
+[data-toggle="buttons"] > .btn > input[type="checkbox"] {\r
+  display: none;\r
+}\r
+.input-group {\r
+  position: relative;\r
+  display: table;\r
+  border-collapse: separate;\r
+}\r
+.input-group[class*="col-"] {\r
+  float: none;\r
+  padding-left: 0;\r
+  padding-right: 0;\r
+}\r
+.input-group .form-control {\r
+  float: left;\r
+  width: 100%;\r
+  margin-bottom: 0;\r
+}\r
+.input-group-lg > .form-control,\r
+.input-group-lg > .input-group-addon,\r
+.input-group-lg > .input-group-btn > .btn {\r
+  height: 45px;\r
+  padding: 10px 16px;\r
+  font-size: 18px;\r
+  line-height: 1.33;\r
+  border-radius: 0px;\r
+}\r
+select.input-group-lg > .form-control,\r
+select.input-group-lg > .input-group-addon,\r
+select.input-group-lg > .input-group-btn > .btn {\r
+  height: 45px;\r
+  line-height: 45px;\r
+}\r
+textarea.input-group-lg > .form-control,\r
+textarea.input-group-lg > .input-group-addon,\r
+textarea.input-group-lg > .input-group-btn > .btn,\r
+select[multiple].input-group-lg > .form-control,\r
+select[multiple].input-group-lg > .input-group-addon,\r
+select[multiple].input-group-lg > .input-group-btn > .btn {\r
+  height: auto;\r
+}\r
+.input-group-sm > .form-control,\r
+.input-group-sm > .input-group-addon,\r
+.input-group-sm > .input-group-btn > .btn {\r
+  height: 30px;\r
+  padding: 5px 10px;\r
+  font-size: 12px;\r
+  line-height: 1.5;\r
+  border-radius: 0px;\r
+}\r
+select.input-group-sm > .form-control,\r
+select.input-group-sm > .input-group-addon,\r
+select.input-group-sm > .input-group-btn > .btn {\r
+  height: 30px;\r
+  line-height: 30px;\r
+}\r
+textarea.input-group-sm > .form-control,\r
+textarea.input-group-sm > .input-group-addon,\r
+textarea.input-group-sm > .input-group-btn > .btn,\r
+select[multiple].input-group-sm > .form-control,\r
+select[multiple].input-group-sm > .input-group-addon,\r
+select[multiple].input-group-sm > .input-group-btn > .btn {\r
+  height: auto;\r
+}\r
+.input-group-addon,\r
+.input-group-btn,\r
+.input-group .form-control {\r
+  display: table-cell;\r
+}\r
+.input-group-addon:not(:first-child):not(:last-child),\r
+.input-group-btn:not(:first-child):not(:last-child),\r
+.input-group .form-control:not(:first-child):not(:last-child) {\r
+  border-radius: 0;\r
+}\r
+.input-group-addon,\r
+.input-group-btn {\r
+  width: 1%;\r
+  white-space: nowrap;\r
+  vertical-align: middle;\r
+}\r
+.input-group-addon {\r
+  padding: 6px 12px;\r
+  font-size: 14px;\r
+  font-weight: normal;\r
+  line-height: 1;\r
+  color: #555555;\r
+  text-align: center;\r
+  background-color: #eeeeee;\r
+  border: 1px solid #cccccc;\r
+  border-radius: 0px;\r
+}\r
+.input-group-addon.input-sm {\r
+  padding: 5px 10px;\r
+  font-size: 12px;\r
+  border-radius: 0px;\r
+}\r
+.input-group-addon.input-lg {\r
+  padding: 10px 16px;\r
+  font-size: 18px;\r
+  border-radius: 0px;\r
+}\r
+.input-group-addon input[type="radio"],\r
+.input-group-addon input[type="checkbox"] {\r
+  margin-top: 0;\r
+}\r
+.input-group .form-control:first-child,\r
+.input-group-addon:first-child,\r
+.input-group-btn:first-child > .btn,\r
+.input-group-btn:first-child > .btn-group > .btn,\r
+.input-group-btn:first-child > .dropdown-toggle,\r
+.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\r
+.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\r
+  border-bottom-right-radius: 0;\r
+  border-top-right-radius: 0;\r
+}\r
+.input-group-addon:first-child {\r
+  border-right: 0;\r
+}\r
+.input-group .form-control:last-child,\r
+.input-group-addon:last-child,\r
+.input-group-btn:last-child > .btn,\r
+.input-group-btn:last-child > .btn-group > .btn,\r
+.input-group-btn:last-child > .dropdown-toggle,\r
+.input-group-btn:first-child > .btn:not(:first-child),\r
+.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\r
+  border-bottom-left-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.input-group-addon:last-child {\r
+  border-left: 0;\r
+}\r
+.input-group-btn {\r
+  position: relative;\r
+  font-size: 0;\r
+  white-space: nowrap;\r
+}\r
+.input-group-btn > .btn {\r
+  position: relative;\r
+}\r
+.input-group-btn > .btn + .btn {\r
+  margin-left: -1px;\r
+}\r
+.input-group-btn > .btn:hover,\r
+.input-group-btn > .btn:focus,\r
+.input-group-btn > .btn:active {\r
+  z-index: 2;\r
+}\r
+.input-group-btn:first-child > .btn,\r
+.input-group-btn:first-child > .btn-group {\r
+  margin-right: -1px;\r
+}\r
+.input-group-btn:last-child > .btn,\r
+.input-group-btn:last-child > .btn-group {\r
+  margin-left: -1px;\r
+}\r
+.nav {\r
+  margin-bottom: 0;\r
+  padding-left: 0;\r
+  list-style: none;\r
+}\r
+.nav > li {\r
+  position: relative;\r
+  display: block;\r
+}\r
+.nav > li > a {\r
+  position: relative;\r
+  display: block;\r
+  padding: 10px 15px;\r
+}\r
+.nav > li > a:hover,\r
+.nav > li > a:focus {\r
+  text-decoration: none;\r
+  background-color: #dddddd;\r
+}\r
+.nav > li.disabled > a {\r
+  color: #999999;\r
+}\r
+.nav > li.disabled > a:hover,\r
+.nav > li.disabled > a:focus {\r
+  color: #999999;\r
+  text-decoration: none;\r
+  background-color: transparent;\r
+  cursor: not-allowed;\r
+}\r
+.nav .open > a,\r
+.nav .open > a:hover,\r
+.nav .open > a:focus {\r
+  background-color: #dddddd;\r
+  border-color: #0d8921;\r
+}\r
+.nav .nav-divider {\r
+  height: 1px;\r
+  margin: 9px 0;\r
+  overflow: hidden;\r
+  background-color: #e5e5e5;\r
+}\r
+.nav > li > a > img {\r
+  max-width: none;\r
+}\r
+.nav-tabs {\r
+  border-bottom: 1px solid #dddddd;\r
+}\r
+.nav-tabs > li {\r
+  float: left;\r
+  margin-bottom: -1px;\r
+}\r
+.nav-tabs > li > a {\r
+  margin-right: 2px;\r
+  line-height: 1.428571429;\r
+  border: 1px solid transparent;\r
+  border-radius: 0px 0px 0 0;\r
+}\r
+.nav-tabs > li > a:hover {\r
+  border-color: #eeeeee #eeeeee #dddddd;\r
+}\r
+.nav-tabs > li.active > a,\r
+.nav-tabs > li.active > a:hover,\r
+.nav-tabs > li.active > a:focus {\r
+  color: #555555;\r
+  background-color: #f2f2f2;\r
+  border: 1px solid #dddddd;\r
+  border-bottom-color: transparent;\r
+  cursor: default;\r
+}\r
+.nav-tabs.nav-justified {\r
+  width: 100%;\r
+  border-bottom: 0;\r
+}\r
+.nav-tabs.nav-justified > li {\r
+  float: none;\r
+}\r
+.nav-tabs.nav-justified > li > a {\r
+  text-align: center;\r
+  margin-bottom: 5px;\r
+}\r
+.nav-tabs.nav-justified > .dropdown .dropdown-menu {\r
+  top: auto;\r
+  left: auto;\r
+}\r
+@media (min-width: 768px) {\r
+  .nav-tabs.nav-justified > li {\r
+    display: table-cell;\r
+    width: 1%;\r
+  }\r
+  .nav-tabs.nav-justified > li > a {\r
+    margin-bottom: 0;\r
+  }\r
+}\r
+.nav-tabs.nav-justified > li > a {\r
+  margin-right: 0;\r
+  border-radius: 0px;\r
+}\r
+.nav-tabs.nav-justified > .active > a,\r
+.nav-tabs.nav-justified > .active > a:hover,\r
+.nav-tabs.nav-justified > .active > a:focus {\r
+  border: 1px solid #dddddd;\r
+}\r
+@media (min-width: 768px) {\r
+  .nav-tabs.nav-justified > li > a {\r
+    border-bottom: 1px solid #dddddd;\r
+    border-radius: 0px 0px 0 0;\r
+  }\r
+  .nav-tabs.nav-justified > .active > a,\r
+  .nav-tabs.nav-justified > .active > a:hover,\r
+  .nav-tabs.nav-justified > .active > a:focus {\r
+    border-bottom-color: #f2f2f2;\r
+  }\r
+}\r
+.nav-pills > li {\r
+  float: left;\r
+}\r
+.nav-pills > li > a {\r
+  border-radius: 0px;\r
+}\r
+.nav-pills > li + li {\r
+  margin-left: 2px;\r
+}\r
+.nav-pills > li.active > a,\r
+.nav-pills > li.active > a:hover,\r
+.nav-pills > li.active > a:focus {\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+}\r
+.nav-stacked > li {\r
+  float: none;\r
+}\r
+.nav-stacked > li + li {\r
+  margin-top: 2px;\r
+  margin-left: 0;\r
+}\r
+.nav-justified {\r
+  width: 100%;\r
+}\r
+.nav-justified > li {\r
+  float: none;\r
+}\r
+.nav-justified > li > a {\r
+  text-align: center;\r
+  margin-bottom: 5px;\r
+}\r
+.nav-justified > .dropdown .dropdown-menu {\r
+  top: auto;\r
+  left: auto;\r
+}\r
+@media (min-width: 768px) {\r
+  .nav-justified > li {\r
+    display: table-cell;\r
+    width: 1%;\r
+  }\r
+  .nav-justified > li > a {\r
+    margin-bottom: 0;\r
+  }\r
+}\r
+.nav-tabs-justified {\r
+  border-bottom: 0;\r
+}\r
+.nav-tabs-justified > li > a {\r
+  margin-right: 0;\r
+  border-radius: 0px;\r
+}\r
+.nav-tabs-justified > .active > a,\r
+.nav-tabs-justified > .active > a:hover,\r
+.nav-tabs-justified > .active > a:focus {\r
+  border: 1px solid #dddddd;\r
+}\r
+@media (min-width: 768px) {\r
+  .nav-tabs-justified > li > a {\r
+    border-bottom: 1px solid #dddddd;\r
+    border-radius: 0px 0px 0 0;\r
+  }\r
+  .nav-tabs-justified > .active > a,\r
+  .nav-tabs-justified > .active > a:hover,\r
+  .nav-tabs-justified > .active > a:focus {\r
+    border-bottom-color: #f2f2f2;\r
+  }\r
+}\r
+.tab-content > .tab-pane {\r
+  display: none;\r
+}\r
+.tab-content > .active {\r
+  display: block;\r
+}\r
+.nav-tabs .dropdown-menu {\r
+  margin-top: -1px;\r
+  border-top-right-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.navbar {\r
+  position: relative;\r
+  min-height: 50px;\r
+  margin-bottom: 20px;\r
+  border: 1px solid transparent;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar {\r
+    border-radius: 0px;\r
+  }\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-header {\r
+    float: left;\r
+  }\r
+}\r
+.navbar-collapse {\r
+  max-height: 340px;\r
+  overflow-x: visible;\r
+  padding-right: 15px;\r
+  padding-left: 15px;\r
+  border-top: 1px solid transparent;\r
+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);\r
+  -webkit-overflow-scrolling: touch;\r
+}\r
+.navbar-collapse.in {\r
+  overflow-y: auto;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-collapse {\r
+    width: auto;\r
+    border-top: 0;\r
+    box-shadow: none;\r
+  }\r
+  .navbar-collapse.collapse {\r
+    display: block !important;\r
+    height: auto !important;\r
+    padding-bottom: 0;\r
+    overflow: visible !important;\r
+  }\r
+  .navbar-collapse.in {\r
+    overflow-y: visible;\r
+  }\r
+  .navbar-fixed-top .navbar-collapse,\r
+  .navbar-static-top .navbar-collapse,\r
+  .navbar-fixed-bottom .navbar-collapse {\r
+    padding-left: 0;\r
+    padding-right: 0;\r
+  }\r
+}\r
+.container > .navbar-header,\r
+.container-fluid > .navbar-header,\r
+.container > .navbar-collapse,\r
+.container-fluid > .navbar-collapse {\r
+  margin-right: -15px;\r
+  margin-left: -15px;\r
+}\r
+@media (min-width: 768px) {\r
+  .container > .navbar-header,\r
+  .container-fluid > .navbar-header,\r
+  .container > .navbar-collapse,\r
+  .container-fluid > .navbar-collapse {\r
+    margin-right: 0;\r
+    margin-left: 0;\r
+  }\r
+}\r
+.navbar-static-top {\r
+  z-index: 1000;\r
+  border-width: 0 0 1px;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-static-top {\r
+    border-radius: 0;\r
+  }\r
+}\r
+.navbar-fixed-top,\r
+.navbar-fixed-bottom {\r
+  position: fixed;\r
+  right: 0;\r
+  left: 0;\r
+  z-index: 1030;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-fixed-top,\r
+  .navbar-fixed-bottom {\r
+    border-radius: 0;\r
+  }\r
+}\r
+.navbar-fixed-top {\r
+  top: 0;\r
+  border-width: 0 0 1px;\r
+}\r
+.navbar-fixed-bottom {\r
+  bottom: 0;\r
+  margin-bottom: 0;\r
+  border-width: 1px 0 0;\r
+}\r
+.navbar-brand {\r
+  float: left;\r
+  padding: 15px 15px;\r
+  font-size: 18px;\r
+  line-height: 20px;\r
+  height: 50px;\r
+}\r
+.navbar-brand:hover,\r
+.navbar-brand:focus {\r
+  text-decoration: none;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar > .container .navbar-brand,\r
+  .navbar > .container-fluid .navbar-brand {\r
+    margin-left: -15px;\r
+  }\r
+}\r
+.navbar-toggle {\r
+  position: relative;\r
+  float: right;\r
+  margin-right: 15px;\r
+  padding: 9px 10px;\r
+  margin-top: 8px;\r
+  margin-bottom: 8px;\r
+  background-color: transparent;\r
+  background-image: none;\r
+  border: 1px solid transparent;\r
+  border-radius: 0px;\r
+}\r
+.navbar-toggle:focus {\r
+  outline: none;\r
+}\r
+.navbar-toggle .icon-bar {\r
+  display: block;\r
+  width: 22px;\r
+  height: 2px;\r
+  border-radius: 1px;\r
+}\r
+.navbar-toggle .icon-bar + .icon-bar {\r
+  margin-top: 4px;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-toggle {\r
+    display: none;\r
+  }\r
+}\r
+.navbar-nav {\r
+  margin: 7.5px -15px;\r
+}\r
+.navbar-nav > li > a {\r
+  padding-top: 10px;\r
+  padding-bottom: 10px;\r
+  line-height: 20px;\r
+}\r
+@media (max-width: 767px) {\r
+  .navbar-nav .open .dropdown-menu {\r
+    position: static;\r
+    float: none;\r
+    width: auto;\r
+    margin-top: 0;\r
+    background-color: transparent;\r
+    border: 0;\r
+    box-shadow: none;\r
+  }\r
+  .navbar-nav .open .dropdown-menu > li > a,\r
+  .navbar-nav .open .dropdown-menu .dropdown-header {\r
+    padding: 5px 15px 5px 25px;\r
+  }\r
+  .navbar-nav .open .dropdown-menu > li > a {\r
+    line-height: 20px;\r
+  }\r
+  .navbar-nav .open .dropdown-menu > li > a:hover,\r
+  .navbar-nav .open .dropdown-menu > li > a:focus {\r
+    background-image: none;\r
+  }\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-nav {\r
+    float: left;\r
+    margin: 0;\r
+  }\r
+  .navbar-nav > li {\r
+    float: left;\r
+  }\r
+  .navbar-nav > li > a {\r
+    padding-top: 15px;\r
+    padding-bottom: 15px;\r
+  }\r
+  .navbar-nav.navbar-right:last-child {\r
+    margin-right: -15px;\r
+  }\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-left {\r
+    float: left !important;\r
+  }\r
+  .navbar-right {\r
+    float: right !important;\r
+  }\r
+}\r
+.navbar-form {\r
+  margin-left: -15px;\r
+  margin-right: -15px;\r
+  padding: 10px 15px;\r
+  border-top: 1px solid transparent;\r
+  border-bottom: 1px solid transparent;\r
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\r
+  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);\r
+  margin-top: 8px;\r
+  margin-bottom: 8px;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-form .form-group {\r
+    display: inline-block;\r
+    margin-bottom: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .navbar-form .form-control {\r
+    display: inline-block;\r
+    width: auto;\r
+    vertical-align: middle;\r
+  }\r
+  .navbar-form .input-group > .form-control {\r
+    width: 100%;\r
+  }\r
+  .navbar-form .control-label {\r
+    margin-bottom: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .navbar-form .radio,\r
+  .navbar-form .checkbox {\r
+    display: inline-block;\r
+    margin-top: 0;\r
+    margin-bottom: 0;\r
+    padding-left: 0;\r
+    vertical-align: middle;\r
+  }\r
+  .navbar-form .radio input[type="radio"],\r
+  .navbar-form .checkbox input[type="checkbox"] {\r
+    float: none;\r
+    margin-left: 0;\r
+  }\r
+  .navbar-form .has-feedback .form-control-feedback {\r
+    top: 0;\r
+  }\r
+}\r
+@media (max-width: 767px) {\r
+  .navbar-form .form-group {\r
+    margin-bottom: 5px;\r
+  }\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-form {\r
+    width: auto;\r
+    border: 0;\r
+    margin-left: 0;\r
+    margin-right: 0;\r
+    padding-top: 0;\r
+    padding-bottom: 0;\r
+    -webkit-box-shadow: none;\r
+    box-shadow: none;\r
+  }\r
+  .navbar-form.navbar-right:last-child {\r
+    margin-right: -15px;\r
+  }\r
+}\r
+.navbar-nav > li > .dropdown-menu {\r
+  margin-top: 0;\r
+  border-top-right-radius: 0;\r
+  border-top-left-radius: 0;\r
+}\r
+.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\r
+  border-bottom-right-radius: 0;\r
+  border-bottom-left-radius: 0;\r
+}\r
+.navbar-btn {\r
+  margin-top: 8px;\r
+  margin-bottom: 8px;\r
+}\r
+.navbar-btn.btn-sm {\r
+  margin-top: 10px;\r
+  margin-bottom: 10px;\r
+}\r
+.navbar-btn.btn-xs {\r
+  margin-top: 14px;\r
+  margin-bottom: 14px;\r
+}\r
+.navbar-text {\r
+  margin-top: 15px;\r
+  margin-bottom: 15px;\r
+}\r
+@media (min-width: 768px) {\r
+  .navbar-text {\r
+    float: left;\r
+    margin-left: 15px;\r
+    margin-right: 15px;\r
+  }\r
+  .navbar-text.navbar-right:last-child {\r
+    margin-right: 0;\r
+  }\r
+}\r
+.navbar-default {\r
+  background-color: #0d8921;\r
+  border-color: none;\r
+}\r
+.navbar-default .navbar-brand {\r
+  color: #ffffff;\r
+}\r
+.navbar-default .navbar-brand:hover,\r
+.navbar-default .navbar-brand:focus {\r
+  color: #e6e6e6;\r
+  background-color: transparent;\r
+}\r
+.navbar-default .navbar-text {\r
+  color: #ffffff;\r
+}\r
+.navbar-default .navbar-nav > li > a {\r
+  color: #ffffff;\r
+}\r
+.navbar-default .navbar-nav > li > a:hover,\r
+.navbar-default .navbar-nav > li > a:focus {\r
+  color: #dddddd;\r
+  background-color: transparent;\r
+}\r
+.navbar-default .navbar-nav > .active > a,\r
+.navbar-default .navbar-nav > .active > a:hover,\r
+.navbar-default .navbar-nav > .active > a:focus {\r
+  color: #ffffff;\r
+  background-color: #0a6b1a;\r
+}\r
+.navbar-default .navbar-nav > .disabled > a,\r
+.navbar-default .navbar-nav > .disabled > a:hover,\r
+.navbar-default .navbar-nav > .disabled > a:focus {\r
+  color: #cccccc;\r
+  background-color: transparent;\r
+}\r
+.navbar-default .navbar-toggle {\r
+  border-color: #dddddd;\r
+}\r
+.navbar-default .navbar-toggle:hover,\r
+.navbar-default .navbar-toggle:focus {\r
+  background-color: #dddddd;\r
+}\r
+.navbar-default .navbar-toggle .icon-bar {\r
+  background-color: #ffffff;\r
+}\r
+.navbar-default .navbar-collapse,\r
+.navbar-default .navbar-form {\r
+  border-color: none;\r
+}\r
+.navbar-default .navbar-nav > .open > a,\r
+.navbar-default .navbar-nav > .open > a:hover,\r
+.navbar-default .navbar-nav > .open > a:focus {\r
+  background-color: #0a6b1a;\r
+  color: #ffffff;\r
+}\r
+@media (max-width: 767px) {\r
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a {\r
+    color: #ffffff;\r
+  }\r
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,\r
+  .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {\r
+    color: #dddddd;\r
+    background-color: transparent;\r
+  }\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a,\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {\r
+    color: #ffffff;\r
+    background-color: #0a6b1a;\r
+  }\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,\r
+  .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {\r
+    color: #cccccc;\r
+    background-color: transparent;\r
+  }\r
+}\r
+.navbar-default .navbar-link {\r
+  color: #ffffff;\r
+}\r
+.navbar-default .navbar-link:hover {\r
+  color: #dddddd;\r
+}\r
+.navbar-inverse {\r
+  background-color: #222222;\r
+  border-color: #080808;\r
+}\r
+.navbar-inverse .navbar-brand {\r
+  color: #999999;\r
+}\r
+.navbar-inverse .navbar-brand:hover,\r
+.navbar-inverse .navbar-brand:focus {\r
+  color: #ffffff;\r
+  background-color: transparent;\r
+}\r
+.navbar-inverse .navbar-text {\r
+  color: #999999;\r
+}\r
+.navbar-inverse .navbar-nav > li > a {\r
+  color: #999999;\r
+}\r
+.navbar-inverse .navbar-nav > li > a:hover,\r
+.navbar-inverse .navbar-nav > li > a:focus {\r
+  color: #ffffff;\r
+  background-color: transparent;\r
+}\r
+.navbar-inverse .navbar-nav > .active > a,\r
+.navbar-inverse .navbar-nav > .active > a:hover,\r
+.navbar-inverse .navbar-nav > .active > a:focus {\r
+  color: #ffffff;\r
+  background-color: #080808;\r
+}\r
+.navbar-inverse .navbar-nav > .disabled > a,\r
+.navbar-inverse .navbar-nav > .disabled > a:hover,\r
+.navbar-inverse .navbar-nav > .disabled > a:focus {\r
+  color: #444444;\r
+  background-color: transparent;\r
+}\r
+.navbar-inverse .navbar-toggle {\r
+  border-color: #333333;\r
+}\r
+.navbar-inverse .navbar-toggle:hover,\r
+.navbar-inverse .navbar-toggle:focus {\r
+  background-color: #333333;\r
+}\r
+.navbar-inverse .navbar-toggle .icon-bar {\r
+  background-color: #ffffff;\r
+}\r
+.navbar-inverse .navbar-collapse,\r
+.navbar-inverse .navbar-form {\r
+  border-color: #101010;\r
+}\r
+.navbar-inverse .navbar-nav > .open > a,\r
+.navbar-inverse .navbar-nav > .open > a:hover,\r
+.navbar-inverse .navbar-nav > .open > a:focus {\r
+  background-color: #080808;\r
+  color: #ffffff;\r
+}\r
+@media (max-width: 767px) {\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {\r
+    border-color: #080808;\r
+  }\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu .divider {\r
+    background-color: #080808;\r
+  }\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {\r
+    color: #999999;\r
+  }\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {\r
+    color: #ffffff;\r
+    background-color: transparent;\r
+  }\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {\r
+    color: #ffffff;\r
+    background-color: #080808;\r
+  }\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,\r
+  .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {\r
+    color: #444444;\r
+    background-color: transparent;\r
+  }\r
+}\r
+.navbar-inverse .navbar-link {\r
+  color: #999999;\r
+}\r
+.navbar-inverse .navbar-link:hover {\r
+  color: #ffffff;\r
+}\r
+.breadcrumb {\r
+  padding: 8px 15px;\r
+  margin-bottom: 20px;\r
+  list-style: none;\r
+  background-color: #f5f5f5;\r
+  border-radius: 0px;\r
+}\r
+.breadcrumb > li {\r
+  display: inline-block;\r
+}\r
+.breadcrumb > li + li:before {\r
+  content: "\00a0";\r
+  padding: 0 5px;\r
+  color: #cccccc;\r
+}\r
+.breadcrumb > .active {\r
+  color: #999999;\r
+}\r
+.pagination {\r
+  display: inline-block;\r
+  padding-left: 0;\r
+  margin: 20px 0;\r
+  border-radius: 0px;\r
+}\r
+.pagination > li {\r
+  display: inline;\r
+}\r
+.pagination > li > a,\r
+.pagination > li > span {\r
+  position: relative;\r
+  float: left;\r
+  padding: 6px 12px;\r
+  line-height: 1.428571429;\r
+  text-decoration: none;\r
+  color: #0d8921;\r
+  background-color: #ffffff;\r
+  border: 1px solid #dddddd;\r
+  margin-left: -1px;\r
+}\r
+.pagination > li:first-child > a,\r
+.pagination > li:first-child > span {\r
+  margin-left: 0;\r
+  border-bottom-left-radius: 0px;\r
+  border-top-left-radius: 0px;\r
+}\r
+.pagination > li:last-child > a,\r
+.pagination > li:last-child > span {\r
+  border-bottom-right-radius: 0px;\r
+  border-top-right-radius: 0px;\r
+}\r
+.pagination > li > a:hover,\r
+.pagination > li > span:hover,\r
+.pagination > li > a:focus,\r
+.pagination > li > span:focus {\r
+  color: #064310;\r
+  background-color: #eeeeee;\r
+  border-color: #dddddd;\r
+}\r
+.pagination > .active > a,\r
+.pagination > .active > span,\r
+.pagination > .active > a:hover,\r
+.pagination > .active > span:hover,\r
+.pagination > .active > a:focus,\r
+.pagination > .active > span:focus {\r
+  z-index: 2;\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+  border-color: #0d8921;\r
+  cursor: default;\r
+}\r
+.pagination > .disabled > span,\r
+.pagination > .disabled > span:hover,\r
+.pagination > .disabled > span:focus,\r
+.pagination > .disabled > a,\r
+.pagination > .disabled > a:hover,\r
+.pagination > .disabled > a:focus {\r
+  color: #999999;\r
+  background-color: #ffffff;\r
+  border-color: #dddddd;\r
+  cursor: not-allowed;\r
+}\r
+.pagination-lg > li > a,\r
+.pagination-lg > li > span {\r
+  padding: 10px 16px;\r
+  font-size: 18px;\r
+}\r
+.pagination-lg > li:first-child > a,\r
+.pagination-lg > li:first-child > span {\r
+  border-bottom-left-radius: 0px;\r
+  border-top-left-radius: 0px;\r
+}\r
+.pagination-lg > li:last-child > a,\r
+.pagination-lg > li:last-child > span {\r
+  border-bottom-right-radius: 0px;\r
+  border-top-right-radius: 0px;\r
+}\r
+.pagination-sm > li > a,\r
+.pagination-sm > li > span {\r
+  padding: 5px 10px;\r
+  font-size: 12px;\r
+}\r
+.pagination-sm > li:first-child > a,\r
+.pagination-sm > li:first-child > span {\r
+  border-bottom-left-radius: 0px;\r
+  border-top-left-radius: 0px;\r
+}\r
+.pagination-sm > li:last-child > a,\r
+.pagination-sm > li:last-child > span {\r
+  border-bottom-right-radius: 0px;\r
+  border-top-right-radius: 0px;\r
+}\r
+.pager {\r
+  padding-left: 0;\r
+  margin: 20px 0;\r
+  list-style: none;\r
+  text-align: center;\r
+}\r
+.pager li {\r
+  display: inline;\r
+}\r
+.pager li > a,\r
+.pager li > span {\r
+  display: inline-block;\r
+  padding: 5px 14px;\r
+  background-color: #ffffff;\r
+  border: 1px solid #dddddd;\r
+  border-radius: 15px;\r
+}\r
+.pager li > a:hover,\r
+.pager li > a:focus {\r
+  text-decoration: none;\r
+  background-color: #eeeeee;\r
+}\r
+.pager .next > a,\r
+.pager .next > span {\r
+  float: right;\r
+}\r
+.pager .previous > a,\r
+.pager .previous > span {\r
+  float: left;\r
+}\r
+.pager .disabled > a,\r
+.pager .disabled > a:hover,\r
+.pager .disabled > a:focus,\r
+.pager .disabled > span {\r
+  color: #999999;\r
+  background-color: #ffffff;\r
+  cursor: not-allowed;\r
+}\r
+.label {\r
+  display: inline;\r
+  padding: .2em .6em .3em;\r
+  font-size: 75%;\r
+  font-weight: bold;\r
+  line-height: 1;\r
+  color: #ffffff;\r
+  text-align: center;\r
+  white-space: nowrap;\r
+  vertical-align: baseline;\r
+  border-radius: .25em;\r
+}\r
+.label[href]:hover,\r
+.label[href]:focus {\r
+  color: #ffffff;\r
+  text-decoration: none;\r
+  cursor: pointer;\r
+}\r
+.label:empty {\r
+  display: none;\r
+}\r
+.btn .label {\r
+  position: relative;\r
+  top: -1px;\r
+}\r
+.label-default {\r
+  background-color: #999999;\r
+}\r
+.label-default[href]:hover,\r
+.label-default[href]:focus {\r
+  background-color: #808080;\r
+}\r
+.label-primary {\r
+  background-color: #0d8921;\r
+}\r
+.label-primary[href]:hover,\r
+.label-primary[href]:focus {\r
+  background-color: #095a16;\r
+}\r
+.label-success {\r
+  background-color: #5cb85c;\r
+}\r
+.label-success[href]:hover,\r
+.label-success[href]:focus {\r
+  background-color: #449d44;\r
+}\r
+.label-info {\r
+  background-color: #5bc0de;\r
+}\r
+.label-info[href]:hover,\r
+.label-info[href]:focus {\r
+  background-color: #31b0d5;\r
+}\r
+.label-warning {\r
+  background-color: #f0ad4e;\r
+}\r
+.label-warning[href]:hover,\r
+.label-warning[href]:focus {\r
+  background-color: #ec971f;\r
+}\r
+.label-danger {\r
+  background-color: #d9534f;\r
+}\r
+.label-danger[href]:hover,\r
+.label-danger[href]:focus {\r
+  background-color: #c9302c;\r
+}\r
+.badge {\r
+  display: inline-block;\r
+  min-width: 10px;\r
+  padding: 3px 7px;\r
+  font-size: 12px;\r
+  font-weight: bold;\r
+  color: : #fff;\r
+  line-height: 1;\r
+  vertical-align: baseline;\r
+  white-space: nowrap;\r
+  text-align: center;\r
+  background-color: #999999;\r
+  border-radius: 10px;\r
+}\r
+.badge:empty {\r
+  display: none;\r
+}\r
+.btn .badge {\r
+  position: relative;\r
+  top: -1px;\r
+}\r
+.btn-xs .badge {\r
+  top: 0;\r
+  padding: 1px 5px;\r
+}\r
+a.badge:hover,\r
+a.badge:focus {\r
+  color: #ffffff;\r
+  text-decoration: none;\r
+  cursor: pointer;\r
+}\r
+a.list-group-item.active > .badge,\r
+.nav-pills > .active > a > .badge {\r
+  color: #0d8921;\r
+  background-color: #ffffff;\r
+}\r
+.nav-pills > li > a > .badge {\r
+  margin-left: 3px;\r
+}\r
+.jumbotron {\r
+  padding: 30px;\r
+  margin-bottom: 30px;\r
+  color: inherit;\r
+  background-color: #ffffff;\r
+}\r
+.jumbotron h1,\r
+.jumbotron .h1 {\r
+  color: inherit;\r
+}\r
+.jumbotron p {\r
+  margin-bottom: 15px;\r
+  font-size: 21px;\r
+  font-weight: 200;\r
+}\r
+.container .jumbotron {\r
+  border-radius: 0px;\r
+}\r
+.jumbotron .container {\r
+  max-width: 100%;\r
+}\r
+@media screen and (min-width: 768px) {\r
+  .jumbotron {\r
+    padding-top: 48px;\r
+    padding-bottom: 48px;\r
+  }\r
+  .container .jumbotron {\r
+    padding-left: 60px;\r
+    padding-right: 60px;\r
+  }\r
+  .jumbotron h1,\r
+  .jumbotron .h1 {\r
+    font-size: 63px;\r
+  }\r
+}\r
+.thumbnail {\r
+  display: block;\r
+  padding: 4px;\r
+  margin-bottom: 20px;\r
+  line-height: 1.428571429;\r
+  background-color: #f2f2f2;\r
+  border: 1px solid #dddddd;\r
+  border-radius: 0px;\r
+  -webkit-transition: all 0.2s ease-in-out;\r
+  transition: all 0.2s ease-in-out;\r
+}\r
+.thumbnail > img,\r
+.thumbnail a > img {\r
+  margin-left: auto;\r
+  margin-right: auto;\r
+}\r
+a.thumbnail:hover,\r
+a.thumbnail:focus,\r
+a.thumbnail.active {\r
+  border-color: #0d8921;\r
+}\r
+.thumbnail .caption {\r
+  padding: 9px;\r
+  color: #333333;\r
+}\r
+.alert {\r
+  padding: 15px;\r
+  margin-bottom: 20px;\r
+  border: 1px solid transparent;\r
+  border-radius: 0px;\r
+}\r
+.alert h4 {\r
+  margin-top: 0;\r
+  color: inherit;\r
+}\r
+.alert .alert-link {\r
+  font-weight: bold;\r
+}\r
+.alert > p,\r
+.alert > ul {\r
+  margin-bottom: 0;\r
+}\r
+.alert > p + p {\r
+  margin-top: 5px;\r
+}\r
+.alert-dismissable {\r
+  padding-right: 35px;\r
+}\r
+.alert-dismissable .close {\r
+  position: relative;\r
+  top: -2px;\r
+  right: -21px;\r
+  color: inherit;\r
+}\r
+.alert-success {\r
+  background-color: #dff0d8;\r
+  border-color: #d6e9c6;\r
+  color: #5cb85c;\r
+}\r
+.alert-success hr {\r
+  border-top-color: #c9e2b3;\r
+}\r
+.alert-success .alert-link {\r
+  color: #449d44;\r
+}\r
+.alert-info {\r
+  background-color: #d9edf7;\r
+  border-color: #bce8f1;\r
+  color: #5bc0de;\r
+}\r
+.alert-info hr {\r
+  border-top-color: #a6e1ec;\r
+}\r
+.alert-info .alert-link {\r
+  color: #31b0d5;\r
+}\r
+.alert-warning {\r
+  background-color: #fcf8e3;\r
+  border-color: #fbeed5;\r
+  color: #f0ad4e;\r
+}\r
+.alert-warning hr {\r
+  border-top-color: #f8e5be;\r
+}\r
+.alert-warning .alert-link {\r
+  color: #ec971f;\r
+}\r
+.alert-danger {\r
+  background-color: #f2dede;\r
+  border-color: #eed3d7;\r
+  color: #d9534f;\r
+}\r
+.alert-danger hr {\r
+  border-top-color: #e6c1c7;\r
+}\r
+.alert-danger .alert-link {\r
+  color: #c9302c;\r
+}\r
+@-webkit-keyframes progress-bar-stripes {\r
+  from {\r
+    background-position: 40px 0;\r
+  }\r
+  to {\r
+    background-position: 0 0;\r
+  }\r
+}\r
+@keyframes progress-bar-stripes {\r
+  from {\r
+    background-position: 40px 0;\r
+  }\r
+  to {\r
+    background-position: 0 0;\r
+  }\r
+}\r
+.progress {\r
+  overflow: hidden;\r
+  height: 20px;\r
+  margin-bottom: 20px;\r
+  background-color: #f5f5f5;\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\r
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);\r
+}\r
+.progress-bar {\r
+  float: left;\r
+  width: 0%;\r
+  height: 100%;\r
+  font-size: 12px;\r
+  line-height: 20px;\r
+  color: #ffffff;\r
+  text-align: center;\r
+  background-color: #0d8921;\r
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\r
+  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);\r
+  -webkit-transition: width 0.6s ease;\r
+  transition: width 0.6s ease;\r
+}\r
+.progress-striped .progress-bar {\r
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-size: 40px 40px;\r
+}\r
+.progress.active .progress-bar {\r
+  -webkit-animation: progress-bar-stripes 2s linear infinite;\r
+  animation: progress-bar-stripes 2s linear infinite;\r
+}\r
+.progress-bar-success {\r
+  background-color: #5cb85c;\r
+}\r
+.progress-striped .progress-bar-success {\r
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+}\r
+.progress-bar-info {\r
+  background-color: #5bc0de;\r
+}\r
+.progress-striped .progress-bar-info {\r
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+}\r
+.progress-bar-warning {\r
+  background-color: #f0ad4e;\r
+}\r
+.progress-striped .progress-bar-warning {\r
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+}\r
+.progress-bar-danger {\r
+  background-color: #d9534f;\r
+}\r
+.progress-striped .progress-bar-danger {\r
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\r
+}\r
+.media,\r
+.media-body {\r
+  overflow: hidden;\r
+  zoom: 1;\r
+}\r
+.media,\r
+.media .media {\r
+  margin-top: 15px;\r
+}\r
+.media:first-child {\r
+  margin-top: 0;\r
+}\r
+.media-object {\r
+  display: block;\r
+}\r
+.media-heading {\r
+  margin: 0 0 5px;\r
+}\r
+.media > .pull-left {\r
+  margin-right: 10px;\r
+}\r
+.media > .pull-right {\r
+  margin-left: 10px;\r
+}\r
+.media-list {\r
+  padding-left: 0;\r
+  list-style: none;\r
+}\r
+.list-group {\r
+  margin-bottom: 20px;\r
+  padding-left: 0;\r
+}\r
+.list-group-item {\r
+  position: relative;\r
+  display: block;\r
+  padding: 10px 15px;\r
+  margin-bottom: -1px;\r
+  background-color: #ffffff;\r
+  border: 1px solid #dddddd;\r
+}\r
+.list-group-item:first-child {\r
+  border-top-right-radius: 0px;\r
+  border-top-left-radius: 0px;\r
+}\r
+.list-group-item:last-child {\r
+  margin-bottom: 0;\r
+  border-bottom-right-radius: 0px;\r
+  border-bottom-left-radius: 0px;\r
+}\r
+.list-group-item > .badge {\r
+  float: right;\r
+}\r
+.list-group-item > .badge + .badge {\r
+  margin-right: 5px;\r
+}\r
+a.list-group-item {\r
+  color: #555555;\r
+}\r
+a.list-group-item .list-group-item-heading {\r
+  color: #333333;\r
+}\r
+a.list-group-item:hover,\r
+a.list-group-item:focus {\r
+  text-decoration: none;\r
+  background-color: #f5f5f5;\r
+}\r
+a.list-group-item.active,\r
+a.list-group-item.active:hover,\r
+a.list-group-item.active:focus {\r
+  z-index: 2;\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+  border-color: #0d8921;\r
+}\r
+a.list-group-item.active .list-group-item-heading,\r
+a.list-group-item.active:hover .list-group-item-heading,\r
+a.list-group-item.active:focus .list-group-item-heading {\r
+  color: inherit;\r
+}\r
+a.list-group-item.active .list-group-item-text,\r
+a.list-group-item.active:hover .list-group-item-text,\r
+a.list-group-item.active:focus .list-group-item-text {\r
+  color: #cccccc;\r
+}\r
+.list-group-item-success {\r
+  color: #5cb85c;\r
+  background-color: #dff0d8;\r
+}\r
+a.list-group-item-success {\r
+  color: #5cb85c;\r
+}\r
+a.list-group-item-success .list-group-item-heading {\r
+  color: inherit;\r
+}\r
+a.list-group-item-success:hover,\r
+a.list-group-item-success:focus {\r
+  color: #5cb85c;\r
+  background-color: #d0e9c6;\r
+}\r
+a.list-group-item-success.active,\r
+a.list-group-item-success.active:hover,\r
+a.list-group-item-success.active:focus {\r
+  color: #fff;\r
+  background-color: #5cb85c;\r
+  border-color: #5cb85c;\r
+}\r
+.list-group-item-info {\r
+  color: #5bc0de;\r
+  background-color: #d9edf7;\r
+}\r
+a.list-group-item-info {\r
+  color: #5bc0de;\r
+}\r
+a.list-group-item-info .list-group-item-heading {\r
+  color: inherit;\r
+}\r
+a.list-group-item-info:hover,\r
+a.list-group-item-info:focus {\r
+  color: #5bc0de;\r
+  background-color: #c4e3f3;\r
+}\r
+a.list-group-item-info.active,\r
+a.list-group-item-info.active:hover,\r
+a.list-group-item-info.active:focus {\r
+  color: #fff;\r
+  background-color: #5bc0de;\r
+  border-color: #5bc0de;\r
+}\r
+.list-group-item-warning {\r
+  color: #f0ad4e;\r
+  background-color: #fcf8e3;\r
+}\r
+a.list-group-item-warning {\r
+  color: #f0ad4e;\r
+}\r
+a.list-group-item-warning .list-group-item-heading {\r
+  color: inherit;\r
+}\r
+a.list-group-item-warning:hover,\r
+a.list-group-item-warning:focus {\r
+  color: #f0ad4e;\r
+  background-color: #faf2cc;\r
+}\r
+a.list-group-item-warning.active,\r
+a.list-group-item-warning.active:hover,\r
+a.list-group-item-warning.active:focus {\r
+  color: #fff;\r
+  background-color: #f0ad4e;\r
+  border-color: #f0ad4e;\r
+}\r
+.list-group-item-danger {\r
+  color: #d9534f;\r
+  background-color: #f2dede;\r
+}\r
+a.list-group-item-danger {\r
+  color: #d9534f;\r
+}\r
+a.list-group-item-danger .list-group-item-heading {\r
+  color: inherit;\r
+}\r
+a.list-group-item-danger:hover,\r
+a.list-group-item-danger:focus {\r
+  color: #d9534f;\r
+  background-color: #ebcccc;\r
+}\r
+a.list-group-item-danger.active,\r
+a.list-group-item-danger.active:hover,\r
+a.list-group-item-danger.active:focus {\r
+  color: #fff;\r
+  background-color: #d9534f;\r
+  border-color: #d9534f;\r
+}\r
+.list-group-item-heading {\r
+  margin-top: 0;\r
+  margin-bottom: 5px;\r
+}\r
+.list-group-item-text {\r
+  margin-bottom: 0;\r
+  line-height: 1.3;\r
+}\r
+.panel {\r
+  margin-bottom: 20px;\r
+  background-color: #ffffff;\r
+  border: 1px solid transparent;\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\r
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);\r
+}\r
+.panel-body {\r
+  padding: 15px;\r
+}\r
+.panel-heading {\r
+  padding: 10px 15px;\r
+  border-bottom: 1px solid transparent;\r
+  border-top-right-radius: -1px;\r
+  border-top-left-radius: -1px;\r
+}\r
+.panel-heading > .dropdown .dropdown-toggle {\r
+  color: inherit;\r
+}\r
+.panel-title {\r
+  margin-top: 0;\r
+  margin-bottom: 0;\r
+  font-size: 16px;\r
+  color: inherit;\r
+}\r
+.panel-title > a {\r
+  color: inherit;\r
+}\r
+.panel-footer {\r
+  padding: 10px 15px;\r
+  background-color: #f5f5f5;\r
+  border-top: 1px solid #dddddd;\r
+  border-bottom-right-radius: -1px;\r
+  border-bottom-left-radius: -1px;\r
+}\r
+.panel > .list-group {\r
+  margin-bottom: 0;\r
+}\r
+.panel > .list-group .list-group-item {\r
+  border-width: 1px 0;\r
+  border-radius: 0;\r
+}\r
+.panel > .list-group .list-group-item:first-child {\r
+  border-top: 0;\r
+}\r
+.panel > .list-group .list-group-item:last-child {\r
+  border-bottom: 0;\r
+}\r
+.panel > .list-group:first-child .list-group-item:first-child {\r
+  border-top-right-radius: -1px;\r
+  border-top-left-radius: -1px;\r
+}\r
+.panel > .list-group:last-child .list-group-item:last-child {\r
+  border-bottom-right-radius: -1px;\r
+  border-bottom-left-radius: -1px;\r
+}\r
+.panel-heading + .list-group .list-group-item:first-child {\r
+  border-top-width: 0;\r
+}\r
+.panel > .table,\r
+.panel > .table-responsive > .table {\r
+  margin-bottom: 0;\r
+}\r
+.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,\r
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,\r
+.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,\r
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,\r
+.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,\r
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,\r
+.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,\r
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {\r
+  border-top-left-radius: -1px;\r
+}\r
+.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,\r
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,\r
+.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,\r
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,\r
+.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,\r
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,\r
+.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,\r
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {\r
+  border-top-right-radius: -1px;\r
+}\r
+.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,\r
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,\r
+.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,\r
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {\r
+  border-bottom-left-radius: -1px;\r
+}\r
+.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,\r
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,\r
+.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,\r
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,\r
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {\r
+  border-bottom-right-radius: -1px;\r
+}\r
+.panel > .panel-body + .table,\r
+.panel > .panel-body + .table-responsive {\r
+  border-top: 1px solid #dddddd;\r
+}\r
+.panel > .table > tbody:first-child > tr:first-child th,\r
+.panel > .table > tbody:first-child > tr:first-child td {\r
+  border-top: 0;\r
+}\r
+.panel > .table-bordered,\r
+.panel > .table-responsive > .table-bordered {\r
+  border: 0;\r
+}\r
+.panel > .table-bordered > thead > tr > th:first-child,\r
+.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,\r
+.panel > .table-bordered > tbody > tr > th:first-child,\r
+.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,\r
+.panel > .table-bordered > tfoot > tr > th:first-child,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,\r
+.panel > .table-bordered > thead > tr > td:first-child,\r
+.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,\r
+.panel > .table-bordered > tbody > tr > td:first-child,\r
+.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,\r
+.panel > .table-bordered > tfoot > tr > td:first-child,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {\r
+  border-left: 0;\r
+}\r
+.panel > .table-bordered > thead > tr > th:last-child,\r
+.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,\r
+.panel > .table-bordered > tbody > tr > th:last-child,\r
+.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,\r
+.panel > .table-bordered > tfoot > tr > th:last-child,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,\r
+.panel > .table-bordered > thead > tr > td:last-child,\r
+.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,\r
+.panel > .table-bordered > tbody > tr > td:last-child,\r
+.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,\r
+.panel > .table-bordered > tfoot > tr > td:last-child,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {\r
+  border-right: 0;\r
+}\r
+.panel > .table-bordered > thead > tr:first-child > td,\r
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,\r
+.panel > .table-bordered > tbody > tr:first-child > td,\r
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,\r
+.panel > .table-bordered > thead > tr:first-child > th,\r
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,\r
+.panel > .table-bordered > tbody > tr:first-child > th,\r
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {\r
+  border-bottom: 0;\r
+}\r
+.panel > .table-bordered > tbody > tr:last-child > td,\r
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,\r
+.panel > .table-bordered > tfoot > tr:last-child > td,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,\r
+.panel > .table-bordered > tbody > tr:last-child > th,\r
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,\r
+.panel > .table-bordered > tfoot > tr:last-child > th,\r
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {\r
+  border-bottom: 0;\r
+}\r
+.panel > .table-responsive {\r
+  border: 0;\r
+  margin-bottom: 0;\r
+}\r
+.panel-group {\r
+  margin-bottom: 20px;\r
+}\r
+.panel-group .panel {\r
+  margin-bottom: 0;\r
+  border-radius: 0px;\r
+  overflow: hidden;\r
+}\r
+.panel-group .panel + .panel {\r
+  margin-top: 5px;\r
+}\r
+.panel-group .panel-heading {\r
+  border-bottom: 0;\r
+}\r
+.panel-group .panel-heading + .panel-collapse .panel-body {\r
+  border-top: 1px solid #dddddd;\r
+}\r
+.panel-group .panel-footer {\r
+  border-top: 0;\r
+}\r
+.panel-group .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom: 1px solid #dddddd;\r
+}\r
+.panel-default {\r
+  border-color: #dddddd;\r
+}\r
+.panel-default > .panel-heading {\r
+  color: #333333;\r
+  background-color: #f5f5f5;\r
+  border-color: #dddddd;\r
+}\r
+.panel-default > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #dddddd;\r
+}\r
+.panel-default > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #dddddd;\r
+}\r
+.panel-primary {\r
+  border-color: #0d8921;\r
+}\r
+.panel-primary > .panel-heading {\r
+  color: #ffffff;\r
+  background-color: #0d8921;\r
+  border-color: #0d8921;\r
+}\r
+.panel-primary > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #0d8921;\r
+}\r
+.panel-primary > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #0d8921;\r
+}\r
+.panel-success {\r
+  border-color: #d6e9c6;\r
+}\r
+.panel-success > .panel-heading {\r
+  color: #5cb85c;\r
+  background-color: #dff0d8;\r
+  border-color: #d6e9c6;\r
+}\r
+.panel-success > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #d6e9c6;\r
+}\r
+.panel-success > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #d6e9c6;\r
+}\r
+.panel-info {\r
+  border-color: #bce8f1;\r
+}\r
+.panel-info > .panel-heading {\r
+  color: #5bc0de;\r
+  background-color: #d9edf7;\r
+  border-color: #bce8f1;\r
+}\r
+.panel-info > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #bce8f1;\r
+}\r
+.panel-info > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #bce8f1;\r
+}\r
+.panel-warning {\r
+  border-color: #fbeed5;\r
+}\r
+.panel-warning > .panel-heading {\r
+  color: #f0ad4e;\r
+  background-color: #fcf8e3;\r
+  border-color: #fbeed5;\r
+}\r
+.panel-warning > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #fbeed5;\r
+}\r
+.panel-warning > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #fbeed5;\r
+}\r
+.panel-danger {\r
+  border-color: #eed3d7;\r
+}\r
+.panel-danger > .panel-heading {\r
+  color: #d9534f;\r
+  background-color: #f2dede;\r
+  border-color: #eed3d7;\r
+}\r
+.panel-danger > .panel-heading + .panel-collapse .panel-body {\r
+  border-top-color: #eed3d7;\r
+}\r
+.panel-danger > .panel-footer + .panel-collapse .panel-body {\r
+  border-bottom-color: #eed3d7;\r
+}\r
+.well {\r
+  min-height: 20px;\r
+  padding: 19px;\r
+  margin-bottom: 20px;\r
+  background-color: #f5f5f5;\r
+  border: 1px solid #e3e3e3;\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\r
+  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);\r
+}\r
+.well blockquote {\r
+  border-color: #ddd;\r
+  border-color: rgba(0, 0, 0, 0.15);\r
+}\r
+.well-lg {\r
+  padding: 24px;\r
+  border-radius: 0px;\r
+}\r
+.well-sm {\r
+  padding: 9px;\r
+  border-radius: 0px;\r
+}\r
+.close {\r
+  float: right;\r
+  font-size: 21px;\r
+  font-weight: bold;\r
+  line-height: 1;\r
+  color: #000000;\r
+  text-shadow: 0 1px 0 #ffffff;\r
+  opacity: 0.2;\r
+  filter: alpha(opacity=20);\r
+}\r
+.close:hover,\r
+.close:focus {\r
+  color: #000000;\r
+  text-decoration: none;\r
+  cursor: pointer;\r
+  opacity: 0.5;\r
+  filter: alpha(opacity=50);\r
+}\r
+button.close {\r
+  padding: 0;\r
+  cursor: pointer;\r
+  background: transparent;\r
+  border: 0;\r
+  -webkit-appearance: none;\r
+}\r
+.modal-open {\r
+  overflow: hidden;\r
+}\r
+.modal {\r
+  display: none;\r
+  overflow: auto;\r
+  overflow-y: scroll;\r
+  position: fixed;\r
+  top: 0;\r
+  right: 0;\r
+  bottom: 0;\r
+  left: 0;\r
+  z-index: 1050;\r
+  -webkit-overflow-scrolling: touch;\r
+  outline: 0;\r
+}\r
+.modal.fade .modal-dialog {\r
+  -webkit-transform: translate(0, -25%);\r
+  -ms-transform: translate(0, -25%);\r
+  transform: translate(0, -25%);\r
+  -webkit-transition: -webkit-transform 0.3s ease-out;\r
+  -moz-transition: -moz-transform 0.3s ease-out;\r
+  -o-transition: -o-transform 0.3s ease-out;\r
+  transition: transform 0.3s ease-out;\r
+}\r
+.modal.in .modal-dialog {\r
+  -webkit-transform: translate(0, 0);\r
+  -ms-transform: translate(0, 0);\r
+  transform: translate(0, 0);\r
+}\r
+.modal-dialog {\r
+  position: relative;\r
+  width: auto;\r
+  margin: 10px;\r
+}\r
+.modal-content {\r
+  position: relative;\r
+  background-color: #ffffff;\r
+  border: 1px solid #999999;\r
+  border: 1px solid rgba(0, 0, 0, 0.2);\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\r
+  box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);\r
+  background-clip: padding-box;\r
+  outline: none;\r
+}\r
+.modal-backdrop {\r
+  position: fixed;\r
+  top: 0;\r
+  right: 0;\r
+  bottom: 0;\r
+  left: 0;\r
+  z-index: 1040;\r
+  background-color: #000000;\r
+}\r
+.modal-backdrop.fade {\r
+  opacity: 0;\r
+  filter: alpha(opacity=0);\r
+}\r
+.modal-backdrop.in {\r
+  opacity: 0.5;\r
+  filter: alpha(opacity=50);\r
+}\r
+.modal-header {\r
+  padding: 15px;\r
+  border-bottom: 1px solid #e5e5e5;\r
+  min-height: 16.428571429px;\r
+}\r
+.modal-header .close {\r
+  margin-top: -2px;\r
+}\r
+.modal-title {\r
+  margin: 0;\r
+  line-height: 1.428571429;\r
+}\r
+.modal-body {\r
+  position: relative;\r
+  padding: 20px;\r
+}\r
+.modal-footer {\r
+  margin-top: 15px;\r
+  padding: 19px 20px 20px;\r
+  text-align: right;\r
+  border-top: 1px solid #e5e5e5;\r
+}\r
+.modal-footer .btn + .btn {\r
+  margin-left: 5px;\r
+  margin-bottom: 0;\r
+}\r
+.modal-footer .btn-group .btn + .btn {\r
+  margin-left: -1px;\r
+}\r
+.modal-footer .btn-block + .btn-block {\r
+  margin-left: 0;\r
+}\r
+@media (min-width: 768px) {\r
+  .modal-dialog {\r
+    width: 600px;\r
+    margin: 30px auto;\r
+  }\r
+  .modal-content {\r
+    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\r
+    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);\r
+  }\r
+  .modal-sm {\r
+    width: 300px;\r
+  }\r
+}\r
+@media (min-width: 992px) {\r
+  .modal-lg {\r
+    width: 900px;\r
+  }\r
+}\r
+.tooltip {\r
+  position: absolute;\r
+  z-index: 1030;\r
+  display: block;\r
+  visibility: visible;\r
+  font-size: 12px;\r
+  line-height: 1.4;\r
+  opacity: 0;\r
+  filter: alpha(opacity=0);\r
+}\r
+.tooltip.in {\r
+  opacity: 0.9;\r
+  filter: alpha(opacity=90);\r
+}\r
+.tooltip.top {\r
+  margin-top: -3px;\r
+  padding: 5px 0;\r
+}\r
+.tooltip.right {\r
+  margin-left: 3px;\r
+  padding: 0 5px;\r
+}\r
+.tooltip.bottom {\r
+  margin-top: 3px;\r
+  padding: 5px 0;\r
+}\r
+.tooltip.left {\r
+  margin-left: -3px;\r
+  padding: 0 5px;\r
+}\r
+.tooltip-inner {\r
+  max-width: 200px;\r
+  padding: 3px 8px;\r
+  color: #ffffff;\r
+  text-align: center;\r
+  text-decoration: none;\r
+  background-color: #000000;\r
+  border-radius: 0px;\r
+}\r
+.tooltip-arrow {\r
+  position: absolute;\r
+  width: 0;\r
+  height: 0;\r
+  border-color: transparent;\r
+  border-style: solid;\r
+}\r
+.tooltip.top .tooltip-arrow {\r
+  bottom: 0;\r
+  left: 50%;\r
+  margin-left: -5px;\r
+  border-width: 5px 5px 0;\r
+  border-top-color: #000000;\r
+}\r
+.tooltip.top-left .tooltip-arrow {\r
+  bottom: 0;\r
+  left: 5px;\r
+  border-width: 5px 5px 0;\r
+  border-top-color: #000000;\r
+}\r
+.tooltip.top-right .tooltip-arrow {\r
+  bottom: 0;\r
+  right: 5px;\r
+  border-width: 5px 5px 0;\r
+  border-top-color: #000000;\r
+}\r
+.tooltip.right .tooltip-arrow {\r
+  top: 50%;\r
+  left: 0;\r
+  margin-top: -5px;\r
+  border-width: 5px 5px 5px 0;\r
+  border-right-color: #000000;\r
+}\r
+.tooltip.left .tooltip-arrow {\r
+  top: 50%;\r
+  right: 0;\r
+  margin-top: -5px;\r
+  border-width: 5px 0 5px 5px;\r
+  border-left-color: #000000;\r
+}\r
+.tooltip.bottom .tooltip-arrow {\r
+  top: 0;\r
+  left: 50%;\r
+  margin-left: -5px;\r
+  border-width: 0 5px 5px;\r
+  border-bottom-color: #000000;\r
+}\r
+.tooltip.bottom-left .tooltip-arrow {\r
+  top: 0;\r
+  left: 5px;\r
+  border-width: 0 5px 5px;\r
+  border-bottom-color: #000000;\r
+}\r
+.tooltip.bottom-right .tooltip-arrow {\r
+  top: 0;\r
+  right: 5px;\r
+  border-width: 0 5px 5px;\r
+  border-bottom-color: #000000;\r
+}\r
+.popover {\r
+  position: absolute;\r
+  top: 0;\r
+  left: 0;\r
+  z-index: 1010;\r
+  display: none;\r
+  max-width: 276px;\r
+  padding: 1px;\r
+  text-align: left;\r
+  background-color: #ffffff;\r
+  background-clip: padding-box;\r
+  border: 1px solid #cccccc;\r
+  border: 1px solid rgba(0, 0, 0, 0.2);\r
+  border-radius: 0px;\r
+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\r
+  box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);\r
+  white-space: normal;\r
+}\r
+.popover.top {\r
+  margin-top: -10px;\r
+}\r
+.popover.right {\r
+  margin-left: 10px;\r
+}\r
+.popover.bottom {\r
+  margin-top: 10px;\r
+}\r
+.popover.left {\r
+  margin-left: -10px;\r
+}\r
+.popover-title {\r
+  margin: 0;\r
+  padding: 8px 14px;\r
+  font-size: 14px;\r
+  font-weight: normal;\r
+  line-height: 18px;\r
+  background-color: #f7f7f7;\r
+  border-bottom: 1px solid #ebebeb;\r
+  border-radius: 5px 5px 0 0;\r
+}\r
+.popover-content {\r
+  padding: 9px 14px;\r
+}\r
+.popover > .arrow,\r
+.popover > .arrow:after {\r
+  position: absolute;\r
+  display: block;\r
+  width: 0;\r
+  height: 0;\r
+  border-color: transparent;\r
+  border-style: solid;\r
+}\r
+.popover > .arrow {\r
+  border-width: 11px;\r
+}\r
+.popover > .arrow:after {\r
+  border-width: 10px;\r
+  content: "";\r
+}\r
+.popover.top > .arrow {\r
+  left: 50%;\r
+  margin-left: -11px;\r
+  border-bottom-width: 0;\r
+  border-top-color: #999999;\r
+  border-top-color: rgba(0, 0, 0, 0.25);\r
+  bottom: -11px;\r
+}\r
+.popover.top > .arrow:after {\r
+  content: " ";\r
+  bottom: 1px;\r
+  margin-left: -10px;\r
+  border-bottom-width: 0;\r
+  border-top-color: #ffffff;\r
+}\r
+.popover.right > .arrow {\r
+  top: 50%;\r
+  left: -11px;\r
+  margin-top: -11px;\r
+  border-left-width: 0;\r
+  border-right-color: #999999;\r
+  border-right-color: rgba(0, 0, 0, 0.25);\r
+}\r
+.popover.right > .arrow:after {\r
+  content: " ";\r
+  left: 1px;\r
+  bottom: -10px;\r
+  border-left-width: 0;\r
+  border-right-color: #ffffff;\r
+}\r
+.popover.bottom > .arrow {\r
+  left: 50%;\r
+  margin-left: -11px;\r
+  border-top-width: 0;\r
+  border-bottom-color: #999999;\r
+  border-bottom-color: rgba(0, 0, 0, 0.25);\r
+  top: -11px;\r
+}\r
+.popover.bottom > .arrow:after {\r
+  content: " ";\r
+  top: 1px;\r
+  margin-left: -10px;\r
+  border-top-width: 0;\r
+  border-bottom-color: #ffffff;\r
+}\r
+.popover.left > .arrow {\r
+  top: 50%;\r
+  right: -11px;\r
+  margin-top: -11px;\r
+  border-right-width: 0;\r
+  border-left-color: #999999;\r
+  border-left-color: rgba(0, 0, 0, 0.25);\r
+}\r
+.popover.left > .arrow:after {\r
+  content: " ";\r
+  right: 1px;\r
+  border-right-width: 0;\r
+  border-left-color: #ffffff;\r
+  bottom: -10px;\r
+}\r
+.carousel {\r
+  position: relative;\r
+}\r
+.carousel-inner {\r
+  position: relative;\r
+  overflow: hidden;\r
+  width: 100%;\r
+}\r
+.carousel-inner > .item {\r
+  display: none;\r
+  position: relative;\r
+  -webkit-transition: 0.6s ease-in-out left;\r
+  transition: 0.6s ease-in-out left;\r
+}\r
+.carousel-inner > .item > img,\r
+.carousel-inner > .item > a > img {\r
+  line-height: 1;\r
+}\r
+.carousel-inner > .active,\r
+.carousel-inner > .next,\r
+.carousel-inner > .prev {\r
+  display: block;\r
+}\r
+.carousel-inner > .active {\r
+  left: 0;\r
+}\r
+.carousel-inner > .next,\r
+.carousel-inner > .prev {\r
+  position: absolute;\r
+  top: 0;\r
+  width: 100%;\r
+}\r
+.carousel-inner > .next {\r
+  left: 100%;\r
+}\r
+.carousel-inner > .prev {\r
+  left: -100%;\r
+}\r
+.carousel-inner > .next.left,\r
+.carousel-inner > .prev.right {\r
+  left: 0;\r
+}\r
+.carousel-inner > .active.left {\r
+  left: -100%;\r
+}\r
+.carousel-inner > .active.right {\r
+  left: 100%;\r
+}\r
+.carousel-control {\r
+  position: absolute;\r
+  top: 0;\r
+  left: 0;\r
+  bottom: 0;\r
+  width: 15%;\r
+  opacity: 0.5;\r
+  filter: alpha(opacity=50);\r
+  font-size: 20px;\r
+  color: #ffffff;\r
+  text-align: center;\r
+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\r
+}\r
+.carousel-control.left {\r
+  background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0%), color-stop(rgba(0, 0, 0, 0.0001) 100%));\r
+  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);\r
+  background-repeat: repeat-x;\r
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);\r
+}\r
+.carousel-control.right {\r
+  left: auto;\r
+  right: 0;\r
+  background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0%), color-stop(rgba(0, 0, 0, 0.5) 100%));\r
+  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);\r
+  background-repeat: repeat-x;\r
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);\r
+}\r
+.carousel-control:hover,\r
+.carousel-control:focus {\r
+  outline: none;\r
+  color: #ffffff;\r
+  text-decoration: none;\r
+  opacity: 0.9;\r
+  filter: alpha(opacity=90);\r
+}\r
+.carousel-control .icon-prev,\r
+.carousel-control .icon-next,\r
+.carousel-control .glyphicon-chevron-left,\r
+.carousel-control .glyphicon-chevron-right {\r
+  position: absolute;\r
+  top: 50%;\r
+  z-index: 5;\r
+  display: inline-block;\r
+}\r
+.carousel-control .icon-prev,\r
+.carousel-control .glyphicon-chevron-left {\r
+  left: 50%;\r
+}\r
+.carousel-control .icon-next,\r
+.carousel-control .glyphicon-chevron-right {\r
+  right: 50%;\r
+}\r
+.carousel-control .icon-prev,\r
+.carousel-control .icon-next {\r
+  width: 20px;\r
+  height: 20px;\r
+  margin-top: -10px;\r
+  margin-left: -10px;\r
+  font-family: serif;\r
+}\r
+.carousel-control .icon-prev:before {\r
+  content: '\2039';\r
+}\r
+.carousel-control .icon-next:before {\r
+  content: '\203a';\r
+}\r
+.carousel-indicators {\r
+  position: absolute;\r
+  bottom: 10px;\r
+  left: 50%;\r
+  z-index: 15;\r
+  width: 60%;\r
+  margin-left: -30%;\r
+  padding-left: 0;\r
+  list-style: none;\r
+  text-align: center;\r
+}\r
+.carousel-indicators li {\r
+  display: inline-block;\r
+  width: 10px;\r
+  height: 10px;\r
+  margin: 1px;\r
+  text-indent: -999px;\r
+  border: 1px solid #ffffff;\r
+  border-radius: 10px;\r
+  cursor: pointer;\r
+  background-color: #000 \9;\r
+  background-color: rgba(0, 0, 0, 0);\r
+}\r
+.carousel-indicators .active {\r
+  margin: 0;\r
+  width: 12px;\r
+  height: 12px;\r
+  background-color: #ffffff;\r
+}\r
+.carousel-caption {\r
+  position: absolute;\r
+  left: 15%;\r
+  right: 15%;\r
+  bottom: 20px;\r
+  z-index: 10;\r
+  padding-top: 20px;\r
+  padding-bottom: 20px;\r
+  color: #ffffff;\r
+  text-align: center;\r
+  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);\r
+}\r
+.carousel-caption .btn {\r
+  text-shadow: none;\r
+}\r
+@media screen and (min-width: 768px) {\r
+  .carousel-control .glyphicons-chevron-left,\r
+  .carousel-control .glyphicons-chevron-right,\r
+  .carousel-control .icon-prev,\r
+  .carousel-control .icon-next {\r
+    width: 30px;\r
+    height: 30px;\r
+    margin-top: -15px;\r
+    margin-left: -15px;\r
+    font-size: 30px;\r
+  }\r
+  .carousel-caption {\r
+    left: 20%;\r
+    right: 20%;\r
+    padding-bottom: 30px;\r
+  }\r
+  .carousel-indicators {\r
+    bottom: 20px;\r
+  }\r
+}\r
+.clearfix:before,\r
+.clearfix:after,\r
+.container:before,\r
+.container:after,\r
+.container-fluid:before,\r
+.container-fluid:after,\r
+.row:before,\r
+.row:after,\r
+.form-horizontal .form-group:before,\r
+.form-horizontal .form-group:after,\r
+.btn-toolbar:before,\r
+.btn-toolbar:after,\r
+.btn-group-vertical > .btn-group:before,\r
+.btn-group-vertical > .btn-group:after,\r
+.nav:before,\r
+.nav:after,\r
+.navbar:before,\r
+.navbar:after,\r
+.navbar-header:before,\r
+.navbar-header:after,\r
+.navbar-collapse:before,\r
+.navbar-collapse:after,\r
+.pager:before,\r
+.pager:after,\r
+.panel-body:before,\r
+.panel-body:after,\r
+.modal-footer:before,\r
+.modal-footer:after {\r
+  content: " ";\r
+  display: table;\r
+}\r
+.clearfix:after,\r
+.container:after,\r
+.container-fluid:after,\r
+.row:after,\r
+.form-horizontal .form-group:after,\r
+.btn-toolbar:after,\r
+.btn-group-vertical > .btn-group:after,\r
+.nav:after,\r
+.navbar:after,\r
+.navbar-header:after,\r
+.navbar-collapse:after,\r
+.pager:after,\r
+.panel-body:after,\r
+.modal-footer:after {\r
+  clear: both;\r
+}\r
+.center-block {\r
+  display: block;\r
+  margin-left: auto;\r
+  margin-right: auto;\r
+}\r
+.pull-right {\r
+  float: right !important;\r
+}\r
+.pull-left {\r
+  float: left !important;\r
+}\r
+.hide {\r
+  display: none !important;\r
+}\r
+.show {\r
+  display: block !important;\r
+}\r
+.invisible {\r
+  visibility: hidden;\r
+}\r
+.text-hide {\r
+  font: 0/0 a;\r
+  color: transparent;\r
+  text-shadow: none;\r
+  background-color: transparent;\r
+  border: 0;\r
+}\r
+.hidden {\r
+  display: none !important;\r
+  visibility: hidden !important;\r
+}\r
+.affix {\r
+  position: fixed;\r
+}\r
+@-ms-viewport {\r
+  width: device-width;\r
+}\r
+.visible-xs,\r
+.visible-sm,\r
+.visible-md,\r
+.visible-lg {\r
+  display: none !important;\r
+}\r
+@media (max-width: 767px) {\r
+  .visible-xs {\r
+    display: block !important;\r
+  }\r
+  table.visible-xs {\r
+    display: table;\r
+  }\r
+  tr.visible-xs {\r
+    display: table-row !important;\r
+  }\r
+  th.visible-xs,\r
+  td.visible-xs {\r
+    display: table-cell !important;\r
+  }\r
+}\r
+@media (min-width: 768px) and (max-width: 991px) {\r
+  .visible-sm {\r
+    display: block !important;\r
+  }\r
+  table.visible-sm {\r
+    display: table;\r
+  }\r
+  tr.visible-sm {\r
+    display: table-row !important;\r
+  }\r
+  th.visible-sm,\r
+  td.visible-sm {\r
+    display: table-cell !important;\r
+  }\r
+}\r
+@media (min-width: 992px) and (max-width: 1199px) {\r
+  .visible-md {\r
+    display: block !important;\r
+  }\r
+  table.visible-md {\r
+    display: table;\r
+  }\r
+  tr.visible-md {\r
+    display: table-row !important;\r
+  }\r
+  th.visible-md,\r
+  td.visible-md {\r
+    display: table-cell !important;\r
+  }\r
+}\r
+@media (min-width: 1200px) {\r
+  .visible-lg {\r
+    display: block !important;\r
+  }\r
+  table.visible-lg {\r
+    display: table;\r
+  }\r
+  tr.visible-lg {\r
+    display: table-row !important;\r
+  }\r
+  th.visible-lg,\r
+  td.visible-lg {\r
+    display: table-cell !important;\r
+  }\r
+}\r
+@media (max-width: 767px) {\r
+  .hidden-xs {\r
+    display: none !important;\r
+  }\r
+}\r
+@media (min-width: 768px) and (max-width: 991px) {\r
+  .hidden-sm {\r
+    display: none !important;\r
+  }\r
+}\r
+@media (min-width: 992px) and (max-width: 1199px) {\r
+  .hidden-md {\r
+    display: none !important;\r
+  }\r
+}\r
+@media (min-width: 1200px) {\r
+  .hidden-lg {\r
+    display: none !important;\r
+  }\r
+}\r
+.visible-print {\r
+  display: none !important;\r
+}\r
+@media print {\r
+  .visible-print {\r
+    display: block !important;\r
+  }\r
+  table.visible-print {\r
+    display: table;\r
+  }\r
+  tr.visible-print {\r
+    display: table-row !important;\r
+  }\r
+  th.visible-print,\r
+  td.visible-print {\r
+    display: table-cell !important;\r
+  }\r
+}\r
+@media print {\r
+  .hidden-print {\r
+    display: none !important;\r
+  }\r
+}\r
+   \r
diff --git a/share/nitweb/views/class.html b/share/nitweb/views/class.html
new file mode 100644 (file)
index 0000000..52bd1e9
--- /dev/null
@@ -0,0 +1,36 @@
+<div class='container-fluid'>
+       <div class='page-header'>
+               <h2><entity-signature mentity='mentity.intro'/></h2>
+               <entity-link mentity='mentity.mpackage' /> :: {{mentity.name}}
+       </div>
+
+       <ul class='nav nav-tabs'>
+               <li class='active'>
+                       <a data-toggle='tab' data-target='#doc'>
+                               <span class='glyphicon glyphicon-book'/> Doc
+                       </a>
+               </li>
+       </ul>
+
+       <div class='tab-content'>
+               <div class='tab-pane fade in active' id='doc'>
+                       <entity-doc mentity='mentity.intro'/>
+
+                       <entity-list list-title='Parents'
+                               list-entities='mentity.parents'
+                               list-object-filter='{}' />
+
+                       <entity-list list-title='Constructors'
+                               list-entities='mentity.all_mproperties'
+                               list-object-filter='{is_init: true}' />
+
+                       <entity-list list-title='Introduced properties'
+                               list-entities='mentity.intro_mproperties'
+                               list-object-filter='{is_init: "!true"}' />
+
+                       <entity-list list-title='Redefined properties'
+                               list-entities='mentity.redef_mproperties'
+                               list-object-filter='{is_init: "!true"}' />
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/classdef.html b/share/nitweb/views/classdef.html
new file mode 100644 (file)
index 0000000..50768ec
--- /dev/null
@@ -0,0 +1,16 @@
+<div class='container-fluid'>
+       <div class='page-header'>
+               <h2><entity-signature mentity='mentity'/></h2>
+               <entity-link mentity='mentity.mpackage' />
+               :: <entity-link mentity='mentity.mmodule' />
+               :: {{mentity.name}}
+       </div>
+
+       <ul class='nav nav-tabs' role='tablist'>
+               <li class='warning'>
+                       <a ng-href='{{mentity.mclass.web_url}}'>
+                               <span class='glyphicon glyphicon-chevron-left'/> Go to class
+                       </a>
+               </li>
+       </ul>
+</div>
diff --git a/share/nitweb/views/group.html b/share/nitweb/views/group.html
new file mode 100644 (file)
index 0000000..ca19974
--- /dev/null
@@ -0,0 +1,30 @@
+<div class='container-fluid'>
+               <div class='page-header'>
+                       <h2><entity-signature mentity='mentity' /></h2>
+                       <entity-link mentity='mentity.mpackage' /> :: {{mentity.name}}
+               </div>
+
+               <ul class='nav nav-tabs'>
+                       <li class='active'>
+                               <a data-toggle='tab' data-target='#doc'>
+                                       <span class='glyphicon glyphicon-book'/> Doc
+                               </a>
+                       </li>
+               </ul>
+
+               <div class='tab-content'>
+                       <div class='tab-pane fade in active' id='doc'>
+                               <entity-doc mentity='mentity'/>
+
+                               <entity-list list-title='Parent group' list-entities='[mentity.parent]'
+                                       list-object-filter='{}' ng-if='mentity.parent' />
+
+                               <entity-list list-title='Subgroups' list-entities='mentity.mgroups'
+                                       list-object-filter='{}' />
+
+                               <entity-list list-title='Modules' list-entities='mentity.mmodules'
+                                       list-object-filter='{}' />
+                       </div>
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/index.html b/share/nitweb/views/index.html
new file mode 100644 (file)
index 0000000..9f9b93d
--- /dev/null
@@ -0,0 +1 @@
+<h1>Hello nitweb!</h1>
diff --git a/share/nitweb/views/module.html b/share/nitweb/views/module.html
new file mode 100644 (file)
index 0000000..79d905d
--- /dev/null
@@ -0,0 +1,30 @@
+<div class='container-fluid'>
+       <div class='page-header'>
+               <h2><entity-signature mentity='mentity'/></h2>
+               <entity-link mentity='mentity.mpackage' /> :: {{mentity.name}}
+       </div>
+
+       <ul class='nav nav-tabs'>
+               <li class='active'>
+                       <a data-toggle='tab' data-target='#doc'>
+                               <span class='glyphicon glyphicon-book'/> Doc
+                       </a>
+               </li>
+       </ul>
+
+       <div class='tab-content'>
+               <div role='tabpanel' class='tab-pane fade in active' id='doc'>
+                       <entity-doc mentity='mentity'/>
+
+                       <entity-list list-title='Imported modules' list-entities='mentity.imports'
+                               list-object-filter='{}' />
+
+                       <entity-list list-title='Introduced classes' list-entities='mentity.intro_mclasses'
+                               list-object-filter='{}' />
+
+                       <entity-list list-title='Class redefinitions' list-entities='mentity.redef_mclassdefs'
+                               list-object-filter='{}' />
+
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/package.html b/share/nitweb/views/package.html
new file mode 100644 (file)
index 0000000..ee19aa5
--- /dev/null
@@ -0,0 +1,22 @@
+<div class='container-fluid'>
+       <div class='page-header'>
+               <h2><entity-signature mentity='mentity'/></h2>
+       </div>
+
+       <ul class='nav nav-tabs' role='tablist'>
+               <li role='presentation' class='active'>
+                       <a data-toggle='tab' role='tab' data-target='#doc' aria-controls="doc">
+                               <span class='glyphicon glyphicon-book'/> Doc
+                       </a>
+               </li>
+       </ul>
+
+       <div class='tab-content'>
+               <div role='tabpanel' class='tab-pane fade in active' id='doc'>
+                       <entity-doc mentity='mentity'/>
+
+                       <entity-list list-title='Groups' list-entities='mentity.mgroups'
+                               list-object-filter='{}' />
+               </div>
+       </div>
+</div>
diff --git a/share/nitweb/views/propdef.html b/share/nitweb/views/propdef.html
new file mode 100644 (file)
index 0000000..2692c9c
--- /dev/null
@@ -0,0 +1,18 @@
+<div class='container-fluid'>
+
+       <div class='page-header'>
+               <h2><entity-signature mentity='mentity'/></h2>
+               <entity-link mentity='mentity.mpackage' />
+               :: <entity-link mentity='mentity.mmodule' />
+               :: <entity-link mentity='mentity.mclassdef' />
+               :: {{mentity.name}}
+       </div>
+
+       <ul class='nav nav-tabs'>
+               <li class='warning'>
+                       <a href='{{mentity.mproperty.web_url}}'>
+                               <span class='glyphicon glyphicon-chevron-left'/> Go to property
+                       </a>
+               </li>
+       </ul>
+</div>
diff --git a/share/nitweb/views/property.html b/share/nitweb/views/property.html
new file mode 100644 (file)
index 0000000..d5e7352
--- /dev/null
@@ -0,0 +1,23 @@
+<div class='container-fluid'>
+
+       <div class='page-header'>
+               <h2><entity-signature mentity='mentity.intro'/></h2>
+               <entity-link mentity='mentity.mpackage' />
+               :: <entity-link mentity='mentity.intro_mclassdef' />
+               :: {{mentity.name}}
+       </div>
+
+       <ul class='nav nav-tabs'>
+               <li class='active'>
+                       <a data-toggle='tab' data-target='#doc'>
+                               <span class='glyphicon glyphicon-book'/> Doc
+                       </a>
+               </li>
+       </ul>
+
+       <div class='tab-content'>
+               <div role='tabpanel' class='tab-pane fade in active' id='doc'>
+                       <entity-doc mentity='mentity.intro'/>
+               </div>
+       </div>
+</div>
index 1743179..92510bc 100644 (file)
@@ -362,8 +362,9 @@ class Catalog
                var doc_score = 0.0
                for g in mpackage.mgroups do
                        mmodules += g.mmodules.length
-                       entity_score += 1.0
-                       if g.mdoc != null then doc_score += 1.0
+                       var gs = 1.0
+                       entity_score += gs
+                       if g.mdoc != null then doc_score += gs
                        for m in g.mmodules do
                                var source = m.location.file
                                if source != null then
@@ -382,21 +383,23 @@ class Catalog
                                                loc += file.line_starts.length - 1
                                        end
                                end
-                               entity_score += 1.0
-                               if m.mdoc != null then doc_score += 1.0
+                               var ms = gs
+                               if m.is_test_suite then ms /= 100.0
+                               entity_score += ms
+                               if m.mdoc != null then doc_score += ms else ms /= 10.0
                                for cd in m.mclassdefs do
-                                       var s = 0.2
-                                       if not cd.is_intro then s /= 10.0
-                                       if not cd.mclass.visibility <= private_visibility then s /= 10.0
-                                       entity_score += s
-                                       if cd.mdoc != null then doc_score += s
+                                       var cs = ms * 0.2
+                                       if not cd.is_intro then cs /= 100.0
+                                       if not cd.mclass.visibility <= private_visibility then cs /= 100.0
+                                       entity_score += cs
+                                       if cd.mdoc != null then doc_score += cs
                                        mclasses += 1
                                        for pd in cd.mpropdefs do
-                                               s = 0.1
-                                               if not pd.is_intro then s /= 10.0
-                                               if not pd.mproperty.visibility <= private_visibility then s /= 10.0
-                                               entity_score += s
-                                               if pd.mdoc != null then doc_score += s
+                                               var ps = ms * 0.1
+                                               if not pd.is_intro then ps /= 100.0
+                                               if not pd.mproperty.visibility <= private_visibility then ps /= 100.0
+                                               entity_score += ps
+                                               if pd.mdoc != null then doc_score += ps
                                                if not pd isa MMethodDef then continue
                                                mmethods += 1
                                        end
index c2ed988..a8d86b6 100644 (file)
@@ -31,9 +31,6 @@ redef class ToolContext
        var opt_source = new OptionString("Format to link source code (%f for filename, " +
                "%l for first line, %L for last line)", "--source")
 
-       # Directory where the CSS and JS is stored.
-       var opt_sharedir = new OptionString("Directory containing nitdoc assets", "--sharedir")
-
        # Use a shareurl instead of copy shared files.
        #
        # This is usefull if you don't want to store the Nitdoc templates with your
@@ -77,7 +74,7 @@ redef class ToolContext
                super
 
                option_context.add_option(
-                       opt_source, opt_sharedir, opt_shareurl, opt_custom_title,
+                       opt_source, opt_share_dir, opt_shareurl, opt_custom_title,
                        opt_custom_footer, opt_custom_intro, opt_custom_brand,
                        opt_github_upstream, opt_github_base_sha1, opt_github_gitdir,
                        opt_piwik_tracker, opt_piwik_site_id,
@@ -120,15 +117,7 @@ class RenderHTMLPhase
                var output_dir = ctx.output_dir
                if not output_dir.file_exists then output_dir.mkdir
                # locate share dir
-               var sharedir = ctx.opt_sharedir.value
-               if sharedir == null then
-                       var dir = ctx.nit_dir
-                       sharedir = dir/"share/nitdoc"
-                       if not sharedir.file_exists then
-                               print "Error: cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
-                               abort
-                       end
-               end
+               var sharedir = ctx.share_dir / "nitdoc"
                # copy shared files
                if ctx.opt_shareurl.value == null then
                        sys.system("cp -r -- {sharedir.to_s.escape_to_sh}/* {output_dir.to_s.escape_to_sh}/")
index 4207a01..4e4a5b3 100644 (file)
@@ -128,7 +128,12 @@ redef class AModule
                srcs.add_all mmodule.ffi_files
 
                # Compiler options specific to this module
-               var ldflags = mmodule.ldflags[""].join(" ")
+               var ldflags_array = mmodule.ldflags[""]
+               if ldflags_array.has("-lrt") and system("sh -c 'uname -s 2>/dev/null || echo not' | grep Darwin >/dev/null") == 0 then
+                       # Remove -lrt on OS X
+                       ldflags_array.remove "-lrt"
+               end
+               var ldflags = ldflags_array.join(" ")
 
                # Protect pkg-config
                var pkgconfigs = mmodule.pkgconfigs
index 9aefe19..e7c3b15 100644 (file)
@@ -30,6 +30,15 @@ import mdoc
 import ordered_tree
 private import more_collections
 
+redef class MEntity
+       # The visibility of the MEntity.
+       #
+       # MPackages, MGroups and MModules are always public.
+       # The visibility of `MClass` and `MProperty` is defined by the keyword used.
+       # `MClassDef` and `MPropDef` return the visibility of `MClass` and `MProperty`.
+       fun visibility: MVisibility do return public_visibility
+end
+
 redef class Model
        # All known classes
        var mclasses = new Array[MClass]
@@ -465,7 +474,7 @@ class MClass
 
        # The visibility of the class
        # In Nit, the visibility of a class cannot evolve in refinements
-       var visibility: MVisibility
+       redef var visibility
 
        init
        do
@@ -555,6 +564,8 @@ class MClass
 
        # Is `self` and abstract class?
        var is_abstract: Bool is lazy do return kind == abstract_kind
+
+       redef fun mdoc_or_fallback do return intro.mdoc_or_fallback
 end
 
 
@@ -593,6 +604,8 @@ class MClassDef
 
        redef var location: Location
 
+       redef fun visibility do return mclass.visibility
+
        # Internal name combining the module and the class
        # Example: "mymodule$MyClass"
        redef var to_s is noinit
@@ -1963,6 +1976,8 @@ abstract class MProperty
 
        redef var location
 
+       redef fun mdoc_or_fallback do return intro.mdoc_or_fallback
+
        # The canonical name of the property.
        #
        # It is currently the short-`name` prefixed by the short-name of the class and the full-name of the module.
@@ -1988,7 +2003,7 @@ abstract class MProperty
        end
 
        # The visibility of the property
-       var visibility: MVisibility
+       redef var visibility
 
        # Is the property usable as an initializer?
        var is_autoinit = false is writable
@@ -2255,6 +2270,8 @@ abstract class MPropDef
 
        redef var location: Location
 
+       redef fun visibility do return mproperty.visibility
+
        init
        do
                mclassdef.mpropdefs.add(self)
index 2590b0d..79970e5 100644 (file)
@@ -112,8 +112,6 @@ redef class MModule
                var mmodules = new HashSet[MModule]
                mmodules.add self
                mmodules.add_all collect_ancestors(view)
-               mmodules.add_all collect_parents(view)
-               mmodules.add_all collect_children(view)
                mmodules.add_all collect_descendants(view)
                return view.mmodules_poset(mmodules)
        end
@@ -220,6 +218,15 @@ redef class MClass
                return res
        end
 
+       # Build a class hierarchy poset for `self` based on its ancestors and descendants.
+       fun hierarchy_poset(mainmodule: MModule, view: ModelView): POSet[MClass] do
+               var mclasses = new HashSet[MClass]
+               mclasses.add self
+               mclasses.add_all collect_ancestors(view)
+               mclasses.add_all collect_descendants(view)
+               return view.mclasses_poset(mainmodule, mclasses)
+       end
+
        # Collect all mproperties introduced in 'self' with `visibility >= min_visibility`.
        fun collect_intro_mproperties(view: ModelView): Set[MProperty] do
                var set = new HashSet[MProperty]
index fc4b101..658e73d 100644 (file)
@@ -59,6 +59,8 @@ redef class MEntity
                obj["class_name"] = class_name
                obj["full_name"] = full_name
                obj["mdoc"] = mdoc_or_fallback
+               obj["visibility"] = visibility
+               obj["location"] = location
                var modifiers = new JsonArray
                for modifier in collect_modifiers do
                        modifiers.add modifier
@@ -114,7 +116,6 @@ redef class MPackage
 
        redef fun json do
                var obj = super
-               obj["visibility"] = public_visibility
                if ini != null then
                        obj["ini"] = new JsonObject.from(ini.as(not null).to_map)
                end
@@ -127,7 +128,6 @@ end
 redef class MGroup
        redef fun json do
                var obj = super
-               obj["visibility"] = public_visibility
                obj["is_root"] = is_root
                obj["mpackage"] = to_mentity_ref(mpackage)
                obj["default_mmodule"] = to_mentity_ref(default_mmodule)
@@ -141,8 +141,6 @@ end
 redef class MModule
        redef fun json do
                var obj = super
-               obj["location"] = location
-               obj["visibility"] = public_visibility
                obj["mpackage"] = to_mentity_ref(mpackage)
                obj["mgroup"] = to_mentity_ref(mgroup)
                obj["intro_mclasses"] = to_mentity_refs(intro_mclasses)
@@ -154,7 +152,6 @@ end
 redef class MClass
        redef fun json do
                var obj = super
-               obj["visibility"] = visibility
                var arr = new JsonArray
                for mparameter in mparameters do arr.add mparameter
                obj["mparameters"] = arr
@@ -169,8 +166,6 @@ end
 redef class MClassDef
        redef fun json do
                var obj = super
-               obj["visibility"] = mclass.visibility
-               obj["location"] = location
                obj["is_intro"] = is_intro
                var arr = new JsonArray
                for mparameter in mclass.mparameters do arr.add mparameter
@@ -186,7 +181,6 @@ end
 redef class MProperty
        redef fun json do
                var obj = super
-               obj["visibility"] = visibility
                obj["intro"] = to_mentity_ref(intro)
                obj["intro_mclassdef"] = to_mentity_ref(intro_mclassdef)
                obj["mpropdefs"] = to_mentity_refs(mpropdefs)
@@ -223,8 +217,6 @@ end
 redef class MPropDef
        redef fun json do
                var obj = super
-               obj["visibility"] = mproperty.visibility
-               obj["location"] = location
                obj["is_intro"] = is_intro
                obj["mclassdef"] = to_mentity_ref(mclassdef)
                obj["mproperty"] = to_mentity_ref(mproperty)
index da881a0..59d3e61 100644 (file)
@@ -144,7 +144,10 @@ redef class MEntity
        # See the specific implementation in the subclasses.
        fun visit_all(v: ModelVisitor) do end
 
-       private fun accept_visibility(min_visibility: nullable MVisibility): Bool do return true
+       private fun accept_visibility(min_visibility: nullable MVisibility): Bool do
+               if min_visibility == null then return true
+               return visibility >= min_visibility
+       end
 end
 
 redef class Model
@@ -183,13 +186,6 @@ redef class MModule
        end
 end
 
-redef class MClass
-       redef fun accept_visibility(min_visibility) do
-               if min_visibility == null then return true
-               return visibility >= min_visibility
-       end
-end
-
 redef class MClassDef
        # Visit all the classes and class definitions of the module.
        #
@@ -202,23 +198,4 @@ redef class MClassDef
                        v.enter_visit(x)
                end
        end
-
-       redef fun accept_visibility(min_visibility) do
-               if min_visibility == null then return true
-               return mclass.visibility >= min_visibility
-       end
-end
-
-redef class MProperty
-       redef fun accept_visibility(min_visibility) do
-               if min_visibility == null then return true
-               return visibility >= min_visibility
-       end
-end
-
-redef class MPropDef
-       redef fun accept_visibility(min_visibility) do
-               if min_visibility == null then return true
-               return mproperty.visibility >= min_visibility
-       end
 end
index 0d89dc6..8d79e80 100644 (file)
@@ -64,6 +64,8 @@ if toolcontext.opt_gen_unit.value then
 end
 
 "NIT_TESTING".setenv("true")
+"NIT_TESTING_ID".setenv(pid.to_s)
+"SRAND".setenv("0")
 
 var test_dir = toolcontext.test_dir
 test_dir.mkdir
@@ -96,19 +98,62 @@ end
 var file = toolcontext.opt_output.value
 if file == null then file = "nitunit.xml"
 page.write_to_file(file)
-# print docunits results
-print "DocUnits:"
-if modelbuilder.unit_entities == 0 then
-       print "No doc units found"
-else if modelbuilder.failed_entities == 0 and not toolcontext.opt_noact.value then
-       print "DocUnits Success"
+
+# Print results
+printn "Docunits: Entities: {modelbuilder.total_entities}; Documented ones: {modelbuilder.doc_entities}; With nitunits: {modelbuilder.unit_entities}"
+if modelbuilder.unit_entities == 0 or toolcontext.opt_noact.value then
+       print ""
+else
+       printn "; Failures: "
+       var cpt = modelbuilder.failed_entities
+       if toolcontext.opt_no_color.value then
+               print cpt
+       else if cpt == 0 then
+               print "0".green.bold
+       else
+               print cpt.to_s.red.bold
+       end
 end
-print "Entities: {modelbuilder.total_entities}; Documented ones: {modelbuilder.doc_entities}; With nitunits: {modelbuilder.unit_entities}; Failures: {modelbuilder.failed_entities}"
-# print testsuites results
-print "\nTestSuites:"
-if modelbuilder.total_tests == 0 then
-       print "No test cases found"
-else if modelbuilder.failed_tests == 0 and not toolcontext.opt_noact.value then
-       print "TestSuites Success"
+printn "Test suites: Classes: {modelbuilder.total_classes}; Test Cases: {modelbuilder.total_tests}"
+if modelbuilder.total_tests == 0 or toolcontext.opt_noact.value then
+       print ""
+else
+       printn "; Failures: "
+       var cpt = modelbuilder.failed_tests
+       if toolcontext.opt_no_color.value then
+               print cpt
+       else if cpt == 0 then
+               print "0".green.bold
+       else
+               print cpt.to_s.red.bold
+       end
 end
-print "Class suites: {modelbuilder.total_classes}; Test Cases: {modelbuilder.total_tests}; Failures: {modelbuilder.failed_tests}"
+
+var total = modelbuilder.unit_entities + modelbuilder.total_tests
+var fail = modelbuilder.failed_entities + modelbuilder.failed_tests
+if toolcontext.opt_noact.value then
+       # nothing
+else if total == 0 then
+       var head = "[NOTHING]"
+       if not toolcontext.opt_no_color.value then
+               head = head.yellow
+       end
+       print "{head} No unit tests to execute."
+else if fail == 0 then
+       var head = "[SUCCESS]"
+       if not toolcontext.opt_no_color.value then
+               head = head.green.bold
+       end
+       print "{head} All {total} tests passed."
+else
+       var head = "[FAILURE]"
+       if not toolcontext.opt_no_color.value then
+               head = head.red.bold
+       end
+       print "{head} {fail}/{total} tests failed."
+
+       print "`{test_dir}` is not removed for investigation."
+       exit 1
+end
+
+test_dir.rmdir
index c055ee9..93d15aa 100644 (file)
@@ -48,12 +48,10 @@ private class NitwebPhase
                var port = toolcontext.opt_port.value
 
                var app = new App
-               app.use("/random", new RandomAction(model))
-               app.use("/doc/:namespace", new DocAction(model, modelbuilder))
-               app.use("/code/:namespace", new CodeAction(model, modelbuilder))
-               app.use("/search/:namespace", new SearchAction(model))
-               app.use("/uml/:namespace", new UMLDiagramAction(model, mainmodule))
-               app.use("/", new TreeAction(model))
+
+               app.use("/api", new APIRouter(model, modelbuilder, mainmodule))
+               app.use("/doc/:namespace", new DocAction(model, mainmodule, modelbuilder))
+               app.use("/*", new StaticHandler(toolcontext.share_dir / "nitweb", "index.html"))
 
                app.listen(host, port.to_i)
        end
index c072314..88e2b7e 100644 (file)
@@ -255,6 +255,8 @@ class RapidTypeAnalysis
                                add_cast(paramtype)
                        end
 
+                       if mmethoddef.is_abstract then continue
+
                        var npropdef = modelbuilder.mpropdef2node(mmethoddef)
 
                        if npropdef isa AClassdef then
@@ -314,7 +316,9 @@ class RapidTypeAnalysis
                                if not check_depth(rt) then continue
                                #print "{ot}/{t} -> {rt}"
                                live_types.add(rt)
-                               todo_types.add(rt)
+                               # unshift means a deep-first visit.
+                               # So that the `check_depth` limit is reached sooner.
+                               todo_types.unshift(rt)
                        end
                end
                #print "MType {live_types.length}: {live_types.join(", ")}"
index 414de9a..ddc167f 100644 (file)
@@ -17,6 +17,8 @@ module testing_base
 
 import modelize
 private import parser_util
+import html
+import console
 
 redef class ToolContext
        # opt --full
@@ -33,7 +35,7 @@ redef class ToolContext
        # Working directory for testing.
        fun test_dir: String do
                var dir = opt_dir.value
-               if dir == null then return ".nitunit"
+               if dir == null then return "nitunit.out"
                return dir
        end
 
@@ -92,6 +94,157 @@ ulimit -t {{{ulimit_usertime}}} 2> /dev/null
        #
        # Default: 10 CPU minute
        var ulimit_usertime = 600 is writable
+
+       # Show a single-line status to use as a progression.
+       #
+       # Note that the line starts with `'\r'` and is not ended by a `'\n'`.
+       # So it is expected that:
+       # * no other output is printed between two calls
+       # * the last `show_unit_status` is followed by a new-line
+       fun show_unit_status(name: String, tests: SequenceRead[UnitTest], more_message: nullable String)
+       do
+               var esc = 27.code_point.to_s
+               var line = "\r{esc}[K* {name} ["
+               var done = tests.length
+               for t in tests do
+                       if not t.is_done then
+                               line += " "
+                               done -= 1
+                       else if t.error == null then
+                               line += ".".green.bold
+                       else
+                               line += "X".red.bold
+                       end
+               end
+
+               if opt_no_color.value then
+                       if done == 0 then
+                               print "* {name} ({tests.length} tests)"
+                       end
+                       return
+               end
+
+               line += "] {done}/{tests.length}"
+               if more_message != null then
+                       line += " " + more_message
+               end
+               printn "{line}"
+       end
+
+       # Shoe the full description of the test-case.
+       #
+       # The output honors `--no-color`.
+       #
+       # `more message`, if any, is added after the error message.
+       fun show_unit(test: UnitTest, more_message: nullable String) do
+               print test.to_screen(more_message, not opt_no_color.value)
+       end
+end
+
+# A unit test is an elementary test discovered, run and reported by nitunit.
+#
+# This class factorizes `DocUnit` and `TestCase`.
+abstract class UnitTest
+       # The name of the unit to show in messages
+       fun full_name: String is abstract
+
+       # The location of the unit test to show in messages.
+       fun location: Location is abstract
+
+       # Flag that indicates if the unit test was compiled/run.
+       var is_done: Bool = false is writable
+
+       # Error message occurred during test-case execution (or compilation).
+       #
+       # e.g.: `Runtime Error`
+       var error: nullable String = null is writable
+
+       # Was the test case executed at least once?
+       #
+       # This will indicate the status of the test (failture or error)
+       var was_exec = false is writable
+
+       # The raw output of the execution (or compilation)
+       #
+       # It merges the standard output and error output
+       var raw_output: nullable String = null is writable
+
+       # The location where the error occurred, if it makes sense.
+       var error_location: nullable Location = null is writable
+
+       # A colorful `[OK]` or `[KO]`.
+       fun status_tag(color: nullable Bool): String do
+               color = color or else true
+               if not is_done then
+                       return "[  ]"
+               else if error != null then
+                       var res = "[KO]"
+                       if color then res = res.red.bold
+                       return res
+               else
+                       var res = "[OK]"
+                       if color then res = res.green.bold
+                       return res
+               end
+       end
+
+       # The full (color) description of the test-case.
+       #
+       # `more message`, if any, is added after the error message.
+       fun to_screen(more_message: nullable String, color: nullable Bool): String do
+               color = color or else true
+               var res
+               var error = self.error
+               if error != null then
+                       if more_message != null then error += " " + more_message
+                       var loc = error_location or else location
+                       if color then
+                               res = "{status_tag(color)} {full_name}\n     {loc.to_s.yellow}: {error}\n{loc.colored_line("1;31")}"
+                       else
+                               res = "{status_tag(color)} {full_name}\n     {loc}: {error}"
+                       end
+                       var output = self.raw_output
+                       if output != null then
+                               res += "\n     Output\n\t{output.chomp.replace("\n", "\n\t")}\n"
+                       end
+               else
+                       res = "{status_tag(color)} {full_name}"
+                       if more_message != null then res += more_message
+               end
+               return res
+       end
+
+       # Return a `<testcase>` XML node in format compatible with Jenkins unit tests.
+       fun to_xml: HTMLTag do
+               var tc = new HTMLTag("testcase")
+               tc.attr("classname", xml_classname)
+               tc.attr("name", xml_name)
+               var error = self.error
+               if error != null then
+                       if was_exec then
+                               tc.open("error").append(error)
+                       else
+                               tc.open("failure").append(error)
+                       end
+               end
+               var output = self.raw_output
+               if output != null then
+                       tc.open("system-err").append(output.trunc(8192).filter_nonprintable)
+               end
+               return tc
+       end
+
+       # The `classname` attribute of the XML format.
+       #
+       # NOTE: jenkins expects a '.' in the classname attr
+       #
+       # See to_xml
+       fun xml_classname: String is abstract
+
+       # The `name` attribute of the XML format.
+       #
+       # See to_xml
+       fun xml_name: String is abstract
 end
 
 redef class String
index 05c1f67..a2fa067 100644 (file)
@@ -36,11 +36,8 @@ class NitUnitExecutor
        # The XML node associated to the module
        var testsuite: HTMLTag
 
-       # All blocks of code from a same `ADoc`
-       var blocks = new Array[Buffer]
-
-       # All failures from a same `ADoc`
-       var failures = new Array[String]
+       # The name of the suite
+       var name: String
 
        # Markdown processor used to parse markdown comments and extract code.
        var mdproc = new MarkdownProcessor
@@ -55,47 +52,59 @@ class NitUnitExecutor
        # used to generate distinct names
        var cpt = 0
 
+       # The last docunit extracted from a mdoc.
+       #
+       # Is used because a new code-block might just be added to it.
+       var last_docunit: nullable DocUnit = null
+
+       var xml_classname: String is noautoinit
+
+       var xml_name: String is noautoinit
+
        # The entry point for a new `ndoc` node
        # Fill `docunits` with new discovered unit of tests.
-       #
-       # `tc` (testcase) is the pre-filled XML node
-       fun extract(mdoc: MDoc, tc: HTMLTag)
+       fun extract(mdoc: MDoc, xml_classname, xml_name: String)
        do
-               blocks.clear
-               failures.clear
+               last_docunit = null
+               self.xml_classname = xml_classname
+               self.xml_name = xml_name
 
                self.mdoc = mdoc
 
                # Populate `blocks` from the markdown decorator
                mdproc.process(mdoc.content.join("\n"))
-
-               toolcontext.check_errors
-
-               if not failures.is_empty then
-                       for msg in failures do
-                               var ne = new HTMLTag("failure")
-                               ne.attr("message", msg)
-                               tc.add ne
-                               toolcontext.modelbuilder.unit_entities += 1
-                               toolcontext.modelbuilder.failed_entities += 1
-                       end
-                       if blocks.is_empty then testsuite.add(tc)
-               end
-
-               if blocks.is_empty then return
-               for block in blocks do
-                       docunits.add new DocUnit(mdoc, tc, block.write_to_string)
-               end
        end
 
        # All extracted docunits
        var docunits = new Array[DocUnit]
 
+       fun show_status(more_message: nullable String)
+       do
+               toolcontext.show_unit_status(name, docunits, more_message)
+       end
+
+       fun mark_done(du: DocUnit)
+       do
+               du.is_done = true
+               show_status(du.full_name + " " + du.status_tag)
+       end
+
        # Execute all the docunits
        fun run_tests
        do
+               if docunits.is_empty then
+                       return
+               end
+
                var simple_du = new Array[DocUnit]
+               show_status
                for du in docunits do
+                       # Skip existing errors
+                       if du.error != null then
+                               mark_done(du)
+                               continue
+                       end
+
                        var ast = toolcontext.parse_something(du.block)
                        if ast isa AExpr then
                                simple_du.add du
@@ -105,6 +114,17 @@ class NitUnitExecutor
                end
 
                test_simple_docunits(simple_du)
+
+               show_status
+               print ""
+
+               for du in docunits do
+                       toolcontext.show_unit(du)
+               end
+
+               for du in docunits do
+                       testsuite.add du.to_xml
+               end
        end
 
        # Executes multiples doc-units in a shared program.
@@ -128,7 +148,7 @@ class NitUnitExecutor
 
                        i += 1
                        f.write("fun run_{i} do\n")
-                       f.write("# {du.testcase.attrs["name"]}\n")
+                       f.write("# {du.full_name}\n")
                        f.write(du.block)
                        f.write("end\n")
                end
@@ -153,35 +173,19 @@ class NitUnitExecutor
 
                i = 0
                for du in dus do
-                       var tc = du.testcase
-                       toolcontext.modelbuilder.unit_entities += 1
                        i += 1
-                       toolcontext.info("Execute doc-unit {du.testcase.attrs["name"]} in {file} {i}", 1)
+                       toolcontext.info("Execute doc-unit {du.full_name} in {file} {i}", 1)
                        var res2 = toolcontext.safe_exec("{file.to_program_name}.bin {i} >'{file}.out1' 2>&1 </dev/null")
+                       du.was_exec = true
 
-                       f = new FileReader.open("{file}.out1")
-                       var n2
-                       n2 = new HTMLTag("system-err")
-                       tc.add n2
-                       var content = f.read_all
-                       var msg = content.trunc(8192).filter_nonprintable
-                       n2.append(msg)
-                       f.close
-
-                       n2 = new HTMLTag("system-out")
-                       tc.add n2
-                       n2.append(du.block)
+                       var content = "{file}.out1".to_path.read_all
+                       du.raw_output = content
 
                        if res2 != 0 then
-                               var ne = new HTMLTag("error")
-                               ne.attr("message", "Runtime error")
-                               tc.add ne
-                               toolcontext.warning(du.mdoc.location, "error", "ERROR: {tc.attrs["classname"]}.{tc.attrs["name"]} (in {file}): Runtime error\n{msg}")
+                               du.error = "Runtime error in {file} with argument {i}"
                                toolcontext.modelbuilder.failed_entities += 1
                        end
-                       toolcontext.check_errors
-
-                       testsuite.add(tc)
+                       mark_done(du)
                end
        end
 
@@ -189,13 +193,10 @@ class NitUnitExecutor
        # Used for docunits larger than a single block of code (with modules, classes, functions etc.)
        fun test_single_docunit(du: DocUnit)
        do
-               var tc = du.testcase
-               toolcontext.modelbuilder.unit_entities += 1
-
                cpt += 1
                var file = "{prefix}-{cpt}.nit"
 
-               toolcontext.info("Execute doc-unit {tc.attrs["name"]} in {file}", 1)
+               toolcontext.info("Execute doc-unit {du.full_name} in {file}", 1)
 
                var f
                f = create_unitfile(file)
@@ -208,38 +209,20 @@ class NitUnitExecutor
                var res2 = 0
                if res == 0 then
                        res2 = toolcontext.safe_exec("{file.to_program_name}.bin >'{file}.out1' 2>&1 </dev/null")
+                       du.was_exec = true
                end
 
-               f = new FileReader.open("{file}.out1")
-               var n2
-               n2 = new HTMLTag("system-err")
-               tc.add n2
-               var content = f.read_all
-               var msg = content.trunc(8192).filter_nonprintable
-               n2.append(msg)
-               f.close
-
-               n2 = new HTMLTag("system-out")
-               tc.add n2
-               n2.append(du.block)
-
+               var content = "{file}.out1".to_path.read_all
+               du.raw_output = content
 
                if res != 0 then
-                       var ne = new HTMLTag("failure")
-                       ne.attr("message", "Compilation Error")
-                       tc.add ne
-                       toolcontext.warning(du.mdoc.location, "failure", "FAILURE: {tc.attrs["classname"]}.{tc.attrs["name"]} (in {file}):\n{msg}")
+                       du.error = "Compilation error in {file}"
                        toolcontext.modelbuilder.failed_entities += 1
                else if res2 != 0 then
-                       var ne = new HTMLTag("error")
-                       ne.attr("message", "Runtime Error")
-                       tc.add ne
-                       toolcontext.warning(du.mdoc.location, "error", "ERROR: {tc.attrs["classname"]}.{tc.attrs["name"]} (in {file}):\n{msg}")
+                       du.error = "Runtime error in {file}"
                        toolcontext.modelbuilder.failed_entities += 1
                end
-               toolcontext.check_errors
-
-               testsuite.add(tc)
+               mark_done(du)
        end
 
        # Create and fill the header of a unit file `file`.
@@ -331,32 +314,124 @@ private class NitunitDecorator
                                message = "Error: Invalid Nit code."
                        end
 
-                       executor.toolcontext.warning(location, "invalid-block", "{message} To suppress this message, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).")
-                       executor.failures.add("{location}: {message}")
+                       var du = new_docunit
+                       du.block += code
+                       du.error_location = location
+                       du.error = message
+                       executor.toolcontext.modelbuilder.failed_entities += 1
                        return
                end
 
                # Create a first block
                # Or create a new block for modules that are more than a main part
-               if executor.blocks.is_empty or ast isa AModule then
-                       executor.blocks.add(new Buffer)
+               var last_docunit = executor.last_docunit
+               if last_docunit == null or ast isa AModule then
+                       last_docunit = new_docunit
+                       executor.last_docunit = last_docunit
                end
 
                # Add it to the file
-               executor.blocks.last.append code
+               last_docunit.block += code
+
+               # In order to retrieve precise positions,
+               # the real position of each line of the raw_content is stored.
+               # See `DocUnit::real_location`
+               line_offset -= loc.line_start - 1
+               for i in [loc.line_start..loc.line_end] do
+                       last_docunit.lines.add i + line_offset
+                       last_docunit.columns.add column_offset
+               end
+       end
+
+       # Return and register a new empty docunit
+       fun new_docunit: DocUnit
+       do
+               var mdoc = executor.mdoc
+               assert mdoc != null
+
+               var next_number = 0
+               var name = executor.xml_name
+               if executor.docunits.not_empty and executor.docunits.last.mdoc == mdoc then
+                       next_number = executor.docunits.last.number + 1
+                       name += "+" + next_number.to_s
+               end
+
+               var res = new DocUnit(mdoc, next_number, "", executor.xml_classname, name)
+               executor.docunits.add res
+               executor.toolcontext.modelbuilder.unit_entities += 1
+               return res
        end
 end
 
-# A unit-test to run
+# A unit-test extracted from some documentation.
+#
+# A docunit is extracted from the code-blocks of mdocs.
+# Each mdoc can contains more than one docunit, and a single docunit can be made of more that a single code-block.
 class DocUnit
+       super UnitTest
+
        # The doc that contains self
        var mdoc: MDoc
 
-       # The XML node that contains the information about the execution
-       var testcase: HTMLTag
+       # The numbering of self in mdoc (starting with 0)
+       var number: Int
+
+       redef fun full_name do
+               var mentity = mdoc.original_mentity
+               if mentity != null then
+                       return mentity.full_name
+               else
+                       return xml_classname + "." + xml_name
+               end
+       end
 
-       # The text of the code to execute
+       # The text of the code to execute.
+       #
+       # This is the verbatim content on one, or more, code-blocks from `mdoc`
        var block: String
+
+       # For each line in `block`, the associated line in the mdoc
+       #
+       # Is used to give precise locations
+       var lines = new Array[Int]
+
+       # For each line in `block`, the associated column in the mdoc
+       #
+       # Is used to give precise locations
+       var columns = new Array[Int]
+
+       # The location of the whole docunit.
+       #
+       # If `self` is made of multiple code-blocks, then the location
+       # starts at the first code-books and finish at the last one, thus includes anything between.
+       redef var location is lazy do
+               return new Location(mdoc.location.file, lines.first, lines.last+1, columns.first+1, 0)
+       end
+
+       # Compute the real location of a node on the `ast` based on `mdoc.location`
+       #
+       # The result is basically: ast_location + markdown location of the piece + mdoc.location
+       #
+       # The fun is that a single docunit can be made of various pieces of code blocks.
+       fun real_location(ast_location: Location): Location
+       do
+               var mdoc = self.mdoc
+               var res = new Location(mdoc.location.file, lines[ast_location.line_start-1],
+                       lines[ast_location.line_end-1],
+                       columns[ast_location.line_start-1] + ast_location.column_start,
+                       columns[ast_location.line_end-1] + ast_location.column_end)
+               return res
+       end
+
+       redef fun to_xml
+       do
+               var res = super
+               res.open("system-out").append(block)
+               return res
+       end
+
+       redef var xml_classname
+       redef var xml_name
 end
 
 redef class ModelBuilder
@@ -395,9 +470,7 @@ redef class ModelBuilder
 
                var prefix = toolcontext.test_dir
                prefix = prefix.join_path(mmodule.to_s)
-               var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts)
-
-               var tc
+               var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts, "Docunits of module {mmodule.full_name}")
 
                do
                        total_entities += 1
@@ -406,11 +479,8 @@ redef class ModelBuilder
                        var ndoc = nmoduledecl.n_doc
                        if ndoc == null then break label x
                        doc_entities += 1
-                       tc = new HTMLTag("testcase")
                        # NOTE: jenkins expects a '.' in the classname attr
-                       tc.attr("classname", "nitunit." + mmodule.full_name + ".<module>")
-                       tc.attr("name", "<module>")
-                       d2m.extract(ndoc.to_mdoc, tc)
+                       d2m.extract(ndoc.to_mdoc, "nitunit." + mmodule.full_name + ".<module>", "<module>")
                end label x
                for nclassdef in nmodule.n_classdefs do
                        var mclassdef = nclassdef.mclassdef
@@ -420,10 +490,7 @@ redef class ModelBuilder
                                var ndoc = nclassdef.n_doc
                                if ndoc != null then
                                        doc_entities += 1
-                                       tc = new HTMLTag("testcase")
-                                       tc.attr("classname", "nitunit." + mmodule.full_name + "." + mclassdef.mclass.full_name)
-                                       tc.attr("name", "<class>")
-                                       d2m.extract(ndoc.to_mdoc, tc)
+                                       d2m.extract(ndoc.to_mdoc, "nitunit." + mmodule.full_name + "." + mclassdef.mclass.full_name, "<class>")
                                end
                        end
                        for npropdef in nclassdef.n_propdefs do
@@ -433,10 +500,7 @@ redef class ModelBuilder
                                var ndoc = npropdef.n_doc
                                if ndoc != null then
                                        doc_entities += 1
-                                       tc = new HTMLTag("testcase")
-                                       tc.attr("classname", "nitunit." + mmodule.full_name + "." + mclassdef.mclass.full_name)
-                                       tc.attr("name", mpropdef.mproperty.full_name)
-                                       d2m.extract(ndoc.to_mdoc, tc)
+                                       d2m.extract(ndoc.to_mdoc, "nitunit." + mmodule.full_name + "." + mclassdef.mclass.full_name, mpropdef.mproperty.full_name)
                                end
                        end
                end
@@ -460,20 +524,15 @@ redef class ModelBuilder
 
                var prefix = toolcontext.test_dir
                prefix = prefix.join_path(mgroup.to_s)
-               var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts)
-
-               var tc
+               var d2m = new NitUnitExecutor(toolcontext, prefix, o, ts, "Docunits of group {mgroup.full_name}")
 
                total_entities += 1
                var mdoc = mgroup.mdoc
                if mdoc == null then return ts
 
                doc_entities += 1
-               tc = new HTMLTag("testcase")
                # NOTE: jenkins expects a '.' in the classname attr
-               tc.attr("classname", "nitunit." + mgroup.full_name)
-               tc.attr("name", "<group>")
-               d2m.extract(mdoc, tc)
+               d2m.extract(mdoc, "nitunit." + mgroup.full_name, "<group>")
 
                d2m.run_tests
 
@@ -491,19 +550,13 @@ redef class ModelBuilder
                ts.attr("package", file)
 
                var prefix = toolcontext.test_dir / "file"
-               var d2m = new NitUnitExecutor(toolcontext, prefix, null, ts)
-
-               var tc
+               var d2m = new NitUnitExecutor(toolcontext, prefix, null, ts, "Docunits of file {file}")
 
                total_entities += 1
                doc_entities += 1
 
-               tc = new HTMLTag("testcase")
                # NOTE: jenkins expects a '.' in the classname attr
-               tc.attr("classname", "nitunit.<file>")
-               tc.attr("name", file)
-
-               d2m.extract(mdoc, tc)
+               d2m.extract(mdoc, "nitunit.<file>", file)
                d2m.run_tests
 
                return ts
index ae1f4a2..f755d25 100644 (file)
@@ -132,8 +132,14 @@ class TestSuite
        # Test to be executed after the whole test suite.
        var after_module: nullable TestCase = null
 
+       fun show_status(more_message: nullable String)
+       do
+               toolcontext.show_unit_status("Test-suite of module " + mmodule.full_name, test_cases, more_message)
+       end
+
        # Execute the test suite
        fun run do
+               show_status
                if not toolcontext.test_dir.file_exists then
                        toolcontext.test_dir.mkdir
                end
@@ -142,9 +148,19 @@ class TestSuite
                toolcontext.info("Execute test-suite {mmodule.name}", 1)
                var before_module = self.before_module
                if not before_module == null then before_module.run
-               for case in test_cases do case.run
+               for case in test_cases do
+                       case.run
+                       show_status(case.full_name + " " + case.status_tag)
+               end
+
+               show_status
+               print ""
+
                var after_module = self.after_module
                if not after_module == null then after_module.run
+               for case in test_cases do
+                       toolcontext.show_unit(case)
+               end
        end
 
        # Write the test unit for `self` in a nit compilable file.
@@ -214,6 +230,7 @@ end
 
 # A test case is a unit test considering only a `MMethodDef`.
 class TestCase
+       super UnitTest
 
        # Test suite wich `self` belongs to.
        var test_suite: TestSuite
@@ -221,6 +238,10 @@ class TestCase
        # Test method to be compiled and tested.
        var test_method: MMethodDef
 
+       redef fun full_name do return test_method.full_name
+
+       redef fun location do return test_method.location
+
        # `ToolContext` to use to display messages and find `nitc` bin.
        var toolcontext: ToolContext
 
@@ -249,17 +270,13 @@ class TestCase
                var test_file = test_suite.test_file
                var res_name = "{test_file}_{method_name.escape_to_c}"
                var res = toolcontext.safe_exec("{test_file}.bin {method_name} > '{res_name}.out1' 2>&1 </dev/null")
-               var f = new FileReader.open("{res_name}.out1")
-               var msg = f.read_all
-               f.close
+               self.raw_output = "{res_name}.out1".to_path.read_all
                # set test case result
-               var loc = test_method.location
                if res != 0 then
-                       error = msg
-                       toolcontext.warning(loc, "failure",
-                          "ERROR: {method_name} (in file {test_file}.nit): {msg}")
+                       error = "Runtime Error in file {test_file}.nit"
                        toolcontext.modelbuilder.failed_tests += 1
                else
+                       # no error, check with res file, if any.
                        var mmodule = test_method.mclassdef.mmodule
                        var file = mmodule.filepath
                        if file != null then
@@ -268,10 +285,8 @@ class TestCase
                                        toolcontext.info("Diff output with {sav}", 1)
                                        res = toolcontext.safe_exec("diff -u --label 'expected:{sav}' --label 'got:{res_name}.out1' '{sav}' '{res_name}.out1' > '{res_name}.diff' 2>&1 </dev/null")
                                        if res != 0 then
-                                               msg = "Diff\n" + "{res_name}.diff".to_path.read_all
-                                               error = msg
-                                               toolcontext.warning(loc, "failure",
-                                               "ERROR: {method_name} (in file {test_file}.nit): {msg}")
+                                               self.raw_output = "Diff\n" + "{res_name}.diff".to_path.read_all
+                                               error = "Difference with expected output: diff -u {sav} {res_name}.out1"
                                                toolcontext.modelbuilder.failed_tests += 1
                                        end
                                else
@@ -279,35 +294,16 @@ class TestCase
                                end
                        end
                end
-               toolcontext.check_errors
+               is_done = true
        end
 
-       # Error occured during test-case execution.
-       var error: nullable String = null
-
-       # Was the test case executed at least one?
-       var was_exec = false
-
-       # Return the `TestCase` in XML format compatible with Jenkins.
-       fun to_xml: HTMLTag do
+       redef fun xml_classname do
                var mclassdef = test_method.mclassdef
-               var tc = new HTMLTag("testcase")
-               # NOTE: jenkins expects a '.' in the classname attr
-               tc.attr("classname", "nitunit." + mclassdef.mmodule.full_name + "." + mclassdef.mclass.full_name)
-               tc.attr("name", test_method.mproperty.full_name)
-               if was_exec then
-                       tc.add  new HTMLTag("system-out")
-                       var n = new HTMLTag("system-err")
-                       tc.add n
-                       var error = self.error
-                       if error != null then
-                               n.append error.trunc(8192).filter_nonprintable
-                               n = new HTMLTag("error")
-                               n.attr("message", "Runtime Error")
-                               tc.add n
-                       end
-               end
-               return tc
+               return "nitunit." + mclassdef.mmodule.full_name + "." + mclassdef.mclass.full_name
+       end
+
+       redef fun xml_name do
+               return test_method.mproperty.full_name
        end
 end
 
index 4583868..de15aed 100644 (file)
@@ -374,6 +374,9 @@ class ToolContext
        # Option --nit-dir
        var opt_nit_dir = new OptionString("Base directory of the Nit installation", "--nit-dir")
 
+       # Option --share-dir
+       var opt_share_dir = new OptionString("Directory containing tools assets", "--share-dir")
+
        # Option --help
        var opt_help = new OptionBool("Show Help (This screen)", "-h", "-?", "--help")
 
@@ -542,6 +545,20 @@ The Nit language documentation and the source code of its tools and libraries ma
        # The identified root directory of the Nit package
        var nit_dir: String is noinit
 
+       # Shared files directory.
+       #
+       # Most often `nit/share/`.
+       var share_dir: String is lazy do
+               var sharedir = opt_share_dir.value
+               if sharedir == null then
+                       sharedir = nit_dir / "share"
+                       if not sharedir.file_exists then
+                               fatal_error(null, "Fatal Error: cannot locate shared files directory in {sharedir}. Uses --share-dir to define it's location.")
+                       end
+               end
+               return sharedir
+       end
+
        private fun compute_nit_dir: String
        do
                # the option has precedence
diff --git a/src/web/model_api.nit b/src/web/model_api.nit
new file mode 100644 (file)
index 0000000..85a2360
--- /dev/null
@@ -0,0 +1,251 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+module model_api
+
+import web_base
+import highlight
+import uml
+
+# Specific handler for nitweb API.
+abstract class APIHandler
+       super ModelHandler
+
+       # The JSON API does not filter anything by default.
+       #
+       # So we can cache the model view.
+       var view: ModelView is lazy do
+               var view = new ModelView(model)
+               view.min_visibility = private_visibility
+               view.include_fictive = true
+               view.include_empty_doc = true
+               view.include_attribute = true
+               view.include_test_suite = true
+               return view
+       end
+
+       # Try to load the mentity from uri with `/:id`.
+       #
+       # Send 400 if `:id` is null.
+       # Send 404 if no entity is found.
+       # Return null in both cases.
+       fun mentity_from_uri(req: HttpRequest, res: HttpResponse): nullable MEntity do
+               var id = req.param("id")
+               if id == null then
+                       res.error 400
+                       return null
+               end
+               var mentity = find_mentity(view, id)
+               if mentity == null then
+                       res.error 404
+               end
+               return mentity
+       end
+end
+
+# Group all api handlers in one router.
+class APIRouter
+       super Router
+
+       # Model to pass to handlers.
+       var model: Model
+
+       # ModelBuilder to pass to handlers.
+       var modelbuilder: ModelBuilder
+
+       # Mainmodule to pass to handlers.
+       var mainmodule: MModule
+
+       init do
+               use("/list", new APIList(model, mainmodule))
+               use("/search", new APISearch(model, mainmodule))
+               use("/random", new APIRandom(model, mainmodule))
+               use("/entity/:id", new APIEntity(model, mainmodule))
+               use("/code/:id", new APIEntityCode(model, mainmodule, modelbuilder))
+               use("/uml/:id", new APIEntityUML(model, mainmodule))
+       end
+end
+
+# Search mentities from a query string.
+#
+# Example: `GET /search?q=Arr`
+class APISearch
+       super APIHandler
+
+       redef fun get(req, res) do
+               var q = req.string_arg("q")
+               if q == null then
+                       res.error 400
+                       return
+               end
+               var arr = new JsonArray
+               for mentity in view.mentities do
+                       if mentity.name.has_prefix(q) then arr.add mentity
+               end
+               res.json arr
+       end
+end
+
+# List all mentities.
+#
+# MEntities can be filtered on their kind using the `k` parameter.
+# Allowed kinds are `package`, `group`, `module`, `class`, `classdef`, `property`, `propdef`.
+#
+# List size can be limited with the `n` parameter.
+#
+# Example: `GET /list?k=module?n=10`
+class APIList
+       super APIHandler
+
+       # List mentities depending on the `k` kind parameter.
+       fun list_mentities(req: HttpRequest): Array[MEntity] do
+               var k = req.string_arg("k")
+               var mentities = new Array[MEntity]
+               if k == "package" then
+                       for mentity in view.mpackages do mentities.add mentity
+               else if k == "group" then
+                       for mentity in view.mgroups do mentities.add mentity
+               else if k == "module" then
+                       for mentity in view.mmodules do mentities.add mentity
+               else if k == "class" then
+                       for mentity in view.mclasses do mentities.add mentity
+               else if k == "classdef" then
+                       for mentity in view.mclassdefs do mentities.add mentity
+               else if k == "property" then
+                       for mentity in view.mproperties do mentities.add mentity
+               else if k == "propdef" then
+                       for mentity in view.mpropdefs do mentities.add mentity
+               else
+                       for mentity in view.mentities do mentities.add mentity
+               end
+               return mentities
+       end
+
+       # Limit mentities depending on the `n` parameter.
+       fun limit_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
+               var n = req.int_arg("n")
+               if n != null then
+                       return mentities.sub(0, n)
+               end
+               return mentities
+       end
+
+       redef fun get(req, res) do
+               var mentities = list_mentities(req)
+               mentities = limit_mentities(req, mentities)
+               var arr = new JsonArray
+               for mentity in mentities do arr.add mentity
+               res.json arr
+       end
+end
+
+# Return a random list of MEntities.
+#
+# Example: `GET /random?n=10&k=module`
+class APIRandom
+       super APIList
+
+       # Randomize mentities order.
+       fun randomize_mentities(req: HttpRequest, mentities: Array[MEntity]): Array[MEntity] do
+               var res = mentities.to_a
+               res.shuffle
+               return res
+       end
+
+       redef fun get(req, res) do
+               var mentities = list_mentities(req)
+               mentities = limit_mentities(req, mentities)
+               mentities = randomize_mentities(req, mentities)
+               var arr = new JsonArray
+               for mentity in mentities do arr.add mentity
+               res.json arr
+       end
+end
+
+# Return the JSON representation of a MEntity.
+#
+# Example: `GET /entity/core::Array`
+class APIEntity
+       super APIHandler
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then return
+               res.json mentity.api_json(self)
+       end
+end
+
+
+# Return a UML representation of MEntity.
+#
+# Example: `GET /entity/core::Array/uml`
+class APIEntityUML
+       super APIHandler
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               var dot
+               if mentity isa MClassDef then mentity = mentity.mclass
+               if mentity isa MClass then
+                       var uml = new UMLModel(view, mainmodule)
+                       dot = uml.generate_class_uml.write_to_string
+               else if mentity isa MModule then
+                       var uml = new UMLModel(view, mentity)
+                       dot = uml.generate_package_uml.write_to_string
+               else
+                       res.error 404
+                       return
+               end
+               res.send render_svg(dot)
+       end
+
+       # Render a `dot` string as a svg image.
+       fun render_svg(dot: String): String do
+               var proc = new ProcessDuplex("dot", "-Tsvg")
+               var svg = proc.write_and_read(dot)
+               proc.close
+               proc.wait
+               return svg
+       end
+end
+
+# Return the source code of MEntity.
+#
+# Example: `GET /entity/core::Array/code`
+class APIEntityCode
+       super APIHandler
+
+       # Modelbuilder used to access sources.
+       var modelbuilder: ModelBuilder
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then return
+               var source = render_source(mentity)
+               if source == null then
+                       res.error 404
+                       return
+               end
+               res.send source
+       end
+
+       # Highlight `mentity` source code.
+       private fun render_source(mentity: MEntity): nullable HTMLTag do
+               var node = modelbuilder.mentity2node(mentity)
+               if node == null then return null
+               var hl = new HighlightVisitor
+               hl.enter_visit node
+               return hl.html
+       end
+end
index 6f581f4..c6a12f0 100644 (file)
@@ -16,3 +16,4 @@
 module web
 
 import web_actions
+import model_api
index e9198ae..cb193fd 100644 (file)
@@ -20,7 +20,7 @@ import uml
 
 # Display the tree of all loaded mentities.
 class TreeAction
-       super ModelAction
+       super ModelHandler
 
        redef fun get(req, res) do
                var model = init_model_view(req)
@@ -29,51 +29,9 @@ class TreeAction
        end
 end
 
-# Display the list of mentities matching `namespace`.
-class SearchAction
-       super ModelAction
-
-       # TODO handle more than full namespaces.
-       redef fun get(req, res) do
-               var namespace = req.param("namespace")
-               var model = init_model_view(req)
-               var mentity = find_mentity(model, namespace)
-               if mentity == null then
-                       res.error(404)
-                       return
-               end
-               if req.is_json_asked then
-                       res.json(mentity.to_json)
-                       return
-               end
-               var view = new HtmlResultPage(namespace or else "null", [mentity])
-               res.send_view(view)
-       end
-end
-
-# Display a MEntity source code.
-class CodeAction
-       super ModelAction
-
-       # Modelbuilder used to access sources.
-       var modelbuilder: ModelBuilder
-
-       redef fun get(req, res) do
-               var namespace = req.param("namespace")
-               var model = init_model_view(req)
-               var mentity = find_mentity(model, namespace)
-               if mentity == null then
-                       res.error(404)
-                       return
-               end
-               var view = new HtmlSourcePage(modelbuilder, mentity)
-               res.send_view(view)
-       end
-end
-
 # Display the doc of a MEntity.
 class DocAction
-       super ModelAction
+       super ModelHandler
 
        # Modelbuilder used to access sources.
        var modelbuilder: ModelBuilder
@@ -86,76 +44,7 @@ class DocAction
                        res.error(404)
                        return
                end
-               if req.is_json_asked then
-                       res.json(mentity.to_json)
-                       return
-               end
-
                var view = new HtmlDocPage(modelbuilder, mentity)
                res.send_view(view)
        end
 end
-
-# Return an UML diagram for `namespace`.
-class UMLDiagramAction
-       super ModelAction
-
-       # Mainmodule used for hierarchy flattening.
-       var mainmodule: MModule
-
-       redef fun get(req, res) do
-               var namespace = req.param("namespace")
-               var model = init_model_view(req)
-               var mentity = find_mentity(model, namespace)
-               if mentity == null then
-                       res.error(404)
-                       return
-               end
-
-               var dot
-               if mentity isa MClassDef then mentity = mentity.mclass
-               if mentity isa MClass then
-                       var uml = new UMLModel(model, mainmodule)
-                       dot = uml.generate_class_uml.write_to_string
-               else if mentity isa MModule then
-                       var uml = new UMLModel(model, mentity)
-                       dot = uml.generate_package_uml.write_to_string
-               else
-                       res.error(404)
-                       return
-               end
-               var view = new HtmlDotPage(dot, mentity.as(not null).html_name)
-               res.send_view(view)
-       end
-end
-
-# Return a random list of MEntities.
-class RandomAction
-       super ModelAction
-
-       redef fun get(req, res) do
-               var n = req.int_arg("n") or else 10
-               var k = req.string_arg("k") or else "modules"
-               var model = init_model_view(req)
-               var mentities: Array[MEntity]
-               if k == "modules" then
-                       mentities = model.mmodules.to_a
-               else if k == "classdefs" then
-                       mentities = model.mclassdefs.to_a
-               else
-                       mentities = model.mpropdefs.to_a
-               end
-               mentities.shuffle
-               mentities = mentities.sub(0, n)
-               if req.is_json_asked then
-                       var json = new JsonArray
-                       for mentity in mentities do
-                               json.add mentity.to_json
-                       end
-                       res.json(json)
-                       return
-               end
-               var view = new HtmlResultPage("random", mentities)
-               res.send_view(view)
-       end
-end
index 258ec60..ec9bf75 100644 (file)
@@ -17,15 +17,19 @@ module web_base
 
 import model::model_views
 import model::model_json
+import doc_down
 import popcorn
 
 # Specific nitcorn Action that uses a Model
-class ModelAction
+class ModelHandler
        super Handler
 
        # Model to use.
        var model: Model
 
+       # MModule used to flatten model.
+       var mainmodule: MModule
+
        # Find the MEntity ` with `full_name`.
        fun find_mentity(model: ModelView, full_name: nullable String): nullable MEntity do
                if full_name == null then return null
@@ -58,9 +62,147 @@ redef class HttpResponse
        fun send_view(view: NitView, status: nullable Int) do send(view.render, status)
 end
 
-redef class HttpRequest
-       # Does the client asked for a json formatted response?
-       #
-       # Checks the URL get parameter `?json=true`.
-       fun is_json_asked: Bool do return bool_arg("json") or else false
+redef class MEntity
+
+       # URL to `self` within the web interface.
+       fun web_url: String is abstract
+
+       # URL to `self` within the JSON api.
+       fun api_url: String do return "/api/entity/" / full_name
+
+       redef fun json do
+               var obj = super
+               obj["web_url"] = web_url
+               obj["api_url"] = api_url
+               return obj
+       end
+
+       # Get the full json repesentation of `self` with MEntityRefs resolved.
+       fun api_json(handler: ModelHandler): JsonObject do return json
+end
+
+redef class MEntityRef
+       redef fun json do
+               var obj = super
+               obj["web_url"] = mentity.web_url
+               obj["api_url"] = mentity.api_url
+               obj["name"] = mentity.name
+               obj["mdoc"] = mentity.mdoc_or_fallback
+               obj["visibility"] = mentity.visibility
+               obj["location"] = mentity.location
+               var modifiers = new JsonArray
+               for modifier in mentity.collect_modifiers do
+                       modifiers.add modifier
+               end
+               obj["modifiers"] = modifiers
+               return obj
+       end
+end
+
+redef class MDoc
+
+       # Add doc down processing
+       redef fun json do
+               var obj = super
+               obj["synopsis"] = synopsis
+               obj["documentation"] = documentation
+               obj["comment"] = comment
+               obj["html_synopsis"] = html_synopsis.write_to_string
+               obj["html_documentation"] = html_documentation.write_to_string
+               obj["html_comment"] = html_comment.write_to_string
+               return obj
+       end
+end
+
+redef class MPackage
+       redef var web_url = "/package/{full_name}" is lazy
+end
+
+redef class MGroup
+       redef var web_url = "/group/{full_name}" is lazy
+end
+
+redef class MModule
+       redef var web_url = "/module/{full_name}" is lazy
+
+       redef fun api_json(handler) do
+               var obj = super
+               obj["intro_mclassdefs"] = to_mentity_refs(collect_intro_mclassdefs(private_view))
+               obj["redef_mclassdefs"] = to_mentity_refs(collect_redef_mclassdefs(private_view))
+               obj["imports"] = to_mentity_refs(in_importation.direct_greaters)
+               return obj
+       end
+end
+
+redef class MClass
+       redef var web_url = "/class/{full_name}" is lazy
+
+       redef fun api_json(handler) do
+               var obj = super
+               obj["all_mproperties"] = to_mentity_refs(collect_accessible_mproperties(private_view))
+               obj["intro_mproperties"] = to_mentity_refs(collect_intro_mproperties(private_view))
+               obj["redef_mproperties"] = to_mentity_refs(collect_redef_mproperties(private_view))
+               var poset = hierarchy_poset(handler.mainmodule, private_view)
+               obj["parents"] = to_mentity_refs(poset[self].direct_greaters)
+               return obj
+       end
+end
+
+redef class MClassDef
+       redef var web_url = "/classdef/{full_name}" is lazy
+
+       redef fun json do
+               var obj = super
+               obj["intro"] = to_mentity_ref(mclass.intro)
+               obj["mpackage"] = to_mentity_ref(mmodule.mpackage)
+               return obj
+       end
+
+       redef fun api_json(handler) do
+               var obj = super
+               obj["intro_mpropdefs"] = to_mentity_refs(collect_intro_mpropdefs(private_view))
+               obj["redef_mpropdefs"] = to_mentity_refs(collect_redef_mpropdefs(private_view))
+               return obj
+       end
+end
+
+redef class MProperty
+       redef var web_url = "/property/{full_name}" is lazy
+
+       redef fun json do
+               var obj = super
+               obj["intro_mclass"] = to_mentity_ref(intro_mclassdef.mclass)
+               obj["mpackage"] = to_mentity_ref(intro_mclassdef.mmodule.mpackage)
+               return obj
+       end
+end
+
+redef class MPropDef
+       redef var web_url = "/propdef/{full_name}" is lazy
+
+       redef fun json do
+               var obj = super
+               obj["intro"] = to_mentity_ref(mproperty.intro)
+               obj["intro_mclassdef"] = to_mentity_ref(mproperty.intro.mclassdef)
+               obj["mmodule"] = to_mentity_ref(mclassdef.mmodule)
+               obj["mgroup"] = to_mentity_ref(mclassdef.mmodule.mgroup)
+               obj["mpackage"] = to_mentity_ref(mclassdef.mmodule.mpackage)
+               return obj
+       end
+end
+
+redef class MClassType
+       redef var web_url = mclass.web_url is lazy
+end
+
+redef class MNullableType
+       redef var web_url = mtype.web_url is lazy
+end
+
+redef class MParameterType
+       redef var web_url = mclass.web_url is lazy
+end
+
+redef class MVirtualType
+       redef var web_url = mproperty.web_url is lazy
 end
index 58016e2..d8f8c9f 100644 (file)
@@ -35,34 +35,6 @@ class HtmlHomePage
        end
 end
 
-# Display a search results list.
-class HtmlResultPage
-       super NitView
-
-       # Initial query.
-       var query: String
-
-       # Result set
-       var results: Array[MEntity]
-
-       redef fun render do
-               var tpl = new Template
-               tpl.add new Header(1, "Results for {query}")
-               if results.is_empty then
-                       tpl.add "<p>No result for {query}.<p>"
-                       return tpl
-               end
-               var list = new UnorderedList
-               for mentity in results do
-                       var link = mentity.html_link
-                       link.text = mentity.html_full_name
-                       list.add_li new ListItem(link)
-               end
-               tpl.add list
-               return tpl
-       end
-end
-
 # Display the source for each mentities
 class HtmlSourcePage
        super NitView
@@ -119,29 +91,3 @@ class HtmlDocPage
                return tpl
        end
 end
-
-# Display the source for each mentities
-class HtmlDotPage
-       super NitView
-
-       # Dot to process.
-       var dot: Text
-
-       # Page title.
-       var title: String
-
-       redef fun render do
-               var tpl = new Template
-               tpl.add new Header(1, title)
-               tpl.add render_dot
-               return tpl
-       end
-
-       private fun render_dot: String do
-               var proc = new ProcessDuplex("dot", "-Tsvg", "-Tcmapx")
-               var svg = proc.write_and_read(dot)
-               proc.close
-               proc.wait
-               return svg
-       end
-end
index b70b71d..6a693ff 100644 (file)
@@ -6,3 +6,4 @@ neo
 mpi
 emscripten
 ui_test
+readline
similarity index 57%
rename from tests/test_realtime.nit
rename to tests/base_gen_infinite2.nit
index 6101a3d..05c4fb6 100644 (file)
@@ -1,7 +1,5 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
-# Copyright 2012 Alexis Laferrière <alexis.laf@xymus.net>
-#
 # 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
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import realtime
+import end
 
-redef extern class Timespec
-       fun simplify : Int
-       do
-               return sec*1000000 + nanosec/1000
-       end
+interface Object
+       type SELF: Object
+       fun output_class_name is intern
 end
 
-var c = new Clock
-var t0 = c.total.simplify
-
-print "sleeping 1s"
-nanosleep(1, 0)
-print c.total.sec >= 1
-print c.lapse.sec >= 1
-
-var t1 = c.total.simplify
+class A[E]
+       new do return new B[E]
+end
 
-print "sleeping 5000ns"
-nanosleep(0, 5000)
-print c.lapse.nanosec >= 5000
+class B[E]
+       super A[E]
 
-var t2 = c.total.simplify
+       fun foo: A[E] do return new A[SELF]
+end
 
-print t0 <= t1
-print t1 <= t2
+(new B[Object]).foo.output_class_name
index a28ab28..70e648a 100755 (executable)
@@ -9,7 +9,6 @@ ls -1 -- "%s\n" "$@" \
        ../examples/*/src/*_android.nit \
        ../examples/*/src/*_linux.nit \
        ../examples/*/src/*_null.nit \
-       ../examples/nitcorn/src/*.nit \
        ../lib/*/examples/*.nit \
        ../lib/*/examples/*/*.nit \
        ../contrib/friendz/src/solver_cmd.nit \
index 571f594..4634f40 100644 (file)
@@ -40,3 +40,4 @@ test_ffi_c_lots_of_refs
 test_rubix_cube
 test_rubix_visual
 test_csv
+repeating_key_xor_solve
index 390709a..2116656 100644 (file)
@@ -1,7 +1,7 @@
 test_nitunit.nit --no-color -o $WRITE
 test_nitunit.nit --gen-suite --only-show
 test_nitunit.nit --gen-suite --only-show --private
-test_nitunit2.nit -o $WRITE
+test_nitunit2.nit --no-color -o $WRITE
 test_doc2.nit --no-color -o $WRITE
 test_nitunit3 --no-color -o $WRITE
 test_nitunit_md.md --no-color -o $WRITE
index 47cb8a0..a68b4cc 100644 (file)
@@ -40,3 +40,4 @@ test_ffi_c_lots_of_refs
 test_rubix_visual
 test_rubix_cube
 test_csv
+repeating_key_xor_solve
diff --git a/tests/repeating_key_xor_solve.args b/tests/repeating_key_xor_solve.args
new file mode 100644 (file)
index 0000000..2f6c9ff
--- /dev/null
@@ -0,0 +1 @@
+../lib/crapto/examples/repeating_key_xor_cipher.txt
diff --git a/tests/sav/base_gen_infinite2.res b/tests/sav/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..e38f38e
--- /dev/null
@@ -0,0 +1 @@
+B[B[Object]]
index 19b015f..f97fee4 100644 (file)
@@ -76,7 +76,7 @@
 <h3>Quality</h3>
 <ul class="box">
 <li>28 warnings (63/kloc)</li>
-<li>93% documented</li>
+<li>95% documented</li>
 </ul>
 <h3>Tags</h3>
 <a href="../index.html#tag_test">test</a>, <a href="../index.html#tag_game">game</a><h3>Requirements</h3>
diff --git a/tests/sav/nitce/base_gen_infinite2.res b/tests/sav/nitce/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..223b783
--- /dev/null
@@ -0,0 +1 @@
+B
diff --git a/tests/sav/nitcg/fixme/base_gen_infinite2.res b/tests/sav/nitcg/fixme/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..2f63d29
--- /dev/null
@@ -0,0 +1 @@
+Fatal Error: limitation in the rapidtype analysis engine: a type depth of 256 is too important, the problematic type is `A[A[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[Sys]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
diff --git a/tests/sav/nitcs/fixme/base_gen_infinite2.res b/tests/sav/nitcs/fixme/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..2f63d29
--- /dev/null
@@ -0,0 +1 @@
+Fatal Error: limitation in the rapidtype analysis engine: a type depth of 256 is too important, the problematic type is `A[A[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[Sys]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
diff --git a/tests/sav/nitcsg/fixme/base_gen_infinite2.res b/tests/sav/nitcsg/fixme/base_gen_infinite2.res
new file mode 100644 (file)
index 0000000..2f63d29
--- /dev/null
@@ -0,0 +1 @@
+Fatal Error: limitation in the rapidtype analysis engine: a type depth of 256 is too important, the problematic type is `A[A[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[B[Sys]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]`.
index 15eee20..a45c8d4 100644 (file)
@@ -1,20 +1,36 @@
-test_nitunit.nit:20,1--22,0: ERROR: nitunit.test_nitunit::test_nitunit.test_nitunit::X.<class> (in .nitunit/test_nitunit-2.nit):
-Runtime error: Assert failed (.nitunit/test_nitunit-2.nit:5)
+* Docunits of module test_nitunit::test_nitunit (4 tests)
 
-test_nitunit.nit:23,2--25,0: FAILURE: nitunit.test_nitunit::test_nitunit.test_nitunit::X.test_nitunit::X::foo (in .nitunit/test_nitunit-3.nit):
-.nitunit/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
+[OK] test_nitunit::test_nitunit
+[KO] test_nitunit$X
+     test_nitunit.nit:21,7--22,0: Runtime error in nitunit.out/test_nitunit-2.nit
+     Output
+       Runtime error: Assert failed (nitunit.out/test_nitunit-2.nit:5)
 
-test_test_nitunit.nit:36,2--40,4: ERROR: test_foo1 (in file .nitunit/gen_test_test_nitunit.nit): Runtime error: Assert failed (test_test_nitunit.nit:39)
+[KO] test_nitunit$X$foo
+     test_nitunit.nit:24,8--25,0: Compilation error in nitunit.out/test_nitunit-3.nit
+     Output
+       nitunit.out/test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
 
-DocUnits:
-Entities: 27; Documented ones: 3; With nitunits: 3; Failures: 2
+[KO] test_nitunit$X$foo1
+     test_nitunit.nit:28,15: Syntax Error: unexpected operator '!'.
+* Test-suite of module test_test_nitunit::test_test_nitunit (3 tests)
 
-TestSuites:
-Class suites: 1; Test Cases: 3; Failures: 1
+[OK] test_test_nitunit$TestX$test_foo
+[KO] test_test_nitunit$TestX$test_foo1
+     test_test_nitunit.nit:36,2--40,4: Runtime Error in file nitunit.out/gen_test_test_nitunit.nit
+     Output
+       Runtime error: Assert failed (test_test_nitunit.nit:39)
+
+[OK] test_test_nitunit$TestX$test_foo2
+Docunits: Entities: 27; Documented ones: 4; With nitunits: 4; Failures: 3
+Test suites: Classes: 1; Test Cases: 3; Failures: 1
+[FAILURE] 4/7 tests failed.
+`nitunit.out` is not removed for investigation.
 <testsuites><testsuite package="test_nitunit::test_nitunit"><testcase classname="nitunit.test_nitunit::test_nitunit.&lt;module&gt;" name="&lt;module&gt;"><system-err></system-err><system-out>assert true
-</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="&lt;class&gt;"><system-err>Runtime error: Assert failed (.nitunit&#47;test_nitunit-2.nit:5)
+</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="&lt;class&gt;"><error>Runtime error in nitunit.out&#47;test_nitunit-2.nit</error><system-err>Runtime error: Assert failed (nitunit.out&#47;test_nitunit-2.nit:5)
 </system-err><system-out>assert false
-</system-out><error message="Runtime Error"></error></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="test_nitunit::X::foo"><system-err>.nitunit&#47;test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
+</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="test_nitunit::X::foo"><failure>Compilation error in nitunit.out&#47;test_nitunit-3.nit</failure><system-err>nitunit.out&#47;test_nitunit-3.nit:5,8--27: Error: method or variable `undefined_identifier` unknown in `Sys`.
 </system-err><system-out>assert undefined_identifier
-</system-out><failure message="Compilation Error"></failure></testcase></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo"><system-out></system-out><system-err></system-err></testcase><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo1"><system-out></system-out><system-err>Runtime error: Assert failed (test_test_nitunit.nit:39)
-</system-err><error message="Runtime Error"></error></testcase><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo2"><system-out></system-out><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
+</system-out></testcase><testcase classname="nitunit.test_nitunit::test_nitunit.test_nitunit::X" name="test_nitunit::X::foo1"><failure>Syntax Error: unexpected operator &#39;!&#39;.</failure><system-out>assert !@#$%^&amp;*()
+</system-out></testcase></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo"><system-err></system-err></testcase><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo1"><error>Runtime Error in file nitunit.out&#47;gen_test_test_nitunit.nit</error><system-err>Runtime error: Assert failed (test_test_nitunit.nit:39)
+</system-err></testcase><testcase classname="nitunit.test_test_nitunit::test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo2"><system-err></system-err></testcase></testsuite></testsuites>
\ No newline at end of file
index 4f4ab53..a1f4cb1 100644 (file)
@@ -1,10 +1,11 @@
-DocUnits:
-DocUnits Success
-Entities: 4; Documented ones: 3; With nitunits: 3; Failures: 0
+* Docunits of module test_nitunit2::test_nitunit2 (3 tests)
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
+[OK] test_nitunit2::test_nitunit2$core::Sys$foo1
+[OK] test_nitunit2::test_nitunit2$core::Sys$bar2
+[OK] test_nitunit2::test_nitunit2$core::Sys$foo3
+Docunits: Entities: 4; Documented ones: 3; With nitunits: 3; Failures: 0
+Test suites: Classes: 0; Test Cases: 0
+[SUCCESS] All 3 tests passed.
 <testsuites><testsuite package="test_nitunit2::test_nitunit2"><testcase classname="nitunit.test_nitunit2::test_nitunit2.core::Sys" name="test_nitunit2::test_nitunit2::Sys::foo1"><system-err></system-err><system-out>if true then
 
    assert true
index dd4f11a..3e25819 100644 (file)
@@ -1,10 +1,11 @@
-DocUnits:
-DocUnits Success
-Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 0
+* Docunits of module test_doc2::test_doc2 (3 tests)
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
+[OK] test_doc2::test_doc2$core::Sys$foo1
+[OK] test_doc2::test_doc2$core::Sys$foo2
+[OK] test_doc2::test_doc2$core::Sys$foo3
+Docunits: Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 0
+Test suites: Classes: 0; Test Cases: 0
+[SUCCESS] All 3 tests passed.
 <testsuites><testsuite package="test_doc2::test_doc2"><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="test_doc2::test_doc2::Sys::foo1"><system-err></system-err><system-out>assert true # tested
 </system-out></testcase><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="test_doc2::test_doc2::Sys::foo2"><system-err></system-err><system-out>assert true # tested
 </system-out></testcase><testcase classname="nitunit.test_doc2::test_doc2.core::Sys" name="test_doc2::test_doc2::Sys::foo3"><system-err></system-err><system-out>assert true # tested
index 2a1e91e..2b931af 100644 (file)
@@ -1,15 +1,22 @@
-test_nitunit3/README.md:7,3--5: Syntax Error: unexpected malformed character '\]. To suppress this message, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).
-test_nitunit3/README.md:1,0--13,0: ERROR: nitunit.test_nitunit3>.<group> (in .nitunit/test_nitunit3-0.nit): Runtime error
-Runtime error: Assert failed (.nitunit/test_nitunit3-0.nit:7)
+* Docunits of group test_nitunit3> (2 tests)
 
-DocUnits:
-Entities: 2; Documented ones: 2; With nitunits: 3; Failures: 2
+[KO] test_nitunit3>
+     test_nitunit3/README.md:4,2--15,0: Runtime error in nitunit.out/test_nitunit3-0.nit with argument 1
+     Output
+       Runtime error: Assert failed (nitunit.out/test_nitunit3-0.nit:7)
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit3&gt;"><testcase classname="nitunit.test_nitunit3&gt;" name="&lt;group&gt;"><failure message="test_nitunit3&#47;README.md:7,3--5: Syntax Error: unexpected malformed character &#39;\]."></failure><system-err>Runtime error: Assert failed (.nitunit&#47;test_nitunit3-0.nit:7)
+[KO] test_nitunit3>
+     test_nitunit3/README.md:7,3--5: Syntax Error: unexpected malformed character '\].
+* Docunits of module test_nitunit3::test_nitunit3 (1 tests)
+
+[OK] test_nitunit3::test_nitunit3
+Docunits: Entities: 2; Documented ones: 2; With nitunits: 3; Failures: 2
+Test suites: Classes: 0; Test Cases: 0
+[FAILURE] 2/3 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_nitunit3&gt;"><testcase classname="nitunit.test_nitunit3&gt;" name="&lt;group&gt;"><error>Runtime error in nitunit.out&#47;test_nitunit3-0.nit with argument 1</error><system-err>Runtime error: Assert failed (nitunit.out&#47;test_nitunit3-0.nit:7)
 </system-err><system-out>assert false
 assert true
-</system-out><error message="Runtime error"></error></testcase></testsuite><testsuite package="test_nitunit3::test_nitunit3"><testcase classname="nitunit.test_nitunit3::test_nitunit3.&lt;module&gt;" name="&lt;module&gt;"><system-err></system-err><system-out>assert true
+</system-out></testcase><testcase classname="nitunit.test_nitunit3&gt;" name="&lt;group&gt;+1"><failure>Syntax Error: unexpected malformed character &#39;\].</failure><system-out>;&#39;\][]
+</system-out></testcase></testsuite><testsuite package="test_nitunit3::test_nitunit3"><testcase classname="nitunit.test_nitunit3::test_nitunit3.&lt;module&gt;" name="&lt;module&gt;"><system-err></system-err><system-out>assert true
 </system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
index 436061a..4550bb5 100644 (file)
@@ -1,14 +1,16 @@
-test_nitunit_md.md:1,0--15,0: ERROR: nitunit.<file>.test_nitunit_md.md:1,0--15,0 (in .nitunit/file-0.nit): Runtime error
-Runtime error: Assert failed (.nitunit/file-0.nit:8)
+* Docunits of file test_nitunit_md.md:1,0--15,0 (1 tests)
 
-DocUnits:
-Entities: 1; Documented ones: 1; With nitunits: 1; Failures: 1
+[KO] nitunit.<file>.test_nitunit_md.md:1,0--15,0
+     test_nitunit_md.md:4,2--16,0: Runtime error in nitunit.out/file-0.nit with argument 1
+     Output
+       Runtime error: Assert failed (nitunit.out/file-0.nit:8)
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit_md.md:1,0--15,0"><testcase classname="nitunit.&lt;file&gt;" name="test_nitunit_md.md:1,0--15,0"><system-err>Runtime error: Assert failed (.nitunit&#47;file-0.nit:8)
+Docunits: Entities: 1; Documented ones: 1; With nitunits: 1; Failures: 1
+Test suites: Classes: 0; Test Cases: 0
+[FAILURE] 1/1 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_nitunit_md.md:1,0--15,0"><testcase classname="nitunit.&lt;file&gt;" name="test_nitunit_md.md:1,0--15,0"><error>Runtime error in nitunit.out&#47;file-0.nit with argument 1</error><system-err>Runtime error: Assert failed (nitunit.out&#47;file-0.nit:8)
 </system-err><system-out>var a = 1
 assert 1 == 1
 assert false
-</system-out><error message="Runtime error"></error></testcase></testsuite></testsuites>
\ No newline at end of file
+</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
index eb25a0d..65fd9b3 100644 (file)
@@ -1,10 +1,16 @@
-test_doc3.nit:17,9--15: Syntax Error: unexpected identifier 'garbage'. To suppress this message, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).
-test_doc3.nit:23,4--10: Syntax Error: unexpected identifier 'garbage'. To suppress this message, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).
-test_doc3.nit:30,4--10: Syntax Error: unexpected identifier 'garbage'. To suppress this message, enclose the block with a fence tagged `nitish` or `raw` (see `man nitdoc`).
-DocUnits:
-Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 3
+* Docunits of module test_doc3::test_doc3 (3 tests)
 
-TestSuites:
-No test cases found
-Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_doc3::test_doc3"><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo1"><failure message="test_doc3.nit:17,9--15: Syntax Error: unexpected identifier &#39;garbage&#39;."></failure></testcase><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo2"><failure message="test_doc3.nit:23,4--10: Syntax Error: unexpected identifier &#39;garbage&#39;."></failure></testcase><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo3"><failure message="test_doc3.nit:30,4--10: Syntax Error: unexpected identifier &#39;garbage&#39;."></failure></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
+[KO] test_doc3::test_doc3$core::Sys$foo1
+     test_doc3.nit:17,9--15: Syntax Error: unexpected identifier 'garbage'.
+[KO] test_doc3::test_doc3$core::Sys$foo2
+     test_doc3.nit:23,4--10: Syntax Error: unexpected identifier 'garbage'.
+[KO] test_doc3::test_doc3$core::Sys$foo3
+     test_doc3.nit:30,4--10: Syntax Error: unexpected identifier 'garbage'.
+Docunits: Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 3
+Test suites: Classes: 0; Test Cases: 0
+[FAILURE] 3/3 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_doc3::test_doc3"><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo1"><failure>Syntax Error: unexpected identifier &#39;garbage&#39;.</failure><system-out> *garbage*
+</system-out></testcase><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo2"><failure>Syntax Error: unexpected identifier &#39;garbage&#39;.</failure><system-out>*garbage*
+</system-out></testcase><testcase classname="nitunit.test_doc3::test_doc3.core::Sys" name="test_doc3::test_doc3::Sys::foo3"><failure>Syntax Error: unexpected identifier &#39;garbage&#39;.</failure><system-out>*garbage*
+</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
index 9cbce91..7aa0887 100644 (file)
@@ -1,33 +1,43 @@
-test_nitunit4/test_nitunit4.nit:22,2--26,4: ERROR: test_foo (in file .nitunit/gen_test_nitunit4.nit): Before Test
-Tested method
-After Test
-Runtime error: Assert failed (test_nitunit4/test_nitunit4_base.nit:31)
+* Test-suite of module test_nitunit4::test_nitunit4 (3 tests)
 
-test_nitunit4/test_nitunit4.nit:32,2--34,4: ERROR: test_baz (in file .nitunit/gen_test_nitunit4.nit): Diff
---- expected:test_nitunit4/test_nitunit4.sav/test_baz.res
-+++ got:.nitunit/gen_test_nitunit4_test_baz.out1
-@@ -1 +1,3 @@
--Bad result file
-+Before Test
-+Tested method
-+After Test
+[KO] test_nitunit4$TestTestSuite$test_foo
+     test_nitunit4/test_nitunit4.nit:22,2--26,4: Runtime Error in file nitunit.out/gen_test_nitunit4.nit
+     Output
+       Before Test
+       Tested method
+       After Test
+       Runtime error: Assert failed (test_nitunit4/test_nitunit4_base.nit:31)
 
-DocUnits:
-No doc units found
-Entities: 12; Documented ones: 0; With nitunits: 0; Failures: 0
+[OK] test_nitunit4$TestTestSuite$test_bar
+[KO] test_nitunit4$TestTestSuite$test_baz
+     test_nitunit4/test_nitunit4.nit:32,2--34,4: Difference with expected output: diff -u test_nitunit4/test_nitunit4.sav/test_baz.res nitunit.out/gen_test_nitunit4_test_baz.out1
+     Output
+       Diff
+       --- expected:test_nitunit4/test_nitunit4.sav/test_baz.res
+       +++ got:nitunit.out/gen_test_nitunit4_test_baz.out1
+       @@ -1 +1,3 @@
+       -Bad result file
+       +Before Test
+       +Tested method
+       +After Test
 
-TestSuites:
-Class suites: 1; Test Cases: 3; Failures: 2
-<testsuites><testsuite package="test_nitunit4&gt;"></testsuite><testsuite package="test_nitunit4::nitunit4"></testsuite><testsuite package="test_nitunit4"><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_foo"><system-out></system-out><system-err>Before Test
+Docunits: Entities: 12; Documented ones: 0; With nitunits: 0
+Test suites: Classes: 1; Test Cases: 3; Failures: 2
+[FAILURE] 2/3 tests failed.
+`nitunit.out` is not removed for investigation.
+<testsuites><testsuite package="test_nitunit4&gt;"></testsuite><testsuite package="test_nitunit4::nitunit4"></testsuite><testsuite package="test_nitunit4"><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_foo"><error>Runtime Error in file nitunit.out&#47;gen_test_nitunit4.nit</error><system-err>Before Test
 Tested method
 After Test
 Runtime error: Assert failed (test_nitunit4&#47;test_nitunit4_base.nit:31)
-</system-err><error message="Runtime Error"></error></testcase><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_bar"><system-out></system-out><system-err></system-err></testcase><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_baz"><system-out></system-out><system-err>Diff
+</system-err></testcase><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_bar"><system-err>Before Test
+Tested method
+After Test
+</system-err></testcase><testcase classname="nitunit.test_nitunit4::test_nitunit4.test_nitunit4::TestTestSuite" name="test_nitunit4::TestTestSuite::test_baz"><error>Difference with expected output: diff -u test_nitunit4&#47;test_nitunit4.sav&#47;test_baz.res nitunit.out&#47;gen_test_nitunit4_test_baz.out1</error><system-err>Diff
 --- expected:test_nitunit4&#47;test_nitunit4.sav&#47;test_baz.res
-+++ got:.nitunit&#47;gen_test_nitunit4_test_baz.out1
++++ got:nitunit.out&#47;gen_test_nitunit4_test_baz.out1
 @@ -1 +1,3 @@
 -Bad result file
 +Before Test
 +Tested method
 +After Test
-</system-err><error message="Runtime Error"></error></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4"></testsuite><testsuite></testsuite><testsuite package="test_nitunit4::test_nitunit4_base"></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
+</system-err></testcase></testsuite><testsuite package="test_nitunit4::test_nitunit4"></testsuite><testsuite></testsuite><testsuite package="test_nitunit4::test_nitunit4_base"></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
diff --git a/tests/sav/repeating_key_xor_solve.res b/tests/sav/repeating_key_xor_solve.res
new file mode 100644 (file)
index 0000000..8a61309
--- /dev/null
@@ -0,0 +1 @@
+Usage: repeating_key_xor_solve <cipher_file>
diff --git a/tests/sav/repeating_key_xor_solve_args1.res b/tests/sav/repeating_key_xor_solve_args1.res
new file mode 100644 (file)
index 0000000..f81264e
--- /dev/null
@@ -0,0 +1,80 @@
+I'm back and I'm ringin' the bell 
+A rockin' on the mike while the fly girls yell 
+In ecstasy in the back of me 
+Well that's my DJ Deshay cuttin' all them Z's 
+Hittin' hard and the girlies goin' crazy 
+Vanilla's on the mike, man I'm not lazy. 
+
+I'm lettin' my drug kick in 
+It controls my mouth and I begin 
+To just let it flow, let my concepts go 
+My posse's to the side yellin', Go Vanilla Go! 
+
+Smooth 'cause that's the way I will be 
+And if you don't give a damn, then 
+Why you starin' at me 
+So get off 'cause I control the stage 
+There's no dissin' allowed 
+I'm in my own phase 
+The girlies sa y they love me and that is ok 
+And I can dance better than any kid n' play 
+
+Stage 2 -- Yea the one ya' wanna listen to 
+It's off my head so let the beat play through 
+So I can funk it up and make it sound good 
+1-2-3 Yo -- Knock on some wood 
+For good luck, I like my rhymes atrocious 
+Supercalafragilisticexpialidocious 
+I'm an effect and that you can bet 
+I can take a fly girl and make her wet. 
+
+I'm like Samson -- Samson to Delilah 
+There's no denyin', You can try to hang 
+But you'll keep tryin' to get my style 
+Over and over, practice makes perfect 
+But not if you're a loafer. 
+
+You'll get nowhere, no place, no time, no girls 
+Soon -- Oh my God, homebody, you probably eat 
+Spaghetti with a spoon! Come on and say it! 
+
+VIP. Vanilla Ice yep, yep, I'm comin' hard like a rhino 
+Intoxicating so you stagger like a wino 
+So punks stop trying and girl stop cryin' 
+Vanilla Ice is sellin' and you people are buyin' 
+'Cause why the freaks are jockin' like Crazy Glue 
+Movin' and groovin' trying to sing along 
+All through the ghetto groovin' this here song 
+Now you're amazed by the VIP posse. 
+
+Steppin' so hard like a German Nazi 
+Startled by the bases hittin' ground 
+There's no trippin' on mine, I'm just gettin' down 
+Sparkamatic, I'm hangin' tight like a fanatic 
+You trapped me once and I thought that 
+You might have it 
+So step down and lend me your ear 
+'89 in my time! You, '90 is my year. 
+
+You're weakenin' fast, YO! and I can tell it 
+Your body's gettin' hot, so, so I can smell it 
+So don't be mad and don't be sad 
+'Cause the lyrics belong to ICE, You can call me Dad 
+You're pitchin' a fit, so step back and endure 
+Let the witch doctor, Ice, do the dance to cure 
+So come up close and don't be square 
+You wanna battle me -- Anytime, anywhere 
+
+You thought that I was weak, Boy, you're dead wrong 
+So come on, everybody and sing this song 
+
+Say -- Play that funky music Say, go white boy, go white boy go 
+play that funky music Go white boy, go white boy, go 
+Lay down and boogie and play that funky music till you die. 
+
+Play that funky music Come on, Come on, let me hear 
+Play that funky music white boy you say it, say it 
+Play that funky music A little louder now 
+Play that funky music, white boy Come on, Come on, Come on 
+Play that funky music 
+
diff --git a/tests/sav/test_copy_to_native.res b/tests/sav/test_copy_to_native.res
new file mode 100644 (file)
index 0000000..764d170
--- /dev/null
@@ -0,0 +1 @@
+gâštr
diff --git a/tests/sav/test_copy_to_native_alt1.res b/tests/sav/test_copy_to_native_alt1.res
new file mode 100644 (file)
index 0000000..785cb5d
--- /dev/null
@@ -0,0 +1 @@
+gâštr%D
diff --git a/tests/sav/test_copy_to_native_alt2.res b/tests/sav/test_copy_to_native_alt2.res
new file mode 100644 (file)
index 0000000..ec88d9a
--- /dev/null
@@ -0,0 +1 @@
+gâštré
index caad05a..445de88 100644 (file)
@@ -116,26 +116,26 @@ names::n3$::n1::P1        MClassDef       names/n3.nit:29,1--35,3 a refinement of a subclass
 names::n3$::n1::P1$A::a        MMethodDef      names/n3.nit:31,2--32,19        a refinement (3 distinct modules)
 names::n3$::n1::P1$::n0::P::p  MMethodDef      names/n3.nit:33,2--34,19        a refinement (3 distinct modules)
 names::n0      MModule names/n0.nit:15,1--67,3 Root module
-names::Object  MClass  names/n0.nit:20,1--22,3 
+names::Object  MClass  names/n0.nit:20,1--22,3 Root interface
 names$Object   MClassDef       names/n0.nit:20,1--22,3 Root interface
 names::Object::init    MMethod names/n0.nit:20,1--22,3 
 names$Object$init      MMethodDef      names/n0.nit:20,1--22,3 
-names::A       MClass  names/n0.nit:24,1--31,3 
+names::A       MClass  names/n0.nit:24,1--31,3 A public class
 names$A        MClassDef       names/n0.nit:24,1--31,3 A public class
-names::A::a    MMethod names/n0.nit:26,2--27,13        
+names::A::a    MMethod names/n0.nit:26,2--27,13        A public method in a public class
 names$A$a      MMethodDef      names/n0.nit:26,2--27,13        A public method in a public class
-names::n0::A::z        MMethod names/n0.nit:29,2--30,21        
+names::n0::A::z        MMethod names/n0.nit:29,2--30,21        A private method in a public class
 names$A$z      MMethodDef      names/n0.nit:29,2--30,21        A private method in a public class
-names::A0      MClass  names/n0.nit:33,1--46,3 
+names::A0      MClass  names/n0.nit:33,1--46,3 A public subclass in the same module
 names$A0       MClassDef       names/n0.nit:33,1--46,3 A public subclass in the same module
 names$A0$A::a  MMethodDef      names/n0.nit:38,2--39,19        Redefinition it the same module of a public method
 names$A0$::n0::A::z    MMethodDef      names/n0.nit:41,2--42,19        Redefinition it the same module of a private method
 names$A0$::n0::P::p    MMethodDef      names/n0.nit:44,2--45,19        Redefinition it the same module of a private method
-names::n0::P   MClass  names/n0.nit:48,1--52,3 
+names::n0::P   MClass  names/n0.nit:48,1--52,3 A private class
 names::n0$P    MClassDef       names/n0.nit:48,1--52,3 A private class
-names::n0::P::p        MMethod names/n0.nit:50,2--51,13        
+names::n0::P::p        MMethod names/n0.nit:50,2--51,13        A private method in a private class
 names::n0$P$p  MMethodDef      names/n0.nit:50,2--51,13        A private method in a private class
-names::n0::P0  MClass  names/n0.nit:54,1--67,3 
+names::n0::P0  MClass  names/n0.nit:54,1--67,3 A private subclass introduced in the same module
 names::n0$P0   MClassDef       names/n0.nit:54,1--67,3 A private subclass introduced in the same module
 names::n0$P0$A::a      MMethodDef      names/n0.nit:59,2--60,19        Redefinition it the same module of a public method
 names::n0$P0$::n0::A::z        MMethodDef      names/n0.nit:62,2--63,19        Redefinition it the same module of a private method
@@ -144,13 +144,13 @@ names::n1 MModule names/n1.nit:15,1--90,3 Second module
 names::n1$A    MClassDef       names/n1.nit:20,1--30,3 A refinement of a class
 names::n1$A$a  MMethodDef      names/n1.nit:22,2--23,19        A refinement in the same class
 names::n1$A$z  MMethodDef      names/n1.nit:25,2--26,19        A refinement in the same class
-names::n1::A::b        MMethod names/n1.nit:28,2--29,13        
+names::n1::A::b        MMethod names/n1.nit:28,2--29,13        A public method introduced in a refinement
 names::n1$A$b  MMethodDef      names/n1.nit:28,2--29,13        A public method introduced in a refinement
 names::n1$A0   MClassDef       names/n1.nit:32,1--42,3 A refinement of a subclass
 names::n1$A0$A::a      MMethodDef      names/n1.nit:34,2--35,19        A refinement+redefinition
 names::n1$A0$::n0::A::z        MMethodDef      names/n1.nit:37,2--38,19        A refinement+redefinition
 names::n1$A0$::n0::P::p        MMethodDef      names/n1.nit:40,2--41,19        A refinement+redefinition
-names::A1      MClass  names/n1.nit:44,1--57,3 
+names::A1      MClass  names/n1.nit:44,1--57,3 A subclass introduced in a submodule
 names$A1       MClassDef       names/n1.nit:44,1--57,3 A subclass introduced in a submodule
 names$A1$A::a  MMethodDef      names/n1.nit:49,2--50,19        A redefinition in a subclass from a different module
 names$A1$::n0::A::z    MMethodDef      names/n1.nit:52,2--53,19        A redefinition in a subclass from a different module
@@ -161,20 +161,20 @@ names::n1$::n0::P0        MClassDef       names/n1.nit:65,1--75,3 A refinement of a subclass
 names::n1$::n0::P0$A::a        MMethodDef      names/n1.nit:67,2--68,19        A refinement+redefinition
 names::n1$::n0::P0$::n0::A::z  MMethodDef      names/n1.nit:70,2--71,19        A refinement+redefinition
 names::n1$::n0::P0$::n0::P::p  MMethodDef      names/n1.nit:73,2--74,19        A refinement+redefinition
-names::n1::P1  MClass  names/n1.nit:77,1--90,3 
+names::n1::P1  MClass  names/n1.nit:77,1--90,3 A private subclass introduced in a different module
 names::n1$P1   MClassDef       names/n1.nit:77,1--90,3 A private subclass introduced in a different module
 names::n1$P1$A::a      MMethodDef      names/n1.nit:82,2--83,19        A redefinition in a subclass from a different module
 names::n1$P1$::n0::A::z        MMethodDef      names/n1.nit:85,2--86,19        A redefinition in a subclass from a different module
 names::n1$P1$::n0::P::p        MMethodDef      names/n1.nit:88,2--89,19        A redefinition in a subclass from a different module
 names::n2      MModule names/n2.nit:15,1--33,3 A alternative second module, used to make name conflicts
 names::n2$A    MClassDef       names/n2.nit:20,1--27,3 A refinement of a class
-names::n2::A::b        MMethod names/n2.nit:22,2--23,13        
+names::n2::A::b        MMethod names/n2.nit:22,2--23,13        Name conflict? A second public method
 names::n2$A$b  MMethodDef      names/n2.nit:22,2--23,13        Name conflict? A second public method
-names::n2::A::z        MMethod names/n2.nit:25,2--26,13        
+names::n2::A::z        MMethod names/n2.nit:25,2--26,13        Name conflict? A second private method
 names::n2$A$z  MMethodDef      names/n2.nit:25,2--26,13        Name conflict? A second private method
-names::n2::P   MClass  names/n2.nit:29,1--33,3 
+names::n2::P   MClass  names/n2.nit:29,1--33,3 Name conflict? A second private class
 names::n2$P    MClassDef       names/n2.nit:29,1--33,3 Name conflict? A second private class
-names::n2::P::p        MMethod names/n2.nit:31,2--32,13        
+names::n2::P::p        MMethod names/n2.nit:31,2--32,13        Name conflict? A private method in an homonym class.
 names::n2$P$p  MMethodDef      names/n2.nit:31,2--32,13        Name conflict? A private method in an homonym class.
 names1 MPackage        names1.nit      An alternative second module in a distinct package
 names1>        MGroup  names1.nit      An alternative second module in a distinct package
@@ -182,13 +182,13 @@ names1::names1    MModule names1.nit:15,1--90,3   An alternative second module in a d
 names1::names1$names::A        MClassDef       names1.nit:20,1--30,3   A refinement of a class
 names1::names1$names::A$a      MMethodDef      names1.nit:22,2--23,19  A refinement in the same class
 names1::names1$names::A$z      MMethodDef      names1.nit:25,2--26,19  A refinement in the same class
-names1::names1::A::b   MMethod names1.nit:28,2--29,13  
+names1::names1::A::b   MMethod names1.nit:28,2--29,13  A public method introduced in a refinement
 names1::names1$names::A$b      MMethodDef      names1.nit:28,2--29,13  A public method introduced in a refinement
 names1::names1$names::A0       MClassDef       names1.nit:32,1--42,3   A refinement of a subclass
 names1::names1$names::A0$names::A::a   MMethodDef      names1.nit:34,2--35,19  A refinement+redefinition
 names1::names1$names::A0$names::n0::A::z       MMethodDef      names1.nit:37,2--38,19  A refinement+redefinition
 names1::names1$names::A0$names::n0::P::p       MMethodDef      names1.nit:40,2--41,19  A refinement+redefinition
-names1::A1     MClass  names1.nit:44,1--57,3   
+names1::A1     MClass  names1.nit:44,1--57,3   A subclass introduced in a submodule
 names1$A1      MClassDef       names1.nit:44,1--57,3   A subclass introduced in a submodule
 names1$A1$names::A::a  MMethodDef      names1.nit:49,2--50,19  A redefinition in a subclass from a different module
 names1$A1$names::n0::A::z      MMethodDef      names1.nit:52,2--53,19  A redefinition in a subclass from a different module
@@ -199,7 +199,7 @@ names1::names1$names::n0::P0        MClassDef       names1.nit:65,1--75,3   A refinement of a s
 names1::names1$names::n0::P0$names::A::a       MMethodDef      names1.nit:67,2--68,19  A refinement+redefinition
 names1::names1$names::n0::P0$names::n0::A::z   MMethodDef      names1.nit:70,2--71,19  A refinement+redefinition
 names1::names1$names::n0::P0$names::n0::P::p   MMethodDef      names1.nit:73,2--74,19  A refinement+redefinition
-names1::names1::P1     MClass  names1.nit:77,1--90,3   
+names1::names1::P1     MClass  names1.nit:77,1--90,3   A private subclass introduced in a different module
 names1::names1$P1      MClassDef       names1.nit:77,1--90,3   A private subclass introduced in a different module
 names1::names1$P1$names::A::a  MMethodDef      names1.nit:82,2--83,19  A redefinition in a subclass from a different module
 names1::names1$P1$names::n0::A::z      MMethodDef      names1.nit:85,2--86,19  A redefinition in a subclass from a different module
diff --git a/tests/sav/test_nativestring_fill_from.res b/tests/sav/test_nativestring_fill_from.res
new file mode 100644 (file)
index 0000000..2c26c70
--- /dev/null
@@ -0,0 +1 @@
+S&éstr
diff --git a/tests/sav/test_nativestring_fill_from_alt1.res b/tests/sav/test_nativestring_fill_from_alt1.res
new file mode 100644 (file)
index 0000000..0667158
--- /dev/null
@@ -0,0 +1 @@
+S&éstrS&éstr
diff --git a/tests/sav/test_nativestring_fill_from_alt2.res b/tests/sav/test_nativestring_fill_from_alt2.res
new file mode 100644 (file)
index 0000000..2c26c70
--- /dev/null
@@ -0,0 +1 @@
+S&éstr
diff --git a/tests/sav/test_nativestring_fill_from_alt3.res b/tests/sav/test_nativestring_fill_from_alt3.res
new file mode 100644 (file)
index 0000000..f28809f
--- /dev/null
@@ -0,0 +1 @@
+&éstr
index 9e5ceab..38c4890 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/core/collection/array.nit:989)
+Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/core/collection/array.nit:991)
 NativeString
 0x4e
 Nit
diff --git a/tests/sav/test_readline.res b/tests/sav/test_readline.res
new file mode 100644 (file)
index 0000000..666312a
--- /dev/null
@@ -0,0 +1,9 @@
+prompt>line 1
+line 1
+prompt>line 2
+line 2
+prompt>line 2bis
+line 2bis
+prompt>line 3 \b \bine\b \b\b \b\b \b3\b \b
+line 3
+prompt>
\ No newline at end of file
diff --git a/tests/sav/test_realtime.res b/tests/sav/test_realtime.res
deleted file mode 100644 (file)
index 65b7078..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-sleeping 1s
-true
-true
-sleeping 5000ns
-true
-true
-true
diff --git a/tests/test_copy_to_native.nit b/tests/test_copy_to_native.nit
new file mode 100644 (file)
index 0000000..380c02a
--- /dev/null
@@ -0,0 +1,28 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+import core
+#alt1 intrude import core::text::ropes
+#alt2 intrude import core::text::ropes
+
+var ons = new NativeString(9)
+var base_str = "%Dégâštr"
+
+var str: String = base_str
+#alt1 str = new Concat(base_str, base_str)
+#alt2 str = new Concat(base_str, base_str.substring_from(2))
+
+var copy_len = (str.bytelen - 4).min(9)
+str.copy_to_native(ons, copy_len, 4, 0)
+print ons.to_s_with_length(copy_len)
index 21728c1..a9f3e61 100644 (file)
@@ -21,12 +21,11 @@ var clock = new Clock
 var p = new Process("sleep", "10")
 
 # Send some signals
-p.signal(sigalarm)
+p.signal sigalarm
 p.kill
 
 # Wait for it to die
 p.wait
-var lapse = clock.lapse
 
 # Let's be generous here, as long as it does not take 5 secs out of 10 it's OK
-print lapse.sec < 5
+print clock.lapse < 5.0
diff --git a/tests/test_nativestring_fill_from.nit b/tests/test_nativestring_fill_from.nit
new file mode 100644 (file)
index 0000000..1b9e491
--- /dev/null
@@ -0,0 +1,27 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+#alt1 intrude import core::text::ropes
+import core
+
+var src_s = "S&éstr"
+var cpstr: Text = src_s
+#alt1 cpstr = new Concat(src_s, src_s)
+#alt2 cpstr = new FlatBuffer.from(src_s)
+#alt3 cpstr = cpstr.substring(1, 5)
+
+var ns = new NativeString(cpstr.bytelen)
+ns.fill_from(cpstr)
+
+print ns.to_s_with_length(cpstr.bytelen)
index e4088a2..5f2ca1c 100644 (file)
@@ -17,8 +17,8 @@ import pthreads
 
 redef class Sys
        var iface: String is lazy do
-               srand
-               return "localhost:{10000+20000.rand}"
+               var testid = "NIT_TESTING_ID".environ.to_i
+               return "localhost:{10000+testid}"
        end
 
        var fs_path: String = getcwd / "../lib/nitcorn/examples/www/hello_world/dir" is lazy
index b583197..d260cd1 100644 (file)
@@ -24,6 +24,8 @@ class X
        #     assert undefined_identifier
        fun foo do end
 
+       # a 'failure' unit test (does not parse)
+       #     assert !@#$%^&*()
        fun foo1(a, b: Int) do end
 
        private fun foo2: Bool do return true
diff --git a/tests/test_postgres_nity.nit b/tests/test_postgres_nity.nit
new file mode 100644 (file)
index 0000000..9565335
--- /dev/null
@@ -0,0 +1,49 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2016 Guilherme Mansur <guilhermerpmansur@gmail.com>
+#
+# 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.
+
+module test_postgres_nity
+
+import postgresql::postgres
+
+var db = new Postgres.open("dbname=postgres")
+assert open_db: not db.is_closed else print db.error
+
+assert create_table: db.create_table("IF NOT EXISTS users (uname TEXT PRIMARY KEY, pass TEXT NOT NULL, activated INTEGER, perc FLOAT)") else
+  print db.error
+end
+
+assert insert1: db.insert("INTO users VALUES('Bob', 'zzz', 1, 77.7)") else
+  print db.error
+end
+
+assert insert2: db.insert("INTO users VALUES('Guilherme', 'xxx', 1, 88)") else
+  print db.error
+end
+
+var result = db.raw_execute("SELECT * FROM users")
+
+assert raw_exec: result.is_ok else print db.error
+
+assert postgres_nfields: result.nfields == 4 else print_error db.error
+assert postgres_fname: result.fname(0) == "uname" else print_error db.error
+assert postgres_isnull: result.is_null(0,0) == false else print_error db.error
+assert postgres_value: result.value(0,0) == "Bob" else print_error db.error
+
+assert drop_table: db.execute("DROP TABLE users") else print db.error
+
+db.finish
+
+assert db.is_closed else print db.error
diff --git a/tests/test_readline.inputs b/tests/test_readline.inputs
new file mode 100644 (file)
index 0000000..59f6fe7
--- /dev/null
@@ -0,0 +1,4 @@
+line 1
+line 2
+line 2bis
+line 3 \bine\b\b\b3\b
diff --git a/tests/test_readline.nit b/tests/test_readline.nit
new file mode 100644 (file)
index 0000000..c254b24
--- /dev/null
@@ -0,0 +1,21 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+import readline
+
+loop
+       var line = readline("prompt>", true)
+       if line == null then break
+       print line
+end
index 84ba34a..d11c60a 100755 (executable)
@@ -21,6 +21,8 @@
 export LANG=C
 export LC_ALL=C
 export NIT_TESTING=true
+# Use the pid as a collision prevention
+export NIT_TESTING_ID=$$
 export NIT_SRAND=0
 
 unset NIT_DIR