From: Jean Privat Date: Fri, 10 Jun 2016 01:55:27 +0000 (-0400) Subject: Merge: Added contributing guidelines and link from readme X-Git-Url: http://nitlanguage.org?hp=f41b065d8f66b4283f19bfda07e241c0c363126b Merge: Added contributing guidelines and link from readme As discussed in #2133, contribution guidelines are hard to find. This PR is an attempt to ease the process by giving newcomer friendly advices and some more insight on the contribution process for Nit. @tonygaetani, do you think a document like this one would have been helpful for you ? Is there anything you would change to make it friendlier/clearer ? Close #2133 Pull-Request: #2134 Reviewed-by: Jean Privat --- diff --git a/.gitattributes b/.gitattributes index 02c0677..e326f39 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,5 +6,5 @@ tables_nit.c -diff c_src/** -diff tests/sav/**/*.res -whitespace -lib/popcorn/tests/res/*.res -whitespace +*.res -whitespace *.patch -whitespace diff --git a/NOTICE b/NOTICE index 33a5cff..514ed50 100644 --- a/NOTICE +++ b/NOTICE @@ -30,6 +30,7 @@ Copyright: 2004-2016 Jean Privat 2015 Jean-Philippe Caissy 2015 Alexandre Blondin Massé 2015-2016 Guilherme Mansur + 2016 Tony Gaetani License: Apache 2.0 (see LICENSE) Comment: The main license of the work, except for the following exceptions diff --git a/README.md b/README.md index 826686a..2679f4d 100644 --- 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 diff --git a/contrib/benitlux/src/client/base.nit b/contrib/benitlux/src/client/base.nit index bcc1efd..438a125 100644 --- a/contrib/benitlux/src/client/base.nit +++ b/contrib/benitlux/src/client/base.nit @@ -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 diff --git a/contrib/benitlux/src/client/features/translations.nit b/contrib/benitlux/src/client/features/translations.nit index 02deb61..be42f57 100644 --- a/contrib/benitlux/src/client/features/translations.nit +++ b/contrib/benitlux/src/client/features/translations.nit @@ -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" diff --git a/contrib/benitlux/src/client/views/user_views.nit b/contrib/benitlux/src/client/views/user_views.nit index e45fcb5..b69b1db 100644 --- a/contrib/benitlux/src/client/views/user_views.nit +++ b/contrib/benitlux/src/client/views/user_views.nit @@ -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 diff --git a/contrib/benitlux/src/server/benitlux_social.nit b/contrib/benitlux/src/server/benitlux_social.nit index bdd83b4..7764c75 100644 --- a/contrib/benitlux/src/server/benitlux_social.nit +++ b/contrib/benitlux/src/server/benitlux_social.nit @@ -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)") diff --git a/contrib/nitiwiki/src/wiki_base.nit b/contrib/nitiwiki/src/wiki_base.nit index 70cf35f..d3adc26 100644 --- a/contrib/nitiwiki/src/wiki_base.nit +++ b/contrib/nitiwiki/src/wiki_base.nit @@ -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 diff --git a/contrib/nitiwiki/src/wiki_html.nit b/contrib/nitiwiki/src/wiki_html.nit index ab79f09..15ae4d7 100644 --- a/contrib/nitiwiki/src/wiki_html.nit +++ b/contrib/nitiwiki/src/wiki_html.nit @@ -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 diff --git a/contrib/nitiwiki/src/wiki_links.nit b/contrib/nitiwiki/src/wiki_links.nit index ac3e171..33dc61f 100644 --- a/contrib/nitiwiki/src/wiki_links.nit +++ b/contrib/nitiwiki/src/wiki_links.nit @@ -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 diff --git a/contrib/nitrpg/src/test_helper.nit b/contrib/nitrpg/src/test_helper.nit index 904862c..ecdc358 100644 --- a/contrib/nitrpg/src/test_helper.nit +++ b/contrib/nitrpg/src/test_helper.nit @@ -15,7 +15,7 @@ # limitations under the License. # Test tools for NitRPG. -module test_helper is test_suite +module test_helper import test_suite import game diff --git a/contrib/pep8analysis/package.ini b/contrib/pep8analysis/package.ini index 43d5e4a..bbf3802 100644 --- a/contrib/pep8analysis/package.ini +++ b/contrib/pep8analysis/package.ini @@ -3,6 +3,8 @@ name=pep8analysis tags=educ,web,cli maintainer=Alexis Laferrière license=Apache-2.0 +[source] +exclude=src/parser/parser_abs.nit [upstream] browse=https://github.com/nitlang/nit/tree/master/contrib/pep8analysis/ git=https://github.com/nitlang/nit.git diff --git a/contrib/refund/src/refund_json.nit b/contrib/refund/src/refund_json.nit index 33032b3..4da71f5 100644 --- a/contrib/refund/src/refund_json.nit +++ b/contrib/refund/src/refund_json.nit @@ -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 diff --git a/contrib/simplan/simplan.nit b/contrib/simplan/simplan.nit index ba32497..301c564 100644 --- a/contrib/simplan/simplan.nit +++ b/contrib/simplan/simplan.nit @@ -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 diff --git a/examples/fibonacci.nit b/examples/fibonacci.nit index d4330ba..66cbfb0 100644 --- a/examples/fibonacci.nit +++ b/examples/fibonacci.nit @@ -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. diff --git a/lib/app/README.md b/lib/app/README.md index c5caa4b..0d2d24c 100644 --- a/lib/app/README.md +++ b/lib/app/README.md @@ -5,13 +5,14 @@ The framework provides services to manage common needs of modern mobile applicat * Life-cycle * User interface * Persistence +* Async HTTP requests * Package metadata * Compilation and packaging The features offered by _app.nit_ are common to all platforms, but may not be available on all devices. -## Application Life-Cycle +# Application Life-Cycle The _app.nit_ application life-cycle is compatible with all target platforms. It relies on the following sequence of events, represented here by their callback method name: @@ -44,7 +45,7 @@ The `App` instance is the first to be notified of these events. Other UI elements, from the `ui` submodule, are notified of the same events using a simple depth first visit. So all UI elements can react separately to live-cycle events. -## User Interface +# User Interface The `app::ui` module defines an abstract API to build a portable graphical application. The API is composed of interactive `Control`s, visible `View`s and an active `Window`. @@ -65,21 +66,20 @@ So there is two ways to customize the behavior on a given event: * Add an observer to a `Button` instance, and implement `on_event` in the observer. -### Usage Example +## Usage Example The calculator example (at `../../examples/calculator/src/calculator.nit`) is a concrete, simple and complete use of the _app.nit_ portable UI. -### Platform-specific UI +## Platform-specific UI You can go beyond the portable UI API of _app.nit_ by using the natives services of a platform. The suggested approach is to use platform specific modules to customize the application on a precise platform. -This module redefine `Window::on_start` to call the native language of the platform and setup a native UI. +See the calculator example for an adaptation of the UI on Android, +the interesting module is in this repository at ../../examples/calculator/src/android_calculator.nit -_TODO complete description and add concrete examples_ - -## Persistent State with data\_store +# Persistent State with data\_store _app.nit_ offers the submodule `app::data_store` to easily save the application state and user preferences. The service is accessible by the method `App::data_store`. The `DataStore` itself defines 2 methods: @@ -90,7 +90,7 @@ Pass `null` to clear the value associated to a key. * `DataStore::[]` returns the object associated to a `String` key. It returns `null` if nothing is associated to the key. -### Usage Example +## Usage Example ~~~ import app::data_store @@ -123,7 +123,14 @@ redef class App end ~~~ -## Metadata annotations +# Async HTTP request + +The module `app::http_request` provides services to execute asynchronous HTTP request. +The class `AsyncHttpRequest` hides the complex parallel logic and +lets the user implement methods acting only on the UI thread. +See the documentation of `AsyncHttpRequest` for more information. + +# Metadata annotations The _app.nit_ framework defines three annotations to customize the application package. @@ -142,7 +149,7 @@ The _app.nit_ framework defines three annotations to customize the application p The special function `git_revision` will use the prefix of the hash of the latest git commit. By default, the version is 0.1. -### Usage Example +## Usage Example ~~~ module my_module is @@ -152,33 +159,38 @@ module my_module is end ~~~ -## Compiling and Packaging an Application +# Compiling and Packaging an Application The Nit compiler detects the target platform from the importations and generates the appropriate application format and package. Applications using only the portable services of _app.nit_ require some special care at compilation. Such an application, let's say `calculator.nit`, does not depend on a specific platform and use the portable UI. -The target platform must be specifed to the compiler for it to produce the correct application package. +The target platform must be specified to the compiler for it to produce the correct application package. There is two main ways to achieve this goal: -* The the mixin option (`-m path`) loads an additionnal module before compiling. +* The mixin option (`-m module`) imports an additional module before compiling. It can be used to load platform specific implementations of the _app.nit_ portable UI. ~~~ # GNU/Linux version, using GTK - nitc calculator.nit -m NIT_DIR/lib/linux/ui.nit + nitc calculator.nit -m linux # Android version - nitc calculator.nit -m NIT_DIR/lib/android/ui/ + nitc calculator.nit -m android + + # iOS version + nitc calculator.nit -m ios ~~~ * A common alternative for larger projects is to use platform specific modules. - Continuing with the `calculator.nit` example, it can be accompagnied by the module `calculator_linux.nit`. - This module imports both `calculator` and `linux::ui`, and can also use other GNU/Linux specific code. + Continuing with the calculator example, it is adapted for Android by the module `android_calculator.nit`. + This module imports both `calculator` and `android`, it can then use Android specific code. ~~~ - module calculator_linux + module android_calculator import calculator - import linux::ui + import android + + # ... ~~~ diff --git a/lib/app/http_request.nit b/lib/app/http_request.nit index 9a5feca..694ba33 100644 --- a/lib/app/http_request.nit +++ b/lib/app/http_request.nit @@ -28,14 +28,21 @@ redef class App fun run_on_ui_thread(task: Task) is abstract end -# Thread executing an HTTP request and deserializing JSON asynchronously +# Thread executing an HTTP request asynchronously # -# This class defines four methods acting on the main/UI thread, -# they should be implemented as needed: -# * before -# * on_load -# * on_fail -# * after +# The request is sent to `rest_server_uri / rest_action`. +# +# If `deserialize_json`, the default behavior, the response is deserialized from JSON +# +# If `delay > 0.0`, sending the reqest is delayed by the given `delay` in seconds. +# It can be used to delay resending a request on error. +# +# Four callback methods act on the main/UI thread, +# they should be implemented as needed in subclasses: +# * `before` +# * `on_load` +# * `on_fail` +# * `after` class AsyncHttpRequest super Thread diff --git a/lib/app/package.ini b/lib/app/package.ini index d5c48c1..88a75fb 100644 --- a/lib/app/package.ini +++ b/lib/app/package.ini @@ -1,6 +1,6 @@ [package] name=app -tags=lib +tags=lib,mobile maintainer=Alexis Laferrière license=Apache-2.0 [upstream] diff --git a/lib/base64.nit b/lib/base64.nit index 8f6565b..29c5bbf 100644 --- a/lib/base64.nit +++ b/lib/base64.nit @@ -17,22 +17,51 @@ # Offers the base 64 encoding and decoding algorithms module base64 -redef class NativeString - # Alphabet used by the base64 algorithm - private fun base64_chars : SequenceRead[Byte] - do - return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".bytes +redef class Char + # Is `self` a valid Base64 character ? + fun is_base64_char: Bool do + if code_point >= 127 then return false + return ascii.is_base64_char end +end - # Reversed alphabet for base64 - private fun inverted_base64_chars : HashMap[Byte, Byte] - do - var inv_base64_chars = new HashMap[Byte, Byte] - var l = base64_chars.length - for k in [0 .. l[ do - inv_base64_chars[base64_chars[k]] = k.to_b +redef class Byte + # Is `self` a valid Base64 character ? + fun is_base64_char: Bool do + if self == b'+' then return true + if self == b'/' then return true + if self > b'Z' then + if self < b'a' then return false + if self <= b'z' then return true + return false + end + if self >= b'A' then return true + if self <= b'9' and self >= b'0' then return true + return false + end + + # Returns the `base64` equivalent of `self` + # + # REQUIRE `self`.`is_base64_char` + fun to_base64_char: Byte do + if self == b'+' then return 62u8 + if self == b'/' then return 63u8 + if self > b'Z' then + if self < b'a' then abort + if self <= b'z' then return self - 71u8 + abort end - return inv_base64_chars + if self >= b'A' then return self - 0x41u8 + if self <= b'9' and self >= b'0' then return self + 4u8 + abort + end +end + +redef class NativeString + # Alphabet used by the base64 algorithm + private fun base64_chars : Bytes + do + return b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" end # Encodes `self` to base64. @@ -40,9 +69,8 @@ redef class NativeString # By default, uses "=" for padding. # # assert "string".encode_base64 == "c3RyaW5n" - private fun encode_base64(length: Int, padding: nullable Byte): Bytes do + private fun encode_base64(length: Int): Bytes do var base64_bytes = once base64_chars - if padding == null then padding = '='.ascii var steps = length / 3 var bytes_in_last_step = length % 3 var result_length = steps * 4 @@ -70,70 +98,102 @@ redef class NativeString result.add base64_bytes[((self[in_off + 1] & 0b0000_1111u8) << 2).to_i] end var rempad = if bytes_in_last_step > 0 then 3 - bytes_in_last_step else 0 - for i in [0 .. rempad[ do result.add padding + for i in [0 .. rempad[ do result.add b'=' return result end # Decodes `self` from base64 # - # assert "c3RyaW5n".decode_base64 == "string" - # assert "c3Rya\nW5n".decode_base64 == "string" + # assert "c3RyaW5n".decode_base64.to_s == "string" + # assert "c3Rya\nW5n".decode_base64.to_s == "string" + # assert "c3RyaW5nCg==".decode_base64.to_s == "string\n" + # assert "c3RyaW5nCg".decode_base64.to_s == "string\n" + # assert "c3RyaW5neQo=".decode_base64.to_s == "stringy\n" + # assert "c3RyaW5neQo".decode_base64.to_s == "stringy\n" # - # REQUIRE: `length % 4 == 0` - private fun decode_base64(length: Int, padding: nullable Byte): Bytes do - if padding == null then padding = '='.ascii - var inv = once inverted_base64_chars + private fun decode_base64(length: Int): Bytes do if length == 0 then return new Bytes.empty - # Remove non-base64 chars - var bytes = new Bytes.with_capacity(length) - for k in [0 .. length[ do - var byte = self[k] - if inv.has_key(byte) or byte == padding then bytes.add(byte) + # Avoids constant unboxing + var pad = b'=' + + var result = new Bytes.with_capacity((length / 4 + 1) * 3) + + var curr = 0 + var cnt = 0 + var endpos = -1 + for i in [0 .. length[ do + var b = self[i] + if b == pad then + endpos = i + break + end + # Ignore whitespaces + if b <= 0x20u8 then continue + if not b.is_base64_char then continue + curr <<= 6 + curr += b.to_base64_char.to_i + cnt += 1 + if cnt == 4 then + result.add(((curr & 0xFF0000) >> 16).to_b) + result.add(((curr & 0xFF00) >> 8).to_b) + result.add((curr & 0xFF).to_b) + curr = 0 + cnt = 0 + end end - length = bytes.length - - var steps = length / 4 - var result_length = steps * 3 - - var epos = length - 1 - var padding_len = 0 - while epos >= 0 and bytes[epos] == padding do - epos -= 1 - padding_len += 1 + if endpos != -1 or cnt != 0 then + var pads = 0 + for i in [endpos .. length[ do + var b = self[i] + if b <= 0x20u8 then continue + pads += 1 + end + if cnt == 2 then + curr >>= 4 + result.add(curr.to_b) + else if cnt == 3 then + curr >>= 2 + result.add(((curr & 0xFF00) >> 8).to_b) + result.add((curr & 0xFF).to_b) + end end + return result + end - if padding_len != 0 then steps -= 1 - if padding_len == 1 then result_length -= 1 - if padding_len == 2 then result_length -= 2 - - var result = new Bytes.with_capacity(result_length + 1) + # Is `self` a well-formed Base64 entity ? + # + # ~~~nit + # assert "Qn03".is_base64 + # assert not "#sd=".is_base64 + # ~~~ + fun is_base64(length: Int): Bool do return check_base64(length) == null - for s in [0 .. steps[ do - var c0 = inv[bytes[s * 4]] - var c1 = inv[bytes[s * 4 + 1]] - var c2 = inv[bytes[s * 4 + 2]] - var c3 = inv[bytes[s * 4 + 3]] - result.add (((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4)) - result.add (((c1 & 0b0000_1111u8) << 4) | ((c2 & 0b0011_1100u8) >> 2)) - result.add (((c2 & 0b0000_0011u8) << 6) | (c3 & 0b0011_1111u8)) + # Is `self` a well-formed Base64 entity ? + # + # Will return an Error otherwise with info on which part is erroneous. + fun check_base64(length: Int): nullable Error do + var rlen = 0 + var opos = length + for i in [0 .. length[ do + if self[i] == b'=' then + opos = i + break + end + if self[i].is_whitespace then continue + if not self[i].is_base64_char then return new Error("Invalid Base64 character at position {i}: {self[i].ascii}") + rlen += 1 + if rlen > 4 then rlen -= 4 end - - var last_start = steps * 4 - if padding_len == 1 then - var c0 = inv[bytes[last_start]] - var c1 = inv[bytes[last_start + 1]] - var c2 = inv[bytes[last_start + 2]] - result.add (((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4)) - result.add (((c1 & 0b0000_1111u8) << 4) | ((c2 & 0b0011_1100u8) >> 2)) - else if padding_len == 2 then - var c0 = inv[bytes[last_start]] - var c1 = inv[bytes[last_start + 1]] - result.add (((c0 & 0b0011_1111u8) << 2) | ((c1 & 0b0011_0000u8) >> 4)) + var pad = 0 + for i in [opos .. length[ do + if self[i].is_whitespace then continue + if self[i] != b'=' then return new Error("Invalid padding character {self[i].ascii} at position {i}") + pad += 1 end - - return result + if rlen + pad != 4 then return new Error("Invalid padding length") + return null end end @@ -142,35 +202,49 @@ redef class Bytes # Encodes the receiver string to base64 using a custom padding character. # # If using the default padding character `=`, see `encode_base64`. - fun encode_base64(padding: nullable Byte): Bytes - do - return items.encode_base64(length, padding) - end + fun encode_base64: Bytes do return items.encode_base64(length) # Decodes the receiver string to base64 using a custom padding character. # # Default padding character `=` - fun decode_base64(padding : nullable Byte) : Bytes - do - return items.decode_base64(length, padding) - end + fun decode_base64: Bytes do return items.decode_base64(length) + + # Is `self` a well-formed Base64 entity ? + fun is_base64: Bool do return items.is_base64(length) + + # Is `self` a well-formed Base64 entity ? + # + # Will return an Error otherwise with info on which part is erroneous. + fun check_base64: nullable Error do return items.check_base64(length) end -redef class String +redef class Text # Encodes the receiver string to base64 using a custom padding character. # # If using the default padding character `=`, see `encode_base64`. - fun encode_base64(padding: nullable Byte): String - do - return to_cstring.encode_base64(bytelen, padding).to_s - end + fun encode_base64: String do return to_cstring.encode_base64(bytelen).to_s # Decodes the receiver string to base64 using a custom padding character. # # Default padding character `=` - fun decode_base64(padding : nullable Byte) : String - do - return to_cstring.decode_base64(bytelen, padding).to_s - end + fun decode_base64: Bytes do return to_cstring.decode_base64(bytelen) + + # Is `self` a well-formed Base64 entity ? + fun is_base64: Bool do return to_cstring.is_base64(bytelen) + + # Is `self` a well-formed Base64 entity ? + # + # Will return an Error otherwise with info on which part is erroneous. + fun check_base64: nullable Error do return to_cstring.check_base64(bytelen) +end + +redef class FlatText + redef fun encode_base64 do return fast_cstring.encode_base64(bytelen).to_s + + redef fun decode_base64 do return fast_cstring.decode_base64(bytelen) + + redef fun is_base64 do return fast_cstring.is_base64(bytelen) + + redef fun check_base64 do return fast_cstring.check_base64(bytelen) end diff --git a/lib/core/collection/abstract_collection.nit b/lib/core/collection/abstract_collection.nit index 5cffbec..5ed911f 100644 --- a/lib/core/collection/abstract_collection.nit +++ b/lib/core/collection/abstract_collection.nit @@ -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 diff --git a/lib/core/collection/array.nit b/lib/core/collection/array.nit index 79e7a70..f01bbb5 100644 --- a/lib/core/collection/array.nit +++ b/lib/core/collection/array.nit @@ -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 diff --git a/lib/core/collection/list.nit b/lib/core/collection/list.nit index ab81a37..241238b 100644 --- a/lib/core/collection/list.nit +++ b/lib/core/collection/list.nit @@ -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 diff --git a/lib/core/error.nit b/lib/core/error.nit index ac13301..6a58480 100644 --- a/lib/core/error.nit +++ b/lib/core/error.nit @@ -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 diff --git a/lib/core/exec.nit b/lib/core/exec.nit index 76d9d63..6c03c7f 100644 --- a/lib/core/exec.nit +++ b/lib/core/exec.nit @@ -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') diff --git a/lib/core/file.nit b/lib/core/file.nit index 7f1d481..8bf8078 100644 --- a/lib/core/file.nit +++ b/lib/core/file.nit @@ -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 diff --git a/lib/core/kernel.nit b/lib/core/kernel.nit index 399487a..fe45072 100644 --- a/lib/core/kernel.nit +++ b/lib/core/kernel.nit @@ -699,6 +699,9 @@ universal Byte return self end end + + # Is `self` an ASCII whitespace ? + fun is_whitespace: Bool do return self == 0x7Fu8 or self <= 0x20u8 end # Native integer numbers. diff --git a/lib/core/math.nit b/lib/core/math.nit index 0f8325f..7b51e2a 100644 --- a/lib/core/math.nit +++ b/lib/core/math.nit @@ -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 diff --git a/lib/core/re.nit b/lib/core/re.nit index 917ec4f..0ac109c 100644 --- a/lib/core/re.nit +++ b/lib/core/re.nit @@ -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 diff --git a/lib/core/text/abstract_text.nit b/lib/core/text/abstract_text.nit index d7dfa48..e07e0d4 100644 --- a/lib/core/text/abstract_text.nit +++ b/lib/core/text/abstract_text.nit @@ -590,10 +590,13 @@ abstract class Text return res.to_s end - # Escape " \ ' and non printable characters using the rules of literal C strings and characters + # Escape `"` `\` `'`, trigraphs and non printable characters using the rules of literal C strings and characters # - # assert "abAB12<>&".escape_to_c == "abAB12<>&" + # assert "abAB12<>&".escape_to_c == "abAB12<>&" # assert "\n\"'\\".escape_to_c == "\\n\\\"\\'\\\\" + # assert "allo???!".escape_to_c == "allo??\\?!" + # assert "??=??/??'??(??)".escape_to_c == "?\\?=?\\?/??\\'?\\?(?\\?)" + # assert "??!????-".escape_to_c == "?\\?!?\\??\\?-" # # Most non-printable characters (bellow ASCII 32) are escaped to an octal form `\nnn`. # Three digits are always used to avoid following digits to be interpreted as an element @@ -617,6 +620,24 @@ abstract class Text b.append("\\\'") else if c == '\\' then b.append("\\\\") + else if c == '?' then + # Escape if it is the last question mark of a ANSI C trigraph. + var j = i + 1 + if j < length then + var next = chars[j] + # We ignore `??'` because it will be escaped as `??\'`. + if + next == '!' or + next == '(' or + next == ')' or + next == '-' or + next == '/' or + next == '<' or + next == '=' or + next == '>' + then b.add('\\') + end + b.add('?') else if c.code_point < 32 then b.add('\\') var oct = c.code_point.to_base(8) @@ -640,6 +661,7 @@ abstract class Text # The result might no be legal in C but be used in other languages # # assert "ab|\{\}".escape_more_to_c("|\{\}") == "ab\\|\\\{\\\}" + # assert "allo???!".escape_more_to_c("") == "allo??\\?!" fun escape_more_to_c(chars: String): String do var b = new Buffer @@ -1360,30 +1382,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 +1489,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 +1507,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 +2136,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. diff --git a/lib/core/text/flat.nit b/lib/core/text/flat.nit index 9e70321..6273609 100644 --- a/lib/core/text/flat.nit +++ b/lib/core/text/flat.nit @@ -225,6 +225,22 @@ redef class FlatText req_esc += 1 else if c == 0x5Cu8 then req_esc += 1 + else if c == 0x3Fu8 then + var j = pos + 1 + if j < length then + var next = its[j] + # We ignore `??'` because it will be escaped as `??\'`. + if + next == 0x21u8 or + next == 0x28u8 or + next == 0x29u8 or + next == 0x2Du8 or + next == 0x2Fu8 or + next == 0x3Cu8 or + next == 0x3Du8 or + next == 0x3Eu8 + then req_esc += 1 + end else if c < 32u8 then req_esc += 3 end @@ -280,6 +296,27 @@ redef class FlatText nns[opos] = 0x5Cu8 nns[opos + 1] = 0x5Cu8 opos += 2 + else if c == 0x3Fu8 then + var j = pos + 1 + if j < length then + var next = its[j] + # We ignore `??'` because it will be escaped as `??\'`. + if + next == 0x21u8 or + next == 0x28u8 or + next == 0x29u8 or + next == 0x2Du8 or + next == 0x2Fu8 or + next == 0x3Cu8 or + next == 0x3Du8 or + next == 0x3Eu8 + then + nns[opos] = 0x5Cu8 + opos += 1 + end + end + nns[opos] = 0x3Fu8 + opos += 1 else if c < 32u8 then nns[opos] = 0x5Cu8 nns[opos + 1] = 0x30u8 diff --git a/lib/core/text/ropes.nit b/lib/core/text/ropes.nit index 0bf97b8..f57b33d 100644 --- a/lib/core/text/ropes.nit +++ b/lib/core/text/ropes.nit @@ -821,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 @@ -928,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 @@ -947,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 diff --git a/lib/core/text/test_abstract_text.nit b/lib/core/text/test_abstract_text.nit new file mode 100644 index 0000000..c67a991 --- /dev/null +++ b/lib/core/text/test_abstract_text.nit @@ -0,0 +1,61 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# This file is free software, which comes along with NIT. This software is +# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. You can modify it is you want, provided this header +# is kept unaltered, and a notification of the changes is added. +# You are allowed to redistribute it and sell it, alone or is a part of +# another product. + +module test_abstract_text is test_suite + +import test_suite +import text +intrude import ropes + +class TestText + super TestSuite + + private var factories: Collection[TextFactory] = [ + new ConcatFactory, + new RopeBufferFactory, + new FlatBufferFactory + : TextFactory] + + fun test_escape_to_c do + for f in factories do + assert f.create("abAB12<>&").escape_to_c == "abAB12<>&" + assert f.create("\n\"'\\").escape_to_c == "\\n\\\"\\'\\\\" + assert f.create("allo???!").escape_to_c == "allo??\\?!" + assert f.create("??=??/??'??(??)").escape_to_c == "?\\?=?\\?/??\\'?\\?(?\\?)" + assert f.create("??!????-").escape_to_c == "?\\?!?\\??\\?-" + end + end +end + +# A factory that creates instances of a particular implementation of `Text` +interface TextFactory + + # Create a `Text` instance from the specified string + fun create(s: String): Text is abstract +end + + +class ConcatFactory + super TextFactory + + redef fun create(s) do return new Concat("", s) +end + +class RopeBufferFactory + super TextFactory + + redef fun create(s) do return new RopeBuffer.from(s) +end + +class FlatBufferFactory + super TextFactory + + redef fun create(s) do return new FlatBuffer.from(s) +end diff --git a/lib/nitcorn/examples/src/htcpcp_server.nit b/lib/nitcorn/examples/src/htcpcp_server.nit index 37fa87e..315d76f 100644 --- a/lib/nitcorn/examples/src/htcpcp_server.nit +++ b/lib/nitcorn/examples/src/htcpcp_server.nit @@ -21,15 +21,19 @@ module htcpcp_server import nitcorn +# Nitcorn Action used to answer requests. class HTCPCPAction super Action + + # Brewing status. var brewing = false - var is_teapot = false + + # Teapot status. + var is_teapot = false redef fun answer(http_request, turi) do var message: String var method = http_request.method - var headers = http_request.header var response: HttpResponse if is_teapot == true then @@ -77,10 +81,13 @@ class HTCPCPAction end end - +# Nitcorn server. class HTCPCServer + + # Port to listen to. var port: Int + # Start listening. fun run do var vh = new VirtualHost("localhost:{port}") vh.routes.add new Route("/", new HTCPCPAction) diff --git a/lib/nitcorn/http_request.nit b/lib/nitcorn/http_request.nit index 9e94493..1833fca 100644 --- a/lib/nitcorn/http_request.nit +++ b/lib/nitcorn/http_request.nit @@ -24,7 +24,7 @@ import core # A request received over HTTP, is build by `HttpRequestParser` class HttpRequest - private init do end + private init is old_style_init do end # HTTP protocol version var http_version: String @@ -114,6 +114,7 @@ class HttpRequestParser # Words of the first line private var first_line = new Array[String] + # Parse the `first_line`, `header_fields` and `body` of `full_request`. fun parse_http_request(full_request: String): nullable HttpRequest do clear_data diff --git a/lib/nitcorn/http_response.nit b/lib/nitcorn/http_response.nit index 3d0fd33..08d2968 100644 --- a/lib/nitcorn/http_response.nit +++ b/lib/nitcorn/http_response.nit @@ -97,7 +97,8 @@ class HttpStatusCodes # All know code and their message var codes = new HashMap[Int, String] - protected init do insert_status_codes + # Init the status `codes` list. + protected init is old_style_init do insert_status_codes # Get the message associated to the status `code`, return `null` in unknown fun [](code: Int): nullable String diff --git a/lib/nitcorn/media_types.nit b/lib/nitcorn/media_types.nit index 37dd9db..305f314 100644 --- a/lib/nitcorn/media_types.nit +++ b/lib/nitcorn/media_types.nit @@ -20,6 +20,8 @@ module media_types # Map of known MIME types class MediaTypes + + # MIME types by extensions. protected var types = new HashMap[String, String] # Get the type/subtype associated to a file extension `ext` @@ -106,4 +108,5 @@ class MediaTypes end end +# MIME types list. fun media_types: MediaTypes do return once new MediaTypes diff --git a/lib/popcorn/Makefile b/lib/popcorn/Makefile index 12db3df..bc59692 100644 --- a/lib/popcorn/Makefile +++ b/lib/popcorn/Makefile @@ -17,8 +17,4 @@ NITUNIT=../../bin/nitunit check: - $(NITUNIT) README.md - $(NITUNIT) pop_routes.nit - $(NITUNIT) pop_handlers.nit - $(NITUNIT) popcorn.nit - cd tests; make check + $(NITUNIT) . diff --git a/lib/popcorn/tests/test_example_angular.nit b/lib/popcorn/examples/angular/test_example_angular.nit similarity index 68% rename from lib/popcorn/tests/test_example_angular.nit rename to lib/popcorn/examples/angular/test_example_angular.nit index 821aa32..b61f1b0 100644 --- a/lib/popcorn/tests/test_example_angular.nit +++ b/lib/popcorn/examples/angular/test_example_angular.nit @@ -14,35 +14,25 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_angular is test_suite + +import pop_tests import example_angular -import base_tests -class TestClient - super ClientThread +class TestExampleAngular + super TestPopcorn - redef fun main do + redef fun client_test do 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("../examples/angular/www/", "index.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 + fun test_example_param_route do + var app = new App + app.use("/counter", new CounterAPI) + app.use("/*", new StaticHandler("../examples/angular/www/", "index.html")) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_angular.res b/lib/popcorn/examples/angular/test_example_angular.sav/test_example_angular.res similarity index 100% rename from lib/popcorn/tests/res/test_example_angular.res rename to lib/popcorn/examples/angular/test_example_angular.sav/test_example_angular.res diff --git a/lib/popcorn/tests/test_example_post.nit b/lib/popcorn/examples/handlers/test_example_post_handler.nit similarity index 75% rename from lib/popcorn/tests/test_example_post.nit rename to lib/popcorn/examples/handlers/test_example_post_handler.nit index bf5330e..45728ef 100644 --- a/lib/popcorn/tests/test_example_post.nit +++ b/lib/popcorn/examples/handlers/test_example_post_handler.nit @@ -14,37 +14,26 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_post_handler is test_suite + +import pop_tests import example_post_handler -import base_tests -class TestClient - super ClientThread +class TestExampleQueryString + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}/ -X POST" system "curl -s {host}:{port}/ --data 'user'" system "curl -s {host}:{port}/ --data 'user=Morriar'" system "curl -s {host}:{port}/ --data 'user=\&order=desc'" system "curl -s {host}:{port}/ --data 'user=Morriar\&order=desc'" - system "curl -s {host}:{port}/" - return null end -end - -var app = new App -app.use("/", new PostHandler) - -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 + fun test_example_glob_route do + var app = new App + app.use("/", new PostHandler) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_post.res b/lib/popcorn/examples/handlers/test_example_post_handler.sav/test_example_post_handler.res similarity index 100% rename from lib/popcorn/tests/res/test_example_post.res rename to lib/popcorn/examples/handlers/test_example_post_handler.sav/test_example_post_handler.res diff --git a/lib/popcorn/tests/test_example_query_string.nit b/lib/popcorn/examples/handlers/test_example_query_string.nit similarity index 73% rename from lib/popcorn/tests/test_example_query_string.nit rename to lib/popcorn/examples/handlers/test_example_query_string.nit index 6c489ba..b584126 100644 --- a/lib/popcorn/tests/test_example_query_string.nit +++ b/lib/popcorn/examples/handlers/test_example_query_string.nit @@ -14,35 +14,25 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_query_string is test_suite + +import pop_tests import example_query_string -import base_tests -class TestClient - super ClientThread +class TestExampleQueryString + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}/" system "curl -s {host}:{port}/?user=Morriar" system "curl -s {host}:{port}/?reload" system "curl -s {host}:{port}/?foo\\&bar=baz" system "curl -s {host}:{port}/?items=10\\&order=asc" - return null end -end - -var app = new App -app.use("/", new QueryStringHandler) - -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 + fun test_example_glob_route do + var app = new App + app.use("/", new QueryStringHandler) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_query_string.res b/lib/popcorn/examples/handlers/test_example_query_string.sav/test_example_query_string.res similarity index 100% rename from lib/popcorn/tests/res/test_example_query_string.res rename to lib/popcorn/examples/handlers/test_example_query_string.sav/test_example_query_string.res diff --git a/lib/popcorn/tests/test_example_hello.nit b/lib/popcorn/examples/hello_world/test_example_hello.nit similarity index 73% rename from lib/popcorn/tests/test_example_hello.nit rename to lib/popcorn/examples/hello_world/test_example_hello.nit index d7327e3..163e624 100644 --- a/lib/popcorn/tests/test_example_hello.nit +++ b/lib/popcorn/examples/hello_world/test_example_hello.nit @@ -14,35 +14,25 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_hello is test_suite + +import pop_tests import example_hello -import base_tests -class TestClient - super ClientThread +class TestExampleHello + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}" system "curl -s {host}:{port}/" system "curl -s {host}:{port}///////////" system "curl -s {host}:{port}/not_found" system "curl -s {host}:{port}/not_found/not_found" - return null end -end - -var app = new App -app.use("/", new HelloHandler) - -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 + fun test_example_hello do + var app = new App + app.use("/", new HelloHandler) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_hello.res b/lib/popcorn/examples/hello_world/test_example_hello.sav/test_example_hello.res similarity index 98% rename from lib/popcorn/tests/res/test_example_hello.res rename to lib/popcorn/examples/hello_world/test_example_hello.sav/test_example_hello.res index 194fb36..f5b3c10 100644 --- a/lib/popcorn/tests/res/test_example_hello.res +++ b/lib/popcorn/examples/hello_world/test_example_hello.sav/test_example_hello.res @@ -26,4 +26,4 @@

404 Not Found

- \ No newline at end of file + diff --git a/lib/popcorn/tests/test_example_advanced_logger.nit b/lib/popcorn/examples/middlewares/test_example_advanced_logger.nit similarity index 65% rename from lib/popcorn/tests/test_example_advanced_logger.nit rename to lib/popcorn/examples/middlewares/test_example_advanced_logger.nit index 29b3fc2..0cd8030 100644 --- a/lib/popcorn/tests/test_example_advanced_logger.nit +++ b/lib/popcorn/examples/middlewares/test_example_advanced_logger.nit @@ -14,34 +14,24 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_advanced_logger is test_suite + +import pop_tests import example_advanced_logger -import base_tests -class TestClient - super ClientThread +class TestExampleAdvancedLogger + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}/" system "curl -s {host}:{port}/about" - return null end -end - -var app = new App -app.use_before("/*", new RequestTimeHandler) -app.use("/", new HelloHandler) -app.use_after("/*", new LogHandler) - -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 + fun test_example_param_route do + var app = new App + app.use_before("/*", new RequestTimeHandler) + app.use("/", new HelloHandler) + app.use_after("/*", new LogHandler) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_advanced_logger.res b/lib/popcorn/examples/middlewares/test_example_advanced_logger.sav/test_example_advanced_logger.res similarity index 100% rename from lib/popcorn/tests/res/test_example_advanced_logger.res rename to lib/popcorn/examples/middlewares/test_example_advanced_logger.sav/test_example_advanced_logger.res diff --git a/lib/popcorn/tests/test_example_html_error_handler.nit b/lib/popcorn/examples/middlewares/test_example_html_error_handler.nit similarity index 70% rename from lib/popcorn/tests/test_example_html_error_handler.nit rename to lib/popcorn/examples/middlewares/test_example_html_error_handler.nit index 95abb7a..b870dd4 100644 --- a/lib/popcorn/tests/test_example_html_error_handler.nit +++ b/lib/popcorn/examples/middlewares/test_example_html_error_handler.nit @@ -14,32 +14,22 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_html_error_handler is test_suite + +import pop_tests import example_html_error_handler -import base_tests -class TestClient - super ClientThread +class TestExampleHtmlErrorHandler + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}/" system "curl -s {host}:{port}/about" - return null end -end - -var app = new App -app.use("/*", new HtmlErrorHandler) - -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 + fun test_example_param_route do + var app = new App + app.use("/*", new HtmlErrorHandler) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_html_error_handler.res b/lib/popcorn/examples/middlewares/test_example_html_error_handler.sav/test_example_html_error_handler.res similarity index 100% rename from lib/popcorn/tests/res/test_example_html_error_handler.res rename to lib/popcorn/examples/middlewares/test_example_html_error_handler.sav/test_example_html_error_handler.res diff --git a/lib/popcorn/tests/test_example_simple_error_handler.nit b/lib/popcorn/examples/middlewares/test_example_simple_error_handler.nit similarity index 68% rename from lib/popcorn/tests/test_example_simple_error_handler.nit rename to lib/popcorn/examples/middlewares/test_example_simple_error_handler.nit index ce671f0..1b1f080 100644 --- a/lib/popcorn/tests/test_example_simple_error_handler.nit +++ b/lib/popcorn/examples/middlewares/test_example_simple_error_handler.nit @@ -14,33 +14,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_simple_error_handler is test_suite + +import pop_tests import example_simple_error_handler -import base_tests -class TestClient - super ClientThread +class TestExampleSimpleErrorHandler + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}/" system "curl -s {host}:{port}/about" - return null end -end - -var app = new App -app.use("/", new HelloHandler) -app.use("/*", new SimpleErrorHandler) - -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 + fun test_example_param_route do + var app = new App + app.use("/", new HelloHandler) + app.use("/*", new SimpleErrorHandler) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_simple_error_handler.res b/lib/popcorn/examples/middlewares/test_example_simple_error_handler.sav/test_example_simple_error_handler.res similarity index 100% rename from lib/popcorn/tests/res/test_example_simple_error_handler.res rename to lib/popcorn/examples/middlewares/test_example_simple_error_handler.sav/test_example_simple_error_handler.res diff --git a/lib/popcorn/tests/test_example_simple_logger.nit b/lib/popcorn/examples/middlewares/test_example_simple_logger.nit similarity index 68% rename from lib/popcorn/tests/test_example_simple_logger.nit rename to lib/popcorn/examples/middlewares/test_example_simple_logger.nit index e8aab41..80a5edf 100644 --- a/lib/popcorn/tests/test_example_simple_logger.nit +++ b/lib/popcorn/examples/middlewares/test_example_simple_logger.nit @@ -14,33 +14,23 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_simple_logger is test_suite + +import pop_tests import example_simple_logger -import base_tests -class TestClient - super ClientThread +class TestExampleSimpleLogger + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}/" system "curl -s {host}:{port}/about" - return null end -end - -var app = new App -app.use_before("/*", new LogHandler) -app.use("/", new HelloHandler) - -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 + fun test_example_param_route do + var app = new App + app.use_before("/*", new LogHandler) + app.use("/", new HelloHandler) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_simple_logger.res b/lib/popcorn/examples/middlewares/test_example_simple_logger.sav/test_example_simple_logger.res similarity index 100% rename from lib/popcorn/tests/res/test_example_simple_logger.res rename to lib/popcorn/examples/middlewares/test_example_simple_logger.sav/test_example_simple_logger.res diff --git a/lib/popcorn/tests/test_example_glob_route.nit b/lib/popcorn/examples/routing/test_example_glob_route.nit similarity index 75% rename from lib/popcorn/tests/test_example_glob_route.nit rename to lib/popcorn/examples/routing/test_example_glob_route.nit index d1ca6c8..c258260 100644 --- a/lib/popcorn/tests/test_example_glob_route.nit +++ b/lib/popcorn/examples/routing/test_example_glob_route.nit @@ -14,38 +14,27 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_glob_route is test_suite + +import pop_tests import example_glob_route -import base_tests -class TestClient - super ClientThread +class TestExampleGlobRoute + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}/user/Morriar/item/10" system "curl -s {host}:{port}/user/Morriar/item/10/" system "curl -s {host}:{port}/user/Morriar/item/10/profile" system "curl -s {host}:{port}/user/Morriar/item/10/profile/settings" - system "curl -s {host}:{port}/" system "curl -s {host}:{port}/not_found" system "curl -s {host}:{port}/not_found/not_found" - return null end -end - -var app = new App -app.use("/user/:user/item/:item/*", new UserItem) - -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 + fun test_example_glob_route do + var app = new App + app.use("/user/:user/item/:item/*", new UserItem) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_glob_route.res b/lib/popcorn/examples/routing/test_example_glob_route.sav/test_example_glob_route.res similarity index 98% rename from lib/popcorn/tests/res/test_example_glob_route.res rename to lib/popcorn/examples/routing/test_example_glob_route.sav/test_example_glob_route.res index 1e91986..ad68ca3 100644 --- a/lib/popcorn/tests/res/test_example_glob_route.res +++ b/lib/popcorn/examples/routing/test_example_glob_route.sav/test_example_glob_route.res @@ -39,4 +39,4 @@ Here the item 10 of the use Morriar.

404 Not Found

- \ No newline at end of file + diff --git a/lib/popcorn/tests/test_example_param_route.nit b/lib/popcorn/examples/routing/test_example_param_route.nit similarity index 73% rename from lib/popcorn/tests/test_example_param_route.nit rename to lib/popcorn/examples/routing/test_example_param_route.nit index 541fde0..52f818a 100644 --- a/lib/popcorn/tests/test_example_param_route.nit +++ b/lib/popcorn/examples/routing/test_example_param_route.nit @@ -14,36 +14,25 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_param_route is test_suite + +import pop_tests import example_param_route -import base_tests -class TestClient - super ClientThread +class TestExampleParamRoute + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}/Morriar" system "curl -s {host}:{port}//" - system "curl -s {host}:{port}/" system "curl -s {host}:{port}/not_found" system "curl -s {host}:{port}/not_found/not_found" - return null end -end - -var app = new App -app.use("/:user", new UserHome) - -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 + fun test_example_param_route do + var app = new App + app.use("/:user", new UserHome) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_param_route.res b/lib/popcorn/examples/routing/test_example_param_route.sav/test_example_param_route.res similarity index 98% rename from lib/popcorn/tests/res/test_example_param_route.res rename to lib/popcorn/examples/routing/test_example_param_route.sav/test_example_param_route.res index f0b898f..87c2dea 100644 --- a/lib/popcorn/tests/res/test_example_param_route.res +++ b/lib/popcorn/examples/routing/test_example_param_route.sav/test_example_param_route.res @@ -35,4 +35,4 @@ Hello not_found

404 Not Found

- \ No newline at end of file + diff --git a/lib/popcorn/tests/test_example_router.nit b/lib/popcorn/examples/routing/test_example_router.nit similarity index 67% rename from lib/popcorn/tests/test_example_router.nit rename to lib/popcorn/examples/routing/test_example_router.nit index f9ab8df..94cc78c 100644 --- a/lib/popcorn/tests/test_example_router.nit +++ b/lib/popcorn/examples/routing/test_example_router.nit @@ -14,45 +14,33 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_router is test_suite + +import pop_tests import example_router -import base_tests -class HelloClient - super ClientThread +class TestExampleRouter + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}" system "curl -s {host}:{port}/" system "curl -s {host}:{port}/user" system "curl -s {host}:{port}/user/" system "curl -s {host}:{port}/user/profile" - system "curl -s {host}:{port}/not_found" system "curl -s {host}:{port}/user/not_found" system "curl -s {host}:{port}/products/not_found" - return null end -end - -var user_router = new Router -user_router.use("/*", new UserLogger) -user_router.use("/", new UserHome) -user_router.use("/profile", new UserProfile) - -var app = new App -app.use("/", new AppHome) -app.use("/user", user_router) -var host = test_host -var port = test_port - -var server = new AppThread(host, port, app) -server.start -0.1.sleep - -var client = new HelloClient(host, port) -client.start -client.join -0.1.sleep - -exit 0 + fun test_example_router do + var user_router = new Router + user_router.use("/*", new UserLogger) + user_router.use("/", new UserHome) + user_router.use("/profile", new UserProfile) + var app = new App + app.use("/", new AppHome) + app.use("/user", user_router) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_router.res b/lib/popcorn/examples/routing/test_example_router.sav/test_example_router.res similarity index 98% rename from lib/popcorn/tests/res/test_example_router.res rename to lib/popcorn/examples/routing/test_example_router.sav/test_example_router.res index fb222c3..d47aeb4 100644 --- a/lib/popcorn/tests/res/test_example_router.res +++ b/lib/popcorn/examples/routing/test_example_router.sav/test_example_router.res @@ -45,4 +45,4 @@ User logged

404 Not Found

- \ No newline at end of file + diff --git a/lib/popcorn/tests/test_example_session.nit b/lib/popcorn/examples/sessions/test_example_session.nit similarity index 71% rename from lib/popcorn/tests/test_example_session.nit rename to lib/popcorn/examples/sessions/test_example_session.nit index b7bedc3..ec6ee71 100644 --- a/lib/popcorn/tests/test_example_session.nit +++ b/lib/popcorn/examples/sessions/test_example_session.nit @@ -14,37 +14,26 @@ # See the License for the specific language governing permissions and # limitations under the License. +module test_example_session is test_suite + +import pop_tests import example_session -import base_tests -class HelloClient - super ClientThread +class TestExampleSession + super TestPopcorn - redef fun main do + redef fun client_test do system "curl -s {host}:{port}/" system "curl -s {host}:{port}/ -X POST" - system "curl -s {host}:{port}/not_found" system "curl -s {host}:{port}/user/not_found" system "curl -s {host}:{port}/products/not_found" - return null end -end - -var app = new App -app.use("/*", new SessionInit) -app.use("/", new AppLogin) - -var host = test_host -var port = test_port -var server = new AppThread(host, port, app) -server.start -0.1.sleep - -var client = new HelloClient(host, port) -client.start -client.join -0.1.sleep - -exit 0 + fun test_example_param_route do + var app = new App + app.use("/*", new SessionInit) + app.use("/", new AppLogin) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_example_session.res b/lib/popcorn/examples/sessions/test_example_session.sav/test_example_session.res similarity index 100% rename from lib/popcorn/tests/res/test_example_session.res rename to lib/popcorn/examples/sessions/test_example_session.sav/test_example_session.res diff --git a/lib/popcorn/tests/test_example_static.nit b/lib/popcorn/examples/static_files/test_example_static.nit similarity index 73% rename from lib/popcorn/tests/test_example_static.nit rename to lib/popcorn/examples/static_files/test_example_static.nit index e23a7be..1b39dcd 100644 --- a/lib/popcorn/tests/test_example_static.nit +++ b/lib/popcorn/examples/static_files/test_example_static.nit @@ -14,39 +14,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -import base_tests +module test_example_static is test_suite + +import pop_tests import example_static -class TestClient - super ClientThread +class TestExampleStatic + super TestPopcorn - redef fun main do + redef fun client_test 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" + end - return null + fun test_example_param_route do + var app = new App + app.use("/", new StaticHandler("../examples/static_files/public/")) + run_test(app) end end - -var app = new App -app.use("/", new StaticHandler("../examples/static_files/public/")) - -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 diff --git a/lib/popcorn/tests/res/test_example_static.res b/lib/popcorn/examples/static_files/test_example_static.sav/test_example_static.res similarity index 100% rename from lib/popcorn/tests/res/test_example_static.res rename to lib/popcorn/examples/static_files/test_example_static.sav/test_example_static.res diff --git a/lib/popcorn/tests/test_example_static_default.nit b/lib/popcorn/examples/static_files/test_example_static_default.nit similarity index 72% rename from lib/popcorn/tests/test_example_static_default.nit rename to lib/popcorn/examples/static_files/test_example_static_default.nit index ced04e2..af2a068 100644 --- a/lib/popcorn/tests/test_example_static_default.nit +++ b/lib/popcorn/examples/static_files/test_example_static_default.nit @@ -14,39 +14,27 @@ # See the License for the specific language governing permissions and # limitations under the License. -import base_tests +module test_example_static_default is test_suite + +import pop_tests import example_static_default -class TestClient - super ClientThread +class TestExampleStaticDefault + super TestPopcorn - redef fun main do + redef fun client_test 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" + end - return null + fun test_example_param_route do + var app = new App + app.use("/", new StaticHandler("../examples/static_files/public/", "default.html")) + run_test(app) 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 diff --git a/lib/popcorn/tests/res/test_example_static_default.res b/lib/popcorn/examples/static_files/test_example_static_default.sav/test_example_static_default.res similarity index 100% rename from lib/popcorn/tests/res/test_example_static_default.res rename to lib/popcorn/examples/static_files/test_example_static_default.sav/test_example_static_default.res diff --git a/lib/popcorn/tests/test_example_static_multiple.nit b/lib/popcorn/examples/static_files/test_example_static_multiple.nit similarity index 67% rename from lib/popcorn/tests/test_example_static_multiple.nit rename to lib/popcorn/examples/static_files/test_example_static_multiple.nit index 5e99ae1..eddfa4c 100644 --- a/lib/popcorn/tests/test_example_static_multiple.nit +++ b/lib/popcorn/examples/static_files/test_example_static_multiple.nit @@ -14,47 +14,34 @@ # See the License for the specific language governing permissions and # limitations under the License. -import base_tests +module test_example_static_multiple is test_suite + +import pop_tests import example_static_multiple -class TestClient - super ClientThread +class TestExampleStaticMultiple + super TestPopcorn - redef fun main do + redef fun client_test 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}/static/css/style.css" system "curl -s {host}:{port}/static/js/app.js" system "curl -s {host}:{port}/static/hello.html" system "curl -s {host}:{port}/static/" - 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" + end - return null + fun test_example_param_route do + var app = new App + app.use("/", new StaticHandler("../examples/static_files/public/")) + app.use("/", new StaticHandler("../examples/static_files/files/")) + app.use("/static", new StaticHandler("../examples/static_files/public/")) + app.use("/static", new StaticHandler("../examples/static_files/files/")) + run_test(app) end end - -var app = new App -app.use("/", new StaticHandler("../examples/static_files/public/")) -app.use("/", new StaticHandler("../examples/static_files/files/")) -app.use("/static", new StaticHandler("../examples/static_files/public/")) -app.use("/static", new StaticHandler("../examples/static_files/files/")) - -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 diff --git a/lib/popcorn/tests/res/test_example_static_multiple.res b/lib/popcorn/examples/static_files/test_example_static_multiple.sav/test_example_static_multiple.res similarity index 100% rename from lib/popcorn/tests/res/test_example_static_multiple.res rename to lib/popcorn/examples/static_files/test_example_static_multiple.sav/test_example_static_multiple.res diff --git a/lib/popcorn/pop_tests.nit b/lib/popcorn/pop_tests.nit new file mode 100644 index 0000000..1deaeab --- /dev/null +++ b/lib/popcorn/pop_tests.nit @@ -0,0 +1,162 @@ +# 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. + +# Popcorn testing services +# +# ## Blackbox testing +# +# Popcorn allows you to test your apps using nitunit blackbox testing. +# +# With blackbox testing you compare the output of your program with a result file. +# +# To get started with blackbox testing, create a nitunit test suite and imports +# the `pop_tests` module. +# +# You then need to build the app that will be tested by nitunit as shown in the +# `test_example_hello` method. +# Calling `run_test` will automatically set the `host` and `port` used for testing. +# +# Redefine the `client_test` method to write your scenario. +# Here we use `curl` to access some URI on the app. +# +# ~~~nitish +# module test_example_hello is test_suite +# +# import pop_tests +# import example_hello +# +# class TestExampleHello +# super TestPopcorn +# +# fun test_example_hello do +# var app = new App +# app.use("/", new HelloHandler) +# run_test(app) +# end +# +# redef fun client_test do +# system "curl -s {host}:{port}" +# system "curl -s {host}:{port}/" +# system "curl -s {host}:{port}///////////" +# system "curl -s {host}:{port}/not_found" +# system "curl -s {host}:{port}/not_found/not_found" +# end +# end +# ~~~ +# +# The blackbox testing needs a reference result file against wich the test output +# will be compared. +# Create your expected result file in `test_example_hello.sav/test_example_hello.res`. +# +# Test your app by running nitunit: +# +# ~~~bash +# nitunit ./example_hello.nit +# ~~~ +# +# See `examples/hello_world` for the complete example. +module pop_tests + +import test_suite +import popcorn +import pthreads + +redef class Sys + + # Use localhost for testing + var test_host = "localhost" + + # Return a new port for each instance + fun test_port: Int do + srand + return 10000+20000.rand + end +end + +# Thread running the App to test. +class AppThread + super Thread + + # Host used by tested App. + var host: String + + # Port used by tested App. + var port: Int + + # App to test. + var app: App + + redef fun main + do + # Hide testing concept to force nitcorn to actually run + "NIT_TESTING".setenv("false") + app.quiet = true + app.listen(host, port) + return null + end +end + +# Thread running the test client. +class ClientThread + super Thread + + # Test suite to execute. + var test_suite: TestPopcorn + + redef fun main do + test_suite.client_test + print "" + return null + end +end + +# TestSuite for Popcorn blackbox testing. +class TestPopcorn + super TestSuite + + # Host used to run App. + var host: String = test_host + + # Port used to run App. + var port: Int = test_port + + # Run the test suite on the App. + fun run_test(app: App) do + var server = new AppThread(host, port, app) + server.start + 0.1.sleep + + var client = new ClientThread(self) + client.start + client.join + 0.1.sleep + + exit 0 + end + + # Redefine this method to implement your test scenario. + fun client_test do end + + # Regex to catch and hide the port from the output to get consistent results + var host_re: Regex = "localhost:\[0-9\]+".to_re + + # Execute a System function. + fun system(cmd: String, title: nullable String) + do + title = title or else cmd + title = title.replace(host_re, "localhost:*****") + print "\n[Client] {title}" + sys.system cmd + end +end diff --git a/lib/popcorn/test_popcorn.nit b/lib/popcorn/test_popcorn.nit new file mode 100644 index 0000000..108eb7a --- /dev/null +++ b/lib/popcorn/test_popcorn.nit @@ -0,0 +1,97 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2016 Alexandre Terrasa +# +# 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_popcorn is test_suite + +import pop_tests +import popcorn + +class TestHandler + super Handler + + var marker: String + + redef fun get(req, res) do res.send marker +end + +class TestPopcornRouter + super TestPopcorn + + redef fun client_test do + system "curl -s {host}:{port}" + system "curl -s {host}:{port}/" + system "curl -s {host}:{port}/user" + system "curl -s {host}:{port}/user/" + system "curl -s {host}:{port}/user/settings" + system "curl -s {host}:{port}/products" + system "curl -s {host}:{port}/products/" + system "curl -s {host}:{port}/products/list" + system "curl -s {host}:{port}/not_found" + system "curl -s {host}:{port}/user/not_found" + system "curl -s {host}:{port}/products/not_found" + end + + fun test_router do + var app = new App + app.use("/", new TestHandler("/")) + app.use("/about", new TestHandler("/about")) + + var router1 = new App + router1.use("/", new TestHandler("/user")) + router1.use("/settings", new TestHandler("/user/settings")) + app.use("/user", router1) + + var router2 = new App + router2.use("/", new TestHandler("/products")) + router2.use("/list", new TestHandler("/products/list")) + app.use("/products", router2) + + run_test(app) + end +end + +class TestPopcornRoutes + super TestPopcorn + + redef fun client_test do + system "curl -s {host}:{port}" + system "curl -s {host}:{port}/" + system "curl -s {host}:{port}/misc" + system "curl -s {host}:{port}/misc/foo" + system "curl -s {host}:{port}/misc/foo/bar" + system "curl -s {host}:{port}/misc/foo/baz" + system "curl -s {host}:{port}/user" + system "curl -s {host}:{port}/user/" + system "curl -s {host}:{port}/user/id" + system "curl -s {host}:{port}/user/id/profile" + system "curl -s {host}:{port}/user/id/misc/foo" + system "curl -s {host}:{port}/user/id/misc/foo/bar" + system "curl -s {host}:{port}/user/id/misc/foo/bar/baz" + system "curl -s {host}:{port}/not_found" + system "curl -s {host}:{port}/user/id/not_found" + end + + fun test_routes do + var app = new App + app.use("/", new TestHandler("/")) + app.use("/user", new TestHandler("/user")) + app.use("/misc/*", new TestHandler("/misc/everything")) + app.use("/user/:id", new TestHandler("/user/id")) + app.use("/user/:id/profile", new TestHandler("/user/id/profile")) + app.use("/user/:id/misc/*", new TestHandler("/user/id/misc/everything")) + run_test(app) + end +end diff --git a/lib/popcorn/tests/res/test_router.res b/lib/popcorn/test_popcorn.sav/test_router.res similarity index 98% rename from lib/popcorn/tests/res/test_router.res rename to lib/popcorn/test_popcorn.sav/test_router.res index 37be3a6..1022411 100644 --- a/lib/popcorn/tests/res/test_router.res +++ b/lib/popcorn/test_popcorn.sav/test_router.res @@ -47,4 +47,4 @@

404 Not Found

- \ No newline at end of file + diff --git a/lib/popcorn/tests/res/test_routes.res b/lib/popcorn/test_popcorn.sav/test_routes.res similarity index 99% rename from lib/popcorn/tests/res/test_routes.res rename to lib/popcorn/test_popcorn.sav/test_routes.res index 0c1abfa..58e47d0 100644 --- a/lib/popcorn/tests/res/test_routes.res +++ b/lib/popcorn/test_popcorn.sav/test_routes.res @@ -46,4 +46,4 @@

404 Not Found

- \ No newline at end of file + diff --git a/lib/popcorn/tests/base_tests.nit b/lib/popcorn/tests/base_tests.nit deleted file mode 100644 index a56a33d..0000000 --- a/lib/popcorn/tests/base_tests.nit +++ /dev/null @@ -1,63 +0,0 @@ -# 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 popcorn -import pthreads - -redef class Sys - var test_host = "localhost" - - # Return a new port for each instance - fun test_port: Int do - srand - return 10000+20000.rand - end -end - -class AppThread - super Thread - - var host: String - var port: Int - var app: App - - redef fun main - do - # Hide testing concept to force nitcorn to actually run - "NIT_TESTING".setenv("false") - app.quiet = true - app.listen(host, port) - return null - end -end - -class ClientThread - super Thread - - var host: String - var port: Int - - redef fun main do return null - - # Regex to catch and hide the port from the output to get consistent results - var host_re: Regex = "localhost:\[0-9\]+".to_re - - fun system(cmd: String, title: nullable String) - do - title = title or else cmd - title = title.replace(host_re, "localhost:*****") - print "\n[Client] {title}" - sys.system cmd - end -end diff --git a/lib/popcorn/tests/test_router.nit b/lib/popcorn/tests/test_router.nit deleted file mode 100644 index 915ce4a..0000000 --- a/lib/popcorn/tests/test_router.nit +++ /dev/null @@ -1,73 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2016 Alexandre Terrasa -# -# 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 - -class TestHandler - super Handler - - var marker: String - - redef fun get(req, res) do res.send marker -end - -class HelloClient - super ClientThread - - redef fun main do - system "curl -s {host}:{port}" - system "curl -s {host}:{port}/" - system "curl -s {host}:{port}/user" - system "curl -s {host}:{port}/user/" - system "curl -s {host}:{port}/user/settings" - system "curl -s {host}:{port}/products" - system "curl -s {host}:{port}/products/" - system "curl -s {host}:{port}/products/list" - - system "curl -s {host}:{port}/not_found" - system "curl -s {host}:{port}/user/not_found" - system "curl -s {host}:{port}/products/not_found" - return null - end -end - -var app = new App -app.use("/", new TestHandler("/")) -app.use("/about", new TestHandler("/about")) - -var router1 = new App -router1.use("/", new TestHandler("/user")) -router1.use("/settings", new TestHandler("/user/settings")) -app.use("/user", router1) - -var router2 = new App -router2.use("/", new TestHandler("/products")) -router2.use("/list", new TestHandler("/products/list")) -app.use("/products", router2) - -var host = test_host -var port = test_port - -var server = new AppThread(host, port, app) -server.start -0.1.sleep - -var client = new HelloClient(host, port) -client.start -client.join -0.1.sleep - -exit 0 diff --git a/lib/popcorn/tests/test_routes.nit b/lib/popcorn/tests/test_routes.nit deleted file mode 100644 index 2db660a..0000000 --- a/lib/popcorn/tests/test_routes.nit +++ /dev/null @@ -1,76 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2016 Alexandre Terrasa -# -# 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 - -class TestHandler - super Handler - - var marker: String - - redef fun get(req, res) do res.send marker -end - -class HelloClient - super ClientThread - - redef fun main do - system "curl -s {host}:{port}" - system "curl -s {host}:{port}/" - - system "curl -s {host}:{port}/misc" - system "curl -s {host}:{port}/misc/foo" - system "curl -s {host}:{port}/misc/foo/bar" - system "curl -s {host}:{port}/misc/foo/baz" - - system "curl -s {host}:{port}/user" - system "curl -s {host}:{port}/user/" - system "curl -s {host}:{port}/user/id" - system "curl -s {host}:{port}/user/id/profile" - system "curl -s {host}:{port}/user/id/misc/foo" - system "curl -s {host}:{port}/user/id/misc/foo/bar" - system "curl -s {host}:{port}/user/id/misc/foo/bar/baz" - - system "curl -s {host}:{port}/not_found" - system "curl -s {host}:{port}/user/id/not_found" - return null - end -end - -var app = new App -app.use("/", new TestHandler("/")) -app.use("/user", new TestHandler("/user")) -app.use("/misc/*", new TestHandler("/misc/everything")) -app.use("/user/:id", new TestHandler("/user/id")) -app.use("/user/:id/profile", new TestHandler("/user/id/profile")) -app.use("/user/:id/misc/*", new TestHandler("/user/id/misc/everything")) - -var host = test_host -var port = test_port - -# First, launch a server in the background -var server = new AppThread(host, port, app) -server.start -0.1.sleep - -# Then, launch a client running test requests -var client = new HelloClient(host, port) -client.start -client.join -0.1.sleep - -# Force quit the server -exit 0 diff --git a/lib/popcorn/tests/tests.sh b/lib/popcorn/tests/tests.sh deleted file mode 100755 index 4406058..0000000 --- a/lib/popcorn/tests/tests.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/bin/bash - -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2016 Alexandre Terrasa . -# -# 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. - -BIN=../bin -OUT=./out -RES=./res - -NITC=../../../bin/nitc - -compile() { - local test="$1" - $NITC $test.nit -o $OUT/$test.bin 1>&2 2> $OUT/$test.cmp_err -} - -test_prog() -{ - local test="$1" - - chmod +x $OUT/$test.bin 2> $OUT/$test.err - $OUT/$test.bin > $OUT/$test.res 2> $OUT/$test.err - - diff $OUT/$test.res $RES/$test.res > $OUT/$test.diff 2> /dev/null -} - -# return -# 0 if the sav not exists -# 1 if the file does match -# 2 if the file does not match -check_result() { - local test="$1" - - if [ -s "$OUT/$test.cmp_err" ]; then - return 0 - elif [ -s "$OUT/$test.err" ]; then - return 1 - elif [ ! -r "$RES/$test.res" ]; then - return 2 - elif [ -s "$OUT/$test.diff" ]; then - return 3 - else - return 4 - fi -} - -echo "Testing..." -echo "" - -rm -rf $OUT 2>/dev/null -mkdir $OUT 2>/dev/null - -all=0 -ok=0 -ko=0 -sk=0 - -for file in `ls test_*.nit`; do - ((all++)) - test="${file%.*}" - echo -n "* $test: " - - compile $test - test_prog $test - check_result $test - - case "$?" in - 0) - echo "compile error (cat $OUT/$test.cmp_err)" - ((ko++)) - ;; - 1) - echo "error (cat $OUT/$test.cmp_err)" - ((ko++)) - ;; - 2) - echo "skip ($test.res not found)" - ((sk++)) - continue;; - 3) - echo "error (diff $OUT/$test.res $RES/$test.res)" - ((ko++)) - ;; - 4) - echo "success" - ((ok++)) - ;; - - esac -done -echo "" -echo "==> success $ok/$all ($ko tests failed, $sk skipped)" - -# return result -test "$ok" == "$all" diff --git a/lib/readline.ini b/lib/readline.ini new file mode 100644 index 0000000..c7e2ee9 --- /dev/null +++ b/lib/readline.ini @@ -0,0 +1,11 @@ +[package] +name=readline +tags=lib +maintainer=Frédéric Vachon +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 index 0000000..783e4e4 --- /dev/null +++ b/lib/readline.nit @@ -0,0 +1,58 @@ +# This file is part of NIT (http://www.nitlanguage.org). +# +# Copyright 2016 Frédéric Vachon +# +# 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 + #include +`} + +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 diff --git a/share/man/nit.md b/share/man/nit.md index 9503883..fcdec6c 100644 --- a/share/man/nit.md +++ b/share/man/nit.md @@ -6,6 +6,8 @@ nit - interprets and debugs Nit programs. nit [*options*] FILE [ARG]... +nit [*options*] - [ARG]... + nit [*options*] -e COMMAND [ARG]... # DESCRIPTION @@ -16,6 +18,9 @@ It takes the main module of a program as the first argument then the options and $ nit examples/hello_world.nit hello world +If `-` is used instead of a module, then the program is read from the standard input. +The whole program is read before its interpretation starts. + The Nit interpreter is usable and valid as a *shebang* interpreted directive. It is however recommended to use with `/usr/bin/env` because the location of the executable is not standardized. diff --git a/share/man/nitc.md b/share/man/nitc.md index 2927a24..a316782 100644 --- a/share/man/nitc.md +++ b/share/man/nitc.md @@ -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. diff --git a/share/man/nitunit.md b/share/man/nitunit.md index e80e80e..2bec157 100644 --- a/share/man/nitunit.md +++ b/share/man/nitunit.md @@ -129,18 +129,25 @@ This flag can be used by libraries and program to prevent (or limit) the executi ## Working with `TestSuites` -TestSuites are Nit files that define a set of TestCases for a particular module. +TestSuites are Nit modules that define a set of TestCases. -The test suite must be called `test_` followed by the name of the module to test. -So for the module `foo.nit` the test suite will be called `test_foo.nit`. +A test suite is a module that uses the annotation `is test_suite`. + +It is common that a test suite focuses on testing a single module. +In this case, the name of the test_suite is often `test_foo.nit` where `foo.nit` is the tested module. The structure of a test suite is the following: ~~~~ # test suite for module `foo` -module test_foo +module test_foo is test_suite + +import test_suite import foo # can be intrude to test private things + class TestFoo + super TestSuite + # test case for `foo::Foo::baz` fun test_baz do var subject = new Foo @@ -153,11 +160,13 @@ Test suite can be executed using the same `nitunit` command: $ nitunit foo.nit -`nitunit` will execute a test for each method named `test_*` in a class named `Test*` -so multiple tests can be executed for a single method: +`nitunit` will execute a test for each method named `test_*` in a class +subclassing `TestSuite` so multiple tests can be executed for a single method: ~~~~ class TestFoo + super TestSuite + fun test_baz_1 do var subject = new Foo assert subject.baz(1, 2) == 3 @@ -173,14 +182,33 @@ end Sometimes, it is easier to validate a `TestCase` by comparing its output with a text file containing the expected result. -For each TestCase `test_bar` of a TestSuite `test_mod.nit`, if the corresponding file `test_mod.sav/test_bar.res` exists, then the output of the test is compared with the file. +For each TestCase `test_bar` of a TestSuite `test_mod.nit`, a corresponding file with the expected output is looked for: + +* "test_mod.sav/test_bar.res". I.e. test-cases grouped by test-suites. + + This is the default and is useful if there is a lot of test-suites and test-cases in a directory + +* "sav/test_bar.res". I.e. all test-cases grouped in a common sub-directory. + + Useful if there is a lot of test-suites OR test-cases in a directory. + +* "test_bar.res" raw in the directory. + + Useful is there is a few test-suites and test-cases in a directory. + +All 3 are exclusive. If more than one exists, the test-case is failed. + +If a corresponding file then the output of the test-case is compared with the file. The `diff(1)` command is used to perform the comparison. The test is failed if non-zero is returned by `diff`. ~~~ module test_mod is test_suite + class TestFoo + super TestSuite + fun test_bar do print "Hello!" end @@ -195,15 +223,19 @@ Hello! If no corresponding `.res` file exists, then the output of the TestCase is ignored. +To helps the management of the expected results, the option `--autosav` can be used to automatically create and update them. + + ## Configuring TestSuites -`TestSuites` also provide methods to configure the test run: +`TestSuite`s also provide methods to configure the test run: `before_test` and `after_test`: methods called before/after each test case. They can be used to factorize repetitive tasks: ~~~~ class TestFoo + super TestSuite var subject: Foo # Mandatory empty init init do end @@ -268,10 +300,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. @@ -286,8 +321,15 @@ Only run test case with name that match pattern. Examples: `TestFoo`, `TestFoo*`, `TestFoo::test_foo`, `TestFoo::test_foo*`, `test_foo`, `test_foo*` -### `-t`, `--target-file` -Specify test suite location. +### `--autosav` +Automatically create/update .res files for black box testing. + +If a black block test fails because a difference between the expected result and the current result then the expected result file is updated (and the test is passed). + +If a test-case of a test-suite passes but that some output is generated, then an expected result file is created. + +It is expected that the created/updated files are checked since the tests are considered passed. +A VCS like `git` is often a good tool to check the creation and modification of those files. ## SUITE GENERATION diff --git a/share/nitweb/directives/contributor-list.html b/share/nitweb/directives/contributor-list.html new file mode 100644 index 0000000..a46cce9 --- /dev/null +++ b/share/nitweb/directives/contributor-list.html @@ -0,0 +1,11 @@ +
+

+ {{listTitle}} +

+
    +
  • + + {{contributor.name}} +
  • +
+
diff --git a/share/nitweb/directives/entity/card.html b/share/nitweb/directives/entity/card.html new file mode 100644 index 0000000..aa9ccb3 --- /dev/null +++ b/share/nitweb/directives/entity/card.html @@ -0,0 +1,11 @@ +
+
+ +
+
+
+ +
+ +
+
diff --git a/share/nitweb/directives/entity/defcard.html b/share/nitweb/directives/entity/defcard.html new file mode 100644 index 0000000..5a024fe --- /dev/null +++ b/share/nitweb/directives/entity/defcard.html @@ -0,0 +1,37 @@ +
+
+
+ + Introduction + + + Redefinition + + + of + + + of + + + in + :: + + + in + +
+ +
+
+
+
+		
+ +
+
diff --git a/share/nitweb/directives/entity/doc.html b/share/nitweb/directives/entity/doc.html new file mode 100644 index 0000000..9d0ffb0 --- /dev/null +++ b/share/nitweb/directives/entity/doc.html @@ -0,0 +1,5 @@ +
+
+
+
+
diff --git a/share/nitweb/directives/entity/linearization.html b/share/nitweb/directives/entity/linearization.html new file mode 100644 index 0000000..afdde43 --- /dev/null +++ b/share/nitweb/directives/entity/linearization.html @@ -0,0 +1,11 @@ +
+

{{listTitle}}

+
+
+ +

+ +

+
+
+
diff --git a/share/nitweb/directives/entity/link.html b/share/nitweb/directives/entity/link.html new file mode 100644 index 0000000..740ea8b --- /dev/null +++ b/share/nitweb/directives/entity/link.html @@ -0,0 +1,3 @@ + + {{mentity.name}} + diff --git a/share/nitweb/directives/entity/list.html b/share/nitweb/directives/entity/list.html new file mode 100644 index 0000000..cc026a4 --- /dev/null +++ b/share/nitweb/directives/entity/list.html @@ -0,0 +1,19 @@ +
+

+ {{listTitle}} + +

+
+ +
+
+ +
+
+ diff --git a/share/nitweb/directives/entity/location.html b/share/nitweb/directives/entity/location.html new file mode 100644 index 0000000..7e817c6 --- /dev/null +++ b/share/nitweb/directives/entity/location.html @@ -0,0 +1,5 @@ + + {{mentity.location.file}} + :{{mentity.location.line_start}} + + diff --git a/share/nitweb/directives/entity/signature.html b/share/nitweb/directives/entity/signature.html new file mode 100644 index 0000000..f3f2dc6 --- /dev/null +++ b/share/nitweb/directives/entity/signature.html @@ -0,0 +1,51 @@ + + + {{modifier}} + + + + + + + [ + + + {{mparam.name}} + : + + + , + + ] + + + + + ( + + + {{mparam.name}} + + : + + + ... + + , + + ) + + + : + + + + + : + + + + : + + + diff --git a/share/nitweb/directives/entity/tag.html b/share/nitweb/directives/entity/tag.html new file mode 100644 index 0000000..7c69f1e --- /dev/null +++ b/share/nitweb/directives/entity/tag.html @@ -0,0 +1,5 @@ + diff --git a/share/nitweb/directives/group-block.html b/share/nitweb/directives/group-block.html new file mode 100644 index 0000000..ad7f00e --- /dev/null +++ b/share/nitweb/directives/group-block.html @@ -0,0 +1,21 @@ +
+
+ +
+
+
+ +
+ {{mentity.mdoc.synopsis}} +
+ +
+
+ +
+
+
diff --git a/share/nitweb/directives/ui-filter-button-vis.html b/share/nitweb/directives/ui-filter-button-vis.html new file mode 100644 index 0000000..8d4f8d4 --- /dev/null +++ b/share/nitweb/directives/ui-filter-button-vis.html @@ -0,0 +1,6 @@ + diff --git a/share/nitweb/directives/ui-filter-field.html b/share/nitweb/directives/ui-filter-field.html new file mode 100644 index 0000000..05c51e4 --- /dev/null +++ b/share/nitweb/directives/ui-filter-field.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/share/nitweb/directives/ui-filter-form.html b/share/nitweb/directives/ui-filter-form.html new file mode 100644 index 0000000..9782af8 --- /dev/null +++ b/share/nitweb/directives/ui-filter-form.html @@ -0,0 +1,6 @@ +
+ +
+ +
+ diff --git a/share/nitweb/directives/ui-filter-group-vis.html b/share/nitweb/directives/ui-filter-group-vis.html new file mode 100644 index 0000000..7fcd475 --- /dev/null +++ b/share/nitweb/directives/ui-filter-group-vis.html @@ -0,0 +1,14 @@ +
+ + + +
diff --git a/share/nitweb/index.html b/share/nitweb/index.html new file mode 100644 index 0000000..1b99a81 --- /dev/null +++ b/share/nitweb/index.html @@ -0,0 +1,61 @@ + + + + + + + + ng-doc + + + + + + + + +
+ + + + + + + + + + + + + + diff --git a/share/nitweb/javascripts/entities.js b/share/nitweb/javascripts/entities.js new file mode 100644 index 0000000..9eefc9f --- /dev/null +++ b/share/nitweb/javascripts/entities.js @@ -0,0 +1,192 @@ +/* + * Copyright 2016 Alexandre Terrasa . + * + * 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) { + this.loadEntityLinearization = function() { + Model.loadEntityLinearization($routeParams.id, + function(data) { + $scope.linearization = data; + }, function(err) { + $scope.error = err; + }); + }; + + this.loadEntityDefs = function() { + Model.loadEntityDefs($routeParams.id, + function(data) { + $scope.defs = data; + }, function(err) { + $scope.error = err; + }); + }; + + this.loadEntityCode = function() { + Model.loadEntityCode($routeParams.id, + function(data) { + $scope.code = data; + }, function(err) { + $scope.code = err; + }); + }; + + 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: '=', + listId: '@', + 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; + }; + } + }; + }) + + .directive('entityLinearization', function() { + return { + restrict: 'E', + scope: { + listEntities: '=', + listTitle: '@', + listFocus: '=' + }, + templateUrl: '/directives/entity/linearization.html' + }; + }) + + .directive('entityDef', ['Model', function(Model, Code) { + return { + restrict: 'E', + scope: { + definition: '=', + focus: '=' + }, + templateUrl: '/directives/entity/defcard.html', + link: function ($scope, element, attrs) { + $scope.codeId = 'code_' + $scope.definition.full_name.replace(/[^a-zA-Z0-9]/g, '_'); + $scope.loadCardCode = function() { + if(!$scope.code) { + Model.loadEntityCode($scope.definition.full_name, + function(data) { + $scope.code = data; + setTimeout(function() { // smooth collapse + $('#' + $scope.codeId).collapse('show') + }, 1); + }, function(err) { + $scope.code = err; + }); + } else { + if($('#' + $scope.codeId).hasClass('in')) { + $('#' + $scope.codeId).collapse('hide'); + } else { + $('#' + $scope.codeId).collapse('show'); + } + } + }; + } + }; + }]) +})(); diff --git a/share/nitweb/javascripts/index.js b/share/nitweb/javascripts/index.js new file mode 100644 index 0000000..534f462 --- /dev/null +++ b/share/nitweb/javascripts/index.js @@ -0,0 +1,92 @@ +/* + * Copyright 2016 Alexandre Terrasa . + * + * 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('index', ['model', 'ngSanitize']) + + .run(['$anchorScroll', function($anchorScroll) { + $anchorScroll.yOffset = 80; + }]) + + .controller('IndexCtrl', ['Catalog', '$routeParams', '$sce', '$scope', '$location', '$anchorScroll', function(Catalog, $routeParams, $sce, $scope, $location, $anchorScroll) { + this.loadHighlighted = function() { + Catalog.loadHightlighted( + function(data) { + $scope.highlighted = data; + }, function(err) { + $scope.error = err; + }); + }; + + this.loadMostRequired = function() { + Catalog.loadMostRequired( + function(data) { + $scope.required = data; + }, function(err) { + $scope.error = err; + }); + }; + + this.loadByTags = function() { + Catalog.loadByTags( + function(data) { + $scope.bytags = data; + }, function(err) { + $scope.error = err; + }); + }; + + this.loadStats = function() { + Catalog.loadStats( + function(data) { + $scope.stats = data; + }, function(err) { + $scope.error = err; + }); + }; + + this.loadContributors = function() { + Catalog.loadContributors( + function(data) { + $scope.contributors = data; + }, function(err) { + $scope.error = err; + }); + }; + + + this.scrollTo = function(hash) { + $anchorScroll(hash); + } + + this.loadHighlighted(); + this.loadStats(); + this.loadContributors(); + }]) + + .directive('contributorList', ['Model', function(Model) { + return { + restrict: 'E', + scope: { + listId: '@', + listTitle: '@', + listContributors: '=' + }, + templateUrl: '/directives/contributor-list.html' + }; + }]) +})(); diff --git a/share/nitweb/javascripts/model.js b/share/nitweb/javascripts/model.js new file mode 100644 index 0000000..2144983 --- /dev/null +++ b/share/nitweb/javascripts/model.js @@ -0,0 +1,91 @@ +/* + * Copyright 2016 Alexandre Terrasa . + * + * 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); + }, + + loadEntityLinearization: function(id, cb, cbErr) { + $http.get(apiUrl + '/linearization/' + id) + .success(cb) + .error(cbErr); + }, + + loadEntityDefs: function(id, cb, cbErr) { + $http.get(apiUrl + '/defs/' + id) + .success(cb) + .error(cbErr); + }, + + loadEntityCode: function(id, cb, cbErr) { + $http.get(apiUrl + '/code/' + id) + .success(cb) + .error(cbErr); + }, + + search: function(q, n, cb, cbErr) { + $http.get(apiUrl + '/search?q=' + q + '&n=' + n) + .success(cb) + .error(cbErr); + } + }; + }]) + + .factory('Catalog', [ '$http', function($http) { + return { + loadHightlighted: function(cb, cbErr) { + $http.get(apiUrl + '/catalog/highlighted') + .success(cb) + .error(cbErr); + }, + + loadMostRequired: function(cb, cbErr) { + $http.get(apiUrl + '/catalog/required') + .success(cb) + .error(cbErr); + }, + + loadByTags: function(cb, cbErr) { + $http.get(apiUrl + '/catalog/bytags') + .success(cb) + .error(cbErr); + }, + + loadStats: function(cb, cbErr) { + $http.get(apiUrl + '/catalog/stats') + .success(cb) + .error(cbErr); + }, + + loadContributors: function(cb, cbErr) { + $http.get(apiUrl + '/catalog/contributors') + .success(cb) + .error(cbErr); + }, + } + }]) +})(); diff --git a/share/nitweb/javascripts/nitweb.js b/share/nitweb/javascripts/nitweb.js new file mode 100644 index 0000000..93b7c3a --- /dev/null +++ b/share/nitweb/javascripts/nitweb.js @@ -0,0 +1,67 @@ +/* + * Copyright 2016 Alexandre Terrasa . + * + * 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', 'index']) + + .config(function($routeProvider, $locationProvider) { + $routeProvider + .when('/', { + templateUrl: 'views/index.html', + controller: 'IndexCtrl', + controllerAs: 'indexCtrl' + }) + .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 index 0000000..cd30d06 --- /dev/null +++ b/share/nitweb/javascripts/ui.js @@ -0,0 +1,168 @@ +/* + * Copyright 2016 Alexandre Terrasa . + * + * 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' ]) + + .controller('SearchCtrl', ['Model', '$routeParams', '$scope', '$window', function(Model, $routeParams, $scope, $window) { + $scope.query = ''; + + $scope.reset = function() { + $scope.activeItem = 0; + $scope.results = []; + } + + $scope.update = function(e) { + if(e.keyCode == 38) { + $scope.selectUp(); + } else if(e.keyCode == 40) { + $scope.selectDown(); + } else if(e.keyCode == 27) { + $scope.selectEscape(); + } else if(e.keyCode == 13) { + $scope.selectEnter(); + } + } + + $scope.selectUp = function() { + if($scope.activeItem > 0) { + $scope.activeItem -= 1; + } + } + + $scope.selectDown = function() { + if($scope.activeItem < $scope.results.length - 1) { + $scope.activeItem += 1; + } + } + + $scope.selectEnter = function() { + $window.location.href = $scope.results[$scope.activeItem].web_url; + $scope.reset(); + } + + $scope.selectEscape = function() { + $scope.reset(); + } + + $scope.search = function() { + if(!$scope.query) { + $scope.reset(); + return; + } + Model.search($scope.query, 10, + function(data) { + $scope.reset(); + $scope.results = data; + }, function(err) { + $scope.reset(); + $scope.error = err; + }); + } + + $scope.reset(); + }]) + + .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 index 0000000..941b5a4 --- /dev/null +++ b/share/nitweb/stylesheets/nitweb.css @@ -0,0 +1,250 @@ +/* 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 */ + +.btn-bar { margin-top: -5px; float: right } +.btn-bar .btn { padding: 5px 10px; } + +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; +} + +/* search */ + +.search-input { + width: 100%; +} + +.search-results { + position: absolute; + right: 0; +} + +.search-results .card.active { + background: #eee; + border-color: #eee; +} + +/* navs */ + +.nav-tabs li { cursor: pointer; } + +.navbar-fixed-top { + background-color: #1E9431; + box-shadow: 0 0 4px rgba(0,0,0,.14),0 4px 8px rgba(0,0,0,.28); +} + +.navbar-fixed-top .form-control:hover, .navbar-fixed-top .form-control:focus { + background: rgba(255, 255, 255, 0.2); +} + +.navbar-fixed-top .form-control { + background: rgba(255, 255, 255, 0.1); + border: none; + color: #fff; + box-shadow: none; +} + +.navbar-fixed-top .form-control-icon { + color: #fff; +} + +.navbar-fixed-top *::-webkit-input-placeholder { + color: #fff; +} +.navbar-fixed-top *:-moz-placeholder { + /* FF 4-18 */ + color: #fff; +} +.navbar-fixed-top *::-moz-placeholder { + /* FF 19+ */ + color: #fff; +} +.navbar-fixed-top *:-ms-input-placeholder { + /* IE 10+ */ + color: #fff; +} + +.navbar-fixed-top .form-group { + margin-top: 8px; + margin-bottom: 0px; +} +/* + * Users + */ + +.avatar { + border-radius: 2px; +} + +/* + * 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 index 0000000..e82732b --- /dev/null +++ b/share/nitweb/stylesheets/nitweb_bootstrap.css @@ -0,0 +1,5769 @@ +/*! normalize.css v3.0.0 | MIT License | git.io/normalize */ +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + font-size: 2em; + margin: 0.67em 0; +} +mark { + background: #ff0; + color: #000; +} +small { + font-size: 80%; +} +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} +sup { + top: -0.5em; +} +sub { + bottom: -0.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + color: inherit; + font: inherit; + margin: 0; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-appearance: textfield; + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + box-sizing: content-box; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} +legend { + border: 0; + padding: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +td, +th { + padding: 0; +} +@media print { + * { + text-shadow: none !important; + color: #000 !important; + background: transparent !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="javascript:"]:after, + a[href^="#"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + select { + background: #fff !important; + } + .navbar { + display: none; + } + .table td, + .table th { + background-color: #fff !important; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 62.5%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: sans-serif; + font-size: 14px; + line-height: 1.428571429; + color: #333333; + background-color: #f2f2f2; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #0d8921; + text-decoration: none; +} +a:hover, +a:focus { + color: #064310; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 0px; +} +.img-thumbnail { + padding: 4px; + line-height: 1.428571429; + background-color: #f2f2f2; + border: 1px solid #dddddd; + border-radius: 0px; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; + display: inline-block; + max-width: 100%; + height: auto; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eeeeee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: sans-serif; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #999999; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 23px; +} +h4, +.h4 { + font-size: 17px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 11px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 200; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +cite { + font-style: normal; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-muted { + color: #999999; +} +.text-primary { + color: #0d8921; +} +a.text-primary:hover { + color: #095a16; +} +.text-success { + color: #5cb85c; +} +a.text-success:hover { + color: #449d44; +} +.text-info { + color: #5bc0de; +} +a.text-info:hover { + color: #31b0d5; +} +.text-warning { + color: #f0ad4e; +} +a.text-warning:hover { + color: #ec971f; +} +.text-danger { + color: #d9534f; +} +a.text-danger:hover { + color: #c9302c; +} +.bg-primary { + color: #fff; + background-color: #0d8921; +} +a.bg-primary:hover { + background-color: #095a16; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eeeeee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-left: 5px; + padding-right: 5px; +} +.list-inline > li:first-child { + padding-left: 0; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.428571429; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + clear: left; + text-align: right; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #999999; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eeeeee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.428571429; + color: #999999; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid #eeeeee; + border-left: 0; + text-align: right; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +blockquote:before, +blockquote:after { + content: ""; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.428571429; +} +code, +kbd, +pre, +samp { + font-family: monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + white-space: nowrap; + border-radius: 0px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #ffffff; + background-color: #333333; + border-radius: 0px; + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25); +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.428571429; + word-break: break-all; + word-wrap: break-word; + color: #333333; + background-color: #f5f5f5; + border: 1px solid #cccccc; + border-radius: 0px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + margin-right: auto; + margin-left: auto; + padding-left: 15px; + padding-right: 15px; +} +.row { + margin-left: -15px; + margin-right: -15px; +} +.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 { + position: relative; + min-height: 1px; + padding-left: 15px; + padding-right: 15px; +} +.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 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666666666666%; +} +.col-xs-10 { + width: 83.33333333333334%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666666666666%; +} +.col-xs-7 { + width: 58.333333333333336%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666666666667%; +} +.col-xs-4 { + width: 33.33333333333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.666666666666664%; +} +.col-xs-1 { + width: 8.333333333333332%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666666666666%; +} +.col-xs-pull-10 { + right: 83.33333333333334%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666666666666%; +} +.col-xs-pull-7 { + right: 58.333333333333336%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666666666667%; +} +.col-xs-pull-4 { + right: 33.33333333333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.666666666666664%; +} +.col-xs-pull-1 { + right: 8.333333333333332%; +} +.col-xs-pull-0 { + right: 0%; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666666666666%; +} +.col-xs-push-10 { + left: 83.33333333333334%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666666666666%; +} +.col-xs-push-7 { + left: 58.333333333333336%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666666666667%; +} +.col-xs-push-4 { + left: 33.33333333333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.666666666666664%; +} +.col-xs-push-1 { + left: 8.333333333333332%; +} +.col-xs-push-0 { + left: 0%; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666666666666%; +} +.col-xs-offset-10 { + margin-left: 83.33333333333334%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666666666666%; +} +.col-xs-offset-7 { + margin-left: 58.333333333333336%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666666666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.666666666666664%; +} +.col-xs-offset-1 { + margin-left: 8.333333333333332%; +} +.col-xs-offset-0 { + margin-left: 0%; +} +@media (min-width: 768px) { + .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 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666666666666%; + } + .col-sm-10 { + width: 83.33333333333334%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666666666666%; + } + .col-sm-7 { + width: 58.333333333333336%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666666666667%; + } + .col-sm-4 { + width: 33.33333333333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.666666666666664%; + } + .col-sm-1 { + width: 8.333333333333332%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666666666666%; + } + .col-sm-pull-10 { + right: 83.33333333333334%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666666666666%; + } + .col-sm-pull-7 { + right: 58.333333333333336%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666666666667%; + } + .col-sm-pull-4 { + right: 33.33333333333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.666666666666664%; + } + .col-sm-pull-1 { + right: 8.333333333333332%; + } + .col-sm-pull-0 { + right: 0%; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666666666666%; + } + .col-sm-push-10 { + left: 83.33333333333334%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666666666666%; + } + .col-sm-push-7 { + left: 58.333333333333336%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666666666667%; + } + .col-sm-push-4 { + left: 33.33333333333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.666666666666664%; + } + .col-sm-push-1 { + left: 8.333333333333332%; + } + .col-sm-push-0 { + left: 0%; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666666666666%; + } + .col-sm-offset-10 { + margin-left: 83.33333333333334%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666666666666%; + } + .col-sm-offset-7 { + margin-left: 58.333333333333336%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666666666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.666666666666664%; + } + .col-sm-offset-1 { + margin-left: 8.333333333333332%; + } + .col-sm-offset-0 { + margin-left: 0%; + } +} +@media (min-width: 992px) { + .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 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666666666666%; + } + .col-md-10 { + width: 83.33333333333334%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666666666666%; + } + .col-md-7 { + width: 58.333333333333336%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666666666667%; + } + .col-md-4 { + width: 33.33333333333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.666666666666664%; + } + .col-md-1 { + width: 8.333333333333332%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666666666666%; + } + .col-md-pull-10 { + right: 83.33333333333334%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666666666666%; + } + .col-md-pull-7 { + right: 58.333333333333336%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666666666667%; + } + .col-md-pull-4 { + right: 33.33333333333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.666666666666664%; + } + .col-md-pull-1 { + right: 8.333333333333332%; + } + .col-md-pull-0 { + right: 0%; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666666666666%; + } + .col-md-push-10 { + left: 83.33333333333334%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666666666666%; + } + .col-md-push-7 { + left: 58.333333333333336%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666666666667%; + } + .col-md-push-4 { + left: 33.33333333333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.666666666666664%; + } + .col-md-push-1 { + left: 8.333333333333332%; + } + .col-md-push-0 { + left: 0%; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666666666666%; + } + .col-md-offset-10 { + margin-left: 83.33333333333334%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666666666666%; + } + .col-md-offset-7 { + margin-left: 58.333333333333336%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666666666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.666666666666664%; + } + .col-md-offset-1 { + margin-left: 8.333333333333332%; + } + .col-md-offset-0 { + margin-left: 0%; + } +} +@media (min-width: 1200px) { + .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 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666666666666%; + } + .col-lg-10 { + width: 83.33333333333334%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666666666666%; + } + .col-lg-7 { + width: 58.333333333333336%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666666666667%; + } + .col-lg-4 { + width: 33.33333333333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.666666666666664%; + } + .col-lg-1 { + width: 8.333333333333332%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666666666666%; + } + .col-lg-pull-10 { + right: 83.33333333333334%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666666666666%; + } + .col-lg-pull-7 { + right: 58.333333333333336%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666666666667%; + } + .col-lg-pull-4 { + right: 33.33333333333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.666666666666664%; + } + .col-lg-pull-1 { + right: 8.333333333333332%; + } + .col-lg-pull-0 { + right: 0%; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666666666666%; + } + .col-lg-push-10 { + left: 83.33333333333334%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666666666666%; + } + .col-lg-push-7 { + left: 58.333333333333336%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666666666667%; + } + .col-lg-push-4 { + left: 33.33333333333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.666666666666664%; + } + .col-lg-push-1 { + left: 8.333333333333332%; + } + .col-lg-push-0 { + left: 0%; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666666666666%; + } + .col-lg-offset-10 { + margin-left: 83.33333333333334%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666666666666%; + } + .col-lg-offset-7 { + margin-left: 58.333333333333336%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666666666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.666666666666664%; + } + .col-lg-offset-1 { + margin-left: 8.333333333333332%; + } + .col-lg-offset-0 { + margin-left: 0%; + } +} +table { + max-width: 100%; + background-color: transparent; +} +th { + text-align: left; +} +.table { + width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.428571429; + vertical-align: top; + border-top: 1px solid #dddddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #dddddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #dddddd; +} +.table .table { + background-color: #f2f2f2; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #dddddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #dddddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-child(odd) > td, +.table-striped > tbody > tr:nth-child(odd) > th { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover > td, +.table-hover > tbody > tr:hover > th { + background-color: #dbdbdb; +} +table col[class*="col-"] { + position: static; + float: none; + display: table-column; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + float: none; + display: table-cell; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #dbdbdb; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr.active:hover > th { + background-color: #cecece; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +@media (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + overflow-x: scroll; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #dddddd; + -webkit-overflow-scrolling: touch; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + padding: 0; + margin: 0; + border: 0; + min-width: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + /* IE8-9 */ + + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.428571429; + color: #555555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.428571429; + color: #555555; + background-color: #ffffff; + background-image: none; + border: 1px solid #cccccc; + border-radius: 0px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6); +} +.form-control::-moz-placeholder { + color: #999999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999999; +} +.form-control::-webkit-input-placeholder { + color: #999999; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eeeeee; + opacity: 1; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +input[type="date"] { + line-height: 34px; +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + display: block; + min-height: 20px; + margin-top: 10px; + margin-bottom: 10px; + padding-left: 20px; +} +.radio label, +.checkbox label { + display: inline; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + float: left; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + vertical-align: middle; + font-weight: normal; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +.radio[disabled], +.radio-inline[disabled], +.checkbox[disabled], +.checkbox-inline[disabled], +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"], +fieldset[disabled] .radio, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.input-sm { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 0px; +} +select.input-sm { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +select[multiple].input-sm { + height: auto; +} +.input-lg { + height: 45px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 0px; +} +select.input-lg { + height: 45px; + line-height: 45px; +} +textarea.input-lg, +select[multiple].input-lg { + height: auto; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.has-feedback .form-control-feedback { + position: absolute; + top: 25px; + right: 0; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline { + color: #5cb85c; +} +.has-success .form-control { + border-color: #5cb85c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-success .form-control:focus { + border-color: #449d44; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #a3d7a3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #a3d7a3; +} +.has-success .input-group-addon { + color: #5cb85c; + border-color: #5cb85c; + background-color: #dff0d8; +} +.has-success .form-control-feedback { + color: #5cb85c; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline { + color: #f0ad4e; +} +.has-warning .form-control { + border-color: #f0ad4e; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-warning .form-control:focus { + border-color: #ec971f; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #f8d9ac; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #f8d9ac; +} +.has-warning .input-group-addon { + color: #f0ad4e; + border-color: #f0ad4e; + background-color: #fcf8e3; +} +.has-warning .form-control-feedback { + color: #f0ad4e; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline { + color: #d9534f; +} +.has-error .form-control { + border-color: #d9534f; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); +} +.has-error .form-control:focus { + border-color: #c9302c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #eba5a3; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #eba5a3; +} +.has-error .input-group-addon { + color: #d9534f; + border-color: #d9534f; + background-color: #f2dede; +} +.has-error .form-control-feedback { + color: #d9534f; +} +.form-control-static { + margin-bottom: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + padding-left: 0; + vertical-align: middle; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .control-label, +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + margin-top: 0; + margin-bottom: 0; + padding-top: 7px; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-left: -15px; + margin-right: -15px; +} +.form-horizontal .form-control-static { + padding-top: 7px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + top: 0; + right: 15px; +} +.btn { + display: inline-block; + margin-bottom: 0; + font-weight: normal; + text-align: center; + vertical-align: middle; + cursor: pointer; + background-image: none; + border: 1px solid transparent; + white-space: nowrap; + padding: 6px 12px; + font-size: 14px; + line-height: 1.428571429; + border-radius: 0px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} +.btn:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus { + color: #333333; + text-decoration: none; +} +.btn:active, +.btn.active { + outline: 0; + background-image: none; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + cursor: not-allowed; + pointer-events: none; + opacity: 0.65; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-default { + color: #333333; + background-color: #ffffff; + border-color: #cccccc; +} +.btn-default:hover, +.btn-default:focus, +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + color: #333333; + background-color: #ebebeb; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #ffffff; + border-color: #cccccc; +} +.btn-default .badge { + color: #ffffff; + background-color: #333333; +} +.btn-primary { + color: #ffffff; + background-color: #0d8921; + border-color: #0b721b; +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + color: #ffffff; + background-color: #096418; + border-color: #053a0e; +} +.btn-primary:active, +.btn-primary.active, +.open .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #0d8921; + border-color: #0b721b; +} +.btn-primary .badge { + color: #0d8921; + background-color: #ffffff; +} +.btn-success { + color: #ffffff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + color: #ffffff; + background-color: #47a447; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #ffffff; +} +.btn-info { + color: #ffffff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + color: #ffffff; + background-color: #39b3d7; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #ffffff; +} +.btn-warning { + color: #ffffff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + color: #ffffff; + background-color: #ed9c28; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #ffffff; +} +.btn-danger { + color: #ffffff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + color: #ffffff; + background-color: #d2322d; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #ffffff; +} +.btn-link { + color: #0d8921; + font-weight: normal; + cursor: pointer; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #064310; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #999999; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 0px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 0px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 0px; +} +.btn-block { + display: block; + width: 100%; + padding-left: 0; + padding-right: 0; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity 0.15s linear; + transition: opacity 0.15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; +} +.collapse.in { + display: block; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition: height 0.35s ease; + transition: height 0.35s ease; +} +@font-face { + font-family: 'Glyphicons Halflings'; + src: url('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/fonts/glyphicons-halflings-regular.eot'); + 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'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\2a"; +} +.glyphicon-plus:before { + content: "\2b"; +} +.glyphicon-euro:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + list-style: none; + font-size: 14px; + background-color: #ffffff; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-clip: padding-box; +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.428571429; + color: #333333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + text-decoration: none; + color: #ffffff; + background-color: #0d8921; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #ffffff; + text-decoration: none; + outline: 0; + background-color: #0d8921; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #999999; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); + cursor: not-allowed; +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + left: auto; + right: 0; +} +.dropdown-menu-left { + left: 0; + right: auto; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.428571429; + color: #999999; +} +.dropdown-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + border-top: 0; + border-bottom: 4px solid; + content: ""; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + left: auto; + right: 0; + } + .navbar-right .dropdown-menu-left { + left: 0; + right: auto; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus { + outline: none; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child > .btn:last-child, +.btn-group > .btn-group:first-child > .dropdown-toggle { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.btn-group > .btn-group:last-child > .btn:first-child { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 0px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-bottom-left-radius: 0px; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + float: none; + display: table-cell; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +[data-toggle="buttons"] > .btn > input[type="radio"], +[data-toggle="buttons"] > .btn > input[type="checkbox"] { + display: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-left: 0; + padding-right: 0; +} +.input-group .form-control { + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 45px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 0px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 45px; + line-height: 45px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 0px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555555; + text-align: center; + background-color: #eeeeee; + border: 1px solid #cccccc; + border-radius: 0px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 0px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 0px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-top-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-bottom-left-radius: 0; + border-top-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + margin-left: -1px; +} +.nav { + margin-bottom: 0; + padding-left: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #dddddd; +} +.nav > li.disabled > a { + color: #999999; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #999999; + text-decoration: none; + background-color: transparent; + cursor: not-allowed; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #dddddd; + border-color: #0d8921; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #dddddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.428571429; + border: 1px solid transparent; + border-radius: 0px 0px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eeeeee #eeeeee #dddddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555555; + background-color: #f2f2f2; + border: 1px solid #dddddd; + border-bottom-color: transparent; + cursor: default; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 0px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #dddddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #dddddd; + border-radius: 0px 0px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #f2f2f2; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 0px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #ffffff; + background-color: #0d8921; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + text-align: center; + margin-bottom: 5px; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 0px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #dddddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #dddddd; + border-radius: 0px 0px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #f2f2f2; + } +} +.tab-content > .tab-pane { + display: none; +} +.tab-content > .active { + display: block; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 0px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + max-height: 340px; + overflow-x: visible; + padding-right: 15px; + padding-left: 15px; + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); + -webkit-overflow-scrolling: touch; +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-left: 0; + padding-right: 0; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; + height: 50px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + margin-right: 15px; + padding: 9px 10px; + margin-top: 8px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 0px; +} +.navbar-toggle:focus { + outline: none; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } + .navbar-nav.navbar-right:last-child { + margin-right: -15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + } +} +.navbar-form { + margin-left: -15px; + margin-right: -15px; + padding: 10px 15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); + margin-top: 8px; + margin-bottom: 8px; +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + padding-left: 0; + vertical-align: middle; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-form.navbar-right:last-child { + margin-right: -15px; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-right-radius: 0; + border-top-left-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-left: 15px; + margin-right: 15px; + } + .navbar-text.navbar-right:last-child { + margin-right: 0; + } +} +.navbar-default { + background-color: #0d8921; + border-color: none; +} +.navbar-default .navbar-brand { + color: #ffffff; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #e6e6e6; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #ffffff; +} +.navbar-default .navbar-nav > li > a { + color: #ffffff; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #dddddd; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #0a6b1a; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #cccccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #dddddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #dddddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #ffffff; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: none; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + background-color: #0a6b1a; + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #ffffff; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #dddddd; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #0a6b1a; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #cccccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #ffffff; +} +.navbar-default .navbar-link:hover { + color: #dddddd; +} +.navbar-inverse { + background-color: #222222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #999999; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #ffffff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #999999; +} +.navbar-inverse .navbar-nav > li > a { + color: #999999; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #ffffff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #ffffff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #ffffff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + background-color: #080808; + color: #ffffff; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #999999; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #ffffff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #ffffff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #999999; +} +.navbar-inverse .navbar-link:hover { + color: #ffffff; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 0px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + content: "\00a0"; + padding: 0 5px; + color: #cccccc; +} +.breadcrumb > .active { + color: #999999; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 0px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + line-height: 1.428571429; + text-decoration: none; + color: #0d8921; + background-color: #ffffff; + border: 1px solid #dddddd; + margin-left: -1px; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-bottom-left-radius: 0px; + border-top-left-radius: 0px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-bottom-right-radius: 0px; + border-top-right-radius: 0px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: #064310; + background-color: #eeeeee; + border-color: #dddddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #ffffff; + background-color: #0d8921; + border-color: #0d8921; + cursor: default; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #999999; + background-color: #ffffff; + border-color: #dddddd; + cursor: not-allowed; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-bottom-left-radius: 0px; + border-top-left-radius: 0px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-bottom-right-radius: 0px; + border-top-right-radius: 0px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-bottom-left-radius: 0px; + border-top-left-radius: 0px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-bottom-right-radius: 0px; + border-top-right-radius: 0px; +} +.pager { + padding-left: 0; + margin: 20px 0; + list-style: none; + text-align: center; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #ffffff; + border: 1px solid #dddddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eeeeee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #999999; + background-color: #ffffff; + cursor: not-allowed; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #ffffff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +.label[href]:hover, +.label[href]:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #999999; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #808080; +} +.label-primary { + background-color: #0d8921; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #095a16; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + color: : #fff; + line-height: 1; + vertical-align: baseline; + white-space: nowrap; + text-align: center; + background-color: #999999; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #ffffff; + text-decoration: none; + cursor: pointer; +} +a.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #0d8921; + background-color: #ffffff; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding: 30px; + margin-bottom: 30px; + color: inherit; + background-color: #ffffff; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.container .jumbotron { + border-radius: 0px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding-top: 48px; + padding-bottom: 48px; + } + .container .jumbotron { + padding-left: 60px; + padding-right: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.428571429; + background-color: #f2f2f2; + border: 1px solid #dddddd; + border-radius: 0px; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-left: auto; + margin-right: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #0d8921; +} +.thumbnail .caption { + padding: 9px; + color: #333333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 0px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable { + padding-right: 35px; +} +.alert-dismissable .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + background-color: #dff0d8; + border-color: #d6e9c6; + color: #5cb85c; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #449d44; +} +.alert-info { + background-color: #d9edf7; + border-color: #bce8f1; + color: #5bc0de; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #31b0d5; +} +.alert-warning { + background-color: #fcf8e3; + border-color: #fbeed5; + color: #f0ad4e; +} +.alert-warning hr { + border-top-color: #f8e5be; +} +.alert-warning .alert-link { + color: #ec971f; +} +.alert-danger { + background-color: #f2dede; + border-color: #eed3d7; + color: #d9534f; +} +.alert-danger hr { + border-top-color: #e6c1c7; +} +.alert-danger .alert-link { + color: #c9302c; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + overflow: hidden; + height: 20px; + margin-bottom: 20px; + background-color: #f5f5f5; + border-radius: 0px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} +.progress-bar { + float: left; + width: 0%; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #ffffff; + text-align: center; + background-color: #0d8921; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); + -webkit-transition: width 0.6s ease; + transition: width 0.6s ease; +} +.progress-striped .progress-bar { + 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); + 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); + background-size: 40px 40px; +} +.progress.active .progress-bar { + -webkit-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + 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); + 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); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + 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); + 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); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + 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); + 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); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + 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); + 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); +} +.media, +.media-body { + overflow: hidden; + zoom: 1; +} +.media, +.media .media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media-object { + display: block; +} +.media-heading { + margin: 0 0 5px; +} +.media > .pull-left { + margin-right: 10px; +} +.media > .pull-right { + margin-left: 10px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + margin-bottom: 20px; + padding-left: 0; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #ffffff; + border: 1px solid #dddddd; +} +.list-group-item:first-child { + border-top-right-radius: 0px; + border-top-left-radius: 0px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 0px; + border-bottom-left-radius: 0px; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +a.list-group-item { + color: #555555; +} +a.list-group-item .list-group-item-heading { + color: #333333; +} +a.list-group-item:hover, +a.list-group-item:focus { + text-decoration: none; + background-color: #f5f5f5; +} +a.list-group-item.active, +a.list-group-item.active:hover, +a.list-group-item.active:focus { + z-index: 2; + color: #ffffff; + background-color: #0d8921; + border-color: #0d8921; +} +a.list-group-item.active .list-group-item-heading, +a.list-group-item.active:hover .list-group-item-heading, +a.list-group-item.active:focus .list-group-item-heading { + color: inherit; +} +a.list-group-item.active .list-group-item-text, +a.list-group-item.active:hover .list-group-item-text, +a.list-group-item.active:focus .list-group-item-text { + color: #cccccc; +} +.list-group-item-success { + color: #5cb85c; + background-color: #dff0d8; +} +a.list-group-item-success { + color: #5cb85c; +} +a.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +a.list-group-item-success:focus { + color: #5cb85c; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +a.list-group-item-success.active:hover, +a.list-group-item-success.active:focus { + color: #fff; + background-color: #5cb85c; + border-color: #5cb85c; +} +.list-group-item-info { + color: #5bc0de; + background-color: #d9edf7; +} +a.list-group-item-info { + color: #5bc0de; +} +a.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +a.list-group-item-info:focus { + color: #5bc0de; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +a.list-group-item-info.active:hover, +a.list-group-item-info.active:focus { + color: #fff; + background-color: #5bc0de; + border-color: #5bc0de; +} +.list-group-item-warning { + color: #f0ad4e; + background-color: #fcf8e3; +} +a.list-group-item-warning { + color: #f0ad4e; +} +a.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +a.list-group-item-warning:focus { + color: #f0ad4e; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus { + color: #fff; + background-color: #f0ad4e; + border-color: #f0ad4e; +} +.list-group-item-danger { + color: #d9534f; + background-color: #f2dede; +} +a.list-group-item-danger { + color: #d9534f; +} +a.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +a.list-group-item-danger:focus { + color: #d9534f; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus { + color: #fff; + background-color: #d9534f; + border-color: #d9534f; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #ffffff; + border: 1px solid transparent; + border-radius: 0px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-right-radius: -1px; + border-top-left-radius: -1px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #dddddd; + border-bottom-right-radius: -1px; + border-bottom-left-radius: -1px; +} +.panel > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group .list-group-item:first-child { + border-top: 0; +} +.panel > .list-group .list-group-item:last-child { + border-bottom: 0; +} +.panel > .list-group:first-child .list-group-item:first-child { + border-top-right-radius: -1px; + border-top-left-radius: -1px; +} +.panel > .list-group:last-child .list-group-item:last-child { + border-bottom-right-radius: -1px; + border-bottom-left-radius: -1px; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table { + margin-bottom: 0; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: -1px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: -1px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: -1px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: -1px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive { + border-top: 1px solid #dddddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + border: 0; + margin-bottom: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 0px; + overflow: hidden; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse .panel-body { + border-top: 1px solid #dddddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #dddddd; +} +.panel-default { + border-color: #dddddd; +} +.panel-default > .panel-heading { + color: #333333; + background-color: #f5f5f5; + border-color: #dddddd; +} +.panel-default > .panel-heading + .panel-collapse .panel-body { + border-top-color: #dddddd; +} +.panel-default > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #dddddd; +} +.panel-primary { + border-color: #0d8921; +} +.panel-primary > .panel-heading { + color: #ffffff; + background-color: #0d8921; + border-color: #0d8921; +} +.panel-primary > .panel-heading + .panel-collapse .panel-body { + border-top-color: #0d8921; +} +.panel-primary > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #0d8921; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #5cb85c; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #5bc0de; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #fbeed5; +} +.panel-warning > .panel-heading { + color: #f0ad4e; + background-color: #fcf8e3; + border-color: #fbeed5; +} +.panel-warning > .panel-heading + .panel-collapse .panel-body { + border-top-color: #fbeed5; +} +.panel-warning > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #fbeed5; +} +.panel-danger { + border-color: #eed3d7; +} +.panel-danger > .panel-heading { + color: #d9534f; + background-color: #f2dede; + border-color: #eed3d7; +} +.panel-danger > .panel-heading + .panel-collapse .panel-body { + border-top-color: #eed3d7; +} +.panel-danger > .panel-footer + .panel-collapse .panel-body { + border-bottom-color: #eed3d7; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 0px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, 0.15); +} +.well-lg { + padding: 24px; + border-radius: 0px; +} +.well-sm { + padding: 9px; + border-radius: 0px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000000; + text-shadow: 0 1px 0 #ffffff; + opacity: 0.2; + filter: alpha(opacity=20); +} +.close:hover, +.close:focus { + color: #000000; + text-decoration: none; + cursor: pointer; + opacity: 0.5; + filter: alpha(opacity=50); +} +button.close { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; +} +.modal-open { + overflow: hidden; +} +.modal { + display: none; + overflow: auto; + overflow-y: scroll; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + transform: translate(0, -25%); + -webkit-transition: -webkit-transform 0.3s ease-out; + -moz-transition: -moz-transform 0.3s ease-out; + -o-transition: -o-transform 0.3s ease-out; + transition: transform 0.3s ease-out; +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #ffffff; + border: 1px solid #999999; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0px; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); + background-clip: padding-box; + outline: none; +} +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000000; +} +.modal-backdrop.fade { + opacity: 0; + filter: alpha(opacity=0); +} +.modal-backdrop.in { + opacity: 0.5; + filter: alpha(opacity=50); +} +.modal-header { + padding: 15px; + border-bottom: 1px solid #e5e5e5; + min-height: 16.428571429px; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.428571429; +} +.modal-body { + position: relative; + padding: 20px; +} +.modal-footer { + margin-top: 15px; + padding: 19px 20px 20px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-left: 5px; + margin-bottom: 0; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1030; + display: block; + visibility: visible; + font-size: 12px; + line-height: 1.4; + opacity: 0; + filter: alpha(opacity=0); +} +.tooltip.in { + opacity: 0.9; + filter: alpha(opacity=90); +} +.tooltip.top { + margin-top: -3px; + padding: 5px 0; +} +.tooltip.right { + margin-left: 3px; + padding: 0 5px; +} +.tooltip.bottom { + margin-top: 3px; + padding: 5px 0; +} +.tooltip.left { + margin-left: -3px; + padding: 0 5px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #ffffff; + text-align: center; + text-decoration: none; + background-color: #000000; + border-radius: 0px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.top-left .tooltip-arrow { + bottom: 0; + left: 5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + right: 5px; + border-width: 5px 5px 0; + border-top-color: #000000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + left: 5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + right: 5px; + border-width: 0 5px 5px; + border-bottom-color: #000000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1010; + display: none; + max-width: 276px; + padding: 1px; + text-align: left; + background-color: #ffffff; + background-clip: padding-box; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); + white-space: normal; +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + margin: 0; + padding: 8px 14px; + font-size: 14px; + font-weight: normal; + line-height: 18px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + border-width: 10px; + content: ""; +} +.popover.top > .arrow { + left: 50%; + margin-left: -11px; + border-bottom-width: 0; + border-top-color: #999999; + border-top-color: rgba(0, 0, 0, 0.25); + bottom: -11px; +} +.popover.top > .arrow:after { + content: " "; + bottom: 1px; + margin-left: -10px; + border-bottom-width: 0; + border-top-color: #ffffff; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-left-width: 0; + border-right-color: #999999; + border-right-color: rgba(0, 0, 0, 0.25); +} +.popover.right > .arrow:after { + content: " "; + left: 1px; + bottom: -10px; + border-left-width: 0; + border-right-color: #ffffff; +} +.popover.bottom > .arrow { + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999999; + border-bottom-color: rgba(0, 0, 0, 0.25); + top: -11px; +} +.popover.bottom > .arrow:after { + content: " "; + top: 1px; + margin-left: -10px; + border-top-width: 0; + border-bottom-color: #ffffff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999999; + border-left-color: rgba(0, 0, 0, 0.25); +} +.popover.left > .arrow:after { + content: " "; + right: 1px; + border-right-width: 0; + border-left-color: #ffffff; + bottom: -10px; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + overflow: hidden; + width: 100%; +} +.carousel-inner > .item { + display: none; + position: relative; + -webkit-transition: 0.6s ease-in-out left; + transition: 0.6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: 15%; + opacity: 0.5; + filter: alpha(opacity=50); + font-size: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0%), color-stop(rgba(0, 0, 0, 0.0001) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); +} +.carousel-control.right { + left: auto; + right: 0; + background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0%), color-stop(rgba(0, 0, 0, 0.5) 100%)); + background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); +} +.carousel-control:hover, +.carousel-control:focus { + outline: none; + color: #ffffff; + text-decoration: none; + opacity: 0.9; + filter: alpha(opacity=90); +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + margin-left: -10px; + font-family: serif; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + margin-left: -30%; + padding-left: 0; + list-style: none; + text-align: center; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + border: 1px solid #ffffff; + border-radius: 10px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); +} +.carousel-indicators .active { + margin: 0; + width: 12px; + height: 12px; + background-color: #ffffff; +} +.carousel-caption { + position: absolute; + left: 15%; + right: 15%; + bottom: 20px; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #ffffff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicons-chevron-left, + .carousel-control .glyphicons-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + margin-left: -15px; + font-size: 30px; + } + .carousel-caption { + left: 20%; + right: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-footer:before, +.modal-footer:after { + content: " "; + display: table; +} +.clearfix:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} + diff --git a/share/nitweb/views/class.html b/share/nitweb/views/class.html new file mode 100644 index 0000000..4950546 --- /dev/null +++ b/share/nitweb/views/class.html @@ -0,0 +1,56 @@ +
+ + + + +
+
+ + + + + + + + + +
+
+ +
+
+ +
+
+
diff --git a/share/nitweb/views/classdef.html b/share/nitweb/views/classdef.html new file mode 100644 index 0000000..6c5354b --- /dev/null +++ b/share/nitweb/views/classdef.html @@ -0,0 +1,43 @@ +
+ + + + +
+
+ +
+
+
+
+
+					
+				
+
+
+
+
diff --git a/share/nitweb/views/group.html b/share/nitweb/views/group.html new file mode 100644 index 0000000..ca19974 --- /dev/null +++ b/share/nitweb/views/group.html @@ -0,0 +1,30 @@ +
+ + + + +
+
+ + + + + + + +
+
+
+ diff --git a/share/nitweb/views/index.html b/share/nitweb/views/index.html new file mode 100644 index 0000000..b7bee8e --- /dev/null +++ b/share/nitweb/views/index.html @@ -0,0 +1,70 @@ +
+ + + + + + + +
+
{{value}} {{key}}
+
+ +
+
+
+
+ +
+
+ +
+
+

Tags

+
+
+ {{packages.length}} + {{tag}} +
+
+
+ +
+
+
+
+
+ + +
+
+
diff --git a/share/nitweb/views/module.html b/share/nitweb/views/module.html new file mode 100644 index 0000000..9a15c76 --- /dev/null +++ b/share/nitweb/views/module.html @@ -0,0 +1,52 @@ +
+ + + + +
+
+ + + + + + + + +
+
+
+
+
+					
+				
+
+
+
+ +
+
+
diff --git a/share/nitweb/views/package.html b/share/nitweb/views/package.html new file mode 100644 index 0000000..ee19aa5 --- /dev/null +++ b/share/nitweb/views/package.html @@ -0,0 +1,22 @@ +
+ + + + +
+
+ + + +
+
+
diff --git a/share/nitweb/views/propdef.html b/share/nitweb/views/propdef.html new file mode 100644 index 0000000..87b0ff3 --- /dev/null +++ b/share/nitweb/views/propdef.html @@ -0,0 +1,44 @@ +
+ + + + +
+
+ +
+
+
+
+
+					
+				
+
+
+
+
diff --git a/share/nitweb/views/property.html b/share/nitweb/views/property.html new file mode 100644 index 0000000..fea0da7 --- /dev/null +++ b/share/nitweb/views/property.html @@ -0,0 +1,34 @@ +
+ + + + + +
+
+ +
+
+ +
+
+
diff --git a/src/catalog.nit b/src/catalog.nit index 1743179..92510bc 100644 --- a/src/catalog.nit +++ b/src/catalog.nit @@ -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 diff --git a/src/loader.nit b/src/loader.nit index 967d335..d1626cb 100644 --- a/src/loader.nit +++ b/src/loader.nit @@ -379,6 +379,13 @@ redef class ModelBuilder var mgrouppath = path.join_path("..").simplify_path var mgroup = identify_group(mgrouppath) + if mgroup != null then + var mpackage = mgroup.mpackage + if not mpackage.accept(path) then + mgroup = null + toolcontext.info("module `{path}` excluded from package `{mpackage}`", 2) + end + end if mgroup == null then # singleton package var loc = new Location.opaque_file(path) @@ -473,6 +480,13 @@ redef class ModelBuilder if not stopper.file_exists then # Recursively get the parent group parent = identify_group(parentpath) + if parent != null then do + var mpackage = parent.mpackage + if not mpackage.accept(dirpath) then + toolcontext.info("directory `{dirpath}` excluded from package `{mpackage}`", 2) + parent = null + end + end if parent == null then # Parent is not a group, thus we are not a group either mgroups[rdp] = null @@ -686,7 +700,7 @@ redef class ModelBuilder if decl != null then var decl_name = decl.n_name.n_id.text if decl_name != mmodule.name then - error(decl.n_name, "Error: module name mismatch; declared {decl_name} file named {mmodule.name}.") + warning(decl.n_name, "module-name-mismatch", "Error: module name mismatch; declared {decl_name} file named {mmodule.name}.") end end @@ -1064,6 +1078,27 @@ redef class MPackage # # Some packages, like stand-alone packages or virtual packages have no `ini` file associated. var ini: nullable ConfigTree = null + + # Array of relative source paths excluded according to the `source.exclude` key of the `ini` + var excludes: nullable Array[String] is lazy do + var ini = self.ini + if ini == null then return null + var exclude = ini["source.exclude"] + if exclude == null then return null + var excludes = exclude.split(":") + return excludes + end + + # Does the source inclusion/inclusion rules of the package `ini` accept such path? + fun accept(filepath: String): Bool + do + var excludes = self.excludes + if excludes != null then + var relpath = root.filepath.relpath(filepath) + if excludes.has(relpath) then return false + end + return true + end end redef class MGroup diff --git a/src/model/model_collect.nit b/src/model/model_collect.nit index 79970e5..bd7d12c 100644 --- a/src/model/model_collect.nit +++ b/src/model/model_collect.nit @@ -37,6 +37,11 @@ redef class MEntity fun collect_modifiers: Array[String] do return new Array[String] end + + # Collect `self` linearization anchored on `mainmodule`. + fun collect_linearization(mainmodule: MModule): nullable Array[MEntity] do + return null + end end redef class MPackage @@ -163,6 +168,12 @@ redef class MClass redef fun collect_modifiers do return intro.collect_modifiers + redef fun collect_linearization(mainmodule) do + var mclassdefs = self.mclassdefs.to_a + mainmodule.linearize_mclassdefs(mclassdefs) + return mclassdefs + end + # Collect direct parents of `self` with `visibility >= to min_visibility`. fun collect_parents(view: ModelView): Set[MClass] do var res = new HashSet[MClass] @@ -411,6 +422,15 @@ end redef class MClassDef + redef fun collect_linearization(mainmodule) do + var mclassdefs = new Array[MClassDef] + for mclassdef in in_hierarchy.as(not null).greaters do + if mclassdef.mclass == self.mclass then mclassdefs.add mclassdef + end + mainmodule.linearize_mclassdefs(mclassdefs) + return mclassdefs + end + # Collect mpropdefs in 'self' with `visibility >= min_visibility`. fun collect_mpropdefs(view: ModelView): Set[MPropDef] do var res = new HashSet[MPropDef] @@ -457,6 +477,12 @@ end redef class MProperty redef fun collect_modifiers do return intro.collect_modifiers + + redef fun collect_linearization(mainmodule) do + var mpropdefs = self.mpropdefs.to_a + mainmodule.linearize_mpropdefs(mpropdefs) + return mpropdefs + end end redef class MPropDef @@ -486,4 +512,16 @@ redef class MPropDef end return res end + + redef fun collect_linearization(mainmodule) do + var mpropdefs = new Array[MPropDef] + var mentity = self + while not mentity.is_intro do + mpropdefs.add mentity + mentity = mentity.lookup_next_definition(mainmodule, mentity.mclassdef.bound_mtype) + end + mpropdefs.add mentity + mainmodule.linearize_mpropdefs(mpropdefs) + return mpropdefs + end end diff --git a/src/modelbuilder_base.nit b/src/modelbuilder_base.nit index 85bb115..5cc235f 100644 --- a/src/modelbuilder_base.nit +++ b/src/modelbuilder_base.nit @@ -348,6 +348,12 @@ class ModelBuilder var name = qid.n_id.text var qname = qid.full_name + if bad_class_names[mmodule].has(qname) then + error(qid, "Error: class `{qname}` not found in module `{mmodule}`.") + return + end + bad_class_names[mmodule].add(qname) + var all_classes = model.get_mclasses_by_name(name) var hints = new Array[String] @@ -404,6 +410,10 @@ class ModelBuilder error(qid, "Error: class `{qname}` not found in module `{mmodule}`.") end + # List of already reported bad class names. + # Used to not perform and repeat hints again and again. + private var bad_class_names = new MultiHashMap[MModule, String] + # Return the static type associated to the node `ntype`. # `mmodule` and `mclassdef` is the context where the call is made (used to understand formal types) # In case of problem, an error is displayed on `ntype` and null is returned. diff --git a/src/nit.nit b/src/nit.nit index 9410b4d..a28bb6f 100644 --- a/src/nit.nit +++ b/src/nit.nit @@ -64,6 +64,12 @@ if opt_eval.value then modelbuilder.load_rt_module(parent, amodule, "-") mmodules = [amodule.mmodule.as(not null)] +else if progname == "-" then + var content = stdin.read_all + var amodule = toolcontext.parse_module(content) + toolcontext.check_errors + modelbuilder.load_rt_module(null, amodule, "-") + mmodules = [amodule.mmodule.as(not null)] else mmodules = modelbuilder.parse([progname]) end diff --git a/src/nitcatalog.nit b/src/nitcatalog.nit index 5c478f0..42dc5dd 100644 --- a/src/nitcatalog.nit +++ b/src/nitcatalog.nit @@ -561,10 +561,16 @@ end # Get files or groups var args = tc.option_context.rest +var mmodules if opt_no_parse.value then - modelbuilder.scan_full(args) + mmodules = modelbuilder.scan_full(args) else - modelbuilder.parse_full(args) + mmodules = modelbuilder.parse_full(args) +end +var mpackages = new Set[MPackage] +for m in mmodules do + var p = m.mpackage + if p != null then mpackages.add p end # Scan packages and compute information @@ -586,7 +592,7 @@ for p in model.mpackages do end end -if not opt_no_git.value then for p in model.mpackages do +if not opt_no_git.value then for p in mpackages do catalog.git_info(p) end @@ -703,7 +709,7 @@ css.write_to_file(out/"style.css") # PAGES -for p in model.mpackages do +for p in mpackages do # print p var f = "p/{p.name}.html" catalog.package_page(p) @@ -726,7 +732,7 @@ index.add catalog.list_best(catalog.score) if catalog.deps.not_empty then index.add "

Most Required

\n" var reqs = new Counter[MPackage] - for p in model.mpackages do + for p in mpackages do reqs[p] = catalog.deps[p].smallers.length - 1 end index.add catalog.list_best(reqs) @@ -743,7 +749,7 @@ index.add """