From cc8ac57ef4ae44077096ca0f9cd196b823a1db06 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 18 Oct 2016 17:01:41 -0700 Subject: [PATCH 01/86] mirrored a row --- identicon/lib/identicon.ex | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/identicon/lib/identicon.ex b/identicon/lib/identicon.ex index 6dad56f..9603212 100644 --- a/identicon/lib/identicon.ex +++ b/identicon/lib/identicon.ex @@ -11,6 +11,12 @@ defmodule Identicon do |> Enum.chunk(3) end + def mirror_row(row) do + [first, second | _tail] = row + + row ++ [second, first] + end + def pick_color(%Identicon.Image{hex: [r, g, b | _tail]} = image) do %Identicon.Image{image | color: {r, g, b}} end From 1be1f44cc2c4992a82e0ed1b0bb7acc233869a21 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 18 Oct 2016 17:11:45 -0700 Subject: [PATCH 02/86] added custom map --- identicon/lib/identicon.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/identicon/lib/identicon.ex b/identicon/lib/identicon.ex index 9603212..a62ac78 100644 --- a/identicon/lib/identicon.ex +++ b/identicon/lib/identicon.ex @@ -9,11 +9,12 @@ defmodule Identicon do def build_grid(%Identicon.Image{hex: hex} = image) do hex |> Enum.chunk(3) + |> Enum.map(&mirror_row/1) end def mirror_row(row) do [first, second | _tail] = row - + row ++ [second, first] end From cd05957b0380fa6c77d7820399a056ebf342aa83 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 18 Oct 2016 17:21:11 -0700 Subject: [PATCH 03/86] finish creating grid --- identicon/lib/identicon.ex | 11 ++++++++--- identicon/lib/image.ex | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/identicon/lib/identicon.ex b/identicon/lib/identicon.ex index a62ac78..d0f8d91 100644 --- a/identicon/lib/identicon.ex +++ b/identicon/lib/identicon.ex @@ -7,9 +7,14 @@ defmodule Identicon do end def build_grid(%Identicon.Image{hex: hex} = image) do - hex - |> Enum.chunk(3) - |> Enum.map(&mirror_row/1) + grid = + hex + |> Enum.chunk(3) + |> Enum.map(&mirror_row/1) + |> List.flatten + |> Enum.with_index + + %Identicon.Image{image | grid: grid} end def mirror_row(row) do diff --git a/identicon/lib/image.ex b/identicon/lib/image.ex index 0e8fcbb..27c3afa 100644 --- a/identicon/lib/image.ex +++ b/identicon/lib/image.ex @@ -1,3 +1,3 @@ defmodule Identicon.Image do - defstruct hex: nil, color: nil + defstruct hex: nil, color: nil, grid: nil end From a0580199faa698ffe817e2b5ddffbb1c3bdb9cd3 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Wed, 19 Oct 2016 15:07:43 -0700 Subject: [PATCH 04/86] finished grid building --- identicon/lib/identicon.ex | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/identicon/lib/identicon.ex b/identicon/lib/identicon.ex index d0f8d91..4237cfb 100644 --- a/identicon/lib/identicon.ex +++ b/identicon/lib/identicon.ex @@ -4,6 +4,15 @@ defmodule Identicon do |> hash_input |> pick_color |> build_grid + |> filter_odd_squares + end + + def filter_odd_squares(%Identicon.Image{grid: grid} = image) do + grid = Enum.filter grid, fn({code, _index}) -> + rem(code, 2) == 0 + end + + %Identicon.Image{image | grid: grid} end def build_grid(%Identicon.Image{hex: hex} = image) do From 1d765373b17b291161fc7e0af67ba7879b22b60e Mon Sep 17 00:00:00 2001 From: stephen grider Date: Wed, 19 Oct 2016 15:40:13 -0700 Subject: [PATCH 05/86] calculated points --- identicon/lib/identicon.ex | 15 +++++++++++++++ identicon/lib/image.ex | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/identicon/lib/identicon.ex b/identicon/lib/identicon.ex index 4237cfb..2f6721e 100644 --- a/identicon/lib/identicon.ex +++ b/identicon/lib/identicon.ex @@ -5,6 +5,21 @@ defmodule Identicon do |> pick_color |> build_grid |> filter_odd_squares + |> build_pixel_map + end + + def build_pixel_map(%Identicon.Image{grid: grid} = image) do + pixel_map = Enum.map grid, fn({_code, index}) -> + horizontal = rem(index, 5) * 50 + vertical = div(index, 5) * 50 + + top_left = {horizontal, vertical} + bottom_right = {horizontal + 50, vertical + 50} + + {top_left, bottom_right} + end + + %Identicon.Image{image | pixel_map: pixel_map} end def filter_odd_squares(%Identicon.Image{grid: grid} = image) do diff --git a/identicon/lib/image.ex b/identicon/lib/image.ex index 27c3afa..f521213 100644 --- a/identicon/lib/image.ex +++ b/identicon/lib/image.ex @@ -1,3 +1,3 @@ defmodule Identicon.Image do - defstruct hex: nil, color: nil, grid: nil + defstruct hex: nil, color: nil, grid: nil, pixel_map: nil end From 40bf858f4f3621538b5e48c51f72bfaa28e26d8d Mon Sep 17 00:00:00 2001 From: stephen grider Date: Fri, 21 Oct 2016 15:24:50 -0700 Subject: [PATCH 06/86] save image --- identicon/lib/identicon.ex | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/identicon/lib/identicon.ex b/identicon/lib/identicon.ex index 2f6721e..2ce065b 100644 --- a/identicon/lib/identicon.ex +++ b/identicon/lib/identicon.ex @@ -6,6 +6,23 @@ defmodule Identicon do |> build_grid |> filter_odd_squares |> build_pixel_map + |> draw_image + |> save_image(input) + end + + def save_image(image, input) do + File.write("#{input}.png", image) + end + + def draw_image(%Identicon.Image{color: color, pixel_map: pixel_map}) do + image = :egd.create(250, 250) + fill = :egd.color(color) + + Enum.each pixel_map, fn({start, stop}) -> + :egd.filledRectangle(image, start, stop, fill) + end + + :egd.render(image) end def build_pixel_map(%Identicon.Image{grid: grid} = image) do From 888fab3a5541892167e39aa8a9295fd12a646fe2 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Fri, 21 Oct 2016 15:34:26 -0700 Subject: [PATCH 07/86] generated project --- discuss/.gitignore | 24 +++++ discuss/README.md | 20 ++++ discuss/brunch-config.js | 69 +++++++++++++ discuss/config/config.exs | 27 +++++ discuss/config/dev.exs | 43 ++++++++ discuss/config/prod.exs | 61 ++++++++++++ discuss/config/test.exs | 19 ++++ discuss/lib/discuss.ex | 31 ++++++ discuss/lib/discuss/endpoint.ex | 42 ++++++++ discuss/lib/discuss/repo.ex | 3 + discuss/mix.exs | 54 ++++++++++ discuss/mix.lock | 19 ++++ discuss/package.json | 20 ++++ discuss/priv/gettext/en/LC_MESSAGES/errors.po | 93 ++++++++++++++++++ discuss/priv/gettext/errors.pot | 91 +++++++++++++++++ discuss/priv/repo/seeds.exs | 11 +++ .../test/controllers/page_controller_test.exs | 8 ++ discuss/test/support/channel_case.ex | 43 ++++++++ discuss/test/support/conn_case.ex | 44 +++++++++ discuss/test/support/model_case.ex | 65 ++++++++++++ discuss/test/test_helper.exs | 4 + discuss/test/views/error_view_test.exs | 21 ++++ discuss/test/views/layout_view_test.exs | 3 + discuss/test/views/page_view_test.exs | 3 + discuss/web/channels/user_socket.ex | 37 +++++++ discuss/web/controllers/page_controller.ex | 7 ++ discuss/web/gettext.ex | 24 +++++ discuss/web/router.ex | 26 +++++ discuss/web/static/assets/favicon.ico | Bin 0 -> 1258 bytes discuss/web/static/assets/images/phoenix.png | Bin 0 -> 13900 bytes discuss/web/static/assets/robots.txt | 5 + discuss/web/static/css/app.css | 1 + discuss/web/static/css/phoenix.css | 82 +++++++++++++++ discuss/web/static/js/app.js | 21 ++++ discuss/web/static/js/socket.js | 62 ++++++++++++ discuss/web/templates/layout/app.html.eex | 35 +++++++ discuss/web/templates/page/index.html.eex | 36 +++++++ discuss/web/views/error_helpers.ex | 40 ++++++++ discuss/web/views/error_view.ex | 17 ++++ discuss/web/views/layout_view.ex | 3 + discuss/web/views/page_view.ex | 3 + discuss/web/web.ex | 81 +++++++++++++++ 42 files changed, 1298 insertions(+) create mode 100644 discuss/.gitignore create mode 100644 discuss/README.md create mode 100644 discuss/brunch-config.js create mode 100644 discuss/config/config.exs create mode 100644 discuss/config/dev.exs create mode 100644 discuss/config/prod.exs create mode 100644 discuss/config/test.exs create mode 100644 discuss/lib/discuss.ex create mode 100644 discuss/lib/discuss/endpoint.ex create mode 100644 discuss/lib/discuss/repo.ex create mode 100644 discuss/mix.exs create mode 100644 discuss/mix.lock create mode 100644 discuss/package.json create mode 100644 discuss/priv/gettext/en/LC_MESSAGES/errors.po create mode 100644 discuss/priv/gettext/errors.pot create mode 100644 discuss/priv/repo/seeds.exs create mode 100644 discuss/test/controllers/page_controller_test.exs create mode 100644 discuss/test/support/channel_case.ex create mode 100644 discuss/test/support/conn_case.ex create mode 100644 discuss/test/support/model_case.ex create mode 100644 discuss/test/test_helper.exs create mode 100644 discuss/test/views/error_view_test.exs create mode 100644 discuss/test/views/layout_view_test.exs create mode 100644 discuss/test/views/page_view_test.exs create mode 100644 discuss/web/channels/user_socket.ex create mode 100644 discuss/web/controllers/page_controller.ex create mode 100644 discuss/web/gettext.ex create mode 100644 discuss/web/router.ex create mode 100644 discuss/web/static/assets/favicon.ico create mode 100644 discuss/web/static/assets/images/phoenix.png create mode 100644 discuss/web/static/assets/robots.txt create mode 100644 discuss/web/static/css/app.css create mode 100644 discuss/web/static/css/phoenix.css create mode 100644 discuss/web/static/js/app.js create mode 100644 discuss/web/static/js/socket.js create mode 100644 discuss/web/templates/layout/app.html.eex create mode 100644 discuss/web/templates/page/index.html.eex create mode 100644 discuss/web/views/error_helpers.ex create mode 100644 discuss/web/views/error_view.ex create mode 100644 discuss/web/views/layout_view.ex create mode 100644 discuss/web/views/page_view.ex create mode 100644 discuss/web/web.ex diff --git a/discuss/.gitignore b/discuss/.gitignore new file mode 100644 index 0000000..19fe3da --- /dev/null +++ b/discuss/.gitignore @@ -0,0 +1,24 @@ +# App artifacts +/_build +/db +/deps +/*.ez + +# Generated on crash by the VM +erl_crash.dump + +# Static artifacts +/node_modules + +# Since we are building assets from web/static, +# we ignore priv/static. You may want to comment +# this depending on your deployment strategy. +/priv/static/ + +# The config/prod.secret.exs file by default contains sensitive +# data and you should not commit it into version control. +# +# Alternatively, you may comment the line below and commit the +# secrets file as long as you replace its contents by environment +# variables. +/config/prod.secret.exs diff --git a/discuss/README.md b/discuss/README.md new file mode 100644 index 0000000..278e891 --- /dev/null +++ b/discuss/README.md @@ -0,0 +1,20 @@ +# Discuss + +To start your Phoenix app: + + * Install dependencies with `mix deps.get` + * Create and migrate your database with `mix ecto.create && mix ecto.migrate` + * Install Node.js dependencies with `npm install` + * Start Phoenix endpoint with `mix phoenix.server` + +Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. + +Ready to run in production? Please [check our deployment guides](http://www.phoenixframework.org/docs/deployment). + +## Learn more + + * Official website: http://www.phoenixframework.org/ + * Guides: http://phoenixframework.org/docs/overview + * Docs: https://hexdocs.pm/phoenix + * Mailing list: http://groups.google.com/group/phoenix-talk + * Source: https://github.com/phoenixframework/phoenix diff --git a/discuss/brunch-config.js b/discuss/brunch-config.js new file mode 100644 index 0000000..d2fe679 --- /dev/null +++ b/discuss/brunch-config.js @@ -0,0 +1,69 @@ +exports.config = { + // See http://brunch.io/#documentation for docs. + files: { + javascripts: { + joinTo: "js/app.js" + + // To use a separate vendor.js bundle, specify two files path + // http://brunch.io/docs/config#-files- + // joinTo: { + // "js/app.js": /^(web\/static\/js)/, + // "js/vendor.js": /^(web\/static\/vendor)|(deps)/ + // } + // + // To change the order of concatenation of files, explicitly mention here + // order: { + // before: [ + // "web/static/vendor/js/jquery-2.1.1.js", + // "web/static/vendor/js/bootstrap.min.js" + // ] + // } + }, + stylesheets: { + joinTo: "css/app.css", + order: { + after: ["web/static/css/app.css"] // concat app.css last + } + }, + templates: { + joinTo: "js/app.js" + } + }, + + conventions: { + // This option sets where we should place non-css and non-js assets in. + // By default, we set this to "/web/static/assets". Files in this directory + // will be copied to `paths.public`, which is "priv/static" by default. + assets: /^(web\/static\/assets)/ + }, + + // Phoenix paths configuration + paths: { + // Dependencies and current project directories to watch + watched: [ + "web/static", + "test/static" + ], + + // Where to compile files to + public: "priv/static" + }, + + // Configure your plugins + plugins: { + babel: { + // Do not use ES6 compiler in vendor code + ignore: [/web\/static\/vendor/] + } + }, + + modules: { + autoRequire: { + "js/app.js": ["web/static/js/app"] + } + }, + + npm: { + enabled: true + } +}; diff --git a/discuss/config/config.exs b/discuss/config/config.exs new file mode 100644 index 0000000..e2974c8 --- /dev/null +++ b/discuss/config/config.exs @@ -0,0 +1,27 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +# +# This configuration file is loaded before any dependency and +# is restricted to this project. +use Mix.Config + +# General application configuration +config :discuss, + ecto_repos: [Discuss.Repo] + +# Configures the endpoint +config :discuss, Discuss.Endpoint, + url: [host: "localhost"], + secret_key_base: "u9hBRCWHemNRWIx/RnJ6J4TY3NYqN0PXCBS22vQwmwxoKgG0oJ1UOUvZYtd1gOb6", + render_errors: [view: Discuss.ErrorView, accepts: ~w(html json)], + pubsub: [name: Discuss.PubSub, + adapter: Phoenix.PubSub.PG2] + +# Configures Elixir's Logger +config :logger, :console, + format: "$time $metadata[$level] $message\n", + metadata: [:request_id] + +# Import environment specific config. This must remain at the bottom +# of this file so it overrides the configuration defined above. +import_config "#{Mix.env}.exs" diff --git a/discuss/config/dev.exs b/discuss/config/dev.exs new file mode 100644 index 0000000..5a4b3a4 --- /dev/null +++ b/discuss/config/dev.exs @@ -0,0 +1,43 @@ +use Mix.Config + +# For development, we disable any cache and enable +# debugging and code reloading. +# +# The watchers configuration can be used to run external +# watchers to your application. For example, we use it +# with brunch.io to recompile .js and .css sources. +config :discuss, Discuss.Endpoint, + http: [port: 4000], + debug_errors: true, + code_reloader: true, + check_origin: false, + watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin", + cd: Path.expand("../", __DIR__)]] + + +# Watch static and templates for browser reloading. +config :discuss, Discuss.Endpoint, + live_reload: [ + patterns: [ + ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$}, + ~r{priv/gettext/.*(po)$}, + ~r{web/views/.*(ex)$}, + ~r{web/templates/.*(eex)$} + ] + ] + +# Do not include metadata nor timestamps in development logs +config :logger, :console, format: "[$level] $message\n" + +# Set a higher stacktrace during development. Avoid configuring such +# in production as building large stacktraces may be expensive. +config :phoenix, :stacktrace_depth, 20 + +# Configure your database +config :discuss, Discuss.Repo, + adapter: Ecto.Adapters.Postgres, + username: "postgres", + password: "postgres", + database: "discuss_dev", + hostname: "localhost", + pool_size: 10 diff --git a/discuss/config/prod.exs b/discuss/config/prod.exs new file mode 100644 index 0000000..341b365 --- /dev/null +++ b/discuss/config/prod.exs @@ -0,0 +1,61 @@ +use Mix.Config + +# For production, we configure the host to read the PORT +# from the system environment. Therefore, you will need +# to set PORT=80 before running your server. +# +# You should also configure the url host to something +# meaningful, we use this information when generating URLs. +# +# Finally, we also include the path to a manifest +# containing the digested version of static files. This +# manifest is generated by the mix phoenix.digest task +# which you typically run after static files are built. +config :discuss, Discuss.Endpoint, + http: [port: {:system, "PORT"}], + url: [host: "example.com", port: 80], + cache_static_manifest: "priv/static/manifest.json" + +# Do not print debug messages in production +config :logger, level: :info + +# ## SSL Support +# +# To get SSL working, you will need to add the `https` key +# to the previous section and set your `:url` port to 443: +# +# config :discuss, Discuss.Endpoint, +# ... +# url: [host: "example.com", port: 443], +# https: [port: 443, +# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), +# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")] +# +# Where those two env variables return an absolute path to +# the key and cert in disk or a relative path inside priv, +# for example "priv/ssl/server.key". +# +# We also recommend setting `force_ssl`, ensuring no data is +# ever sent via http, always redirecting to https: +# +# config :discuss, Discuss.Endpoint, +# force_ssl: [hsts: true] +# +# Check `Plug.SSL` for all available options in `force_ssl`. + +# ## Using releases +# +# If you are doing OTP releases, you need to instruct Phoenix +# to start the server for all endpoints: +# +# config :phoenix, :serve_endpoints, true +# +# Alternatively, you can configure exactly which server to +# start per endpoint: +# +# config :discuss, Discuss.Endpoint, server: true +# + +# Finally import the config/prod.secret.exs +# which should be versioned separately. +import_config "prod.secret.exs" diff --git a/discuss/config/test.exs b/discuss/config/test.exs new file mode 100644 index 0000000..258d032 --- /dev/null +++ b/discuss/config/test.exs @@ -0,0 +1,19 @@ +use Mix.Config + +# We don't run a server during test. If one is required, +# you can enable the server option below. +config :discuss, Discuss.Endpoint, + http: [port: 4001], + server: false + +# Print only warnings and errors during test +config :logger, level: :warn + +# Configure your database +config :discuss, Discuss.Repo, + adapter: Ecto.Adapters.Postgres, + username: "postgres", + password: "postgres", + database: "discuss_test", + hostname: "localhost", + pool: Ecto.Adapters.SQL.Sandbox diff --git a/discuss/lib/discuss.ex b/discuss/lib/discuss.ex new file mode 100644 index 0000000..0079ec4 --- /dev/null +++ b/discuss/lib/discuss.ex @@ -0,0 +1,31 @@ +defmodule Discuss do + use Application + + # See http://elixir-lang.org/docs/stable/elixir/Application.html + # for more information on OTP Applications + def start(_type, _args) do + import Supervisor.Spec + + # Define workers and child supervisors to be supervised + children = [ + # Start the Ecto repository + supervisor(Discuss.Repo, []), + # Start the endpoint when the application starts + supervisor(Discuss.Endpoint, []), + # Start your own worker by calling: Discuss.Worker.start_link(arg1, arg2, arg3) + # worker(Discuss.Worker, [arg1, arg2, arg3]), + ] + + # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Discuss.Supervisor] + Supervisor.start_link(children, opts) + end + + # Tell Phoenix to update the endpoint configuration + # whenever the application is updated. + def config_change(changed, _new, removed) do + Discuss.Endpoint.config_change(changed, removed) + :ok + end +end diff --git a/discuss/lib/discuss/endpoint.ex b/discuss/lib/discuss/endpoint.ex new file mode 100644 index 0000000..525909b --- /dev/null +++ b/discuss/lib/discuss/endpoint.ex @@ -0,0 +1,42 @@ +defmodule Discuss.Endpoint do + use Phoenix.Endpoint, otp_app: :discuss + + socket "/socket", Discuss.UserSocket + + # Serve at "/" the static files from "priv/static" directory. + # + # You should set gzip to true if you are running phoenix.digest + # when deploying your static files in production. + plug Plug.Static, + at: "/", from: :discuss, gzip: false, + only: ~w(css fonts images js favicon.ico robots.txt) + + # Code reloading can be explicitly enabled under the + # :code_reloader configuration of your endpoint. + if code_reloading? do + socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket + plug Phoenix.LiveReloader + plug Phoenix.CodeReloader + end + + plug Plug.RequestId + plug Plug.Logger + + plug Plug.Parsers, + parsers: [:urlencoded, :multipart, :json], + pass: ["*/*"], + json_decoder: Poison + + plug Plug.MethodOverride + plug Plug.Head + + # The session will be stored in the cookie and signed, + # this means its contents can be read but not tampered with. + # Set :encryption_salt if you would also like to encrypt it. + plug Plug.Session, + store: :cookie, + key: "_discuss_key", + signing_salt: "b+pDMONc" + + plug Discuss.Router +end diff --git a/discuss/lib/discuss/repo.ex b/discuss/lib/discuss/repo.ex new file mode 100644 index 0000000..359d489 --- /dev/null +++ b/discuss/lib/discuss/repo.ex @@ -0,0 +1,3 @@ +defmodule Discuss.Repo do + use Ecto.Repo, otp_app: :discuss +end diff --git a/discuss/mix.exs b/discuss/mix.exs new file mode 100644 index 0000000..6d4fad0 --- /dev/null +++ b/discuss/mix.exs @@ -0,0 +1,54 @@ +defmodule Discuss.Mixfile do + use Mix.Project + + def project do + [app: :discuss, + version: "0.0.1", + elixir: "~> 1.2", + elixirc_paths: elixirc_paths(Mix.env), + compilers: [:phoenix, :gettext] ++ Mix.compilers, + build_embedded: Mix.env == :prod, + start_permanent: Mix.env == :prod, + aliases: aliases(), + deps: deps()] + end + + # Configuration for the OTP application. + # + # Type `mix help compile.app` for more information. + def application do + [mod: {Discuss, []}, + applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext, + :phoenix_ecto, :postgrex]] + end + + # Specifies which paths to compile per environment. + defp elixirc_paths(:test), do: ["lib", "web", "test/support"] + defp elixirc_paths(_), do: ["lib", "web"] + + # Specifies your project dependencies. + # + # Type `mix help deps` for examples and options. + defp deps do + [{:phoenix, "~> 1.2.1"}, + {:phoenix_pubsub, "~> 1.0"}, + {:phoenix_ecto, "~> 3.0"}, + {:postgrex, ">= 0.0.0"}, + {:phoenix_html, "~> 2.6"}, + {:phoenix_live_reload, "~> 1.0", only: :dev}, + {:gettext, "~> 0.11"}, + {:cowboy, "~> 1.0"}] + end + + # Aliases are shortcuts or tasks specific to the current project. + # For example, to create, migrate and run the seeds file at once: + # + # $ mix ecto.setup + # + # See the documentation for `Mix` for more info on aliases. + defp aliases do + ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], + "ecto.reset": ["ecto.drop", "ecto.setup"], + "test": ["ecto.create --quiet", "ecto.migrate", "test"]] + end +end diff --git a/discuss/mix.lock b/discuss/mix.lock new file mode 100644 index 0000000..ed8089c --- /dev/null +++ b/discuss/mix.lock @@ -0,0 +1,19 @@ +%{"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []}, + "cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:rebar, :make], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]}, + "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, + "db_connection": {:hex, :db_connection, "1.0.0", "63c03e520d54886a66104d34e32397ba960db6e74b596ce221592c07d6a40d8d", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]}, + "decimal": {:hex, :decimal, "1.2.0", "462960fd71af282e570f7b477f6be56bf8968e68277d4d0b641a635269bf4b0d", [:mix], []}, + "ecto": {:hex, :ecto, "2.0.5", "7f4c79ac41ffba1a4c032b69d7045489f0069c256de606523c65d9f8188e502d", [:mix], [{:db_connection, "~> 1.0-rc.4", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.1.2 or ~> 1.2", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.7.7", [hex: :mariaex, optional: true]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.12.0", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}]}, + "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []}, + "gettext": {:hex, :gettext, "0.11.0", "80c1dd42d270482418fa158ec5ba073d2980e3718bacad86f3d4ad71d5667679", [:mix], []}, + "mime": {:hex, :mime, "1.0.1", "05c393850524767d13a53627df71beeebb016205eb43bfbd92d14d24ec7a1b51", [:mix], []}, + "phoenix": {:hex, :phoenix, "1.2.1", "6dc592249ab73c67575769765b66ad164ad25d83defa3492dc6ae269bd2a68ab", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.1", [hex: :plug, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}]}, + "phoenix_ecto": {:hex, :phoenix_ecto, "3.0.1", "42eb486ef732cf209d0a353e791806721f33ff40beab0a86f02070a5649ed00a", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}, {:phoenix_html, "~> 2.6", [hex: :phoenix_html, optional: true]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]}, + "phoenix_html": {:hex, :phoenix_html, "2.7.0", "19e12e2044340c2e43df206a06d059677c59ea1868bd1c35165438d592cd420b", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]}, + "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.5", "829218c4152ba1e9848e2bf8e161fcde6b4ec679a516259442561d21fde68d0b", [:mix], [{:fs, "~> 0.9.1", [hex: :fs, optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2-rc", [hex: :phoenix, optional: false]}]}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.1", "c10ddf6237007c804bf2b8f3c4d5b99009b42eca3a0dfac04ea2d8001186056a", [:mix], []}, + "plug": {:hex, :plug, "1.2.2", "cfbda521b54c92ab8ddffb173fbaabed8d8fc94bec07cd9bb58a84c1c501b0bd", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]}, + "poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [:mix], []}, + "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], []}, + "postgrex": {:hex, :postgrex, "0.12.1", "2f8b46cb3a44dcd42f42938abedbfffe7e103ba4ce810ccbeee8dcf27ca0fb06", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}, {:db_connection, "~> 1.0-rc.4", [hex: :db_connection, optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}]}, + "ranch": {:hex, :ranch, "1.2.1", "a6fb992c10f2187b46ffd17ce398ddf8a54f691b81768f9ef5f461ea7e28c762", [:make], []}} diff --git a/discuss/package.json b/discuss/package.json new file mode 100644 index 0000000..13de858 --- /dev/null +++ b/discuss/package.json @@ -0,0 +1,20 @@ +{ + "repository": {}, + "license": "MIT", + "scripts": { + "deploy": "brunch build --production", + "watch": "brunch watch --stdin" + }, + "dependencies": { + "phoenix": "file:deps/phoenix", + "phoenix_html": "file:deps/phoenix_html" + }, + "devDependencies": { + "babel-brunch": "~6.0.0", + "brunch": "2.7.4", + "clean-css-brunch": "~2.0.0", + "css-brunch": "~2.0.0", + "javascript-brunch": "~2.0.0", + "uglify-js-brunch": "~2.0.1" + } +} diff --git a/discuss/priv/gettext/en/LC_MESSAGES/errors.po b/discuss/priv/gettext/en/LC_MESSAGES/errors.po new file mode 100644 index 0000000..087d374 --- /dev/null +++ b/discuss/priv/gettext/en/LC_MESSAGES/errors.po @@ -0,0 +1,93 @@ +## `msgid`s in this file come from POT (.pot) files. +## +## Do not add, change, or remove `msgid`s manually here as +## they're tied to the ones in the corresponding POT file +## (with the same domain). +## +## Use `mix gettext.extract --merge` or `mix gettext.merge` +## to merge POT files into PO files. +msgid "" +msgstr "" +"Language: en\n" + +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated to this entry" +msgstr "" + +msgid "are still associated to this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" diff --git a/discuss/priv/gettext/errors.pot b/discuss/priv/gettext/errors.pot new file mode 100644 index 0000000..a228957 --- /dev/null +++ b/discuss/priv/gettext/errors.pot @@ -0,0 +1,91 @@ +## This file is a PO Template file. +## +## `msgid`s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run `mix gettext.extract` to bring this file up to +## date. Leave `msgstr`s empty as changing them here as no +## effect: edit them in PO (`.po`) files instead. + +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated to this entry" +msgstr "" + +msgid "are still associated to this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" diff --git a/discuss/priv/repo/seeds.exs b/discuss/priv/repo/seeds.exs new file mode 100644 index 0000000..aa811bf --- /dev/null +++ b/discuss/priv/repo/seeds.exs @@ -0,0 +1,11 @@ +# Script for populating the database. You can run it as: +# +# mix run priv/repo/seeds.exs +# +# Inside the script, you can read and write to any of your +# repositories directly: +# +# Discuss.Repo.insert!(%Discuss.SomeModel{}) +# +# We recommend using the bang functions (`insert!`, `update!` +# and so on) as they will fail if something goes wrong. diff --git a/discuss/test/controllers/page_controller_test.exs b/discuss/test/controllers/page_controller_test.exs new file mode 100644 index 0000000..ea94961 --- /dev/null +++ b/discuss/test/controllers/page_controller_test.exs @@ -0,0 +1,8 @@ +defmodule Discuss.PageControllerTest do + use Discuss.ConnCase + + test "GET /", %{conn: conn} do + conn = get conn, "/" + assert html_response(conn, 200) =~ "Welcome to Phoenix!" + end +end diff --git a/discuss/test/support/channel_case.ex b/discuss/test/support/channel_case.ex new file mode 100644 index 0000000..71b1126 --- /dev/null +++ b/discuss/test/support/channel_case.ex @@ -0,0 +1,43 @@ +defmodule Discuss.ChannelCase do + @moduledoc """ + This module defines the test case to be used by + channel tests. + + Such tests rely on `Phoenix.ChannelTest` and also + import other functionality to make it easier + to build and query models. + + Finally, if the test case interacts with the database, + it cannot be async. For this reason, every test runs + inside a transaction which is reset at the beginning + of the test unless the test case is marked as async. + """ + + use ExUnit.CaseTemplate + + using do + quote do + # Import conveniences for testing with channels + use Phoenix.ChannelTest + + alias Discuss.Repo + import Ecto + import Ecto.Changeset + import Ecto.Query + + + # The default endpoint for testing + @endpoint Discuss.Endpoint + end + end + + setup tags do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Discuss.Repo) + + unless tags[:async] do + Ecto.Adapters.SQL.Sandbox.mode(Discuss.Repo, {:shared, self()}) + end + + :ok + end +end diff --git a/discuss/test/support/conn_case.ex b/discuss/test/support/conn_case.ex new file mode 100644 index 0000000..80194df --- /dev/null +++ b/discuss/test/support/conn_case.ex @@ -0,0 +1,44 @@ +defmodule Discuss.ConnCase do + @moduledoc """ + This module defines the test case to be used by + tests that require setting up a connection. + + Such tests rely on `Phoenix.ConnTest` and also + import other functionality to make it easier + to build and query models. + + Finally, if the test case interacts with the database, + it cannot be async. For this reason, every test runs + inside a transaction which is reset at the beginning + of the test unless the test case is marked as async. + """ + + use ExUnit.CaseTemplate + + using do + quote do + # Import conveniences for testing with connections + use Phoenix.ConnTest + + alias Discuss.Repo + import Ecto + import Ecto.Changeset + import Ecto.Query + + import Discuss.Router.Helpers + + # The default endpoint for testing + @endpoint Discuss.Endpoint + end + end + + setup tags do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Discuss.Repo) + + unless tags[:async] do + Ecto.Adapters.SQL.Sandbox.mode(Discuss.Repo, {:shared, self()}) + end + + {:ok, conn: Phoenix.ConnTest.build_conn()} + end +end diff --git a/discuss/test/support/model_case.ex b/discuss/test/support/model_case.ex new file mode 100644 index 0000000..9fb3eb0 --- /dev/null +++ b/discuss/test/support/model_case.ex @@ -0,0 +1,65 @@ +defmodule Discuss.ModelCase do + @moduledoc """ + This module defines the test case to be used by + model tests. + + You may define functions here to be used as helpers in + your model tests. See `errors_on/2`'s definition as reference. + + Finally, if the test case interacts with the database, + it cannot be async. For this reason, every test runs + inside a transaction which is reset at the beginning + of the test unless the test case is marked as async. + """ + + use ExUnit.CaseTemplate + + using do + quote do + alias Discuss.Repo + + import Ecto + import Ecto.Changeset + import Ecto.Query + import Discuss.ModelCase + end + end + + setup tags do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Discuss.Repo) + + unless tags[:async] do + Ecto.Adapters.SQL.Sandbox.mode(Discuss.Repo, {:shared, self()}) + end + + :ok + end + + @doc """ + Helper for returning list of errors in a struct when given certain data. + + ## Examples + + Given a User schema that lists `:name` as a required field and validates + `:password` to be safe, it would return: + + iex> errors_on(%User{}, %{password: "password"}) + [password: "is unsafe", name: "is blank"] + + You could then write your assertion like: + + assert {:password, "is unsafe"} in errors_on(%User{}, %{password: "password"}) + + You can also create the changeset manually and retrieve the errors + field directly: + + iex> changeset = User.changeset(%User{}, password: "password") + iex> {:password, "is unsafe"} in changeset.errors + true + """ + def errors_on(struct, data) do + struct.__struct__.changeset(struct, data) + |> Ecto.Changeset.traverse_errors(&Discuss.ErrorHelpers.translate_error/1) + |> Enum.flat_map(fn {key, errors} -> for msg <- errors, do: {key, msg} end) + end +end diff --git a/discuss/test/test_helper.exs b/discuss/test/test_helper.exs new file mode 100644 index 0000000..42e7bf7 --- /dev/null +++ b/discuss/test/test_helper.exs @@ -0,0 +1,4 @@ +ExUnit.start + +Ecto.Adapters.SQL.Sandbox.mode(Discuss.Repo, :manual) + diff --git a/discuss/test/views/error_view_test.exs b/discuss/test/views/error_view_test.exs new file mode 100644 index 0000000..877b518 --- /dev/null +++ b/discuss/test/views/error_view_test.exs @@ -0,0 +1,21 @@ +defmodule Discuss.ErrorViewTest do + use Discuss.ConnCase, async: true + + # Bring render/3 and render_to_string/3 for testing custom views + import Phoenix.View + + test "renders 404.html" do + assert render_to_string(Discuss.ErrorView, "404.html", []) == + "Page not found" + end + + test "render 500.html" do + assert render_to_string(Discuss.ErrorView, "500.html", []) == + "Internal server error" + end + + test "render any other" do + assert render_to_string(Discuss.ErrorView, "505.html", []) == + "Internal server error" + end +end diff --git a/discuss/test/views/layout_view_test.exs b/discuss/test/views/layout_view_test.exs new file mode 100644 index 0000000..7281361 --- /dev/null +++ b/discuss/test/views/layout_view_test.exs @@ -0,0 +1,3 @@ +defmodule Discuss.LayoutViewTest do + use Discuss.ConnCase, async: true +end diff --git a/discuss/test/views/page_view_test.exs b/discuss/test/views/page_view_test.exs new file mode 100644 index 0000000..5ac052b --- /dev/null +++ b/discuss/test/views/page_view_test.exs @@ -0,0 +1,3 @@ +defmodule Discuss.PageViewTest do + use Discuss.ConnCase, async: true +end diff --git a/discuss/web/channels/user_socket.ex b/discuss/web/channels/user_socket.ex new file mode 100644 index 0000000..84977e0 --- /dev/null +++ b/discuss/web/channels/user_socket.ex @@ -0,0 +1,37 @@ +defmodule Discuss.UserSocket do + use Phoenix.Socket + + ## Channels + # channel "room:*", Discuss.RoomChannel + + ## Transports + transport :websocket, Phoenix.Transports.WebSocket + # transport :longpoll, Phoenix.Transports.LongPoll + + # Socket params are passed from the client and can + # be used to verify and authenticate a user. After + # verification, you can put default assigns into + # the socket that will be set for all channels, ie + # + # {:ok, assign(socket, :user_id, verified_user_id)} + # + # To deny connection, return `:error`. + # + # See `Phoenix.Token` documentation for examples in + # performing token verification on connect. + def connect(_params, socket) do + {:ok, socket} + end + + # Socket id's are topics that allow you to identify all sockets for a given user: + # + # def id(socket), do: "users_socket:#{socket.assigns.user_id}" + # + # Would allow you to broadcast a "disconnect" event and terminate + # all active sockets and channels for a given user: + # + # Discuss.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{}) + # + # Returning `nil` makes this socket anonymous. + def id(_socket), do: nil +end diff --git a/discuss/web/controllers/page_controller.ex b/discuss/web/controllers/page_controller.ex new file mode 100644 index 0000000..9bd91fb --- /dev/null +++ b/discuss/web/controllers/page_controller.ex @@ -0,0 +1,7 @@ +defmodule Discuss.PageController do + use Discuss.Web, :controller + + def index(conn, _params) do + render conn, "index.html" + end +end diff --git a/discuss/web/gettext.ex b/discuss/web/gettext.ex new file mode 100644 index 0000000..3133b99 --- /dev/null +++ b/discuss/web/gettext.ex @@ -0,0 +1,24 @@ +defmodule Discuss.Gettext do + @moduledoc """ + A module providing Internationalization with a gettext-based API. + + By using [Gettext](https://hexdocs.pm/gettext), + your module gains a set of macros for translations, for example: + + import Discuss.Gettext + + # Simple translation + gettext "Here is the string to translate" + + # Plural translation + ngettext "Here is the string to translate", + "Here are the strings to translate", + 3 + + # Domain-based translation + dgettext "errors", "Here is the error message to translate" + + See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. + """ + use Gettext, otp_app: :discuss +end diff --git a/discuss/web/router.ex b/discuss/web/router.ex new file mode 100644 index 0000000..f1e18de --- /dev/null +++ b/discuss/web/router.ex @@ -0,0 +1,26 @@ +defmodule Discuss.Router do + use Discuss.Web, :router + + pipeline :browser do + plug :accepts, ["html"] + plug :fetch_session + plug :fetch_flash + plug :protect_from_forgery + plug :put_secure_browser_headers + end + + pipeline :api do + plug :accepts, ["json"] + end + + scope "/", Discuss do + pipe_through :browser # Use the default browser stack + + get "/", PageController, :index + end + + # Other scopes may use custom stacks. + # scope "/api", Discuss do + # pipe_through :api + # end +end diff --git a/discuss/web/static/assets/favicon.ico b/discuss/web/static/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..73de524aaadcf60fbe9d32881db0aa86b58b5cb9 GIT binary patch literal 1258 zcmbtUO>fgM7{=qN=;Mz_82;lvPEdVaxv-<-&=sZLwab?3I zBP>U*&(Hv<5n@9ZQ$vhg#|u$Zmtq8BV;+W*7(?jOx-{r?#TE&$Sdq77MbdJjD5`-q zMm_z(jLv3t>5NhzK{%aG(Yudfpjd3AFdKe2U7&zdepTe>^s(@!&0X8TJ`h+-I?84Ml# literal 0 HcmV?d00001 diff --git a/discuss/web/static/assets/images/phoenix.png b/discuss/web/static/assets/images/phoenix.png new file mode 100644 index 0000000000000000000000000000000000000000..9c81075f63d2151e6f40e9aa66f665749a87cc6a GIT binary patch literal 13900 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 UWxWXg`G1#+f~I_}taaG`2PLnS&Hw-a literal 0 HcmV?d00001 diff --git a/discuss/web/static/assets/robots.txt b/discuss/web/static/assets/robots.txt new file mode 100644 index 0000000..3c9c7c0 --- /dev/null +++ b/discuss/web/static/assets/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-agent: * +# Disallow: / diff --git a/discuss/web/static/css/app.css b/discuss/web/static/css/app.css new file mode 100644 index 0000000..5314c34 --- /dev/null +++ b/discuss/web/static/css/app.css @@ -0,0 +1 @@ +/* This file is for your main application css. */ \ No newline at end of file diff --git a/discuss/web/static/css/phoenix.css b/discuss/web/static/css/phoenix.css new file mode 100644 index 0000000..df69d80 --- /dev/null +++ b/discuss/web/static/css/phoenix.css @@ -0,0 +1,82 @@ +/* Includes Bootstrap as well as some default style for the starter + * application. This can be safely deleted to start fresh. + */ + +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,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-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}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{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;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-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!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:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../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:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.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"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;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}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,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,.h5,.h6,h4,h5,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:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}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{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.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-md-1,.col-md-10,.col-md-11,.col-md-12,.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-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.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-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\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=checkbox]:focus,input[type=radio]: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.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-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,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top: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 .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{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;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.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:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.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,.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-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-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:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(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:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.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-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;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:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.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:3px}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}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn: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:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{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-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-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:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-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:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{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{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.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 #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.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:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.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{margin-bottom:5px;text-align:center}.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:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;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-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{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-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{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;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.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;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{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-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@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 .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;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-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@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:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-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{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-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,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.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,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.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,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.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,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.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:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.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 th:first-child,.panel>.table-responsive:first-child>.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 th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.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 th:last-child,.panel>.table-responsive:first-child>.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 th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.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 th:first-child,.panel>.table-responsive:last-child>.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 th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.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 th:last-child,.panel>.table-responsive:last-child>.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 th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.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:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;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{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.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;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.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%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left: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}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!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!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} + +/* Space out content a bit */ +body, form, ul, table { + margin-top: 20px; + margin-bottom: 20px; +} + +/* Phoenix flash messages */ +.alert:empty { display: none; } + +/* Phoenix inline forms in links and buttons */ +form.link, form.button { + display: inline; +} + +/* Custom page header */ +.header { + border-bottom: 1px solid #e5e5e5; +} +.logo { + width: 519px; + height: 71px; + display: inline-block; + margin-bottom: 1em; + background-image: url("/images/phoenix.png"); + background-size: 519px 71px; +} + +/* Everything but the jumbotron gets side spacing for mobile first views */ +.header, +.marketing { + padding-right: 15px; + padding-left: 15px; +} + +/* Customize container */ +@media (min-width: 768px) { + .container { + max-width: 730px; + } +} +.container-narrow > hr { + margin: 30px 0; +} + +/* Main marketing message */ +.jumbotron { + text-align: center; + border-bottom: 1px solid #e5e5e5; +} + +/* Supporting marketing content */ +.marketing { + margin: 35px 0; +} + +/* Responsive: Portrait tablets and up */ +@media screen and (min-width: 768px) { + /* Remove the padding we set earlier */ + .header, + .marketing { + padding-right: 0; + padding-left: 0; + } + /* Space out the masthead */ + .header { + margin-bottom: 30px; + } + /* Remove the bottom border on the jumbotron for visual effect */ + .jumbotron { + border-bottom: 0; + } +} \ No newline at end of file diff --git a/discuss/web/static/js/app.js b/discuss/web/static/js/app.js new file mode 100644 index 0000000..e7549b9 --- /dev/null +++ b/discuss/web/static/js/app.js @@ -0,0 +1,21 @@ +// Brunch automatically concatenates all files in your +// watched paths. Those paths can be configured at +// config.paths.watched in "brunch-config.js". +// +// However, those files will only be executed if +// explicitly imported. The only exception are files +// in vendor, which are never wrapped in imports and +// therefore are always executed. + +// Import dependencies +// +// If you no longer want to use a dependency, remember +// to also remove its path from "config.paths.watched". +import "phoenix_html" + +// Import local files +// +// Local files can be imported directly using relative +// paths "./socket" or full ones "web/static/js/socket". + +// import socket from "./socket" diff --git a/discuss/web/static/js/socket.js b/discuss/web/static/js/socket.js new file mode 100644 index 0000000..0f8d461 --- /dev/null +++ b/discuss/web/static/js/socket.js @@ -0,0 +1,62 @@ +// NOTE: The contents of this file will only be executed if +// you uncomment its entry in "web/static/js/app.js". + +// To use Phoenix channels, the first step is to import Socket +// and connect at the socket path in "lib/my_app/endpoint.ex": +import {Socket} from "phoenix" + +let socket = new Socket("/socket", {params: {token: window.userToken}}) + +// When you connect, you'll often need to authenticate the client. +// For example, imagine you have an authentication plug, `MyAuth`, +// which authenticates the session and assigns a `:current_user`. +// If the current user exists you can assign the user's token in +// the connection for use in the layout. +// +// In your "web/router.ex": +// +// pipeline :browser do +// ... +// plug MyAuth +// plug :put_user_token +// end +// +// defp put_user_token(conn, _) do +// if current_user = conn.assigns[:current_user] do +// token = Phoenix.Token.sign(conn, "user socket", current_user.id) +// assign(conn, :user_token, token) +// else +// conn +// end +// end +// +// Now you need to pass this token to JavaScript. You can do so +// inside a script tag in "web/templates/layout/app.html.eex": +// +// +// +// You will need to verify the user token in the "connect/2" function +// in "web/channels/user_socket.ex": +// +// def connect(%{"token" => token}, socket) do +// # max_age: 1209600 is equivalent to two weeks in seconds +// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do +// {:ok, user_id} -> +// {:ok, assign(socket, :user, user_id)} +// {:error, reason} -> +// :error +// end +// end +// +// Finally, pass the token on connect as below. Or remove it +// from connect if you don't care about authentication. + +socket.connect() + +// Now that you are connected, you can join channels with a topic: +let channel = socket.channel("topic:subtopic", {}) +channel.join() + .receive("ok", resp => { console.log("Joined successfully", resp) }) + .receive("error", resp => { console.log("Unable to join", resp) }) + +export default socket diff --git a/discuss/web/templates/layout/app.html.eex b/discuss/web/templates/layout/app.html.eex new file mode 100644 index 0000000..1a12df1 --- /dev/null +++ b/discuss/web/templates/layout/app.html.eex @@ -0,0 +1,35 @@ + + + + + + + + + + Hello Discuss! + "> + + + +
+
+ + +
+ + + + +
+ <%= render @view_module, @view_template, assigns %> +
+ +
+ + + diff --git a/discuss/web/templates/page/index.html.eex b/discuss/web/templates/page/index.html.eex new file mode 100644 index 0000000..8ff4b81 --- /dev/null +++ b/discuss/web/templates/page/index.html.eex @@ -0,0 +1,36 @@ +
+

<%= gettext "Welcome to %{name}", name: "Phoenix!" %>

+

A productive web framework that
does not compromise speed and maintainability.

+
+ +
+
+

Resources

+ +
+ + +
diff --git a/discuss/web/views/error_helpers.ex b/discuss/web/views/error_helpers.ex new file mode 100644 index 0000000..2d09888 --- /dev/null +++ b/discuss/web/views/error_helpers.ex @@ -0,0 +1,40 @@ +defmodule Discuss.ErrorHelpers do + @moduledoc """ + Conveniences for translating and building error messages. + """ + + use Phoenix.HTML + + @doc """ + Generates tag for inlined form input errors. + """ + def error_tag(form, field) do + if error = form.errors[field] do + content_tag :span, translate_error(error), class: "help-block" + end + end + + @doc """ + Translates an error message using gettext. + """ + def translate_error({msg, opts}) do + # Because error messages were defined within Ecto, we must + # call the Gettext module passing our Gettext backend. We + # also use the "errors" domain as translations are placed + # in the errors.po file. + # Ecto will pass the :count keyword if the error message is + # meant to be pluralized. + # On your own code and templates, depending on whether you + # need the message to be pluralized or not, this could be + # written simply as: + # + # dngettext "errors", "1 file", "%{count} files", count + # dgettext "errors", "is invalid" + # + if count = opts[:count] do + Gettext.dngettext(Discuss.Gettext, "errors", msg, msg, count, opts) + else + Gettext.dgettext(Discuss.Gettext, "errors", msg, opts) + end + end +end diff --git a/discuss/web/views/error_view.ex b/discuss/web/views/error_view.ex new file mode 100644 index 0000000..e0baf9e --- /dev/null +++ b/discuss/web/views/error_view.ex @@ -0,0 +1,17 @@ +defmodule Discuss.ErrorView do + use Discuss.Web, :view + + def render("404.html", _assigns) do + "Page not found" + end + + def render("500.html", _assigns) do + "Internal server error" + end + + # In case no render clause matches or no + # template is found, let's render it as 500 + def template_not_found(_template, assigns) do + render "500.html", assigns + end +end diff --git a/discuss/web/views/layout_view.ex b/discuss/web/views/layout_view.ex new file mode 100644 index 0000000..29e1136 --- /dev/null +++ b/discuss/web/views/layout_view.ex @@ -0,0 +1,3 @@ +defmodule Discuss.LayoutView do + use Discuss.Web, :view +end diff --git a/discuss/web/views/page_view.ex b/discuss/web/views/page_view.ex new file mode 100644 index 0000000..81b57c5 --- /dev/null +++ b/discuss/web/views/page_view.ex @@ -0,0 +1,3 @@ +defmodule Discuss.PageView do + use Discuss.Web, :view +end diff --git a/discuss/web/web.ex b/discuss/web/web.ex new file mode 100644 index 0000000..05f684f --- /dev/null +++ b/discuss/web/web.ex @@ -0,0 +1,81 @@ +defmodule Discuss.Web do + @moduledoc """ + A module that keeps using definitions for controllers, + views and so on. + + This can be used in your application as: + + use Discuss.Web, :controller + use Discuss.Web, :view + + The definitions below will be executed for every view, + controller, etc, so keep them short and clean, focused + on imports, uses and aliases. + + Do NOT define functions inside the quoted expressions + below. + """ + + def model do + quote do + use Ecto.Schema + + import Ecto + import Ecto.Changeset + import Ecto.Query + end + end + + def controller do + quote do + use Phoenix.Controller + + alias Discuss.Repo + import Ecto + import Ecto.Query + + import Discuss.Router.Helpers + import Discuss.Gettext + end + end + + def view do + quote do + use Phoenix.View, root: "web/templates" + + # Import convenience functions from controllers + import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1] + + # Use all HTML functionality (forms, tags, etc) + use Phoenix.HTML + + import Discuss.Router.Helpers + import Discuss.ErrorHelpers + import Discuss.Gettext + end + end + + def router do + quote do + use Phoenix.Router + end + end + + def channel do + quote do + use Phoenix.Channel + + alias Discuss.Repo + import Ecto + import Ecto.Query + import Discuss.Gettext + end + end + + @doc """ + When used, dispatch to the appropriate controller/view/etc. + """ + defmacro __using__(which) when is_atom(which) do + apply(__MODULE__, which, []) + end +end From 77c4feff4b8192ca9026ac0de5b1b9c15f1e3304 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Fri, 21 Oct 2016 15:45:32 -0700 Subject: [PATCH 08/86] configured database --- discuss/config/dev.exs | 4 ++-- discuss/web/templates/page/index.html.eex | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/discuss/config/dev.exs b/discuss/config/dev.exs index 5a4b3a4..906fecf 100644 --- a/discuss/config/dev.exs +++ b/discuss/config/dev.exs @@ -36,8 +36,8 @@ config :phoenix, :stacktrace_depth, 20 # Configure your database config :discuss, Discuss.Repo, adapter: Ecto.Adapters.Postgres, - username: "postgres", - password: "postgres", + username: "stephengrider", + password: "", database: "discuss_dev", hostname: "localhost", pool_size: 10 diff --git a/discuss/web/templates/page/index.html.eex b/discuss/web/templates/page/index.html.eex index 8ff4b81..1152374 100644 --- a/discuss/web/templates/page/index.html.eex +++ b/discuss/web/templates/page/index.html.eex @@ -16,6 +16,9 @@
  • Source
  • +
  • + Google +
  • From b4aca9dc183ba070f5495eef89fd5e49642a3f3f Mon Sep 17 00:00:00 2001 From: stephen grider Date: Fri, 21 Oct 2016 17:08:53 -0700 Subject: [PATCH 09/86] added material css --- discuss/web/templates/layout/app.html.eex | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/discuss/web/templates/layout/app.html.eex b/discuss/web/templates/layout/app.html.eex index 1a12df1..522c074 100644 --- a/discuss/web/templates/layout/app.html.eex +++ b/discuss/web/templates/layout/app.html.eex @@ -9,18 +9,19 @@ Hello Discuss! "> + + +
    -
    - - -
    From d07d22f02cb78f1552356ad9187c9534e342f8d2 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 11:01:54 -0700 Subject: [PATCH 10/86] added topics migration --- .../priv/repo/migrations/20161024175806_add_topics.exs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 discuss/priv/repo/migrations/20161024175806_add_topics.exs diff --git a/discuss/priv/repo/migrations/20161024175806_add_topics.exs b/discuss/priv/repo/migrations/20161024175806_add_topics.exs new file mode 100644 index 0000000..0c5677e --- /dev/null +++ b/discuss/priv/repo/migrations/20161024175806_add_topics.exs @@ -0,0 +1,9 @@ +defmodule Discuss.Repo.Migrations.AddTopics do + use Ecto.Migration + + def change do + create table(:topics) do + add :title, :string + end + end +end From 6df926e81d105a36475da5fceaec56002dc39212 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 11:24:37 -0700 Subject: [PATCH 11/86] added new route --- discuss/web/router.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/discuss/web/router.ex b/discuss/web/router.ex index f1e18de..e326dad 100644 --- a/discuss/web/router.ex +++ b/discuss/web/router.ex @@ -17,6 +17,7 @@ defmodule Discuss.Router do pipe_through :browser # Use the default browser stack get "/", PageController, :index + get "/topics/new", TopicController, :new end # Other scopes may use custom stacks. From 7ea0383e947216f00906645381777576e315ad39 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 11:31:35 -0700 Subject: [PATCH 12/86] added topic controller --- discuss/web/controllers/topic_controller.ex | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 discuss/web/controllers/topic_controller.ex diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex new file mode 100644 index 0000000..4584552 --- /dev/null +++ b/discuss/web/controllers/topic_controller.ex @@ -0,0 +1,5 @@ +defmodule Discuss.TopicController do + def new() do + + end +end From 4b8fa1a6c7aee0c03c9e21ea1cbf4c56b74aaa9b Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 12:00:58 -0700 Subject: [PATCH 13/86] receive conn argument --- discuss/web/controllers/topic_controller.ex | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 4584552..2e4fdf6 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -1,5 +1,11 @@ defmodule Discuss.TopicController do - def new() do + use Discuss.Web, :controller + def new(conn, params) do + IO.puts "++++" + IO.inspect conn + IO.puts "++++" + IO.inspect params + IO.puts "++++" end end From ad606cefc40bad440828a7198fc13ed7c4ef948a Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 12:17:23 -0700 Subject: [PATCH 14/86] delete log --- discuss/web/controllers/topic_controller.ex | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 2e4fdf6..ef2d79c 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -2,10 +2,6 @@ defmodule Discuss.TopicController do use Discuss.Web, :controller def new(conn, params) do - IO.puts "++++" - IO.inspect conn - IO.puts "++++" - IO.inspect params - IO.puts "++++" + end end From d988f0163bebac5d4450c5dad99ae23bdec8eb7c Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 12:28:01 -0700 Subject: [PATCH 15/86] added topic --- discuss/web/models/topic.ex | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 discuss/web/models/topic.ex diff --git a/discuss/web/models/topic.ex b/discuss/web/models/topic.ex new file mode 100644 index 0000000..cd0a224 --- /dev/null +++ b/discuss/web/models/topic.ex @@ -0,0 +1,7 @@ +defmodule Discuss.Topic do + use Discuss.Web, :model + + schema "topics" do + field :title, :string + end +end From b1e3e082f4c7be3c398db261ed0c65860633afcb Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 12:48:40 -0700 Subject: [PATCH 16/86] added changeset --- discuss/web/models/topic.ex | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/discuss/web/models/topic.ex b/discuss/web/models/topic.ex index cd0a224..fae7f4f 100644 --- a/discuss/web/models/topic.ex +++ b/discuss/web/models/topic.ex @@ -4,4 +4,10 @@ defmodule Discuss.Topic do schema "topics" do field :title, :string end + + def changeset(struct, params \\ %{}) do + struct + |> cast(params, [:title]) + |> validate_required([:title]) + end end From c3d141c6c9119c145b4f8b362a404403f7bc81c7 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 13:09:31 -0700 Subject: [PATCH 17/86] made changeset --- discuss/web/controllers/topic_controller.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index ef2d79c..8fdd887 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -1,7 +1,9 @@ defmodule Discuss.TopicController do use Discuss.Web, :controller - def new(conn, params) do + alias Discuss.Topic + def new(conn, params) do + changeset = Topic.changeset(%Topic{}, %{}) end end From dcfc80f1764a273c12aec1ca91ae8ec1837250e8 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 13:31:39 -0700 Subject: [PATCH 18/86] added form tag --- discuss/web/controllers/topic_controller.ex | 2 ++ discuss/web/templates/topic/new.html.eex | 3 +++ discuss/web/views/topic_view.ex | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 discuss/web/templates/topic/new.html.eex create mode 100644 discuss/web/views/topic_view.ex diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 8fdd887..dd94a05 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -5,5 +5,7 @@ defmodule Discuss.TopicController do def new(conn, params) do changeset = Topic.changeset(%Topic{}, %{}) + + render conn, "new.html" end end diff --git a/discuss/web/templates/topic/new.html.eex b/discuss/web/templates/topic/new.html.eex new file mode 100644 index 0000000..6f140b6 --- /dev/null +++ b/discuss/web/templates/topic/new.html.eex @@ -0,0 +1,3 @@ +<%= form_for @changeset, post_path(@conn, :create), fn f -> %> + +<% end %> diff --git a/discuss/web/views/topic_view.ex b/discuss/web/views/topic_view.ex new file mode 100644 index 0000000..4a7df37 --- /dev/null +++ b/discuss/web/views/topic_view.ex @@ -0,0 +1,3 @@ +defmodule Discuss.TopicView do + use Discuss.Web, :view +end From 79d2a1ad52290f01ad6734b422c982fe920381a7 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 14:01:25 -0700 Subject: [PATCH 19/86] added form tags --- discuss/web/controllers/topic_controller.ex | 2 +- discuss/web/templates/topic/new.html.eex | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index dd94a05..826f704 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -6,6 +6,6 @@ defmodule Discuss.TopicController do def new(conn, params) do changeset = Topic.changeset(%Topic{}, %{}) - render conn, "new.html" + render conn, "new.html", changeset: changeset end end diff --git a/discuss/web/templates/topic/new.html.eex b/discuss/web/templates/topic/new.html.eex index 6f140b6..bd7d5c9 100644 --- a/discuss/web/templates/topic/new.html.eex +++ b/discuss/web/templates/topic/new.html.eex @@ -1,3 +1,7 @@ -<%= form_for @changeset, post_path(@conn, :create), fn f -> %> +<%= form_for @changeset, topic_path(@conn, :create), fn f -> %> +
    + <%= text_input f, :title, placeholder: "Title", class: "form-control" %> +
    + <%= submit "Save Topic", class: "btn btn-primary" %> <% end %> From e0a231e3e42234bdd7e4f2ab1f80e383820dafee Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 14:18:12 -0700 Subject: [PATCH 20/86] added create henadler --- discuss/web/controllers/topic_controller.ex | 4 ++++ discuss/web/router.ex | 1 + 2 files changed, 5 insertions(+) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 826f704..ed6c967 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -8,4 +8,8 @@ defmodule Discuss.TopicController do render conn, "new.html", changeset: changeset end + + def create(conn, %{"topic" => topic}) do + + end end diff --git a/discuss/web/router.ex b/discuss/web/router.ex index e326dad..d1e9faf 100644 --- a/discuss/web/router.ex +++ b/discuss/web/router.ex @@ -18,6 +18,7 @@ defmodule Discuss.Router do get "/", PageController, :index get "/topics/new", TopicController, :new + post "/topics", TopicController, :create end # Other scopes may use custom stacks. From e8c4936c9dc3bbf4887ab3ca6beca787b4a0442b Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 14:29:37 -0700 Subject: [PATCH 21/86] handle warning --- discuss/web/controllers/topic_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index ed6c967..a3a2b8b 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -3,7 +3,7 @@ defmodule Discuss.TopicController do alias Discuss.Topic - def new(conn, params) do + def new(conn, _params) do changeset = Topic.changeset(%Topic{}, %{}) render conn, "new.html", changeset: changeset From de2d70713b5461e601191bd365197fd512389e65 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 15:18:06 -0700 Subject: [PATCH 22/86] insert new topic --- discuss/web/controllers/topic_controller.ex | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index a3a2b8b..4c19ac1 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -10,6 +10,11 @@ defmodule Discuss.TopicController do end def create(conn, %{"topic" => topic}) do + changeset = Topic.changeset(%Topic{}, topic) + case Repo.insert(changeset) do + {:ok, post} -> IO.inspect(post) + {:error, changeset} -> IO.inspect(changeset) + end end end From 61a68b8ab529e170ea43dbf01c8d2ae6cb4584f6 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 15:27:47 -0700 Subject: [PATCH 23/86] handled fail to insert topic --- discuss/web/controllers/topic_controller.ex | 3 ++- discuss/web/static/css/app.css | 2 +- discuss/web/templates/topic/new.html.eex | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 4c19ac1..5c9beaf 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -14,7 +14,8 @@ defmodule Discuss.TopicController do case Repo.insert(changeset) do {:ok, post} -> IO.inspect(post) - {:error, changeset} -> IO.inspect(changeset) + {:error, changeset} -> + render conn, "new.html", changeset: changeset end end end diff --git a/discuss/web/static/css/app.css b/discuss/web/static/css/app.css index 5314c34..21d59de 100644 --- a/discuss/web/static/css/app.css +++ b/discuss/web/static/css/app.css @@ -1 +1 @@ -/* This file is for your main application css. */ \ No newline at end of file +/* This file is for your main application css. */ diff --git a/discuss/web/templates/topic/new.html.eex b/discuss/web/templates/topic/new.html.eex index bd7d5c9..820af65 100644 --- a/discuss/web/templates/topic/new.html.eex +++ b/discuss/web/templates/topic/new.html.eex @@ -1,6 +1,7 @@ <%= form_for @changeset, topic_path(@conn, :create), fn f -> %>
    <%= text_input f, :title, placeholder: "Title", class: "form-control" %> + <%= error_tag f, :title %>
    <%= submit "Save Topic", class: "btn btn-primary" %> From 4ef702c8c63776a33780cb7ca9987100e884c211 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 15:35:12 -0700 Subject: [PATCH 24/86] added in css --- discuss/web/static/css/app.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/discuss/web/static/css/app.css b/discuss/web/static/css/app.css index 21d59de..84007bf 100644 --- a/discuss/web/static/css/app.css +++ b/discuss/web/static/css/app.css @@ -1 +1,13 @@ /* This file is for your main application css. */ + +.help-block { + color: red; + text-transform: capitalize; + position: absolute; + top: 37px; +} + +.form-group { + position: relative; + margin-bottom: 25px; +} From 6697c10b5bf236fd243666f295fc765afc3d124a Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 15:43:59 -0700 Subject: [PATCH 25/86] added new route --- discuss/web/controllers/topic_controller.ex | 4 ++++ discuss/web/router.ex | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 5c9beaf..c48bc19 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -3,6 +3,10 @@ defmodule Discuss.TopicController do alias Discuss.Topic + def index(conn, _params) do + + end + def new(conn, _params) do changeset = Topic.changeset(%Topic{}, %{}) diff --git a/discuss/web/router.ex b/discuss/web/router.ex index d1e9faf..4acbfe9 100644 --- a/discuss/web/router.ex +++ b/discuss/web/router.ex @@ -16,7 +16,7 @@ defmodule Discuss.Router do scope "/", Discuss do pipe_through :browser # Use the default browser stack - get "/", PageController, :index + get "/", TopicController, :index get "/topics/new", TopicController, :new post "/topics", TopicController, :create end From cca353e7b951c909b147311b02c0084cb6428f70 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Mon, 24 Oct 2016 15:51:44 -0700 Subject: [PATCH 26/86] added query for all topics --- discuss/web/controllers/topic_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index c48bc19..2579663 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -4,7 +4,7 @@ defmodule Discuss.TopicController do alias Discuss.Topic def index(conn, _params) do - + topics = Repo.all(Topic) end def new(conn, _params) do From 3c3b642e66f3a74cadd7d1920f6bf3e550c6d65a Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 11:04:27 -0700 Subject: [PATCH 27/86] render list of topics --- discuss/web/controllers/topic_controller.ex | 1 + discuss/web/templates/topic/index.html.eex | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 discuss/web/templates/topic/index.html.eex diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 2579663..48002d2 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -5,6 +5,7 @@ defmodule Discuss.TopicController do def index(conn, _params) do topics = Repo.all(Topic) + render conn, "index.html", topics: topics end def new(conn, _params) do diff --git a/discuss/web/templates/topic/index.html.eex b/discuss/web/templates/topic/index.html.eex new file mode 100644 index 0000000..b6bd304 --- /dev/null +++ b/discuss/web/templates/topic/index.html.eex @@ -0,0 +1,9 @@ +

    Topics

    + +
      + <%= for topic <- @topics do %> +
    • + <%= topic.title %> +
    • + <% end %> +
    From 4e74f34cb5ad425ccfb55aad97dfaa29647fcc9e Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 11:15:14 -0700 Subject: [PATCH 28/86] redirect from topic create to topic index --- discuss/web/controllers/topic_controller.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 48002d2..a64757b 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -18,7 +18,10 @@ defmodule Discuss.TopicController do changeset = Topic.changeset(%Topic{}, topic) case Repo.insert(changeset) do - {:ok, post} -> IO.inspect(post) + {:ok, post} -> + conn + |> put_flash(:info, "Topic Created") + |> redirect(to: topic_path(conn, :index)) {:error, changeset} -> render conn, "new.html", changeset: changeset end From 876309a47f2934afdac466a2438c39c81a302a06 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 11:39:36 -0700 Subject: [PATCH 29/86] added link tag to topic index --- discuss/web/templates/layout/app.html.eex | 1 + discuss/web/templates/topic/index.html.eex | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/discuss/web/templates/layout/app.html.eex b/discuss/web/templates/layout/app.html.eex index 522c074..6dcc6eb 100644 --- a/discuss/web/templates/layout/app.html.eex +++ b/discuss/web/templates/layout/app.html.eex @@ -10,6 +10,7 @@ Hello Discuss! "> + diff --git a/discuss/web/templates/topic/index.html.eex b/discuss/web/templates/topic/index.html.eex index b6bd304..c96dcbb 100644 --- a/discuss/web/templates/topic/index.html.eex +++ b/discuss/web/templates/topic/index.html.eex @@ -1,4 +1,4 @@ -

    Topics

    +
    Topics
      <%= for topic <- @topics do %> @@ -7,3 +7,10 @@ <% end %>
    + +
    + <%= link to: topic_path(@conn, :new), class: "btn-floating btn-large waves-effect waves-light red" do %> + add + <% end %> + +
    From 673c355e2229be37868c7b6ad1f741843d6f3afd Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 12:02:12 -0700 Subject: [PATCH 30/86] added edit route --- discuss/web/controllers/topic_controller.ex | 4 ++++ discuss/web/router.ex | 1 + 2 files changed, 5 insertions(+) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index a64757b..e92eb43 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -26,4 +26,8 @@ defmodule Discuss.TopicController do render conn, "new.html", changeset: changeset end end + + def edit(conn, %{"id" => topic_id}) do + + end end diff --git a/discuss/web/router.ex b/discuss/web/router.ex index 4acbfe9..e7f1836 100644 --- a/discuss/web/router.ex +++ b/discuss/web/router.ex @@ -19,6 +19,7 @@ defmodule Discuss.Router do get "/", TopicController, :index get "/topics/new", TopicController, :new post "/topics", TopicController, :create + get "/topics/:id/edit", TopicController, :edit end # Other scopes may use custom stacks. From df6ca1eb79bf63bdac51d4ada2d86fc7bedb2ba1 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 12:15:13 -0700 Subject: [PATCH 31/86] added edit form --- discuss/web/controllers/topic_controller.ex | 3 +++ discuss/web/templates/topic/edit.html.eex | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 discuss/web/templates/topic/edit.html.eex diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index e92eb43..2834e11 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -28,6 +28,9 @@ defmodule Discuss.TopicController do end def edit(conn, %{"id" => topic_id}) do + topic = Repo.get(Topic, topic_id) + changeset = Topic.changeset(topic) + render conn, "edit.html", changeset: changeset, topic: topic end end diff --git a/discuss/web/templates/topic/edit.html.eex b/discuss/web/templates/topic/edit.html.eex new file mode 100644 index 0000000..cc31292 --- /dev/null +++ b/discuss/web/templates/topic/edit.html.eex @@ -0,0 +1,3 @@ +<%= form_for @changeset, topic_path(@conn, :update, @topic), fn f -> %> + +<% end %> From 299f99e54550867fa100fc68269d47dc01a029c1 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 12:30:47 -0700 Subject: [PATCH 32/86] added update route --- discuss/web/router.ex | 1 + discuss/web/templates/topic/edit.html.eex | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/discuss/web/router.ex b/discuss/web/router.ex index e7f1836..46c1343 100644 --- a/discuss/web/router.ex +++ b/discuss/web/router.ex @@ -20,6 +20,7 @@ defmodule Discuss.Router do get "/topics/new", TopicController, :new post "/topics", TopicController, :create get "/topics/:id/edit", TopicController, :edit + put "/topics/:id", TopicController, :update end # Other scopes may use custom stacks. diff --git a/discuss/web/templates/topic/edit.html.eex b/discuss/web/templates/topic/edit.html.eex index cc31292..70fd339 100644 --- a/discuss/web/templates/topic/edit.html.eex +++ b/discuss/web/templates/topic/edit.html.eex @@ -1,3 +1,8 @@ <%= form_for @changeset, topic_path(@conn, :update, @topic), fn f -> %> +
    + <%= text_input f, :title, placeholder: "Title", class: "form-control" %> + <%= error_tag f, :title %> +
    + <%= submit "Save Topic", class: "btn btn-primary" %> <% end %> From fcb9d210a7e009b9117f269f82f007df94b8a0d9 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 12:42:51 -0700 Subject: [PATCH 33/86] topics can be edited --- discuss/web/controllers/topic_controller.ex | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 2834e11..ee92528 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -18,7 +18,7 @@ defmodule Discuss.TopicController do changeset = Topic.changeset(%Topic{}, topic) case Repo.insert(changeset) do - {:ok, post} -> + {:ok, _topic} -> conn |> put_flash(:info, "Topic Created") |> redirect(to: topic_path(conn, :index)) @@ -33,4 +33,17 @@ defmodule Discuss.TopicController do render conn, "edit.html", changeset: changeset, topic: topic end + + def update(conn, %{"id" => topic_id, "topic" => topic}) do + changeset = Repo.get(Topic, topic_id) |> Topic.changeset(topic) + + case Repo.update(changeset) do + {:ok, _topic} -> + conn + |> put_flash(:info, "Topic Updated") + |> redirect(to: topic_path(conn, :index)) + {:error, changeset} -> + render conn, "edit.html", changeset: changeset + end + end end From 625aa60306329a0fa614f86608392e5abb0ba0fc Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 12:52:14 -0700 Subject: [PATCH 34/86] added link tag --- discuss/web/controllers/topic_controller.ex | 5 +++-- discuss/web/templates/topic/index.html.eex | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index ee92528..dbb06c0 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -35,7 +35,8 @@ defmodule Discuss.TopicController do end def update(conn, %{"id" => topic_id, "topic" => topic}) do - changeset = Repo.get(Topic, topic_id) |> Topic.changeset(topic) + old_topic = Repo.get(Topic, topic_id) + changeset = Topic.changeset(old_topic, topic) case Repo.update(changeset) do {:ok, _topic} -> @@ -43,7 +44,7 @@ defmodule Discuss.TopicController do |> put_flash(:info, "Topic Updated") |> redirect(to: topic_path(conn, :index)) {:error, changeset} -> - render conn, "edit.html", changeset: changeset + render conn, "edit.html", changeset: changeset, topic: old_topic end end end diff --git a/discuss/web/templates/topic/index.html.eex b/discuss/web/templates/topic/index.html.eex index c96dcbb..e5b68b0 100644 --- a/discuss/web/templates/topic/index.html.eex +++ b/discuss/web/templates/topic/index.html.eex @@ -4,6 +4,10 @@ <%= for topic <- @topics do %>
  • <%= topic.title %> + +
    + <%= link "Edit", to: topic_path(@conn, :edit, topic) %> +
  • <% end %> From 406b3fed3dd4716e964aa8a7d447dcd801db9466 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 13:02:44 -0700 Subject: [PATCH 35/86] changed router to use resources --- discuss/web/router.ex | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/discuss/web/router.ex b/discuss/web/router.ex index 46c1343..ce8628c 100644 --- a/discuss/web/router.ex +++ b/discuss/web/router.ex @@ -16,11 +16,7 @@ defmodule Discuss.Router do scope "/", Discuss do pipe_through :browser # Use the default browser stack - get "/", TopicController, :index - get "/topics/new", TopicController, :new - post "/topics", TopicController, :create - get "/topics/:id/edit", TopicController, :edit - put "/topics/:id", TopicController, :update + resources "/", TopicController end # Other scopes may use custom stacks. From c7782cb90e37d1a51d6586d10c4c63e11d35ef7b Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 13:16:45 -0700 Subject: [PATCH 36/86] added delete handler --- discuss/web/controllers/topic_controller.ex | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index dbb06c0..4a6e059 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -47,4 +47,12 @@ defmodule Discuss.TopicController do render conn, "edit.html", changeset: changeset, topic: old_topic end end + + def delete(conn, %{"id" => topic_id}) do + Repo.get!(Topic, topic_id) |> Repo.delete! + + conn + |> put_flash(:info, "Topic Deleted") + |> redirect(to: topic_path(conn, :index)) + end end From f3b75ae67321c80cbfdb2fb261ca93c5a5416462 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 13:28:46 -0700 Subject: [PATCH 37/86] finish topiccontroller --- discuss/web/templates/topic/index.html.eex | 1 + 1 file changed, 1 insertion(+) diff --git a/discuss/web/templates/topic/index.html.eex b/discuss/web/templates/topic/index.html.eex index e5b68b0..b26d0c6 100644 --- a/discuss/web/templates/topic/index.html.eex +++ b/discuss/web/templates/topic/index.html.eex @@ -7,6 +7,7 @@
    <%= link "Edit", to: topic_path(@conn, :edit, topic) %> + <%= link "Delete", to: topic_path(@conn, :delete, topic), method: :delete %>
    <% end %> From 37b8b9f5b5efb8c38a130c5576997550fcde6c42 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 14:34:48 -0700 Subject: [PATCH 38/86] installed ueberauth --- discuss/config/config.exs | 9 +++++++++ discuss/mix.exs | 6 ++++-- discuss/mix.lock | 15 +++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/discuss/config/config.exs b/discuss/config/config.exs index e2974c8..9e1a4f5 100644 --- a/discuss/config/config.exs +++ b/discuss/config/config.exs @@ -25,3 +25,12 @@ config :logger, :console, # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env}.exs" + +config :ueberauth, Ueberauth, + providers: [ + github: { Ueberauth.Strategy.Github, [] } + ] + +config :ueberauth, Ueberauth.Strategy.Github.Oauth, + client_id: "9239f35642a875b098aa", + client_secret: "fbfacf2758ec121ddaa3931d7bc1ba1b4441fc5d" diff --git a/discuss/mix.exs b/discuss/mix.exs index 6d4fad0..c25fe2b 100644 --- a/discuss/mix.exs +++ b/discuss/mix.exs @@ -19,7 +19,7 @@ defmodule Discuss.Mixfile do def application do [mod: {Discuss, []}, applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext, - :phoenix_ecto, :postgrex]] + :phoenix_ecto, :postgrex, :ueberauth, :ueberauth_github]] end # Specifies which paths to compile per environment. @@ -37,7 +37,9 @@ defmodule Discuss.Mixfile do {:phoenix_html, "~> 2.6"}, {:phoenix_live_reload, "~> 1.0", only: :dev}, {:gettext, "~> 0.11"}, - {:cowboy, "~> 1.0"}] + {:cowboy, "~> 1.0"}, + {:ueberauth, "~> 0.3"}, + {:ueberauth_github, "~> 0.4"}] end # Aliases are shortcuts or tasks specific to the current project. diff --git a/discuss/mix.lock b/discuss/mix.lock index ed8089c..4831ac3 100644 --- a/discuss/mix.lock +++ b/discuss/mix.lock @@ -1,4 +1,5 @@ -%{"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []}, +%{"certifi": {:hex, :certifi, "0.7.0", "861a57f3808f7eb0c2d1802afeaae0fa5de813b0df0979153cbafcd853ababaf", [:rebar3], []}, + "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []}, "cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:rebar, :make], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]}, "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, "db_connection": {:hex, :db_connection, "1.0.0", "63c03e520d54886a66104d34e32397ba960db6e74b596ce221592c07d6a40d8d", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]}, @@ -6,7 +7,14 @@ "ecto": {:hex, :ecto, "2.0.5", "7f4c79ac41ffba1a4c032b69d7045489f0069c256de606523c65d9f8188e502d", [:mix], [{:db_connection, "~> 1.0-rc.4", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.1.2 or ~> 1.2", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.7.7", [hex: :mariaex, optional: true]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.12.0", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}]}, "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []}, "gettext": {:hex, :gettext, "0.11.0", "80c1dd42d270482418fa158ec5ba073d2980e3718bacad86f3d4ad71d5667679", [:mix], []}, + "hackney": {:hex, :hackney, "1.6.2", "96a0a5e7e65b7acad8031d231965718cc70a9b4131a8b033b7543bbd673b8210", [:rebar3, :mix], [{:certifi, "0.7.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]}, + "httpoison": {:hex, :httpoison, "0.9.2", "a211a8e87403a043c41218e64df250d321f236ac57f786c6a0ccf3e9e817c819", [:mix], [{:hackney, "~> 1.6.0", [hex: :hackney, optional: false]}]}, + "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, "mime": {:hex, :mime, "1.0.1", "05c393850524767d13a53627df71beeebb016205eb43bfbd92d14d24ec7a1b51", [:mix], []}, + "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, + "mimetype_parser": {:hex, :mimetype_parser, "0.1.2", "221d2d3f727e89d80de5e1610fc2ce444514aa56f873da1b8fc9c033143e5d6a", [:mix], []}, + "oauth2": {:hex, :oauth2, "0.6.0", "f3967aba3f358e64fb70046f659f04ea753d7a407ce032046dd048fc2e2e4be3", [:mix], [{:httpoison, "~> 0.7", [hex: :httpoison, optional: false]}, {:mimetype_parser, "~> 0.1", [hex: :mimetype_parser, optional: false]}, {:poison, "~> 1.3 or ~> 2.0", [hex: :poison, optional: false]}]}, "phoenix": {:hex, :phoenix, "1.2.1", "6dc592249ab73c67575769765b66ad164ad25d83defa3492dc6ae269bd2a68ab", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.1", [hex: :plug, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}]}, "phoenix_ecto": {:hex, :phoenix_ecto, "3.0.1", "42eb486ef732cf209d0a353e791806721f33ff40beab0a86f02070a5649ed00a", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}, {:phoenix_html, "~> 2.6", [hex: :phoenix_html, optional: true]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]}, "phoenix_html": {:hex, :phoenix_html, "2.7.0", "19e12e2044340c2e43df206a06d059677c59ea1868bd1c35165438d592cd420b", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]}, @@ -16,4 +24,7 @@ "poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [:mix], []}, "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], []}, "postgrex": {:hex, :postgrex, "0.12.1", "2f8b46cb3a44dcd42f42938abedbfffe7e103ba4ce810ccbeee8dcf27ca0fb06", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}, {:db_connection, "~> 1.0-rc.4", [hex: :db_connection, optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}]}, - "ranch": {:hex, :ranch, "1.2.1", "a6fb992c10f2187b46ffd17ce398ddf8a54f691b81768f9ef5f461ea7e28c762", [:make], []}} + "ranch": {:hex, :ranch, "1.2.1", "a6fb992c10f2187b46ffd17ce398ddf8a54f691b81768f9ef5f461ea7e28c762", [:make], []}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:rebar, :make], []}, + "ueberauth": {:hex, :ueberauth, "0.4.0", "bc72d5e5a7bdcbfcf28a756e34630816edabc926303bdce7e171f7ac7ffa4f91", [:mix], [{:plug, "~> 1.2", [hex: :plug, optional: false]}]}, + "ueberauth_github": {:hex, :ueberauth_github, "0.4.0", "3fe9a20b88c3379bc1eed55f43a6cbf37b4c5e088d565abd07382dc1c99230a2", [:mix], [{:oauth2, "0.6.0", [hex: :oauth2, optional: false]}, {:ueberauth, "~> 0.4", [hex: :ueberauth, optional: false]}]}} From a8a58e2535f793220134601a88ff9ac151dbf11d Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 15:14:51 -0700 Subject: [PATCH 39/86] added oauth routes --- discuss/web/controllers/auth_controller.ex | 12 ++++++++++++ discuss/web/router.ex | 7 +++++++ 2 files changed, 19 insertions(+) create mode 100644 discuss/web/controllers/auth_controller.ex diff --git a/discuss/web/controllers/auth_controller.ex b/discuss/web/controllers/auth_controller.ex new file mode 100644 index 0000000..000b2e2 --- /dev/null +++ b/discuss/web/controllers/auth_controller.ex @@ -0,0 +1,12 @@ +defmodule Discuss.AuthController do + use Discuss.Web, :controller + plug Ueberauth + + def callback(conn, params) do + IO.puts "+++++" + IO.inspect(conn.assigns) + IO.puts "+++++" + IO.inspect(params) + IO.puts "+++++" + end +end diff --git a/discuss/web/router.ex b/discuss/web/router.ex index ce8628c..08dd90c 100644 --- a/discuss/web/router.ex +++ b/discuss/web/router.ex @@ -19,6 +19,13 @@ defmodule Discuss.Router do resources "/", TopicController end + scope "/auth", Discuss do + pipe_through :browser + + get "/:provider", AuthController, :request + get "/:provider/callback", AuthController, :callback + end + # Other scopes may use custom stacks. # scope "/api", Discuss do # pipe_through :api From 511cf814f1b899e3d88b98f9d41d8c058681773a Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 15:30:35 -0700 Subject: [PATCH 40/86] fixed config file --- discuss/config/config.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discuss/config/config.exs b/discuss/config/config.exs index 9e1a4f5..cb36295 100644 --- a/discuss/config/config.exs +++ b/discuss/config/config.exs @@ -31,6 +31,6 @@ config :ueberauth, Ueberauth, github: { Ueberauth.Strategy.Github, [] } ] -config :ueberauth, Ueberauth.Strategy.Github.Oauth, +config :ueberauth, Ueberauth.Strategy.Github.OAuth, client_id: "9239f35642a875b098aa", client_secret: "fbfacf2758ec121ddaa3931d7bc1ba1b4441fc5d" From 8bc1549a5a9e5c3bfd4304a9886064b82e830085 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 15:41:04 -0700 Subject: [PATCH 41/86] added users migration --- .../repo/migrations/20161025223705_add_users.exs | 13 +++++++++++++ discuss/web/controllers/auth_controller.ex | 6 +----- 2 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 discuss/priv/repo/migrations/20161025223705_add_users.exs diff --git a/discuss/priv/repo/migrations/20161025223705_add_users.exs b/discuss/priv/repo/migrations/20161025223705_add_users.exs new file mode 100644 index 0000000..fddfc24 --- /dev/null +++ b/discuss/priv/repo/migrations/20161025223705_add_users.exs @@ -0,0 +1,13 @@ +defmodule Discuss.Repo.Migrations.AddUsers do + use Ecto.Migration + + def change do + create table(:users) do + add :email, :string + add :provider, :string + add :token, :string + + timestamps() + end + end +end diff --git a/discuss/web/controllers/auth_controller.ex b/discuss/web/controllers/auth_controller.ex index 000b2e2..47306e3 100644 --- a/discuss/web/controllers/auth_controller.ex +++ b/discuss/web/controllers/auth_controller.ex @@ -3,10 +3,6 @@ defmodule Discuss.AuthController do plug Ueberauth def callback(conn, params) do - IO.puts "+++++" - IO.inspect(conn.assigns) - IO.puts "+++++" - IO.inspect(params) - IO.puts "+++++" + end end From 20c8280499bb3856f54f08ce92e7aa72559c79eb Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 15:54:52 -0700 Subject: [PATCH 42/86] added user model --- discuss/web/controllers/auth_controller.ex | 4 ++-- discuss/web/models/user.ex | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 discuss/web/models/user.ex diff --git a/discuss/web/controllers/auth_controller.ex b/discuss/web/controllers/auth_controller.ex index 47306e3..9f066c4 100644 --- a/discuss/web/controllers/auth_controller.ex +++ b/discuss/web/controllers/auth_controller.ex @@ -2,7 +2,7 @@ defmodule Discuss.AuthController do use Discuss.Web, :controller plug Ueberauth - def callback(conn, params) do - + def callback(%{assigns: %{ueberauth_auth: auth}} = conn, params) do + end end diff --git a/discuss/web/models/user.ex b/discuss/web/models/user.ex new file mode 100644 index 0000000..5da9b09 --- /dev/null +++ b/discuss/web/models/user.ex @@ -0,0 +1,17 @@ +defmodule Discuss.User do + use Discuss.Web, :model + + schema "users" do + field :email, :string + field :provider, :string + field :token, :string + + timestamps() + end + + def changeset(struct, params \\ %{}) do + struct + |> cast(params, [:email, :provider, :token]) + |> validate_required([:email, :provider, :token]) + end +end From ff02e9170116ba98773503ee57dad1501a10c7e0 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 16:47:09 -0700 Subject: [PATCH 43/86] made user changeset --- discuss/web/controllers/auth_controller.ex | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/auth_controller.ex b/discuss/web/controllers/auth_controller.ex index 9f066c4..7def4a4 100644 --- a/discuss/web/controllers/auth_controller.ex +++ b/discuss/web/controllers/auth_controller.ex @@ -2,7 +2,10 @@ defmodule Discuss.AuthController do use Discuss.Web, :controller plug Ueberauth + alias Discuss.User + def callback(%{assigns: %{ueberauth_auth: auth}} = conn, params) do - + user_params = %{token: auth.credentials.token, email: auth.info.email, provider: "github"} + changeset = User.changeset(%User{}, user_params) end end From 06c419c289909feeb65da1bb49a179e832846b54 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 16:56:19 -0700 Subject: [PATCH 44/86] added new user --- discuss/web/controllers/auth_controller.ex | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/discuss/web/controllers/auth_controller.ex b/discuss/web/controllers/auth_controller.ex index 7def4a4..be9118b 100644 --- a/discuss/web/controllers/auth_controller.ex +++ b/discuss/web/controllers/auth_controller.ex @@ -7,5 +7,16 @@ defmodule Discuss.AuthController do def callback(%{assigns: %{ueberauth_auth: auth}} = conn, params) do user_params = %{token: auth.credentials.token, email: auth.info.email, provider: "github"} changeset = User.changeset(%User{}, user_params) + + insert_or_update_user(changeset) + end + + defp insert_or_update_user(changeset) do + case Repo.get_by(User, email: changeset.changes.email) do + nil -> + Repo.insert(changeset) + user -> + {:ok, user} + end end end From e3ffd31fbaf4c0473d2e58e1829f30858e2d86f2 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 17:10:09 -0700 Subject: [PATCH 45/86] added signin helper func --- discuss/web/controllers/auth_controller.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/auth_controller.ex b/discuss/web/controllers/auth_controller.ex index be9118b..4c1829c 100644 --- a/discuss/web/controllers/auth_controller.ex +++ b/discuss/web/controllers/auth_controller.ex @@ -8,7 +8,11 @@ defmodule Discuss.AuthController do user_params = %{token: auth.credentials.token, email: auth.info.email, provider: "github"} changeset = User.changeset(%User{}, user_params) - insert_or_update_user(changeset) + signin(conn, changeset) + end + + defp signin(conn, changeset) do + end defp insert_or_update_user(changeset) do From 3ab4afd62b85f2fde265ac5b1c92ffa033af8697 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 17:18:10 -0700 Subject: [PATCH 46/86] successfully auth user --- discuss/web/controllers/auth_controller.ex | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/auth_controller.ex b/discuss/web/controllers/auth_controller.ex index 4c1829c..4cce5ba 100644 --- a/discuss/web/controllers/auth_controller.ex +++ b/discuss/web/controllers/auth_controller.ex @@ -12,7 +12,17 @@ defmodule Discuss.AuthController do end defp signin(conn, changeset) do - + case insert_or_update_user(changeset) do + {:ok, user} -> + conn + |> put_flash(:info, "Welcome back!") + |> put_session(:user_id, user.id) + |> redirect(to: topic_path(conn, :index)) + {:error, _reason} -> + conn + |> put_flash(:error, "Error signing in") + |> redirect(to: topic_path(conn, :index)) + end end defp insert_or_update_user(changeset) do From c650a857bdfbd15d67026c59402e4ab32198323a Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 17:34:43 -0700 Subject: [PATCH 47/86] added setuser plug --- discuss/web/controllers/plugs/set_user.ex | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 discuss/web/controllers/plugs/set_user.ex diff --git a/discuss/web/controllers/plugs/set_user.ex b/discuss/web/controllers/plugs/set_user.ex new file mode 100644 index 0000000..ec63ef7 --- /dev/null +++ b/discuss/web/controllers/plugs/set_user.ex @@ -0,0 +1,15 @@ +defmodule Discuss.Plugs.SetUser do + import Plug.Conn + import Phoenix.Controller + + alias Discuss.Repo + alias Discuss.User + alias Discuss.Router.Helpers + + def init(_params) do + end + + def call(conn, _params) do + + end +end From acf7b84028105546ef9cccdc4618864270b8e95d Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 17:47:13 -0700 Subject: [PATCH 48/86] use a module plug to set current user --- discuss/web/controllers/plugs/set_user.ex | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/plugs/set_user.ex b/discuss/web/controllers/plugs/set_user.ex index ec63ef7..d8b09dc 100644 --- a/discuss/web/controllers/plugs/set_user.ex +++ b/discuss/web/controllers/plugs/set_user.ex @@ -4,12 +4,18 @@ defmodule Discuss.Plugs.SetUser do alias Discuss.Repo alias Discuss.User - alias Discuss.Router.Helpers def init(_params) do end def call(conn, _params) do + user_id = get_session(conn, :user_id) + cond do + user = user_id && Repo.get(User, user_id) -> + assign(conn, :user, user) + true -> + assign(conn, :user, nil) + end end end From 29057c34938f9c066a97892ae885b28e974fff9d Mon Sep 17 00:00:00 2001 From: stephen grider Date: Tue, 25 Oct 2016 17:57:52 -0700 Subject: [PATCH 49/86] added module plug to pipeline --- discuss/web/router.ex | 1 + discuss/web/templates/layout/app.html.eex | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/discuss/web/router.ex b/discuss/web/router.ex index 08dd90c..88df053 100644 --- a/discuss/web/router.ex +++ b/discuss/web/router.ex @@ -7,6 +7,7 @@ defmodule Discuss.Router do plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers + plug Discuss.Plugs.SetUser end pipeline :api do diff --git a/discuss/web/templates/layout/app.html.eex b/discuss/web/templates/layout/app.html.eex index 6dcc6eb..39487bb 100644 --- a/discuss/web/templates/layout/app.html.eex +++ b/discuss/web/templates/layout/app.html.eex @@ -19,6 +19,17 @@ +
      + <%= if @conn.assigns[:user] do %> +
    • + Logout +
    • + <% else %> +
    • + <%= link "Sign in with Github", to: auth_path(@conn, :request, "github") %> +
    • + <% end %> +
    From 5166b895937bae20536023ab87f84c9eeefe1815 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 10:38:04 -0700 Subject: [PATCH 50/86] users can signout --- discuss/web/controllers/auth_controller.ex | 6 ++++++ discuss/web/router.ex | 1 + discuss/web/templates/layout/app.html.eex | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/auth_controller.ex b/discuss/web/controllers/auth_controller.ex index 4cce5ba..9d4dee2 100644 --- a/discuss/web/controllers/auth_controller.ex +++ b/discuss/web/controllers/auth_controller.ex @@ -11,6 +11,12 @@ defmodule Discuss.AuthController do signin(conn, changeset) end + def signout(conn, _params) do + conn + |> configure_session(drop: true) + |> redirect(to: topic_path(conn, :index)) + end + defp signin(conn, changeset) do case insert_or_update_user(changeset) do {:ok, user} -> diff --git a/discuss/web/router.ex b/discuss/web/router.ex index 88df053..2eee937 100644 --- a/discuss/web/router.ex +++ b/discuss/web/router.ex @@ -23,6 +23,7 @@ defmodule Discuss.Router do scope "/auth", Discuss do pipe_through :browser + get "/signout", AuthController, :signout get "/:provider", AuthController, :request get "/:provider/callback", AuthController, :callback end diff --git a/discuss/web/templates/layout/app.html.eex b/discuss/web/templates/layout/app.html.eex index 39487bb..0b072d5 100644 --- a/discuss/web/templates/layout/app.html.eex +++ b/discuss/web/templates/layout/app.html.eex @@ -22,7 +22,7 @@
      <%= if @conn.assigns[:user] do %>
    • - Logout + <%= link "Sign Out", to: auth_path(@conn, :signout) %>
    • <% else %>
    • From e18d5829f3e1bb3e5ef4da65ab7444f2104e50d6 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 11:10:48 -0700 Subject: [PATCH 51/86] added require auth plug --- discuss/web/controllers/plugs/require_auth.ex | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 discuss/web/controllers/plugs/require_auth.ex diff --git a/discuss/web/controllers/plugs/require_auth.ex b/discuss/web/controllers/plugs/require_auth.ex new file mode 100644 index 0000000..16e858f --- /dev/null +++ b/discuss/web/controllers/plugs/require_auth.ex @@ -0,0 +1,20 @@ +defmodule Discuss.Plugs.RequireAuth do + import Plug.Conn + import Phoenix.Controller + + alias Discuss.Router.Helpers + + def init(_params) do + end + + def call(conn, _params) do + if conn.assigns[:user] do + conn + else + conn + |> put_flash(:error, "You must be logged in.") + |> redirect(to: Helpers.topic_path(conn, :index)) + |> halt() + end + end +end From c9cf198b1b7b58fe66d2fbe6127e2b4d0a643fe1 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 12:02:28 -0700 Subject: [PATCH 52/86] added plug in controller --- discuss/web/controllers/topic_controller.ex | 2 ++ 1 file changed, 2 insertions(+) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 4a6e059..42f4f7b 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -3,6 +3,8 @@ defmodule Discuss.TopicController do alias Discuss.Topic + plug Discuss.Plugs.RequireAuth when action in [:new, :create, :edit, :update, :delete] + def index(conn, _params) do topics = Repo.all(Topic) render conn, "index.html", topics: topics From 32f198d7f0f7812b913ac9331a818a1dd6593a51 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 12:15:45 -0700 Subject: [PATCH 53/86] added migration --- .../migrations/20161027191357_add_user_id_to_topics.exs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 discuss/priv/repo/migrations/20161027191357_add_user_id_to_topics.exs diff --git a/discuss/priv/repo/migrations/20161027191357_add_user_id_to_topics.exs b/discuss/priv/repo/migrations/20161027191357_add_user_id_to_topics.exs new file mode 100644 index 0000000..3d0a791 --- /dev/null +++ b/discuss/priv/repo/migrations/20161027191357_add_user_id_to_topics.exs @@ -0,0 +1,7 @@ +defmodule Discuss.Repo.Migrations.AddUserIdToTopics do + use Ecto.Migration + + def change do + + end +end From ed9520670f7553c6bc160826cfb7c62a4dbc1528 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 12:19:35 -0700 Subject: [PATCH 54/86] finished migration --- .../repo/migrations/20161027191357_add_user_id_to_topics.exs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discuss/priv/repo/migrations/20161027191357_add_user_id_to_topics.exs b/discuss/priv/repo/migrations/20161027191357_add_user_id_to_topics.exs index 3d0a791..b05a281 100644 --- a/discuss/priv/repo/migrations/20161027191357_add_user_id_to_topics.exs +++ b/discuss/priv/repo/migrations/20161027191357_add_user_id_to_topics.exs @@ -2,6 +2,8 @@ defmodule Discuss.Repo.Migrations.AddUserIdToTopics do use Ecto.Migration def change do - + alter table(:topics) do + add :user_id, references(:users) + end end end From 30e4ccdc589da55bd9f2cb77768bf50eb5316e95 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 12:46:30 -0700 Subject: [PATCH 55/86] updated topic and user --- discuss/web/models/topic.ex | 1 + discuss/web/models/user.ex | 1 + 2 files changed, 2 insertions(+) diff --git a/discuss/web/models/topic.ex b/discuss/web/models/topic.ex index fae7f4f..d00621f 100644 --- a/discuss/web/models/topic.ex +++ b/discuss/web/models/topic.ex @@ -3,6 +3,7 @@ defmodule Discuss.Topic do schema "topics" do field :title, :string + belongs_to :user, Discuss.User end def changeset(struct, params \\ %{}) do diff --git a/discuss/web/models/user.ex b/discuss/web/models/user.ex index 5da9b09..393630a 100644 --- a/discuss/web/models/user.ex +++ b/discuss/web/models/user.ex @@ -5,6 +5,7 @@ defmodule Discuss.User do field :email, :string field :provider, :string field :token, :string + has_many :topics, Discuss.Topic timestamps() end From a0a0a6afa8cf20970a89f89f0bf019c7100fe311 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 13:01:08 -0700 Subject: [PATCH 56/86] added build assoc call --- discuss/web/controllers/topic_controller.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 42f4f7b..aa9ad45 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -17,7 +17,9 @@ defmodule Discuss.TopicController do end def create(conn, %{"topic" => topic}) do - changeset = Topic.changeset(%Topic{}, topic) + changeset = conn.assigns.user + |> build_assoc(:topics) + |> Topic.changeset(topic) case Repo.insert(changeset) do {:ok, _topic} -> From d242d54ebcd395dd9629686ee87fe6bd6d18dcc7 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 13:54:43 -0700 Subject: [PATCH 57/86] check topics owner --- discuss/web/controllers/topic_controller.ex | 14 ++++++++++++++ discuss/web/templates/topic/index.html.eex | 10 ++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index aa9ad45..49cc6a6 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -4,6 +4,7 @@ defmodule Discuss.TopicController do alias Discuss.Topic plug Discuss.Plugs.RequireAuth when action in [:new, :create, :edit, :update, :delete] + plug :check_topic_owner when action in [:update, :edit, :delete] def index(conn, _params) do topics = Repo.all(Topic) @@ -59,4 +60,17 @@ defmodule Discuss.TopicController do |> put_flash(:info, "Topic Deleted") |> redirect(to: topic_path(conn, :index)) end + + def check_topic_owner(conn, _params) do + %{params: %{"id" => topic_id}} = conn + + if Repo.get(Topic, topic_id).user_id == conn.assigns.user.id do + conn + else + conn + |> put_flash(:error, "You cannot edit that") + |> redirect(to: topic_path(conn, :index)) + |> halt() + end + end end diff --git a/discuss/web/templates/topic/index.html.eex b/discuss/web/templates/topic/index.html.eex index b26d0c6..db55929 100644 --- a/discuss/web/templates/topic/index.html.eex +++ b/discuss/web/templates/topic/index.html.eex @@ -5,10 +5,12 @@
    • <%= topic.title %> -
      - <%= link "Edit", to: topic_path(@conn, :edit, topic) %> - <%= link "Delete", to: topic_path(@conn, :delete, topic), method: :delete %> -
      + <%= if @conn.assigns.user.id == topic.user_id do %> +
      + <%= link "Edit", to: topic_path(@conn, :edit, topic) %> + <%= link "Delete", to: topic_path(@conn, :delete, topic), method: :delete %> +
      + <% end %>
    • <% end %>
    From 086b2a616aed399d91e28b3d50ddeed0a8306f04 Mon Sep 17 00:00:00 2001 From: Stephen Grider Date: Thu, 27 Oct 2016 14:12:38 -0700 Subject: [PATCH 58/86] Create README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..2302cde --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# ElixirCode + + + +Each example from the course can be found within this repo. You can either look at the files in a completed state, or check out the changes that were made in each video by using the branch browser. From 430f430ba7bc39cdc8d0dcad4dd70657687cac63 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 15:02:32 -0700 Subject: [PATCH 59/86] added topic --- discuss/web/controllers/topic_controller.ex | 5 +++++ discuss/web/templates/topic/index.html.eex | 2 +- discuss/web/templates/topic/show.html.eex | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 discuss/web/templates/topic/show.html.eex diff --git a/discuss/web/controllers/topic_controller.ex b/discuss/web/controllers/topic_controller.ex index 49cc6a6..ca79fa9 100644 --- a/discuss/web/controllers/topic_controller.ex +++ b/discuss/web/controllers/topic_controller.ex @@ -11,6 +11,11 @@ defmodule Discuss.TopicController do render conn, "index.html", topics: topics end + def show(conn, %{"id" => topic_id}) do + topic = Repo.get!(Topic, topic_id) + render conn, "show.html", topic: topic + end + def new(conn, _params) do changeset = Topic.changeset(%Topic{}, %{}) diff --git a/discuss/web/templates/topic/index.html.eex b/discuss/web/templates/topic/index.html.eex index db55929..09dfab5 100644 --- a/discuss/web/templates/topic/index.html.eex +++ b/discuss/web/templates/topic/index.html.eex @@ -3,7 +3,7 @@
      <%= for topic <- @topics do %>
    • - <%= topic.title %> + <%= link topic.title, to: topic_path(@conn, :show, topic) %> <%= if @conn.assigns.user.id == topic.user_id do %>
      diff --git a/discuss/web/templates/topic/show.html.eex b/discuss/web/templates/topic/show.html.eex new file mode 100644 index 0000000..bd9193d --- /dev/null +++ b/discuss/web/templates/topic/show.html.eex @@ -0,0 +1 @@ +<%= @topic.title %> From b34d5f9ca337dc2f1ef6bc3c8493d3527d24685a Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 15:07:32 -0700 Subject: [PATCH 60/86] added comments migration --- .../repo/migrations/20161027220424_add_comments.exs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 discuss/priv/repo/migrations/20161027220424_add_comments.exs diff --git a/discuss/priv/repo/migrations/20161027220424_add_comments.exs b/discuss/priv/repo/migrations/20161027220424_add_comments.exs new file mode 100644 index 0000000..542f89e --- /dev/null +++ b/discuss/priv/repo/migrations/20161027220424_add_comments.exs @@ -0,0 +1,13 @@ +defmodule Discuss.Repo.Migrations.AddComments do + use Ecto.Migration + + def change do + create table(:comments) do + add :content, :string + add :user_id, references(:users) + add :topic_id, references(:topics) + + timestamps() + end + end +end From fb844933d684aa5c590440ab989b8962a3b619c3 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 15:11:48 -0700 Subject: [PATCH 61/86] added comments model --- discuss/web/models/comment.ex | 17 +++++++++++++++++ discuss/web/models/topic.ex | 1 + discuss/web/models/user.ex | 1 + 3 files changed, 19 insertions(+) create mode 100644 discuss/web/models/comment.ex diff --git a/discuss/web/models/comment.ex b/discuss/web/models/comment.ex new file mode 100644 index 0000000..45f1aa4 --- /dev/null +++ b/discuss/web/models/comment.ex @@ -0,0 +1,17 @@ +defmodule Discuss.Comment do + use Discuss.Web, :model + + schema "comments" do + field :content, :string + belongs_to :user, Discuss.User + belongs_to :topic, Discuss.Topic + + timestamps() + end + + def changeset(struct, params \\ %{}) do + struct + |> cast(params, [:content]) + |> validate_required([:content]) + end +end diff --git a/discuss/web/models/topic.ex b/discuss/web/models/topic.ex index d00621f..995132c 100644 --- a/discuss/web/models/topic.ex +++ b/discuss/web/models/topic.ex @@ -4,6 +4,7 @@ defmodule Discuss.Topic do schema "topics" do field :title, :string belongs_to :user, Discuss.User + has_many :comments, Discuss.Comment end def changeset(struct, params \\ %{}) do diff --git a/discuss/web/models/user.ex b/discuss/web/models/user.ex index 393630a..2f2d932 100644 --- a/discuss/web/models/user.ex +++ b/discuss/web/models/user.ex @@ -6,6 +6,7 @@ defmodule Discuss.User do field :provider, :string field :token, :string has_many :topics, Discuss.Topic + has_many :comments, Discuss.Comment timestamps() end From bbb1ddb636579b87407350c207f8bcbc20cf2a4e Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 16:23:21 -0700 Subject: [PATCH 62/86] added comments channel --- discuss/web/channels/comments_channel.ex | 11 +++++ discuss/web/channels/user_socket.ex | 26 +----------- discuss/web/static/js/socket.js | 51 ------------------------ 3 files changed, 12 insertions(+), 76 deletions(-) create mode 100644 discuss/web/channels/comments_channel.ex diff --git a/discuss/web/channels/comments_channel.ex b/discuss/web/channels/comments_channel.ex new file mode 100644 index 0000000..58f05cb --- /dev/null +++ b/discuss/web/channels/comments_channel.ex @@ -0,0 +1,11 @@ +defmodule Discuss.CommentsChannel do + use Discuss.Web, :channel + + def join() do + + end + + def handle_in() do + + end +end diff --git a/discuss/web/channels/user_socket.ex b/discuss/web/channels/user_socket.ex index 84977e0..9c887ec 100644 --- a/discuss/web/channels/user_socket.ex +++ b/discuss/web/channels/user_socket.ex @@ -1,37 +1,13 @@ defmodule Discuss.UserSocket do use Phoenix.Socket - ## Channels - # channel "room:*", Discuss.RoomChannel + channel "comments:*", Discuss.CommentsChannel - ## Transports transport :websocket, Phoenix.Transports.WebSocket - # transport :longpoll, Phoenix.Transports.LongPoll - # Socket params are passed from the client and can - # be used to verify and authenticate a user. After - # verification, you can put default assigns into - # the socket that will be set for all channels, ie - # - # {:ok, assign(socket, :user_id, verified_user_id)} - # - # To deny connection, return `:error`. - # - # See `Phoenix.Token` documentation for examples in - # performing token verification on connect. def connect(_params, socket) do {:ok, socket} end - # Socket id's are topics that allow you to identify all sockets for a given user: - # - # def id(socket), do: "users_socket:#{socket.assigns.user_id}" - # - # Would allow you to broadcast a "disconnect" event and terminate - # all active sockets and channels for a given user: - # - # Discuss.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{}) - # - # Returning `nil` makes this socket anonymous. def id(_socket), do: nil end diff --git a/discuss/web/static/js/socket.js b/discuss/web/static/js/socket.js index 0f8d461..c08c796 100644 --- a/discuss/web/static/js/socket.js +++ b/discuss/web/static/js/socket.js @@ -1,59 +1,8 @@ -// NOTE: The contents of this file will only be executed if -// you uncomment its entry in "web/static/js/app.js". - -// To use Phoenix channels, the first step is to import Socket -// and connect at the socket path in "lib/my_app/endpoint.ex": import {Socket} from "phoenix" - let socket = new Socket("/socket", {params: {token: window.userToken}}) -// When you connect, you'll often need to authenticate the client. -// For example, imagine you have an authentication plug, `MyAuth`, -// which authenticates the session and assigns a `:current_user`. -// If the current user exists you can assign the user's token in -// the connection for use in the layout. -// -// In your "web/router.ex": -// -// pipeline :browser do -// ... -// plug MyAuth -// plug :put_user_token -// end -// -// defp put_user_token(conn, _) do -// if current_user = conn.assigns[:current_user] do -// token = Phoenix.Token.sign(conn, "user socket", current_user.id) -// assign(conn, :user_token, token) -// else -// conn -// end -// end -// -// Now you need to pass this token to JavaScript. You can do so -// inside a script tag in "web/templates/layout/app.html.eex": -// -// -// -// You will need to verify the user token in the "connect/2" function -// in "web/channels/user_socket.ex": -// -// def connect(%{"token" => token}, socket) do -// # max_age: 1209600 is equivalent to two weeks in seconds -// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do -// {:ok, user_id} -> -// {:ok, assign(socket, :user, user_id)} -// {:error, reason} -> -// :error -// end -// end -// -// Finally, pass the token on connect as below. Or remove it -// from connect if you don't care about authentication. - socket.connect() -// Now that you are connected, you can join channels with a topic: let channel = socket.channel("topic:subtopic", {}) channel.join() .receive("ok", resp => { console.log("Joined successfully", resp) }) From 580b3b8e82392b8fe6eff992ae483cf559cdcaac Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 17:07:24 -0700 Subject: [PATCH 63/86] progress with phoenix sockets --- discuss/web/channels/comments_channel.ex | 6 ++++-- discuss/web/static/js/app.js | 2 +- discuss/web/static/js/socket.js | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/discuss/web/channels/comments_channel.ex b/discuss/web/channels/comments_channel.ex index 58f05cb..db12aa9 100644 --- a/discuss/web/channels/comments_channel.ex +++ b/discuss/web/channels/comments_channel.ex @@ -1,8 +1,10 @@ defmodule Discuss.CommentsChannel do use Discuss.Web, :channel - def join() do - + def join(name, _params, socket) do + IO.puts("++++++") + IO.puts(name) + {:ok, %{hey: "there"}, socket} end def handle_in() do diff --git a/discuss/web/static/js/app.js b/discuss/web/static/js/app.js index e7549b9..7b9bcfd 100644 --- a/discuss/web/static/js/app.js +++ b/discuss/web/static/js/app.js @@ -18,4 +18,4 @@ import "phoenix_html" // Local files can be imported directly using relative // paths "./socket" or full ones "web/static/js/socket". -// import socket from "./socket" +import socket from "./socket" diff --git a/discuss/web/static/js/socket.js b/discuss/web/static/js/socket.js index c08c796..5784566 100644 --- a/discuss/web/static/js/socket.js +++ b/discuss/web/static/js/socket.js @@ -3,7 +3,7 @@ let socket = new Socket("/socket", {params: {token: window.userToken}}) socket.connect() -let channel = socket.channel("topic:subtopic", {}) +let channel = socket.channel("comments:1", {}) channel.join() .receive("ok", resp => { console.log("Joined successfully", resp) }) .receive("error", resp => { console.log("Unable to join", resp) }) From 2abff6acd050925f8f89bfa7727903dfbb3fa11c Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 17:51:52 -0700 Subject: [PATCH 64/86] publish message --- discuss/web/channels/comments_channel.ex | 10 ++++++---- discuss/web/static/js/socket.js | 4 ++++ discuss/web/templates/topic/show.html.eex | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/discuss/web/channels/comments_channel.ex b/discuss/web/channels/comments_channel.ex index db12aa9..bc8e4a8 100644 --- a/discuss/web/channels/comments_channel.ex +++ b/discuss/web/channels/comments_channel.ex @@ -2,12 +2,14 @@ defmodule Discuss.CommentsChannel do use Discuss.Web, :channel def join(name, _params, socket) do - IO.puts("++++++") - IO.puts(name) {:ok, %{hey: "there"}, socket} end - def handle_in() do - + def handle_in(name, message, socket) do + IO.puts("++++") + IO.puts(name) + IO.inspect(message) + + {:reply, :ok, socket} end end diff --git a/discuss/web/static/js/socket.js b/discuss/web/static/js/socket.js index 5784566..b8f5ad8 100644 --- a/discuss/web/static/js/socket.js +++ b/discuss/web/static/js/socket.js @@ -8,4 +8,8 @@ channel.join() .receive("ok", resp => { console.log("Joined successfully", resp) }) .receive("error", resp => { console.log("Unable to join", resp) }) +document.querySelector('button').addEventListener('click', function() { + channel.push('comment:hello', { hi: 'there!' }); +}); + export default socket diff --git a/discuss/web/templates/topic/show.html.eex b/discuss/web/templates/topic/show.html.eex index bd9193d..54f0f63 100644 --- a/discuss/web/templates/topic/show.html.eex +++ b/discuss/web/templates/topic/show.html.eex @@ -1 +1,2 @@ <%= @topic.title %> + From 8f5a9a9e817ab16a25afc70c2c78a6af451e60b9 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 18:19:07 -0700 Subject: [PATCH 65/86] clean up log --- discuss/web/channels/comments_channel.ex | 4 ---- discuss/web/static/js/socket.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/discuss/web/channels/comments_channel.ex b/discuss/web/channels/comments_channel.ex index bc8e4a8..bc3f77c 100644 --- a/discuss/web/channels/comments_channel.ex +++ b/discuss/web/channels/comments_channel.ex @@ -6,10 +6,6 @@ defmodule Discuss.CommentsChannel do end def handle_in(name, message, socket) do - IO.puts("++++") - IO.puts(name) - IO.inspect(message) - {:reply, :ok, socket} end end diff --git a/discuss/web/static/js/socket.js b/discuss/web/static/js/socket.js index b8f5ad8..c70ded4 100644 --- a/discuss/web/static/js/socket.js +++ b/discuss/web/static/js/socket.js @@ -9,7 +9,7 @@ channel.join() .receive("error", resp => { console.log("Unable to join", resp) }) document.querySelector('button').addEventListener('click', function() { - channel.push('comment:hello', { hi: 'there!' }); + channel.push('comments:hello', { hi: 'there!' }); }); export default socket From a7455759eb669f6533909db9505c0dad90fbb77d Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 18:30:37 -0700 Subject: [PATCH 66/86] refactored to pass along the topic id --- discuss/web/channels/comments_channel.ex | 1 + discuss/web/static/js/app.js | 2 +- discuss/web/static/js/socket.js | 18 ++++++++---------- discuss/web/templates/topic/show.html.eex | 6 +++++- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/discuss/web/channels/comments_channel.ex b/discuss/web/channels/comments_channel.ex index bc3f77c..e512b65 100644 --- a/discuss/web/channels/comments_channel.ex +++ b/discuss/web/channels/comments_channel.ex @@ -2,6 +2,7 @@ defmodule Discuss.CommentsChannel do use Discuss.Web, :channel def join(name, _params, socket) do + IO.puts(name) {:ok, %{hey: "there"}, socket} end diff --git a/discuss/web/static/js/app.js b/discuss/web/static/js/app.js index 7b9bcfd..f72263d 100644 --- a/discuss/web/static/js/app.js +++ b/discuss/web/static/js/app.js @@ -18,4 +18,4 @@ import "phoenix_html" // Local files can be imported directly using relative // paths "./socket" or full ones "web/static/js/socket". -import socket from "./socket" +import "./socket" diff --git a/discuss/web/static/js/socket.js b/discuss/web/static/js/socket.js index c70ded4..0386dcb 100644 --- a/discuss/web/static/js/socket.js +++ b/discuss/web/static/js/socket.js @@ -3,13 +3,11 @@ let socket = new Socket("/socket", {params: {token: window.userToken}}) socket.connect() -let channel = socket.channel("comments:1", {}) -channel.join() - .receive("ok", resp => { console.log("Joined successfully", resp) }) - .receive("error", resp => { console.log("Unable to join", resp) }) - -document.querySelector('button').addEventListener('click', function() { - channel.push('comments:hello', { hi: 'there!' }); -}); - -export default socket +const createSocket = (topicId) => { + let channel = socket.channel(`comments:${topicId}`, {}) + channel.join() + .receive("ok", resp => { console.log("Joined successfully", resp) }) + .receive("error", resp => { console.log("Unable to join", resp) }) +} + +window.createSocket = createSocket; diff --git a/discuss/web/templates/topic/show.html.eex b/discuss/web/templates/topic/show.html.eex index 54f0f63..01c8aa4 100644 --- a/discuss/web/templates/topic/show.html.eex +++ b/discuss/web/templates/topic/show.html.eex @@ -1,2 +1,6 @@ <%= @topic.title %> - + From 229689c4f890d425ac9170b80e3999e1f7917434 Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 18:37:16 -0700 Subject: [PATCH 67/86] pass back topic --- discuss/web/channels/comments_channel.ex | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/discuss/web/channels/comments_channel.ex b/discuss/web/channels/comments_channel.ex index e512b65..aea7445 100644 --- a/discuss/web/channels/comments_channel.ex +++ b/discuss/web/channels/comments_channel.ex @@ -1,9 +1,12 @@ defmodule Discuss.CommentsChannel do use Discuss.Web, :channel + alias Discuss.Topic - def join(name, _params, socket) do - IO.puts(name) - {:ok, %{hey: "there"}, socket} + def join("comments:" <> topic_id, _params, socket) do + topic_id = String.to_integer(topic_id) + topic = Repo.get(Topic, topic_id) + + {:ok, topic, socket} end def handle_in(name, message, socket) do From 7410e8ffd80dfbf5ae79b470c6f467e60afd481d Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 18:41:31 -0700 Subject: [PATCH 68/86] removed debug stuff --- discuss/web/channels/comments_channel.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discuss/web/channels/comments_channel.ex b/discuss/web/channels/comments_channel.ex index aea7445..cac66ff 100644 --- a/discuss/web/channels/comments_channel.ex +++ b/discuss/web/channels/comments_channel.ex @@ -6,7 +6,7 @@ defmodule Discuss.CommentsChannel do topic_id = String.to_integer(topic_id) topic = Repo.get(Topic, topic_id) - {:ok, topic, socket} + {:ok, %{}, socket} end def handle_in(name, message, socket) do From 15989cb845935d57b189544d03633d64bcfa1faf Mon Sep 17 00:00:00 2001 From: stephen grider Date: Thu, 27 Oct 2016 19:06:24 -0700 Subject: [PATCH 69/86] add comments into db --- discuss/web/channels/comments_channel.ex | 19 +++++++++++++++---- discuss/web/static/js/socket.js | 6 ++++++ discuss/web/templates/topic/show.html.eex | 8 +++++++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/discuss/web/channels/comments_channel.ex b/discuss/web/channels/comments_channel.ex index cac66ff..5ed0d96 100644 --- a/discuss/web/channels/comments_channel.ex +++ b/discuss/web/channels/comments_channel.ex @@ -1,15 +1,26 @@ defmodule Discuss.CommentsChannel do use Discuss.Web, :channel - alias Discuss.Topic + alias Discuss.{Topic, Comment} def join("comments:" <> topic_id, _params, socket) do topic_id = String.to_integer(topic_id) topic = Repo.get(Topic, topic_id) - {:ok, %{}, socket} + {:ok, %{}, assign(socket, :topic, topic)} end - def handle_in(name, message, socket) do - {:reply, :ok, socket} + def handle_in(name, %{"content" => content}, socket) do + topic = socket.assigns.topic + + changeset = topic + |> build_assoc(:comments) + |> Comment.changeset(%{content: content}) + + case Repo.insert(changeset) do + {:ok, comment} -> + {:reply, :ok, socket} + {:error, _reason} -> + {:reply, {:error, %{errors: changeset}}, socket} + end end end diff --git a/discuss/web/static/js/socket.js b/discuss/web/static/js/socket.js index 0386dcb..c4d4aa1 100644 --- a/discuss/web/static/js/socket.js +++ b/discuss/web/static/js/socket.js @@ -8,6 +8,12 @@ const createSocket = (topicId) => { channel.join() .receive("ok", resp => { console.log("Joined successfully", resp) }) .receive("error", resp => { console.log("Unable to join", resp) }) + + document.querySelector('button').addEventListener('click', () => { + const content = document.querySelector('textarea').value; + + channel.push('comment:add', { content: content }); + }); } window.createSocket = createSocket; diff --git a/discuss/web/templates/topic/show.html.eex b/discuss/web/templates/topic/show.html.eex index 01c8aa4..4a54e28 100644 --- a/discuss/web/templates/topic/show.html.eex +++ b/discuss/web/templates/topic/show.html.eex @@ -1,4 +1,10 @@ -<%= @topic.title %> +
      <%= @topic.title %>
      + +
      + + +
      + From eef95a94ad28fe3a8a97f843446ac9fccc504a9e Mon Sep 17 00:00:00 2001 From: stephengrider Date: Mon, 11 Sep 2017 11:21:33 -0700 Subject: [PATCH 75/86] associate --- discuss/web/channels/comments_channel.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/discuss/web/channels/comments_channel.ex b/discuss/web/channels/comments_channel.ex index c44759a..c25becf 100644 --- a/discuss/web/channels/comments_channel.ex +++ b/discuss/web/channels/comments_channel.ex @@ -13,9 +13,10 @@ defmodule Discuss.CommentsChannel do def handle_in(name, %{"content" => content}, socket) do topic = socket.assigns.topic + user_id = socket.assigns.user_id changeset = topic - |> build_assoc(:comments) + |> build_assoc(:comments, user_id: user_id) |> Comment.changeset(%{content: content}) case Repo.insert(changeset) do From 289ad7c9016da29534d66c9706f046e1da218684 Mon Sep 17 00:00:00 2001 From: stephengrider Date: Mon, 11 Sep 2017 11:38:22 -0700 Subject: [PATCH 76/86] fetch users --- discuss/web/channels/comments_channel.ex | 2 +- discuss/web/models/comment.ex | 2 +- discuss/web/models/user.ex | 2 ++ discuss/web/static/js/socket.js | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/discuss/web/channels/comments_channel.ex b/discuss/web/channels/comments_channel.ex index c25becf..1f2cd77 100644 --- a/discuss/web/channels/comments_channel.ex +++ b/discuss/web/channels/comments_channel.ex @@ -6,7 +6,7 @@ defmodule Discuss.CommentsChannel do topic_id = String.to_integer(topic_id) topic = Topic |> Repo.get(topic_id) - |> Repo.preload(:comments) + |> Repo.preload(comments: [:user]) {:ok, %{comments: topic.comments}, assign(socket, :topic, topic)} end diff --git a/discuss/web/models/comment.ex b/discuss/web/models/comment.ex index bb00276..a5f2e88 100644 --- a/discuss/web/models/comment.ex +++ b/discuss/web/models/comment.ex @@ -1,7 +1,7 @@ defmodule Discuss.Comment do use Discuss.Web, :model - @derive {Poison.Encoder, only: [:content]} + @derive {Poison.Encoder, only: [:content, :user]} schema "comments" do field :content, :string diff --git a/discuss/web/models/user.ex b/discuss/web/models/user.ex index 2f2d932..08cc8af 100644 --- a/discuss/web/models/user.ex +++ b/discuss/web/models/user.ex @@ -1,6 +1,8 @@ defmodule Discuss.User do use Discuss.Web, :model + @derive {Poison.Encoder, only: [:email]} + schema "users" do field :email, :string field :provider, :string diff --git a/discuss/web/static/js/socket.js b/discuss/web/static/js/socket.js index bb8ddf4..4d60e63 100644 --- a/discuss/web/static/js/socket.js +++ b/discuss/web/static/js/socket.js @@ -8,6 +8,7 @@ const createSocket = topicId => { channel .join() .receive('ok', resp => { + console.log(resp); renderComments(resp.comments); }) .receive('error', resp => { From eaf9e6a9084f5cce6422d720529df6b1ab18a676 Mon Sep 17 00:00:00 2001 From: Stephen Grider Date: Mon, 11 Sep 2017 13:00:25 -0700 Subject: [PATCH 77/86] Added diagrams.xml --- diagrams.xml | 1 + 1 file changed, 1 insertion(+) create mode 100644 diagrams.xml diff --git a/diagrams.xml b/diagrams.xml new file mode 100644 index 0000000..1f0ea6b --- /dev/null +++ b/diagrams.xml @@ -0,0 +1 @@ +UzV2zq1wL0osyPDNT0nNUTV2VTV2LsrPL4GwciucU3NyVI0MMlNUjV1UjYwMgFjVyA2HrCFY1qAgsSg1rwSLBiADYTaQg2Y1AA== \ No newline at end of file From 40b1ae60403472df93ad345a312e404c35394ef1 Mon Sep 17 00:00:00 2001 From: Stephen Grider Date: Mon, 11 Sep 2017 13:00:41 -0700 Subject: [PATCH 78/86] Update diagrams.xml --- diagrams.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagrams.xml b/diagrams.xml index 1f0ea6b..a9d1210 100644 --- a/diagrams.xml +++ b/diagrams.xml @@ -1 +1 @@ -UzV2zq1wL0osyPDNT0nNUTV2VTV2LsrPL4GwciucU3NyVI0MMlNUjV1UjYwMgFjVyA2HrCFY1qAgsSg1rwSLBiADYTaQg2Y1AA== \ No newline at end of file +dZHBEoIgEIafhrtKzXg3q0snD51JEJjQdRBH6+nTWDPG4sLu9/+7yyyEZvV4sqxVF+DCkCTiI6EHkiRxtEunayYPT/YUgbSao2kFhX6KpRJpr7noAqMDME63ISyhaUTpAsashSG0VWDCqS2TYgOKkpktvWruFNI4ilbhLLRUODrdo3Bj5V1a6BucRxJavY+Xa7b0Qn+nGIfhC9Gc0MwCOB/VYybMvNtlbb7u+Ef9vNuKxv0omIK195QEH0jzFw== \ No newline at end of file From 85716dabc9d47506945c046bd858ce10a31e03ca Mon Sep 17 00:00:00 2001 From: stephengrider Date: Mon, 11 Sep 2017 15:03:19 -0700 Subject: [PATCH 79/86] render users --- discuss/web/static/js/socket.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/discuss/web/static/js/socket.js b/discuss/web/static/js/socket.js index 4d60e63..c7209af 100644 --- a/discuss/web/static/js/socket.js +++ b/discuss/web/static/js/socket.js @@ -39,9 +39,17 @@ function renderComment(event) { } function commentTemplate(comment) { + let email = 'Anonymous'; + if (comment.user) { + email = comment.user.email; + } + return `
    • ${comment.content} +
      + ${email} +
    • `; } From ce595fe1c4ca98f4e14a9da49c7a80c645b2f4aa Mon Sep 17 00:00:00 2001 From: Stephen Grider Date: Mon, 11 Sep 2017 15:31:09 -0700 Subject: [PATCH 80/86] Update diagrams.xml --- diagrams.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagrams.xml b/diagrams.xml index a9d1210..409f2b6 100644 --- a/diagrams.xml +++ b/diagrams.xml @@ -1 +1 @@ -dZHBEoIgEIafhrtKzXg3q0snD51JEJjQdRBH6+nTWDPG4sLu9/+7yyyEZvV4sqxVF+DCkCTiI6EHkiRxtEunayYPT/YUgbSao2kFhX6KpRJpr7noAqMDME63ISyhaUTpAsashSG0VWDCqS2TYgOKkpktvWruFNI4ilbhLLRUODrdo3Bj5V1a6BucRxJavY+Xa7b0Qn+nGIfhC9Gc0MwCOB/VYybMvNtlbb7u+Ef9vNuKxv0omIK195QEH0jzFw== \ No newline at end of file +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 \ No newline at end of file From 613095bcc4968e8c867f8992e26dcc2d9a6ebb24 Mon Sep 17 00:00:00 2001 From: Stephen Grider Date: Mon, 11 Sep 2017 15:31:31 -0700 Subject: [PATCH 81/86] Update diagrams.xml --- diagrams.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagrams.xml b/diagrams.xml index 409f2b6..42405dc 100644 --- a/diagrams.xml +++ b/diagrams.xml @@ -1 +1 @@ -7ZhNc9owEIZ/DdeMLWNwjglJ2kM705kcmp46wl5sNbLkkUWA/vpK8tpYscMkITQXYIaR3l197SOtsCfRotx+UbQqvssM+IQE2XYS3UwImc+m5tcKu0aI4stGyBXLGincC/fsL6AYoLpmGdSeo5aSa1b5YiqFgFR7GlVKbny3leT+qBXNYSDcp5QP1Z8s0wWqYRDsDV+B5QUOncRoWNL0MVdyLXC8CYlW7tOYS9r2hf51QTO56UnR7SRaKCl1Uyq3C+A2tG3YmnZ3L1i7eSsQ+jUN5jgNvWuXDpmJBFal0oXMpaD8dq9eu+WB7SAwtUKX3BRDU4Qt0w+98i/rckFiWxVa7R6wias0RmurNVX6ylIzkpACWu2Ocd41yVqPlNO6Zmkjoosd8Q9ovcOtRNdaGmk//29SVuhXayUfYSG5VG7FUeA+naUFHhllJYXGLiOC9bGWTRRt6F7kgFIt1ypFL4I7m6oc0Cvu9oA5WiBLMJEyLgo41ezJ753iJs87vz1oU0DW49yT03Ofn9n6bGf/iS0O/UT5GjvdwHLA26e5KZiG+4q66W9MTvcJd2nK+nK6BH7dJbo2Zh3d5wgS+x0gcCE3hHuemCk9NOQQGspZLuy2MSRAdayeQGnYHqY15IANSIJ3F15dJMR4bnoXQZvui94dMEuORxcN0P0oJAi2NWJ4QQYQzTL1M1Je+JFJP84oDUJng8bMDXiFhpJlmTv2Y1vD3zxv2B1vO3THgZwHHsjLYMjxcoRjFBzPcXqQY3Tm+AaO8fTzOMYDjrSqBC3hnE5fS2/m0wun0yG++YnS6ewlfL/PN+L7EZJk5ASeCmH7RNhjuDBrp0zU9nFrXTMBtS1ymZs/jmYw8+hlHjyppktagxXS1Hl8eMrlsNKfkXDDQxvA+eEqyQfAnz/LvmE88ncoHIHf+h0FPzwEP5UZuBcDrlyWa2EwaHBzM1MzlsJVRo76Gf674JMkORl8U92/9nC23qul6PYf7VfJbtswEP0aXQIkkCXLdo6x47QFugE+tDkVtDSW2NCkStFbvr5DabRFtqE06AI0DmKLb4YUOe89kXL82Xr/RrM0+aAiEI7nRnvHv3U873owxm8LHApg6LsFEGseFdCgBhb8EQgs0zY8gqyVaJQShqdtMFRSQmhaGNNa7dppKyXad01ZDB1gETLRRb/wyCSEDly3DrwFHid060lAgSULH2KtNpLu53j+Kv8U4TUrx6L8LGGR2jUgf+74M62UKa7W+xkIW9qybEW/uxPRat4apOnTwSs6bJnY0NIjnoWbLPu2gyVN0RzKsuQLA9vVdfzpLuEGFikLbXSHOkAsMWuBrQFeVkuzuYItQUyr4syUUBpDUkmwqUarByhBrJk7sX9VpOQAyzNdcSEamVRdxJU0JCTPo3ZzxPyDOBM8loiFWCHA4JQqANrA/mQVBxU3KHlQazD6gCnUwbsmOku5T6i9a4hnTFjS0M1oQpolvcbV0DVneEG0HafQP0XhK3096RsN/iJ9QYcmiPBZRE2lTaJiJZmY1+i0TWSDNNhz89XCVzhu0bwv+TRMmxv7bGwSh9gdt3PLRwIZlRmhYFnGwwKkFDvMdzDmQESxjVEI1XN8r1Ra3e6pJkoG25rwn8F9USlbnvNEYzXVRoeUNaT9g+kYTOOp15WDBsEM37ZHfwm344417+eLDt8oXNOmUUPGH9kyT7ALTxWXJp9HMHWC22M2sAbguIHdUGDNoyjXygnr/or3TlqNtmGacb27NZkJzlrw0r1yveGwZcNLGr03LTT6Z1utRoparTJk/ilv1SR6UTn6PTYdv9r0tE39P2TTScemHz/9py4dnXUpmnQ4uP6HXDnsMHer8L0BZZRAvuLI/rzDfwloRPsWYbd1jScfu5Wzrf25YPJgEi7jizIhyrM4Kt+uMwS+xSheY60tkKVKRgWSp7Mc/bGBDKt410M5bdeRy5snI4L66+fYYa79+HnGee7FSut/9PKD9tHLC7pHL3905OhVnrif4Xts1i9Whdjql1d//hM=7ZvPb9sgFMf/mlwrG+z8ODZZux02aVIP204TtalNi02ESZPsr98jxokd3CpxUnYYqVSFL2Cb93kYeK8d4UWx+SzJMv8mUspHKEg3I/xphNAsnMBvLWxrIcJBLWSSpbUUHoQH9ocasWm2YimtOg2VEFyxZVdMRFnSRHU0IqVYd5s9Cd6965Jk1BIeEsJt9QdLVW7UMAgOFV8oy3Jz62lsKh5J8pJJsSrN/UYIP+0+dXVBmmuZ9lVOUrFuSfhuhBdSCFV/KzYLyrVpG7PV/e7fqN0/t6SlOqUDqju8Er4yQ1/TR/NoatuYYzcgqrsEIzxf50zRhyVJdO0a+IOWq4JDKYSv+yHptpw8Uj7fG2UhuJBQVYqS6qZKihfaiGCrYKp/9jWN7cEs8yfGeaulsSroolTGgRAy5fYVdx/QCWdZCVoClqFQOTcjp1LRzZvWC/dMwNWpKKiSW2hiOqCpwWjcPJyY8rrlNI1r5C1/QVPjq8ZPs/2lD6zgi8HVjw5b6L7ngpZsA2J4gyyIMEx1RKpjfsOkbWcjWabTRmMwW25NRcHSVN+m1zW6znOGd7TI4vfIXgPk+Agkim2Qsx6QzXvtEpCRhYqm8DYyRSFVLjJREn53UI+M2mJKN0z91PJNbEq/TCMwhdy2qnSxqasUkepWvzjbkxO0e6Yf2/RPmxYJJ1XFklo0TfTNn6lSW4OMrJQA6fD4X4VY9jpem2V33uOzvUBb7n0fAEOLlUxoZxLBUDOqWpLtKZJyothr9+qXYI/fnb/Yz98z5u84wv9u/k4skElOYF/CK7+QnsgPB0fv39nU3UI6tfnB0KXgnEqPcChCmJTuEM4shIU+knh6g+nFkTt6zamvhQ82H7BCeXxD8U2RQ3yhhQ+2h1T52XcyPtTFh0OH58hwbK9/lWc3mB12uHUJ7b3ns0c3GN2459TwYejsbaeixRIOmdQTHLru4Rl2SNDedb4yuvb0htKLmku4oIfsTSfYGMZ2A+PxBAcSjGYOCdrJC8588uLkmFnT5V8kL3qyFymrkpXfeJ4R8xx3+bmMmTVJC5vfb59AHM7Q6bm9JwNBkgSs/L+c3HcdzRjRNWiOj2i6DIEi+xi/FJVHeSWULuOhPqN0fYBOQ2o+pfQxDF2G1npySj4+czHBscMVEdsnfB+fuYjezOEiiO2sko/PXEowamImTgja8RmfF7woRBPFDjcx2A7R+NzSGSf7qIMuDlyufHZ0xqd0h7OLJh+374Ti4a/9d3Wt/6jAd38B \ No newline at end of file +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 \ No newline at end of file From 228976dc4826dee2f9c390b5e7a8d113e0dded85 Mon Sep 17 00:00:00 2001 From: Stephen Grider Date: Mon, 11 Sep 2017 15:34:47 -0700 Subject: [PATCH 82/86] Update diagrams.xml --- diagrams.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagrams.xml b/diagrams.xml index 42405dc..a9136a5 100644 --- a/diagrams.xml +++ b/diagrams.xml @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file From 56cd088ce3f465a5b0b8e4c6a25c86cf1c1df576 Mon Sep 17 00:00:00 2001 From: Stephen Grider Date: Tue, 12 Sep 2017 11:19:25 -0700 Subject: [PATCH 83/86] Update diagrams.xml --- diagrams.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/diagrams.xml b/diagrams.xml index a9136a5..3bd5cb9 100644 --- a/diagrams.xml +++ b/diagrams.xml @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file From b56b3a4a32c98392593ee7546795161f76e0308a Mon Sep 17 00:00:00 2001 From: stephengrider Date: Mon, 30 Oct 2017 09:56:55 -0700 Subject: [PATCH 84/86] added update --- update/.gitignore | 27 +++ update/README.md | 20 ++ update/assets/brunch-config.js | 62 ++++++ update/assets/css/app.css | 1 + update/assets/css/phoenix.css | 77 +++++++ update/assets/js/app.js | 21 ++ update/assets/js/socket.js | 62 ++++++ update/assets/package.json | 18 ++ update/assets/static/favicon.ico | Bin 0 -> 1258 bytes update/assets/static/images/phoenix.png | Bin 0 -> 13900 bytes update/assets/static/robots.txt | 5 + update/config/config.exs | 27 +++ update/config/dev.exs | 58 +++++ update/config/prod.exs | 64 ++++++ update/config/test.exs | 19 ++ update/lib/update.ex | 9 + update/lib/update/accounts/accounts.ex | 104 +++++++++ update/lib/update/accounts/user.ex | 19 ++ update/lib/update/application.ex | 31 +++ update/lib/update/discussions/comment.ex | 19 ++ update/lib/update/discussions/discussions.ex | 200 ++++++++++++++++++ update/lib/update/discussions/topic.ex | 19 ++ update/lib/update/repo.ex | 11 + update/lib/update_web.ex | 67 ++++++ update/lib/update_web/channels/user_socket.ex | 37 ++++ .../controllers/comment_controller.ex | 60 ++++++ .../update_web/controllers/page_controller.ex | 7 + .../controllers/topic_controller.ex | 60 ++++++ .../update_web/controllers/user_controller.ex | 60 ++++++ update/lib/update_web/endpoint.ex | 57 +++++ update/lib/update_web/gettext.ex | 24 +++ update/lib/update_web/router.ex | 26 +++ .../templates/comment/edit.html.eex | 5 + .../templates/comment/form.html.eex | 17 ++ .../templates/comment/index.html.eex | 26 +++ .../update_web/templates/comment/new.html.eex | 5 + .../templates/comment/show.html.eex | 13 ++ .../update_web/templates/layout/app.html.eex | 35 +++ .../update_web/templates/page/index.html.eex | 36 ++++ .../update_web/templates/topic/edit.html.eex | 5 + .../update_web/templates/topic/form.html.eex | 17 ++ .../update_web/templates/topic/index.html.eex | 26 +++ .../update_web/templates/topic/new.html.eex | 5 + .../update_web/templates/topic/show.html.eex | 13 ++ .../update_web/templates/user/edit.html.eex | 5 + .../update_web/templates/user/form.html.eex | 17 ++ .../update_web/templates/user/index.html.eex | 26 +++ .../update_web/templates/user/new.html.eex | 5 + .../update_web/templates/user/show.html.eex | 13 ++ update/lib/update_web/views/comment_view.ex | 3 + update/lib/update_web/views/error_helpers.ex | 40 ++++ update/lib/update_web/views/error_view.ex | 17 ++ update/lib/update_web/views/layout_view.ex | 3 + update/lib/update_web/views/page_view.ex | 3 + update/lib/update_web/views/topic_view.ex | 3 + update/lib/update_web/views/user_view.ex | 3 + update/mix.exs | 60 ++++++ update/mix.lock | 19 ++ update/priv/gettext/en/LC_MESSAGES/errors.po | 97 +++++++++ update/priv/gettext/errors.pot | 95 +++++++++ .../20170911234511_create_users.exs | 12 ++ .../20170911234518_create_topics.exs | 12 ++ .../20170911234524_create_comments.exs | 12 ++ update/priv/repo/seeds.exs | 11 + update/test/support/channel_case.ex | 37 ++++ update/test/support/conn_case.ex | 38 ++++ update/test/support/data_case.ex | 53 +++++ update/test/test_helper.exs | 4 + update/test/update/accounts/accounts_test.exs | 65 ++++++ .../update/discussions/discussions_test.exs | 125 +++++++++++ .../controllers/comment_controller_test.exs | 88 ++++++++ .../controllers/page_controller_test.exs | 8 + .../controllers/topic_controller_test.exs | 88 ++++++++ .../controllers/user_controller_test.exs | 88 ++++++++ .../test/update_web/views/error_view_test.exs | 21 ++ .../update_web/views/layout_view_test.exs | 3 + .../test/update_web/views/page_view_test.exs | 3 + 77 files changed, 2551 insertions(+) create mode 100644 update/.gitignore create mode 100644 update/README.md create mode 100644 update/assets/brunch-config.js create mode 100644 update/assets/css/app.css create mode 100644 update/assets/css/phoenix.css create mode 100644 update/assets/js/app.js create mode 100644 update/assets/js/socket.js create mode 100644 update/assets/package.json create mode 100644 update/assets/static/favicon.ico create mode 100644 update/assets/static/images/phoenix.png create mode 100644 update/assets/static/robots.txt create mode 100644 update/config/config.exs create mode 100644 update/config/dev.exs create mode 100644 update/config/prod.exs create mode 100644 update/config/test.exs create mode 100644 update/lib/update.ex create mode 100644 update/lib/update/accounts/accounts.ex create mode 100644 update/lib/update/accounts/user.ex create mode 100644 update/lib/update/application.ex create mode 100644 update/lib/update/discussions/comment.ex create mode 100644 update/lib/update/discussions/discussions.ex create mode 100644 update/lib/update/discussions/topic.ex create mode 100644 update/lib/update/repo.ex create mode 100644 update/lib/update_web.ex create mode 100644 update/lib/update_web/channels/user_socket.ex create mode 100644 update/lib/update_web/controllers/comment_controller.ex create mode 100644 update/lib/update_web/controllers/page_controller.ex create mode 100644 update/lib/update_web/controllers/topic_controller.ex create mode 100644 update/lib/update_web/controllers/user_controller.ex create mode 100644 update/lib/update_web/endpoint.ex create mode 100644 update/lib/update_web/gettext.ex create mode 100644 update/lib/update_web/router.ex create mode 100644 update/lib/update_web/templates/comment/edit.html.eex create mode 100644 update/lib/update_web/templates/comment/form.html.eex create mode 100644 update/lib/update_web/templates/comment/index.html.eex create mode 100644 update/lib/update_web/templates/comment/new.html.eex create mode 100644 update/lib/update_web/templates/comment/show.html.eex create mode 100644 update/lib/update_web/templates/layout/app.html.eex create mode 100644 update/lib/update_web/templates/page/index.html.eex create mode 100644 update/lib/update_web/templates/topic/edit.html.eex create mode 100644 update/lib/update_web/templates/topic/form.html.eex create mode 100644 update/lib/update_web/templates/topic/index.html.eex create mode 100644 update/lib/update_web/templates/topic/new.html.eex create mode 100644 update/lib/update_web/templates/topic/show.html.eex create mode 100644 update/lib/update_web/templates/user/edit.html.eex create mode 100644 update/lib/update_web/templates/user/form.html.eex create mode 100644 update/lib/update_web/templates/user/index.html.eex create mode 100644 update/lib/update_web/templates/user/new.html.eex create mode 100644 update/lib/update_web/templates/user/show.html.eex create mode 100644 update/lib/update_web/views/comment_view.ex create mode 100644 update/lib/update_web/views/error_helpers.ex create mode 100644 update/lib/update_web/views/error_view.ex create mode 100644 update/lib/update_web/views/layout_view.ex create mode 100644 update/lib/update_web/views/page_view.ex create mode 100644 update/lib/update_web/views/topic_view.ex create mode 100644 update/lib/update_web/views/user_view.ex create mode 100644 update/mix.exs create mode 100644 update/mix.lock create mode 100644 update/priv/gettext/en/LC_MESSAGES/errors.po create mode 100644 update/priv/gettext/errors.pot create mode 100644 update/priv/repo/migrations/20170911234511_create_users.exs create mode 100644 update/priv/repo/migrations/20170911234518_create_topics.exs create mode 100644 update/priv/repo/migrations/20170911234524_create_comments.exs create mode 100644 update/priv/repo/seeds.exs create mode 100644 update/test/support/channel_case.ex create mode 100644 update/test/support/conn_case.ex create mode 100644 update/test/support/data_case.ex create mode 100644 update/test/test_helper.exs create mode 100644 update/test/update/accounts/accounts_test.exs create mode 100644 update/test/update/discussions/discussions_test.exs create mode 100644 update/test/update_web/controllers/comment_controller_test.exs create mode 100644 update/test/update_web/controllers/page_controller_test.exs create mode 100644 update/test/update_web/controllers/topic_controller_test.exs create mode 100644 update/test/update_web/controllers/user_controller_test.exs create mode 100644 update/test/update_web/views/error_view_test.exs create mode 100644 update/test/update_web/views/layout_view_test.exs create mode 100644 update/test/update_web/views/page_view_test.exs diff --git a/update/.gitignore b/update/.gitignore new file mode 100644 index 0000000..d5be28f --- /dev/null +++ b/update/.gitignore @@ -0,0 +1,27 @@ +# App artifacts +/_build +/db +/deps +/*.ez + +# Generated on crash by the VM +erl_crash.dump + +# Generated on crash by NPM +npm-debug.log + +# Static artifacts +/assets/node_modules + +# Since we are building assets from assets/, +# we ignore priv/static. You may want to comment +# this depending on your deployment strategy. +/priv/static/ + +# Files matching config/*.secret.exs pattern contain sensitive +# data and you should not commit them into version control. +# +# Alternatively, you may comment the line below and commit the +# secrets files as long as you replace their contents by environment +# variables. +/config/*.secret.exs \ No newline at end of file diff --git a/update/README.md b/update/README.md new file mode 100644 index 0000000..2b80f3a --- /dev/null +++ b/update/README.md @@ -0,0 +1,20 @@ +# Update + +To start your Phoenix server: + + * Install dependencies with `mix deps.get` + * Create and migrate your database with `mix ecto.create && mix ecto.migrate` + * Install Node.js dependencies with `cd assets && npm install` + * Start Phoenix endpoint with `mix phx.server` + +Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. + +Ready to run in production? Please [check our deployment guides](http://www.phoenixframework.org/docs/deployment). + +## Learn more + + * Official website: http://www.phoenixframework.org/ + * Guides: http://phoenixframework.org/docs/overview + * Docs: https://hexdocs.pm/phoenix + * Mailing list: http://groups.google.com/group/phoenix-talk + * Source: https://github.com/phoenixframework/phoenix diff --git a/update/assets/brunch-config.js b/update/assets/brunch-config.js new file mode 100644 index 0000000..b15df46 --- /dev/null +++ b/update/assets/brunch-config.js @@ -0,0 +1,62 @@ +exports.config = { + // See http://brunch.io/#documentation for docs. + files: { + javascripts: { + joinTo: "js/app.js" + + // To use a separate vendor.js bundle, specify two files path + // http://brunch.io/docs/config#-files- + // joinTo: { + // "js/app.js": /^js/, + // "js/vendor.js": /^(?!js)/ + // } + // + // To change the order of concatenation of files, explicitly mention here + // order: { + // before: [ + // "vendor/js/jquery-2.1.1.js", + // "vendor/js/bootstrap.min.js" + // ] + // } + }, + stylesheets: { + joinTo: "css/app.css" + }, + templates: { + joinTo: "js/app.js" + } + }, + + conventions: { + // This option sets where we should place non-css and non-js assets in. + // By default, we set this to "/assets/static". Files in this directory + // will be copied to `paths.public`, which is "priv/static" by default. + assets: /^(static)/ + }, + + // Phoenix paths configuration + paths: { + // Dependencies and current project directories to watch + watched: ["static", "css", "js", "vendor"], + // Where to compile files to + public: "../priv/static" + }, + + // Configure your plugins + plugins: { + babel: { + // Do not use ES6 compiler in vendor code + ignore: [/vendor/] + } + }, + + modules: { + autoRequire: { + "js/app.js": ["js/app"] + } + }, + + npm: { + enabled: true + } +}; diff --git a/update/assets/css/app.css b/update/assets/css/app.css new file mode 100644 index 0000000..5314c34 --- /dev/null +++ b/update/assets/css/app.css @@ -0,0 +1 @@ +/* This file is for your main application css. */ \ No newline at end of file diff --git a/update/assets/css/phoenix.css b/update/assets/css/phoenix.css new file mode 100644 index 0000000..0b406d7 --- /dev/null +++ b/update/assets/css/phoenix.css @@ -0,0 +1,77 @@ +/* Includes Bootstrap as well as some default style for the starter + * application. This can be safely deleted to start fresh. + */ + +/*! + * Bootstrap v3.3.5 (http://getbootstrap.com) + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,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-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}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{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;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-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!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:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../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:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.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"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;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}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,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,.h5,.h6,h4,h5,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:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}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{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.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-md-1,.col-md-10,.col-md-11,.col-md-12,.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-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.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-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\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=checkbox]:focus,input[type=radio]: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.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-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,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top: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 .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{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;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.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:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.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,.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-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-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:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(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:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.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-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;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:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.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:3px}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}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn: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:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{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-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-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:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-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:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{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{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.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 #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.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:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.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{margin-bottom:5px;text-align:center}.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:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;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-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{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-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{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;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.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;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{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-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@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 .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;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-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@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:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:3;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-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{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-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,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.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,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.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,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.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,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.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:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.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 th:first-child,.panel>.table-responsive:first-child>.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 th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.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 th:last-child,.panel>.table-responsive:first-child>.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 th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.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 th:first-child,.panel>.table-responsive:last-child>.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 th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.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 th:last-child,.panel>.table-responsive:last-child>.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 th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.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:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;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{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.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;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.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%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left: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}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!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!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} + +/* Space out content a bit */ +body, form, ul, table { + margin-top: 20px; + margin-bottom: 20px; +} + +/* Phoenix flash messages */ +.alert:empty { display: none; } + +/* Custom page header */ +.header { + border-bottom: 1px solid #e5e5e5; +} +.logo { + width: 519px; + height: 71px; + display: inline-block; + margin-bottom: 1em; + background-image: url("/images/phoenix.png"); + background-size: 519px 71px; +} + +/* Everything but the jumbotron gets side spacing for mobile first views */ +.header, +.marketing { + padding-right: 15px; + padding-left: 15px; +} + +/* Customize container */ +@media (min-width: 768px) { + .container { + max-width: 730px; + } +} +.container-narrow > hr { + margin: 30px 0; +} + +/* Main marketing message */ +.jumbotron { + text-align: center; + border-bottom: 1px solid #e5e5e5; +} + +/* Supporting marketing content */ +.marketing { + margin: 35px 0; +} + +/* Responsive: Portrait tablets and up */ +@media screen and (min-width: 768px) { + /* Remove the padding we set earlier */ + .header, + .marketing { + padding-right: 0; + padding-left: 0; + } + /* Space out the masthead */ + .header { + margin-bottom: 30px; + } + /* Remove the bottom border on the jumbotron for visual effect */ + .jumbotron { + border-bottom: 0; + } +} \ No newline at end of file diff --git a/update/assets/js/app.js b/update/assets/js/app.js new file mode 100644 index 0000000..e7549b9 --- /dev/null +++ b/update/assets/js/app.js @@ -0,0 +1,21 @@ +// Brunch automatically concatenates all files in your +// watched paths. Those paths can be configured at +// config.paths.watched in "brunch-config.js". +// +// However, those files will only be executed if +// explicitly imported. The only exception are files +// in vendor, which are never wrapped in imports and +// therefore are always executed. + +// Import dependencies +// +// If you no longer want to use a dependency, remember +// to also remove its path from "config.paths.watched". +import "phoenix_html" + +// Import local files +// +// Local files can be imported directly using relative +// paths "./socket" or full ones "web/static/js/socket". + +// import socket from "./socket" diff --git a/update/assets/js/socket.js b/update/assets/js/socket.js new file mode 100644 index 0000000..5c23a67 --- /dev/null +++ b/update/assets/js/socket.js @@ -0,0 +1,62 @@ +// NOTE: The contents of this file will only be executed if +// you uncomment its entry in "assets/js/app.js". + +// To use Phoenix channels, the first step is to import Socket +// and connect at the socket path in "lib/web/endpoint.ex": +import {Socket} from "phoenix" + +let socket = new Socket("/socket", {params: {token: window.userToken}}) + +// When you connect, you'll often need to authenticate the client. +// For example, imagine you have an authentication plug, `MyAuth`, +// which authenticates the session and assigns a `:current_user`. +// If the current user exists you can assign the user's token in +// the connection for use in the layout. +// +// In your "lib/web/router.ex": +// +// pipeline :browser do +// ... +// plug MyAuth +// plug :put_user_token +// end +// +// defp put_user_token(conn, _) do +// if current_user = conn.assigns[:current_user] do +// token = Phoenix.Token.sign(conn, "user socket", current_user.id) +// assign(conn, :user_token, token) +// else +// conn +// end +// end +// +// Now you need to pass this token to JavaScript. You can do so +// inside a script tag in "lib/web/templates/layout/app.html.eex": +// +// +// +// You will need to verify the user token in the "connect/2" function +// in "lib/web/channels/user_socket.ex": +// +// def connect(%{"token" => token}, socket) do +// # max_age: 1209600 is equivalent to two weeks in seconds +// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do +// {:ok, user_id} -> +// {:ok, assign(socket, :user, user_id)} +// {:error, reason} -> +// :error +// end +// end +// +// Finally, pass the token on connect as below. Or remove it +// from connect if you don't care about authentication. + +socket.connect() + +// Now that you are connected, you can join channels with a topic: +let channel = socket.channel("topic:subtopic", {}) +channel.join() + .receive("ok", resp => { console.log("Joined successfully", resp) }) + .receive("error", resp => { console.log("Unable to join", resp) }) + +export default socket diff --git a/update/assets/package.json b/update/assets/package.json new file mode 100644 index 0000000..4cc3986 --- /dev/null +++ b/update/assets/package.json @@ -0,0 +1,18 @@ +{ + "repository": {}, + "license": "MIT", + "scripts": { + "deploy": "brunch build --production", + "watch": "brunch watch --stdin" + }, + "dependencies": { + "phoenix": "file:../deps/phoenix", + "phoenix_html": "file:../deps/phoenix_html" + }, + "devDependencies": { + "babel-brunch": "6.1.1", + "brunch": "2.10.9", + "clean-css-brunch": "2.10.0", + "uglify-js-brunch": "2.10.0" + } +} diff --git a/update/assets/static/favicon.ico b/update/assets/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..73de524aaadcf60fbe9d32881db0aa86b58b5cb9 GIT binary patch literal 1258 zcmbtUO>fgM7{=qN=;Mz_82;lvPEdVaxv-<-&=sZLwab?3I zBP>U*&(Hv<5n@9ZQ$vhg#|u$Zmtq8BV;+W*7(?jOx-{r?#TE&$Sdq77MbdJjD5`-q zMm_z(jLv3t>5NhzK{%aG(Yudfpjd3AFdKe2U7&zdepTe>^s(@!&0X8TJ`h+-I?84Ml# literal 0 HcmV?d00001 diff --git a/update/assets/static/images/phoenix.png b/update/assets/static/images/phoenix.png new file mode 100644 index 0000000000000000000000000000000000000000..9c81075f63d2151e6f40e9aa66f665749a87cc6a GIT binary patch literal 13900 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 UWxWXg`G1#+f~I_}taaG`2PLnS&Hw-a literal 0 HcmV?d00001 diff --git a/update/assets/static/robots.txt b/update/assets/static/robots.txt new file mode 100644 index 0000000..3c9c7c0 --- /dev/null +++ b/update/assets/static/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-agent: * +# Disallow: / diff --git a/update/config/config.exs b/update/config/config.exs new file mode 100644 index 0000000..9dff2f9 --- /dev/null +++ b/update/config/config.exs @@ -0,0 +1,27 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +# +# This configuration file is loaded before any dependency and +# is restricted to this project. +use Mix.Config + +# General application configuration +config :update, + ecto_repos: [Update.Repo] + +# Configures the endpoint +config :update, UpdateWeb.Endpoint, + url: [host: "localhost"], + secret_key_base: "VAgvpUJWWIViThsi3S2z5bbfE8V9EAc8Cag7sJcjxHRkMQjzac19F5fWs/bciVt+", + render_errors: [view: UpdateWeb.ErrorView, accepts: ~w(html json)], + pubsub: [name: Update.PubSub, + adapter: Phoenix.PubSub.PG2] + +# Configures Elixir's Logger +config :logger, :console, + format: "$time $metadata[$level] $message\n", + metadata: [:request_id] + +# Import environment specific config. This must remain at the bottom +# of this file so it overrides the configuration defined above. +import_config "#{Mix.env}.exs" diff --git a/update/config/dev.exs b/update/config/dev.exs new file mode 100644 index 0000000..dcf608c --- /dev/null +++ b/update/config/dev.exs @@ -0,0 +1,58 @@ +use Mix.Config + +# For development, we disable any cache and enable +# debugging and code reloading. +# +# The watchers configuration can be used to run external +# watchers to your application. For example, we use it +# with brunch.io to recompile .js and .css sources. +config :update, UpdateWeb.Endpoint, + http: [port: 4000], + debug_errors: true, + code_reloader: true, + check_origin: false, + watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin", + cd: Path.expand("../assets", __DIR__)]] + +# ## SSL Support +# +# In order to use HTTPS in development, a self-signed +# certificate can be generated by running the following +# command from your terminal: +# +# openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout priv/server.key -out priv/server.pem +# +# The `http:` config above can be replaced with: +# +# https: [port: 4000, keyfile: "priv/server.key", certfile: "priv/server.pem"], +# +# If desired, both `http:` and `https:` keys can be +# configured to run both http and https servers on +# different ports. + +# Watch static and templates for browser reloading. +config :update, UpdateWeb.Endpoint, + live_reload: [ + patterns: [ + ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$}, + ~r{priv/gettext/.*(po)$}, + ~r{lib/update_web/views/.*(ex)$}, + ~r{lib/update_web/templates/.*(eex)$} + ] + ] + +# Do not include metadata nor timestamps in development logs +config :logger, :console, format: "[$level] $message\n" + +# Set a higher stacktrace during development. Avoid configuring such +# in production as building large stacktraces may be expensive. +config :phoenix, :stacktrace_depth, 20 + +# Configure your database +config :update, Update.Repo, + adapter: Ecto.Adapters.Postgres, + username: "stephengrider", + password: "", + database: "update_dev", + hostname: "localhost", + pool_size: 10 diff --git a/update/config/prod.exs b/update/config/prod.exs new file mode 100644 index 0000000..5acc065 --- /dev/null +++ b/update/config/prod.exs @@ -0,0 +1,64 @@ +use Mix.Config + +# For production, we often load configuration from external +# sources, such as your system environment. For this reason, +# you won't find the :http configuration below, but set inside +# UpdateWeb.Endpoint.init/2 when load_from_system_env is +# true. Any dynamic configuration should be done there. +# +# Don't forget to configure the url host to something meaningful, +# Phoenix uses this information when generating URLs. +# +# Finally, we also include the path to a cache manifest +# containing the digested version of static files. This +# manifest is generated by the mix phx.digest task +# which you typically run after static files are built. +config :update, UpdateWeb.Endpoint, + load_from_system_env: true, + url: [host: "example.com", port: 80], + cache_static_manifest: "priv/static/cache_manifest.json" + +# Do not print debug messages in production +config :logger, level: :info + +# ## SSL Support +# +# To get SSL working, you will need to add the `https` key +# to the previous section and set your `:url` port to 443: +# +# config :update, UpdateWeb.Endpoint, +# ... +# url: [host: "example.com", port: 443], +# https: [:inet6, +# port: 443, +# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), +# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")] +# +# Where those two env variables return an absolute path to +# the key and cert in disk or a relative path inside priv, +# for example "priv/ssl/server.key". +# +# We also recommend setting `force_ssl`, ensuring no data is +# ever sent via http, always redirecting to https: +# +# config :update, UpdateWeb.Endpoint, +# force_ssl: [hsts: true] +# +# Check `Plug.SSL` for all available options in `force_ssl`. + +# ## Using releases +# +# If you are doing OTP releases, you need to instruct Phoenix +# to start the server for all endpoints: +# +# config :phoenix, :serve_endpoints, true +# +# Alternatively, you can configure exactly which server to +# start per endpoint: +# +# config :update, UpdateWeb.Endpoint, server: true +# + +# Finally import the config/prod.secret.exs +# which should be versioned separately. +import_config "prod.secret.exs" diff --git a/update/config/test.exs b/update/config/test.exs new file mode 100644 index 0000000..a26f161 --- /dev/null +++ b/update/config/test.exs @@ -0,0 +1,19 @@ +use Mix.Config + +# We don't run a server during test. If one is required, +# you can enable the server option below. +config :update, UpdateWeb.Endpoint, + http: [port: 4001], + server: false + +# Print only warnings and errors during test +config :logger, level: :warn + +# Configure your database +config :update, Update.Repo, + adapter: Ecto.Adapters.Postgres, + username: "postgres", + password: "postgres", + database: "update_test", + hostname: "localhost", + pool: Ecto.Adapters.SQL.Sandbox diff --git a/update/lib/update.ex b/update/lib/update.ex new file mode 100644 index 0000000..e07b486 --- /dev/null +++ b/update/lib/update.ex @@ -0,0 +1,9 @@ +defmodule Update do + @moduledoc """ + Update keeps the contexts that define your domain + and business logic. + + Contexts are also responsible for managing your data, regardless + if it comes from the database, an external API or others. + """ +end diff --git a/update/lib/update/accounts/accounts.ex b/update/lib/update/accounts/accounts.ex new file mode 100644 index 0000000..6acb4b6 --- /dev/null +++ b/update/lib/update/accounts/accounts.ex @@ -0,0 +1,104 @@ +defmodule Update.Accounts do + @moduledoc """ + The Accounts context. + """ + + import Ecto.Query, warn: false + alias Update.Repo + + alias Update.Accounts.User + + @doc """ + Returns the list of users. + + ## Examples + + iex> list_users() + [%User{}, ...] + + """ + def list_users do + Repo.all(User) + end + + @doc """ + Gets a single user. + + Raises `Ecto.NoResultsError` if the User does not exist. + + ## Examples + + iex> get_user!(123) + %User{} + + iex> get_user!(456) + ** (Ecto.NoResultsError) + + """ + def get_user!(id), do: Repo.get!(User, id) + + @doc """ + Creates a user. + + ## Examples + + iex> create_user(%{field: value}) + {:ok, %User{}} + + iex> create_user(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_user(attrs \\ %{}) do + %User{} + |> User.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a user. + + ## Examples + + iex> update_user(user, %{field: new_value}) + {:ok, %User{}} + + iex> update_user(user, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_user(%User{} = user, attrs) do + user + |> User.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a User. + + ## Examples + + iex> delete_user(user) + {:ok, %User{}} + + iex> delete_user(user) + {:error, %Ecto.Changeset{}} + + """ + def delete_user(%User{} = user) do + Repo.delete(user) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking user changes. + + ## Examples + + iex> change_user(user) + %Ecto.Changeset{source: %User{}} + + """ + def change_user(%User{} = user) do + User.changeset(user, %{}) + end +end diff --git a/update/lib/update/accounts/user.ex b/update/lib/update/accounts/user.ex new file mode 100644 index 0000000..ee17d40 --- /dev/null +++ b/update/lib/update/accounts/user.ex @@ -0,0 +1,19 @@ +defmodule Update.Accounts.User do + use Ecto.Schema + import Ecto.Changeset + alias Update.Accounts.User + + + schema "users" do + field :email, :string + + timestamps() + end + + @doc false + def changeset(%User{} = user, attrs) do + user + |> cast(attrs, [:email]) + |> validate_required([:email]) + end +end diff --git a/update/lib/update/application.ex b/update/lib/update/application.ex new file mode 100644 index 0000000..28b28b4 --- /dev/null +++ b/update/lib/update/application.ex @@ -0,0 +1,31 @@ +defmodule Update.Application do + use Application + + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + def start(_type, _args) do + import Supervisor.Spec + + # Define workers and child supervisors to be supervised + children = [ + # Start the Ecto repository + supervisor(Update.Repo, []), + # Start the endpoint when the application starts + supervisor(UpdateWeb.Endpoint, []), + # Start your own worker by calling: Update.Worker.start_link(arg1, arg2, arg3) + # worker(Update.Worker, [arg1, arg2, arg3]), + ] + + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Update.Supervisor] + Supervisor.start_link(children, opts) + end + + # Tell Phoenix to update the endpoint configuration + # whenever the application is updated. + def config_change(changed, _new, removed) do + UpdateWeb.Endpoint.config_change(changed, removed) + :ok + end +end diff --git a/update/lib/update/discussions/comment.ex b/update/lib/update/discussions/comment.ex new file mode 100644 index 0000000..c6e5f89 --- /dev/null +++ b/update/lib/update/discussions/comment.ex @@ -0,0 +1,19 @@ +defmodule Update.Discussions.Comment do + use Ecto.Schema + import Ecto.Changeset + alias Update.Discussions.Comment + + + schema "comments" do + field :content, :string + + timestamps() + end + + @doc false + def changeset(%Comment{} = comment, attrs) do + comment + |> cast(attrs, [:content]) + |> validate_required([:content]) + end +end diff --git a/update/lib/update/discussions/discussions.ex b/update/lib/update/discussions/discussions.ex new file mode 100644 index 0000000..5603c26 --- /dev/null +++ b/update/lib/update/discussions/discussions.ex @@ -0,0 +1,200 @@ +defmodule Update.Discussions do + @moduledoc """ + The Discussions context. + """ + + import Ecto.Query, warn: false + alias Update.Repo + + alias Update.Discussions.Topic + + @doc """ + Returns the list of topics. + + ## Examples + + iex> list_topics() + [%Topic{}, ...] + + """ + def list_topics do + Repo.all(Topic) + end + + @doc """ + Gets a single topic. + + Raises `Ecto.NoResultsError` if the Topic does not exist. + + ## Examples + + iex> get_topic!(123) + %Topic{} + + iex> get_topic!(456) + ** (Ecto.NoResultsError) + + """ + def get_topic!(id), do: Repo.get!(Topic, id) + + @doc """ + Creates a topic. + + ## Examples + + iex> create_topic(%{field: value}) + {:ok, %Topic{}} + + iex> create_topic(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_topic(attrs \\ %{}) do + %Topic{} + |> Topic.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a topic. + + ## Examples + + iex> update_topic(topic, %{field: new_value}) + {:ok, %Topic{}} + + iex> update_topic(topic, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_topic(%Topic{} = topic, attrs) do + topic + |> Topic.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a Topic. + + ## Examples + + iex> delete_topic(topic) + {:ok, %Topic{}} + + iex> delete_topic(topic) + {:error, %Ecto.Changeset{}} + + """ + def delete_topic(%Topic{} = topic) do + Repo.delete(topic) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking topic changes. + + ## Examples + + iex> change_topic(topic) + %Ecto.Changeset{source: %Topic{}} + + """ + def change_topic(%Topic{} = topic) do + Topic.changeset(topic, %{}) + end + + alias Update.Discussions.Comment + + @doc """ + Returns the list of comments. + + ## Examples + + iex> list_comments() + [%Comment{}, ...] + + """ + def list_comments do + Repo.all(Comment) + end + + @doc """ + Gets a single comment. + + Raises `Ecto.NoResultsError` if the Comment does not exist. + + ## Examples + + iex> get_comment!(123) + %Comment{} + + iex> get_comment!(456) + ** (Ecto.NoResultsError) + + """ + def get_comment!(id), do: Repo.get!(Comment, id) + + @doc """ + Creates a comment. + + ## Examples + + iex> create_comment(%{field: value}) + {:ok, %Comment{}} + + iex> create_comment(%{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def create_comment(attrs \\ %{}) do + %Comment{} + |> Comment.changeset(attrs) + |> Repo.insert() + end + + @doc """ + Updates a comment. + + ## Examples + + iex> update_comment(comment, %{field: new_value}) + {:ok, %Comment{}} + + iex> update_comment(comment, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_comment(%Comment{} = comment, attrs) do + comment + |> Comment.changeset(attrs) + |> Repo.update() + end + + @doc """ + Deletes a Comment. + + ## Examples + + iex> delete_comment(comment) + {:ok, %Comment{}} + + iex> delete_comment(comment) + {:error, %Ecto.Changeset{}} + + """ + def delete_comment(%Comment{} = comment) do + Repo.delete(comment) + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking comment changes. + + ## Examples + + iex> change_comment(comment) + %Ecto.Changeset{source: %Comment{}} + + """ + def change_comment(%Comment{} = comment) do + Comment.changeset(comment, %{}) + end +end diff --git a/update/lib/update/discussions/topic.ex b/update/lib/update/discussions/topic.ex new file mode 100644 index 0000000..52cfaf9 --- /dev/null +++ b/update/lib/update/discussions/topic.ex @@ -0,0 +1,19 @@ +defmodule Update.Discussions.Topic do + use Ecto.Schema + import Ecto.Changeset + alias Update.Discussions.Topic + + + schema "topics" do + field :title, :string + + timestamps() + end + + @doc false + def changeset(%Topic{} = topic, attrs) do + topic + |> cast(attrs, [:title]) + |> validate_required([:title]) + end +end diff --git a/update/lib/update/repo.ex b/update/lib/update/repo.ex new file mode 100644 index 0000000..7c8c7dd --- /dev/null +++ b/update/lib/update/repo.ex @@ -0,0 +1,11 @@ +defmodule Update.Repo do + use Ecto.Repo, otp_app: :update + + @doc """ + Dynamically loads the repository url from the + DATABASE_URL environment variable. + """ + def init(_, opts) do + {:ok, Keyword.put(opts, :url, System.get_env("DATABASE_URL"))} + end +end diff --git a/update/lib/update_web.ex b/update/lib/update_web.ex new file mode 100644 index 0000000..9077957 --- /dev/null +++ b/update/lib/update_web.ex @@ -0,0 +1,67 @@ +defmodule UpdateWeb do + @moduledoc """ + The entrypoint for defining your web interface, such + as controllers, views, channels and so on. + + This can be used in your application as: + + use UpdateWeb, :controller + use UpdateWeb, :view + + The definitions below will be executed for every view, + controller, etc, so keep them short and clean, focused + on imports, uses and aliases. + + Do NOT define functions inside the quoted expressions + below. Instead, define any helper function in modules + and import those modules here. + """ + + def controller do + quote do + use Phoenix.Controller, namespace: UpdateWeb + import Plug.Conn + import UpdateWeb.Router.Helpers + import UpdateWeb.Gettext + end + end + + def view do + quote do + use Phoenix.View, root: "lib/update_web/templates", + namespace: UpdateWeb + + # Import convenience functions from controllers + import Phoenix.Controller, only: [get_flash: 2, view_module: 1] + + # Use all HTML functionality (forms, tags, etc) + use Phoenix.HTML + + import UpdateWeb.Router.Helpers + import UpdateWeb.ErrorHelpers + import UpdateWeb.Gettext + end + end + + def router do + quote do + use Phoenix.Router + import Plug.Conn + import Phoenix.Controller + end + end + + def channel do + quote do + use Phoenix.Channel + import UpdateWeb.Gettext + end + end + + @doc """ + When used, dispatch to the appropriate controller/view/etc. + """ + defmacro __using__(which) when is_atom(which) do + apply(__MODULE__, which, []) + end +end diff --git a/update/lib/update_web/channels/user_socket.ex b/update/lib/update_web/channels/user_socket.ex new file mode 100644 index 0000000..3e3966e --- /dev/null +++ b/update/lib/update_web/channels/user_socket.ex @@ -0,0 +1,37 @@ +defmodule UpdateWeb.UserSocket do + use Phoenix.Socket + + ## Channels + # channel "room:*", UpdateWeb.RoomChannel + + ## Transports + transport :websocket, Phoenix.Transports.WebSocket + # transport :longpoll, Phoenix.Transports.LongPoll + + # Socket params are passed from the client and can + # be used to verify and authenticate a user. After + # verification, you can put default assigns into + # the socket that will be set for all channels, ie + # + # {:ok, assign(socket, :user_id, verified_user_id)} + # + # To deny connection, return `:error`. + # + # See `Phoenix.Token` documentation for examples in + # performing token verification on connect. + def connect(_params, socket) do + {:ok, socket} + end + + # Socket id's are topics that allow you to identify all sockets for a given user: + # + # def id(socket), do: "user_socket:#{socket.assigns.user_id}" + # + # Would allow you to broadcast a "disconnect" event and terminate + # all active sockets and channels for a given user: + # + # UpdateWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{}) + # + # Returning `nil` makes this socket anonymous. + def id(_socket), do: nil +end diff --git a/update/lib/update_web/controllers/comment_controller.ex b/update/lib/update_web/controllers/comment_controller.ex new file mode 100644 index 0000000..2a5ef1e --- /dev/null +++ b/update/lib/update_web/controllers/comment_controller.ex @@ -0,0 +1,60 @@ +defmodule UpdateWeb.CommentController do + use UpdateWeb, :controller + + alias Update.Discussions + alias Update.Discussions.Comment + + def index(conn, _params) do + comments = Discussions.list_comments() + render(conn, "index.html", comments: comments) + end + + def new(conn, _params) do + changeset = Discussions.change_comment(%Comment{}) + render(conn, "new.html", changeset: changeset) + end + + def create(conn, %{"comment" => comment_params}) do + case Discussions.create_comment(comment_params) do + {:ok, comment} -> + conn + |> put_flash(:info, "Comment created successfully.") + |> redirect(to: comment_path(conn, :show, comment)) + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "new.html", changeset: changeset) + end + end + + def show(conn, %{"id" => id}) do + comment = Discussions.get_comment!(id) + render(conn, "show.html", comment: comment) + end + + def edit(conn, %{"id" => id}) do + comment = Discussions.get_comment!(id) + changeset = Discussions.change_comment(comment) + render(conn, "edit.html", comment: comment, changeset: changeset) + end + + def update(conn, %{"id" => id, "comment" => comment_params}) do + comment = Discussions.get_comment!(id) + + case Discussions.update_comment(comment, comment_params) do + {:ok, comment} -> + conn + |> put_flash(:info, "Comment updated successfully.") + |> redirect(to: comment_path(conn, :show, comment)) + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "edit.html", comment: comment, changeset: changeset) + end + end + + def delete(conn, %{"id" => id}) do + comment = Discussions.get_comment!(id) + {:ok, _comment} = Discussions.delete_comment(comment) + + conn + |> put_flash(:info, "Comment deleted successfully.") + |> redirect(to: comment_path(conn, :index)) + end +end diff --git a/update/lib/update_web/controllers/page_controller.ex b/update/lib/update_web/controllers/page_controller.ex new file mode 100644 index 0000000..c90af53 --- /dev/null +++ b/update/lib/update_web/controllers/page_controller.ex @@ -0,0 +1,7 @@ +defmodule UpdateWeb.PageController do + use UpdateWeb, :controller + + def index(conn, _params) do + render conn, "index.html" + end +end diff --git a/update/lib/update_web/controllers/topic_controller.ex b/update/lib/update_web/controllers/topic_controller.ex new file mode 100644 index 0000000..3d7859a --- /dev/null +++ b/update/lib/update_web/controllers/topic_controller.ex @@ -0,0 +1,60 @@ +defmodule UpdateWeb.TopicController do + use UpdateWeb, :controller + + alias Update.Discussions + alias Update.Discussions.Topic + + def index(conn, _params) do + topics = Discussions.list_topics() + render(conn, "index.html", topics: topics) + end + + def new(conn, _params) do + changeset = Discussions.change_topic(%Topic{}) + render(conn, "new.html", changeset: changeset) + end + + def create(conn, %{"topic" => topic_params}) do + case Discussions.create_topic(topic_params) do + {:ok, topic} -> + conn + |> put_flash(:info, "Topic created successfully.") + |> redirect(to: topic_path(conn, :show, topic)) + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "new.html", changeset: changeset) + end + end + + def show(conn, %{"id" => id}) do + topic = Discussions.get_topic!(id) + render(conn, "show.html", topic: topic) + end + + def edit(conn, %{"id" => id}) do + topic = Discussions.get_topic!(id) + changeset = Discussions.change_topic(topic) + render(conn, "edit.html", topic: topic, changeset: changeset) + end + + def update(conn, %{"id" => id, "topic" => topic_params}) do + topic = Discussions.get_topic!(id) + + case Discussions.update_topic(topic, topic_params) do + {:ok, topic} -> + conn + |> put_flash(:info, "Topic updated successfully.") + |> redirect(to: topic_path(conn, :show, topic)) + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "edit.html", topic: topic, changeset: changeset) + end + end + + def delete(conn, %{"id" => id}) do + topic = Discussions.get_topic!(id) + {:ok, _topic} = Discussions.delete_topic(topic) + + conn + |> put_flash(:info, "Topic deleted successfully.") + |> redirect(to: topic_path(conn, :index)) + end +end diff --git a/update/lib/update_web/controllers/user_controller.ex b/update/lib/update_web/controllers/user_controller.ex new file mode 100644 index 0000000..098bf59 --- /dev/null +++ b/update/lib/update_web/controllers/user_controller.ex @@ -0,0 +1,60 @@ +defmodule UpdateWeb.UserController do + use UpdateWeb, :controller + + alias Update.Accounts + alias Update.Accounts.User + + def index(conn, _params) do + users = Accounts.list_users() + render(conn, "index.html", users: users) + end + + def new(conn, _params) do + changeset = Accounts.change_user(%User{}) + render(conn, "new.html", changeset: changeset) + end + + def create(conn, %{"user" => user_params}) do + case Accounts.create_user(user_params) do + {:ok, user} -> + conn + |> put_flash(:info, "User created successfully.") + |> redirect(to: user_path(conn, :show, user)) + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "new.html", changeset: changeset) + end + end + + def show(conn, %{"id" => id}) do + user = Accounts.get_user!(id) + render(conn, "show.html", user: user) + end + + def edit(conn, %{"id" => id}) do + user = Accounts.get_user!(id) + changeset = Accounts.change_user(user) + render(conn, "edit.html", user: user, changeset: changeset) + end + + def update(conn, %{"id" => id, "user" => user_params}) do + user = Accounts.get_user!(id) + + case Accounts.update_user(user, user_params) do + {:ok, user} -> + conn + |> put_flash(:info, "User updated successfully.") + |> redirect(to: user_path(conn, :show, user)) + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "edit.html", user: user, changeset: changeset) + end + end + + def delete(conn, %{"id" => id}) do + user = Accounts.get_user!(id) + {:ok, _user} = Accounts.delete_user(user) + + conn + |> put_flash(:info, "User deleted successfully.") + |> redirect(to: user_path(conn, :index)) + end +end diff --git a/update/lib/update_web/endpoint.ex b/update/lib/update_web/endpoint.ex new file mode 100644 index 0000000..d326bdd --- /dev/null +++ b/update/lib/update_web/endpoint.ex @@ -0,0 +1,57 @@ +defmodule UpdateWeb.Endpoint do + use Phoenix.Endpoint, otp_app: :update + + socket "/socket", UpdateWeb.UserSocket + + # Serve at "/" the static files from "priv/static" directory. + # + # You should set gzip to true if you are running phoenix.digest + # when deploying your static files in production. + plug Plug.Static, + at: "/", from: :update, gzip: false, + only: ~w(css fonts images js favicon.ico robots.txt) + + # Code reloading can be explicitly enabled under the + # :code_reloader configuration of your endpoint. + if code_reloading? do + socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket + plug Phoenix.LiveReloader + plug Phoenix.CodeReloader + end + + plug Plug.RequestId + plug Plug.Logger + + plug Plug.Parsers, + parsers: [:urlencoded, :multipart, :json], + pass: ["*/*"], + json_decoder: Poison + + plug Plug.MethodOverride + plug Plug.Head + + # The session will be stored in the cookie and signed, + # this means its contents can be read but not tampered with. + # Set :encryption_salt if you would also like to encrypt it. + plug Plug.Session, + store: :cookie, + key: "_update_key", + signing_salt: "+q7m+/9E" + + plug UpdateWeb.Router + + @doc """ + Callback invoked for dynamically configuring the endpoint. + + It receives the endpoint configuration and checks if + configuration should be loaded from the system environment. + """ + def init(_key, config) do + if config[:load_from_system_env] do + port = System.get_env("PORT") || raise "expected the PORT environment variable to be set" + {:ok, Keyword.put(config, :http, [:inet6, port: port])} + else + {:ok, config} + end + end +end diff --git a/update/lib/update_web/gettext.ex b/update/lib/update_web/gettext.ex new file mode 100644 index 0000000..7979338 --- /dev/null +++ b/update/lib/update_web/gettext.ex @@ -0,0 +1,24 @@ +defmodule UpdateWeb.Gettext do + @moduledoc """ + A module providing Internationalization with a gettext-based API. + + By using [Gettext](https://hexdocs.pm/gettext), + your module gains a set of macros for translations, for example: + + import UpdateWeb.Gettext + + # Simple translation + gettext "Here is the string to translate" + + # Plural translation + ngettext "Here is the string to translate", + "Here are the strings to translate", + 3 + + # Domain-based translation + dgettext "errors", "Here is the error message to translate" + + See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. + """ + use Gettext, otp_app: :update +end diff --git a/update/lib/update_web/router.ex b/update/lib/update_web/router.ex new file mode 100644 index 0000000..3a4113d --- /dev/null +++ b/update/lib/update_web/router.ex @@ -0,0 +1,26 @@ +defmodule UpdateWeb.Router do + use UpdateWeb, :router + + pipeline :browser do + plug :accepts, ["html"] + plug :fetch_session + plug :fetch_flash + plug :protect_from_forgery + plug :put_secure_browser_headers + end + + pipeline :api do + plug :accepts, ["json"] + end + + scope "/", UpdateWeb do + pipe_through :browser # Use the default browser stack + + get "/", PageController, :index + end + + # Other scopes may use custom stacks. + # scope "/api", UpdateWeb do + # pipe_through :api + # end +end diff --git a/update/lib/update_web/templates/comment/edit.html.eex b/update/lib/update_web/templates/comment/edit.html.eex new file mode 100644 index 0000000..f9cb7d9 --- /dev/null +++ b/update/lib/update_web/templates/comment/edit.html.eex @@ -0,0 +1,5 @@ +

      Edit Comment

      + +<%= render "form.html", Map.put(assigns, :action, comment_path(@conn, :update, @comment)) %> + +<%= link "Back", to: comment_path(@conn, :index) %> diff --git a/update/lib/update_web/templates/comment/form.html.eex b/update/lib/update_web/templates/comment/form.html.eex new file mode 100644 index 0000000..e7ac690 --- /dev/null +++ b/update/lib/update_web/templates/comment/form.html.eex @@ -0,0 +1,17 @@ +<%= form_for @changeset, @action, fn f -> %> + <%= if @changeset.action do %> +
      +

      Oops, something went wrong! Please check the errors below.

      +
      + <% end %> + +
      + <%= label f, :content, class: "control-label" %> + <%= text_input f, :content, class: "form-control" %> + <%= error_tag f, :content %> +
      + +
      + <%= submit "Submit", class: "btn btn-primary" %> +
      +<% end %> diff --git a/update/lib/update_web/templates/comment/index.html.eex b/update/lib/update_web/templates/comment/index.html.eex new file mode 100644 index 0000000..27dfc8d --- /dev/null +++ b/update/lib/update_web/templates/comment/index.html.eex @@ -0,0 +1,26 @@ +

      Listing Comments

      + + + + + + + + + + +<%= for comment <- @comments do %> + + + + + +<% end %> + +
      Content
      <%= comment.content %> + <%= link "Show", to: comment_path(@conn, :show, comment), class: "btn btn-default btn-xs" %> + <%= link "Edit", to: comment_path(@conn, :edit, comment), class: "btn btn-default btn-xs" %> + <%= link "Delete", to: comment_path(@conn, :delete, comment), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %> +
      + +<%= link "New Comment", to: comment_path(@conn, :new) %> diff --git a/update/lib/update_web/templates/comment/new.html.eex b/update/lib/update_web/templates/comment/new.html.eex new file mode 100644 index 0000000..0425f5a --- /dev/null +++ b/update/lib/update_web/templates/comment/new.html.eex @@ -0,0 +1,5 @@ +

      New Comment

      + +<%= render "form.html", Map.put(assigns, :action, comment_path(@conn, :create)) %> + +<%= link "Back", to: comment_path(@conn, :index) %> diff --git a/update/lib/update_web/templates/comment/show.html.eex b/update/lib/update_web/templates/comment/show.html.eex new file mode 100644 index 0000000..d05c1cd --- /dev/null +++ b/update/lib/update_web/templates/comment/show.html.eex @@ -0,0 +1,13 @@ +

      Show Comment

      + +
        + +
      • + Content: + <%= @comment.content %> +
      • + +
      + +<%= link "Edit", to: comment_path(@conn, :edit, @comment) %> +<%= link "Back", to: comment_path(@conn, :index) %> diff --git a/update/lib/update_web/templates/layout/app.html.eex b/update/lib/update_web/templates/layout/app.html.eex new file mode 100644 index 0000000..2cbc427 --- /dev/null +++ b/update/lib/update_web/templates/layout/app.html.eex @@ -0,0 +1,35 @@ + + + + + + + + + + Hello Update! + "> + + + +
      +
      + + +
      + + + + +
      + <%= render @view_module, @view_template, assigns %> +
      + +
      + + + diff --git a/update/lib/update_web/templates/page/index.html.eex b/update/lib/update_web/templates/page/index.html.eex new file mode 100644 index 0000000..0988ea5 --- /dev/null +++ b/update/lib/update_web/templates/page/index.html.eex @@ -0,0 +1,36 @@ +
      +

      <%= gettext "Welcome to %{name}!", name: "Phoenix" %>

      +

      A productive web framework that
      does not compromise speed and maintainability.

      +
      + +
      +
      +

      Resources

      + +
      + + +
      diff --git a/update/lib/update_web/templates/topic/edit.html.eex b/update/lib/update_web/templates/topic/edit.html.eex new file mode 100644 index 0000000..f660bdb --- /dev/null +++ b/update/lib/update_web/templates/topic/edit.html.eex @@ -0,0 +1,5 @@ +

      Edit Topic

      + +<%= render "form.html", Map.put(assigns, :action, topic_path(@conn, :update, @topic)) %> + +<%= link "Back", to: topic_path(@conn, :index) %> diff --git a/update/lib/update_web/templates/topic/form.html.eex b/update/lib/update_web/templates/topic/form.html.eex new file mode 100644 index 0000000..1912b19 --- /dev/null +++ b/update/lib/update_web/templates/topic/form.html.eex @@ -0,0 +1,17 @@ +<%= form_for @changeset, @action, fn f -> %> + <%= if @changeset.action do %> +
      +

      Oops, something went wrong! Please check the errors below.

      +
      + <% end %> + +
      + <%= label f, :title, class: "control-label" %> + <%= text_input f, :title, class: "form-control" %> + <%= error_tag f, :title %> +
      + +
      + <%= submit "Submit", class: "btn btn-primary" %> +
      +<% end %> diff --git a/update/lib/update_web/templates/topic/index.html.eex b/update/lib/update_web/templates/topic/index.html.eex new file mode 100644 index 0000000..a893329 --- /dev/null +++ b/update/lib/update_web/templates/topic/index.html.eex @@ -0,0 +1,26 @@ +

      Listing Topics

      + + + + + + + + + + +<%= for topic <- @topics do %> + + + + + +<% end %> + +
      Title
      <%= topic.title %> + <%= link "Show", to: topic_path(@conn, :show, topic), class: "btn btn-default btn-xs" %> + <%= link "Edit", to: topic_path(@conn, :edit, topic), class: "btn btn-default btn-xs" %> + <%= link "Delete", to: topic_path(@conn, :delete, topic), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %> +
      + +<%= link "New Topic", to: topic_path(@conn, :new) %> diff --git a/update/lib/update_web/templates/topic/new.html.eex b/update/lib/update_web/templates/topic/new.html.eex new file mode 100644 index 0000000..8798eed --- /dev/null +++ b/update/lib/update_web/templates/topic/new.html.eex @@ -0,0 +1,5 @@ +

      New Topic

      + +<%= render "form.html", Map.put(assigns, :action, topic_path(@conn, :create)) %> + +<%= link "Back", to: topic_path(@conn, :index) %> diff --git a/update/lib/update_web/templates/topic/show.html.eex b/update/lib/update_web/templates/topic/show.html.eex new file mode 100644 index 0000000..6b5985f --- /dev/null +++ b/update/lib/update_web/templates/topic/show.html.eex @@ -0,0 +1,13 @@ +

      Show Topic

      + +
        + +
      • + Title: + <%= @topic.title %> +
      • + +
      + +<%= link "Edit", to: topic_path(@conn, :edit, @topic) %> +<%= link "Back", to: topic_path(@conn, :index) %> diff --git a/update/lib/update_web/templates/user/edit.html.eex b/update/lib/update_web/templates/user/edit.html.eex new file mode 100644 index 0000000..338ced8 --- /dev/null +++ b/update/lib/update_web/templates/user/edit.html.eex @@ -0,0 +1,5 @@ +

      Edit User

      + +<%= render "form.html", Map.put(assigns, :action, user_path(@conn, :update, @user)) %> + +<%= link "Back", to: user_path(@conn, :index) %> diff --git a/update/lib/update_web/templates/user/form.html.eex b/update/lib/update_web/templates/user/form.html.eex new file mode 100644 index 0000000..5262651 --- /dev/null +++ b/update/lib/update_web/templates/user/form.html.eex @@ -0,0 +1,17 @@ +<%= form_for @changeset, @action, fn f -> %> + <%= if @changeset.action do %> +
      +

      Oops, something went wrong! Please check the errors below.

      +
      + <% end %> + +
      + <%= label f, :email, class: "control-label" %> + <%= text_input f, :email, class: "form-control" %> + <%= error_tag f, :email %> +
      + +
      + <%= submit "Submit", class: "btn btn-primary" %> +
      +<% end %> diff --git a/update/lib/update_web/templates/user/index.html.eex b/update/lib/update_web/templates/user/index.html.eex new file mode 100644 index 0000000..508d87c --- /dev/null +++ b/update/lib/update_web/templates/user/index.html.eex @@ -0,0 +1,26 @@ +

      Listing Users

      + + + + + + + + + + +<%= for user <- @users do %> + + + + + +<% end %> + +
      Email
      <%= user.email %> + <%= link "Show", to: user_path(@conn, :show, user), class: "btn btn-default btn-xs" %> + <%= link "Edit", to: user_path(@conn, :edit, user), class: "btn btn-default btn-xs" %> + <%= link "Delete", to: user_path(@conn, :delete, user), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %> +
      + +<%= link "New User", to: user_path(@conn, :new) %> diff --git a/update/lib/update_web/templates/user/new.html.eex b/update/lib/update_web/templates/user/new.html.eex new file mode 100644 index 0000000..95a34c2 --- /dev/null +++ b/update/lib/update_web/templates/user/new.html.eex @@ -0,0 +1,5 @@ +

      New User

      + +<%= render "form.html", Map.put(assigns, :action, user_path(@conn, :create)) %> + +<%= link "Back", to: user_path(@conn, :index) %> diff --git a/update/lib/update_web/templates/user/show.html.eex b/update/lib/update_web/templates/user/show.html.eex new file mode 100644 index 0000000..65fc7ec --- /dev/null +++ b/update/lib/update_web/templates/user/show.html.eex @@ -0,0 +1,13 @@ +

      Show User

      + +
        + +
      • + Email: + <%= @user.email %> +
      • + +
      + +<%= link "Edit", to: user_path(@conn, :edit, @user) %> +<%= link "Back", to: user_path(@conn, :index) %> diff --git a/update/lib/update_web/views/comment_view.ex b/update/lib/update_web/views/comment_view.ex new file mode 100644 index 0000000..b11bdd3 --- /dev/null +++ b/update/lib/update_web/views/comment_view.ex @@ -0,0 +1,3 @@ +defmodule UpdateWeb.CommentView do + use UpdateWeb, :view +end diff --git a/update/lib/update_web/views/error_helpers.ex b/update/lib/update_web/views/error_helpers.ex new file mode 100644 index 0000000..de35ae7 --- /dev/null +++ b/update/lib/update_web/views/error_helpers.ex @@ -0,0 +1,40 @@ +defmodule UpdateWeb.ErrorHelpers do + @moduledoc """ + Conveniences for translating and building error messages. + """ + + use Phoenix.HTML + + @doc """ + Generates tag for inlined form input errors. + """ + def error_tag(form, field) do + Enum.map(Keyword.get_values(form.errors, field), fn (error) -> + content_tag :span, translate_error(error), class: "help-block" + end) + end + + @doc """ + Translates an error message using gettext. + """ + def translate_error({msg, opts}) do + # Because error messages were defined within Ecto, we must + # call the Gettext module passing our Gettext backend. We + # also use the "errors" domain as translations are placed + # in the errors.po file. + # Ecto will pass the :count keyword if the error message is + # meant to be pluralized. + # On your own code and templates, depending on whether you + # need the message to be pluralized or not, this could be + # written simply as: + # + # dngettext "errors", "1 file", "%{count} files", count + # dgettext "errors", "is invalid" + # + if count = opts[:count] do + Gettext.dngettext(UpdateWeb.Gettext, "errors", msg, msg, count, opts) + else + Gettext.dgettext(UpdateWeb.Gettext, "errors", msg, opts) + end + end +end diff --git a/update/lib/update_web/views/error_view.ex b/update/lib/update_web/views/error_view.ex new file mode 100644 index 0000000..0fa4d02 --- /dev/null +++ b/update/lib/update_web/views/error_view.ex @@ -0,0 +1,17 @@ +defmodule UpdateWeb.ErrorView do + use UpdateWeb, :view + + def render("404.html", _assigns) do + "Page not found" + end + + def render("500.html", _assigns) do + "Internal server error" + end + + # In case no render clause matches or no + # template is found, let's render it as 500 + def template_not_found(_template, assigns) do + render "500.html", assigns + end +end diff --git a/update/lib/update_web/views/layout_view.ex b/update/lib/update_web/views/layout_view.ex new file mode 100644 index 0000000..d0cb357 --- /dev/null +++ b/update/lib/update_web/views/layout_view.ex @@ -0,0 +1,3 @@ +defmodule UpdateWeb.LayoutView do + use UpdateWeb, :view +end diff --git a/update/lib/update_web/views/page_view.ex b/update/lib/update_web/views/page_view.ex new file mode 100644 index 0000000..89b0880 --- /dev/null +++ b/update/lib/update_web/views/page_view.ex @@ -0,0 +1,3 @@ +defmodule UpdateWeb.PageView do + use UpdateWeb, :view +end diff --git a/update/lib/update_web/views/topic_view.ex b/update/lib/update_web/views/topic_view.ex new file mode 100644 index 0000000..c1ab7c2 --- /dev/null +++ b/update/lib/update_web/views/topic_view.ex @@ -0,0 +1,3 @@ +defmodule UpdateWeb.TopicView do + use UpdateWeb, :view +end diff --git a/update/lib/update_web/views/user_view.ex b/update/lib/update_web/views/user_view.ex new file mode 100644 index 0000000..9e1defa --- /dev/null +++ b/update/lib/update_web/views/user_view.ex @@ -0,0 +1,3 @@ +defmodule UpdateWeb.UserView do + use UpdateWeb, :view +end diff --git a/update/mix.exs b/update/mix.exs new file mode 100644 index 0000000..1c13a76 --- /dev/null +++ b/update/mix.exs @@ -0,0 +1,60 @@ +defmodule Update.Mixfile do + use Mix.Project + + def project do + [ + app: :update, + version: "0.0.1", + elixir: "~> 1.4", + elixirc_paths: elixirc_paths(Mix.env), + compilers: [:phoenix, :gettext] ++ Mix.compilers, + start_permanent: Mix.env == :prod, + aliases: aliases(), + deps: deps() + ] + end + + # Configuration for the OTP application. + # + # Type `mix help compile.app` for more information. + def application do + [ + mod: {Update.Application, []}, + extra_applications: [:logger, :runtime_tools] + ] + end + + # Specifies which paths to compile per environment. + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] + + # Specifies your project dependencies. + # + # Type `mix help deps` for examples and options. + defp deps do + [ + {:phoenix, "~> 1.3.0"}, + {:phoenix_pubsub, "~> 1.0"}, + {:phoenix_ecto, "~> 3.2"}, + {:postgrex, ">= 0.0.0"}, + {:phoenix_html, "~> 2.10"}, + {:phoenix_live_reload, "~> 1.0", only: :dev}, + {:gettext, "~> 0.11"}, + {:cowboy, "~> 1.0"} + ] + end + + # Aliases are shortcuts or tasks specific to the current project. + # For example, to create, migrate and run the seeds file at once: + # + # $ mix ecto.setup + # + # See the documentation for `Mix` for more info on aliases. + defp aliases do + [ + "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], + "ecto.reset": ["ecto.drop", "ecto.setup"], + "test": ["ecto.create --quiet", "ecto.migrate", "test"] + ] + end +end diff --git a/update/mix.lock b/update/mix.lock new file mode 100644 index 0000000..9448ccf --- /dev/null +++ b/update/mix.lock @@ -0,0 +1,19 @@ +%{"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [], [], "hexpm"}, + "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, + "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"}, + "db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, + "decimal": {:hex, :decimal, "1.4.0", "fac965ce71a46aab53d3a6ce45662806bdd708a4a95a65cde8a12eb0124a1333", [], [], "hexpm"}, + "ecto": {:hex, :ecto, "2.2.2", "e9bd6ebc044eaaab1cb369e3465686d8aca830aa5bf545ef2bae000a3d42c54b", [], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"}, + "file_system": {:hex, :file_system, "0.2.1", "c4bec8f187d2aabace4beb890f0d4e468f65ca051593db768e533a274d0df587", [], [], "hexpm"}, + "gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [], [], "hexpm"}, + "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"}, + "phoenix": {:hex, :phoenix, "1.3.0", "1c01124caa1b4a7af46f2050ff11b267baa3edb441b45dbf243e979cd4c5891b", [], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, + "phoenix_ecto": {:hex, :phoenix_ecto, "3.2.3", "450c749876ff1de4a78fdb305a142a76817c77a1cd79aeca29e5fc9a6c630b26", [], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, + "phoenix_html": {:hex, :phoenix_html, "2.10.4", "d4f99c32d5dc4918b531fdf163e1fd7cf20acdd7703f16f5d02d4db36de803b7", [], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, + "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.1.1", "8c9f0fb95d5592f3769f83fda852d3aab806309c946831f7e50fe447c2859e13", [], [{:file_system, "~> 0.2", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [], [], "hexpm"}, + "plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"}, + "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [], [], "hexpm"}, + "postgrex": {:hex, :postgrex, "0.13.3", "c277cfb2a9c5034d445a722494c13359e361d344ef6f25d604c2353185682bfc", [], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"}, + "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [], [], "hexpm"}} diff --git a/update/priv/gettext/en/LC_MESSAGES/errors.po b/update/priv/gettext/en/LC_MESSAGES/errors.po new file mode 100644 index 0000000..a589998 --- /dev/null +++ b/update/priv/gettext/en/LC_MESSAGES/errors.po @@ -0,0 +1,97 @@ +## `msgid`s in this file come from POT (.pot) files. +## +## Do not add, change, or remove `msgid`s manually here as +## they're tied to the ones in the corresponding POT file +## (with the same domain). +## +## Use `mix gettext.extract --merge` or `mix gettext.merge` +## to merge POT files into PO files. +msgid "" +msgstr "" +"Language: en\n" + +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_acceptance/3 +msgid "must be accepted" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated with this entry" +msgstr "" + +msgid "are still associated with this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" diff --git a/update/priv/gettext/errors.pot b/update/priv/gettext/errors.pot new file mode 100644 index 0000000..7b2d5ca --- /dev/null +++ b/update/priv/gettext/errors.pot @@ -0,0 +1,95 @@ +## This file is a PO Template file. +## +## `msgid`s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run `mix gettext.extract` to bring this file up to +## date. Leave `msgstr`s empty as changing them here as no +## effect: edit them in PO (`.po`) files instead. + +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_acceptance/3 +msgid "must be accepted" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated with this entry" +msgstr "" + +msgid "are still associated with this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" diff --git a/update/priv/repo/migrations/20170911234511_create_users.exs b/update/priv/repo/migrations/20170911234511_create_users.exs new file mode 100644 index 0000000..fb145d9 --- /dev/null +++ b/update/priv/repo/migrations/20170911234511_create_users.exs @@ -0,0 +1,12 @@ +defmodule Update.Repo.Migrations.CreateUsers do + use Ecto.Migration + + def change do + create table(:users) do + add :email, :string + + timestamps() + end + + end +end diff --git a/update/priv/repo/migrations/20170911234518_create_topics.exs b/update/priv/repo/migrations/20170911234518_create_topics.exs new file mode 100644 index 0000000..fc67f67 --- /dev/null +++ b/update/priv/repo/migrations/20170911234518_create_topics.exs @@ -0,0 +1,12 @@ +defmodule Update.Repo.Migrations.CreateTopics do + use Ecto.Migration + + def change do + create table(:topics) do + add :title, :string + + timestamps() + end + + end +end diff --git a/update/priv/repo/migrations/20170911234524_create_comments.exs b/update/priv/repo/migrations/20170911234524_create_comments.exs new file mode 100644 index 0000000..5168256 --- /dev/null +++ b/update/priv/repo/migrations/20170911234524_create_comments.exs @@ -0,0 +1,12 @@ +defmodule Update.Repo.Migrations.CreateComments do + use Ecto.Migration + + def change do + create table(:comments) do + add :content, :string + + timestamps() + end + + end +end diff --git a/update/priv/repo/seeds.exs b/update/priv/repo/seeds.exs new file mode 100644 index 0000000..2db1967 --- /dev/null +++ b/update/priv/repo/seeds.exs @@ -0,0 +1,11 @@ +# Script for populating the database. You can run it as: +# +# mix run priv/repo/seeds.exs +# +# Inside the script, you can read and write to any of your +# repositories directly: +# +# Update.Repo.insert!(%Update.SomeSchema{}) +# +# We recommend using the bang functions (`insert!`, `update!` +# and so on) as they will fail if something goes wrong. diff --git a/update/test/support/channel_case.ex b/update/test/support/channel_case.ex new file mode 100644 index 0000000..3724590 --- /dev/null +++ b/update/test/support/channel_case.ex @@ -0,0 +1,37 @@ +defmodule UpdateWeb.ChannelCase do + @moduledoc """ + This module defines the test case to be used by + channel tests. + + Such tests rely on `Phoenix.ChannelTest` and also + import other functionality to make it easier + to build common datastructures and query the data layer. + + Finally, if the test case interacts with the database, + it cannot be async. For this reason, every test runs + inside a transaction which is reset at the beginning + of the test unless the test case is marked as async. + """ + + use ExUnit.CaseTemplate + + using do + quote do + # Import conveniences for testing with channels + use Phoenix.ChannelTest + + # The default endpoint for testing + @endpoint UpdateWeb.Endpoint + end + end + + + setup tags do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Update.Repo) + unless tags[:async] do + Ecto.Adapters.SQL.Sandbox.mode(Update.Repo, {:shared, self()}) + end + :ok + end + +end diff --git a/update/test/support/conn_case.ex b/update/test/support/conn_case.ex new file mode 100644 index 0000000..7dbc091 --- /dev/null +++ b/update/test/support/conn_case.ex @@ -0,0 +1,38 @@ +defmodule UpdateWeb.ConnCase do + @moduledoc """ + This module defines the test case to be used by + tests that require setting up a connection. + + Such tests rely on `Phoenix.ConnTest` and also + import other functionality to make it easier + to build common datastructures and query the data layer. + + Finally, if the test case interacts with the database, + it cannot be async. For this reason, every test runs + inside a transaction which is reset at the beginning + of the test unless the test case is marked as async. + """ + + use ExUnit.CaseTemplate + + using do + quote do + # Import conveniences for testing with connections + use Phoenix.ConnTest + import UpdateWeb.Router.Helpers + + # The default endpoint for testing + @endpoint UpdateWeb.Endpoint + end + end + + + setup tags do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Update.Repo) + unless tags[:async] do + Ecto.Adapters.SQL.Sandbox.mode(Update.Repo, {:shared, self()}) + end + {:ok, conn: Phoenix.ConnTest.build_conn()} + end + +end diff --git a/update/test/support/data_case.ex b/update/test/support/data_case.ex new file mode 100644 index 0000000..d0ad8d0 --- /dev/null +++ b/update/test/support/data_case.ex @@ -0,0 +1,53 @@ +defmodule Update.DataCase do + @moduledoc """ + This module defines the setup for tests requiring + access to the application's data layer. + + You may define functions here to be used as helpers in + your tests. + + Finally, if the test case interacts with the database, + it cannot be async. For this reason, every test runs + inside a transaction which is reset at the beginning + of the test unless the test case is marked as async. + """ + + use ExUnit.CaseTemplate + + using do + quote do + alias Update.Repo + + import Ecto + import Ecto.Changeset + import Ecto.Query + import Update.DataCase + end + end + + setup tags do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Update.Repo) + + unless tags[:async] do + Ecto.Adapters.SQL.Sandbox.mode(Update.Repo, {:shared, self()}) + end + + :ok + end + + @doc """ + A helper that transform changeset errors to a map of messages. + + assert {:error, changeset} = Accounts.create_user(%{password: "short"}) + assert "password is too short" in errors_on(changeset).password + assert %{password: ["password is too short"]} = errors_on(changeset) + + """ + def errors_on(changeset) do + Ecto.Changeset.traverse_errors(changeset, fn {message, opts} -> + Enum.reduce(opts, message, fn {key, value}, acc -> + String.replace(acc, "%{#{key}}", to_string(value)) + end) + end) + end +end diff --git a/update/test/test_helper.exs b/update/test/test_helper.exs new file mode 100644 index 0000000..8ad4e76 --- /dev/null +++ b/update/test/test_helper.exs @@ -0,0 +1,4 @@ +ExUnit.start() + +Ecto.Adapters.SQL.Sandbox.mode(Update.Repo, :manual) + diff --git a/update/test/update/accounts/accounts_test.exs b/update/test/update/accounts/accounts_test.exs new file mode 100644 index 0000000..f7e2410 --- /dev/null +++ b/update/test/update/accounts/accounts_test.exs @@ -0,0 +1,65 @@ +defmodule Update.AccountsTest do + use Update.DataCase + + alias Update.Accounts + + describe "users" do + alias Update.Accounts.User + + @valid_attrs %{email: "some email"} + @update_attrs %{email: "some updated email"} + @invalid_attrs %{email: nil} + + def user_fixture(attrs \\ %{}) do + {:ok, user} = + attrs + |> Enum.into(@valid_attrs) + |> Accounts.create_user() + + user + end + + test "list_users/0 returns all users" do + user = user_fixture() + assert Accounts.list_users() == [user] + end + + test "get_user!/1 returns the user with given id" do + user = user_fixture() + assert Accounts.get_user!(user.id) == user + end + + test "create_user/1 with valid data creates a user" do + assert {:ok, %User{} = user} = Accounts.create_user(@valid_attrs) + assert user.email == "some email" + end + + test "create_user/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Accounts.create_user(@invalid_attrs) + end + + test "update_user/2 with valid data updates the user" do + user = user_fixture() + assert {:ok, user} = Accounts.update_user(user, @update_attrs) + assert %User{} = user + assert user.email == "some updated email" + end + + test "update_user/2 with invalid data returns error changeset" do + user = user_fixture() + assert {:error, %Ecto.Changeset{}} = Accounts.update_user(user, @invalid_attrs) + assert user == Accounts.get_user!(user.id) + end + + test "delete_user/1 deletes the user" do + user = user_fixture() + assert {:ok, %User{}} = Accounts.delete_user(user) + assert_raise Ecto.NoResultsError, fn -> Accounts.get_user!(user.id) end + end + + test "change_user/1 returns a user changeset" do + user = user_fixture() + assert %Ecto.Changeset{} = Accounts.change_user(user) + end + end +end diff --git a/update/test/update/discussions/discussions_test.exs b/update/test/update/discussions/discussions_test.exs new file mode 100644 index 0000000..9ed00d5 --- /dev/null +++ b/update/test/update/discussions/discussions_test.exs @@ -0,0 +1,125 @@ +defmodule Update.DiscussionsTest do + use Update.DataCase + + alias Update.Discussions + + describe "topics" do + alias Update.Discussions.Topic + + @valid_attrs %{title: "some title"} + @update_attrs %{title: "some updated title"} + @invalid_attrs %{title: nil} + + def topic_fixture(attrs \\ %{}) do + {:ok, topic} = + attrs + |> Enum.into(@valid_attrs) + |> Discussions.create_topic() + + topic + end + + test "list_topics/0 returns all topics" do + topic = topic_fixture() + assert Discussions.list_topics() == [topic] + end + + test "get_topic!/1 returns the topic with given id" do + topic = topic_fixture() + assert Discussions.get_topic!(topic.id) == topic + end + + test "create_topic/1 with valid data creates a topic" do + assert {:ok, %Topic{} = topic} = Discussions.create_topic(@valid_attrs) + assert topic.title == "some title" + end + + test "create_topic/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Discussions.create_topic(@invalid_attrs) + end + + test "update_topic/2 with valid data updates the topic" do + topic = topic_fixture() + assert {:ok, topic} = Discussions.update_topic(topic, @update_attrs) + assert %Topic{} = topic + assert topic.title == "some updated title" + end + + test "update_topic/2 with invalid data returns error changeset" do + topic = topic_fixture() + assert {:error, %Ecto.Changeset{}} = Discussions.update_topic(topic, @invalid_attrs) + assert topic == Discussions.get_topic!(topic.id) + end + + test "delete_topic/1 deletes the topic" do + topic = topic_fixture() + assert {:ok, %Topic{}} = Discussions.delete_topic(topic) + assert_raise Ecto.NoResultsError, fn -> Discussions.get_topic!(topic.id) end + end + + test "change_topic/1 returns a topic changeset" do + topic = topic_fixture() + assert %Ecto.Changeset{} = Discussions.change_topic(topic) + end + end + + describe "comments" do + alias Update.Discussions.Comment + + @valid_attrs %{content: "some content"} + @update_attrs %{content: "some updated content"} + @invalid_attrs %{content: nil} + + def comment_fixture(attrs \\ %{}) do + {:ok, comment} = + attrs + |> Enum.into(@valid_attrs) + |> Discussions.create_comment() + + comment + end + + test "list_comments/0 returns all comments" do + comment = comment_fixture() + assert Discussions.list_comments() == [comment] + end + + test "get_comment!/1 returns the comment with given id" do + comment = comment_fixture() + assert Discussions.get_comment!(comment.id) == comment + end + + test "create_comment/1 with valid data creates a comment" do + assert {:ok, %Comment{} = comment} = Discussions.create_comment(@valid_attrs) + assert comment.content == "some content" + end + + test "create_comment/1 with invalid data returns error changeset" do + assert {:error, %Ecto.Changeset{}} = Discussions.create_comment(@invalid_attrs) + end + + test "update_comment/2 with valid data updates the comment" do + comment = comment_fixture() + assert {:ok, comment} = Discussions.update_comment(comment, @update_attrs) + assert %Comment{} = comment + assert comment.content == "some updated content" + end + + test "update_comment/2 with invalid data returns error changeset" do + comment = comment_fixture() + assert {:error, %Ecto.Changeset{}} = Discussions.update_comment(comment, @invalid_attrs) + assert comment == Discussions.get_comment!(comment.id) + end + + test "delete_comment/1 deletes the comment" do + comment = comment_fixture() + assert {:ok, %Comment{}} = Discussions.delete_comment(comment) + assert_raise Ecto.NoResultsError, fn -> Discussions.get_comment!(comment.id) end + end + + test "change_comment/1 returns a comment changeset" do + comment = comment_fixture() + assert %Ecto.Changeset{} = Discussions.change_comment(comment) + end + end +end diff --git a/update/test/update_web/controllers/comment_controller_test.exs b/update/test/update_web/controllers/comment_controller_test.exs new file mode 100644 index 0000000..f7c247f --- /dev/null +++ b/update/test/update_web/controllers/comment_controller_test.exs @@ -0,0 +1,88 @@ +defmodule UpdateWeb.CommentControllerTest do + use UpdateWeb.ConnCase + + alias Update.Discussions + + @create_attrs %{content: "some content"} + @update_attrs %{content: "some updated content"} + @invalid_attrs %{content: nil} + + def fixture(:comment) do + {:ok, comment} = Discussions.create_comment(@create_attrs) + comment + end + + describe "index" do + test "lists all comments", %{conn: conn} do + conn = get conn, comment_path(conn, :index) + assert html_response(conn, 200) =~ "Listing Comments" + end + end + + describe "new comment" do + test "renders form", %{conn: conn} do + conn = get conn, comment_path(conn, :new) + assert html_response(conn, 200) =~ "New Comment" + end + end + + describe "create comment" do + test "redirects to show when data is valid", %{conn: conn} do + conn = post conn, comment_path(conn, :create), comment: @create_attrs + + assert %{id: id} = redirected_params(conn) + assert redirected_to(conn) == comment_path(conn, :show, id) + + conn = get conn, comment_path(conn, :show, id) + assert html_response(conn, 200) =~ "Show Comment" + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post conn, comment_path(conn, :create), comment: @invalid_attrs + assert html_response(conn, 200) =~ "New Comment" + end + end + + describe "edit comment" do + setup [:create_comment] + + test "renders form for editing chosen comment", %{conn: conn, comment: comment} do + conn = get conn, comment_path(conn, :edit, comment) + assert html_response(conn, 200) =~ "Edit Comment" + end + end + + describe "update comment" do + setup [:create_comment] + + test "redirects when data is valid", %{conn: conn, comment: comment} do + conn = put conn, comment_path(conn, :update, comment), comment: @update_attrs + assert redirected_to(conn) == comment_path(conn, :show, comment) + + conn = get conn, comment_path(conn, :show, comment) + assert html_response(conn, 200) =~ "some updated content" + end + + test "renders errors when data is invalid", %{conn: conn, comment: comment} do + conn = put conn, comment_path(conn, :update, comment), comment: @invalid_attrs + assert html_response(conn, 200) =~ "Edit Comment" + end + end + + describe "delete comment" do + setup [:create_comment] + + test "deletes chosen comment", %{conn: conn, comment: comment} do + conn = delete conn, comment_path(conn, :delete, comment) + assert redirected_to(conn) == comment_path(conn, :index) + assert_error_sent 404, fn -> + get conn, comment_path(conn, :show, comment) + end + end + end + + defp create_comment(_) do + comment = fixture(:comment) + {:ok, comment: comment} + end +end diff --git a/update/test/update_web/controllers/page_controller_test.exs b/update/test/update_web/controllers/page_controller_test.exs new file mode 100644 index 0000000..37a8a89 --- /dev/null +++ b/update/test/update_web/controllers/page_controller_test.exs @@ -0,0 +1,8 @@ +defmodule UpdateWeb.PageControllerTest do + use UpdateWeb.ConnCase + + test "GET /", %{conn: conn} do + conn = get conn, "/" + assert html_response(conn, 200) =~ "Welcome to Phoenix!" + end +end diff --git a/update/test/update_web/controllers/topic_controller_test.exs b/update/test/update_web/controllers/topic_controller_test.exs new file mode 100644 index 0000000..f897f2f --- /dev/null +++ b/update/test/update_web/controllers/topic_controller_test.exs @@ -0,0 +1,88 @@ +defmodule UpdateWeb.TopicControllerTest do + use UpdateWeb.ConnCase + + alias Update.Discussions + + @create_attrs %{title: "some title"} + @update_attrs %{title: "some updated title"} + @invalid_attrs %{title: nil} + + def fixture(:topic) do + {:ok, topic} = Discussions.create_topic(@create_attrs) + topic + end + + describe "index" do + test "lists all topics", %{conn: conn} do + conn = get conn, topic_path(conn, :index) + assert html_response(conn, 200) =~ "Listing Topics" + end + end + + describe "new topic" do + test "renders form", %{conn: conn} do + conn = get conn, topic_path(conn, :new) + assert html_response(conn, 200) =~ "New Topic" + end + end + + describe "create topic" do + test "redirects to show when data is valid", %{conn: conn} do + conn = post conn, topic_path(conn, :create), topic: @create_attrs + + assert %{id: id} = redirected_params(conn) + assert redirected_to(conn) == topic_path(conn, :show, id) + + conn = get conn, topic_path(conn, :show, id) + assert html_response(conn, 200) =~ "Show Topic" + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post conn, topic_path(conn, :create), topic: @invalid_attrs + assert html_response(conn, 200) =~ "New Topic" + end + end + + describe "edit topic" do + setup [:create_topic] + + test "renders form for editing chosen topic", %{conn: conn, topic: topic} do + conn = get conn, topic_path(conn, :edit, topic) + assert html_response(conn, 200) =~ "Edit Topic" + end + end + + describe "update topic" do + setup [:create_topic] + + test "redirects when data is valid", %{conn: conn, topic: topic} do + conn = put conn, topic_path(conn, :update, topic), topic: @update_attrs + assert redirected_to(conn) == topic_path(conn, :show, topic) + + conn = get conn, topic_path(conn, :show, topic) + assert html_response(conn, 200) =~ "some updated title" + end + + test "renders errors when data is invalid", %{conn: conn, topic: topic} do + conn = put conn, topic_path(conn, :update, topic), topic: @invalid_attrs + assert html_response(conn, 200) =~ "Edit Topic" + end + end + + describe "delete topic" do + setup [:create_topic] + + test "deletes chosen topic", %{conn: conn, topic: topic} do + conn = delete conn, topic_path(conn, :delete, topic) + assert redirected_to(conn) == topic_path(conn, :index) + assert_error_sent 404, fn -> + get conn, topic_path(conn, :show, topic) + end + end + end + + defp create_topic(_) do + topic = fixture(:topic) + {:ok, topic: topic} + end +end diff --git a/update/test/update_web/controllers/user_controller_test.exs b/update/test/update_web/controllers/user_controller_test.exs new file mode 100644 index 0000000..6e188e6 --- /dev/null +++ b/update/test/update_web/controllers/user_controller_test.exs @@ -0,0 +1,88 @@ +defmodule UpdateWeb.UserControllerTest do + use UpdateWeb.ConnCase + + alias Update.Accounts + + @create_attrs %{email: "some email"} + @update_attrs %{email: "some updated email"} + @invalid_attrs %{email: nil} + + def fixture(:user) do + {:ok, user} = Accounts.create_user(@create_attrs) + user + end + + describe "index" do + test "lists all users", %{conn: conn} do + conn = get conn, user_path(conn, :index) + assert html_response(conn, 200) =~ "Listing Users" + end + end + + describe "new user" do + test "renders form", %{conn: conn} do + conn = get conn, user_path(conn, :new) + assert html_response(conn, 200) =~ "New User" + end + end + + describe "create user" do + test "redirects to show when data is valid", %{conn: conn} do + conn = post conn, user_path(conn, :create), user: @create_attrs + + assert %{id: id} = redirected_params(conn) + assert redirected_to(conn) == user_path(conn, :show, id) + + conn = get conn, user_path(conn, :show, id) + assert html_response(conn, 200) =~ "Show User" + end + + test "renders errors when data is invalid", %{conn: conn} do + conn = post conn, user_path(conn, :create), user: @invalid_attrs + assert html_response(conn, 200) =~ "New User" + end + end + + describe "edit user" do + setup [:create_user] + + test "renders form for editing chosen user", %{conn: conn, user: user} do + conn = get conn, user_path(conn, :edit, user) + assert html_response(conn, 200) =~ "Edit User" + end + end + + describe "update user" do + setup [:create_user] + + test "redirects when data is valid", %{conn: conn, user: user} do + conn = put conn, user_path(conn, :update, user), user: @update_attrs + assert redirected_to(conn) == user_path(conn, :show, user) + + conn = get conn, user_path(conn, :show, user) + assert html_response(conn, 200) =~ "some updated email" + end + + test "renders errors when data is invalid", %{conn: conn, user: user} do + conn = put conn, user_path(conn, :update, user), user: @invalid_attrs + assert html_response(conn, 200) =~ "Edit User" + end + end + + describe "delete user" do + setup [:create_user] + + test "deletes chosen user", %{conn: conn, user: user} do + conn = delete conn, user_path(conn, :delete, user) + assert redirected_to(conn) == user_path(conn, :index) + assert_error_sent 404, fn -> + get conn, user_path(conn, :show, user) + end + end + end + + defp create_user(_) do + user = fixture(:user) + {:ok, user: user} + end +end diff --git a/update/test/update_web/views/error_view_test.exs b/update/test/update_web/views/error_view_test.exs new file mode 100644 index 0000000..65cfe73 --- /dev/null +++ b/update/test/update_web/views/error_view_test.exs @@ -0,0 +1,21 @@ +defmodule UpdateWeb.ErrorViewTest do + use UpdateWeb.ConnCase, async: true + + # Bring render/3 and render_to_string/3 for testing custom views + import Phoenix.View + + test "renders 404.html" do + assert render_to_string(UpdateWeb.ErrorView, "404.html", []) == + "Page not found" + end + + test "render 500.html" do + assert render_to_string(UpdateWeb.ErrorView, "500.html", []) == + "Internal server error" + end + + test "render any other" do + assert render_to_string(UpdateWeb.ErrorView, "505.html", []) == + "Internal server error" + end +end diff --git a/update/test/update_web/views/layout_view_test.exs b/update/test/update_web/views/layout_view_test.exs new file mode 100644 index 0000000..97ad818 --- /dev/null +++ b/update/test/update_web/views/layout_view_test.exs @@ -0,0 +1,3 @@ +defmodule UpdateWeb.LayoutViewTest do + use UpdateWeb.ConnCase, async: true +end diff --git a/update/test/update_web/views/page_view_test.exs b/update/test/update_web/views/page_view_test.exs new file mode 100644 index 0000000..e2e0067 --- /dev/null +++ b/update/test/update_web/views/page_view_test.exs @@ -0,0 +1,3 @@ +defmodule UpdateWeb.PageViewTest do + use UpdateWeb.ConnCase, async: true +end From 755a8051ac45aaef59ff3ccfd07a36ead3e5f785 Mon Sep 17 00:00:00 2001 From: stephengrider Date: Mon, 22 Jan 2018 10:46:10 -0800 Subject: [PATCH 85/86] added react example --- discuss/mix.exs | 2 +- discuss/mix.lock | 12 +- react-phoenix/.gitignore | 24 + react-phoenix/README.md | 20 + react-phoenix/brunch-config.js | 70 + react-phoenix/config/config.exs | 36 + react-phoenix/config/dev.exs | 43 + react-phoenix/config/prod.exs | 61 + react-phoenix/config/test.exs | 19 + react-phoenix/lib/reddit.ex | 31 + react-phoenix/lib/reddit/endpoint.ex | 42 + react-phoenix/lib/reddit/repo.ex | 3 + react-phoenix/mix.exs | 59 + react-phoenix/mix.lock | 31 + react-phoenix/package-lock.json | 2894 +++++++++++++++++ react-phoenix/package.json | 24 + .../priv/gettext/en/LC_MESSAGES/errors.po | 93 + react-phoenix/priv/gettext/errors.pot | 91 + .../migrations/20160927181632_create_user.exs | 14 + .../migrations/20160929203558_add_posts.exs | 12 + .../migrations/20160929203753_comments.exs | 13 + react-phoenix/priv/repo/seeds.exs | 11 + .../test/controllers/post_controller_test.exs | 58 + .../controllers/session_controller_test.exs | 48 + react-phoenix/test/models/post_test.exs | 18 + react-phoenix/test/models/user_test.exs | 18 + react-phoenix/test/support/channel_case.ex | 43 + react-phoenix/test/support/conn_case.ex | 45 + react-phoenix/test/support/model_case.ex | 65 + react-phoenix/test/support/model_helpers.ex | 31 + react-phoenix/test/test_helper.exs | 4 + react-phoenix/test/views/error_view_test.exs | 21 + react-phoenix/test/views/layout_view_test.exs | 3 + react-phoenix/test/views/page_view_test.exs | 3 + .../web/channels/comments_channel.ex | 38 + react-phoenix/web/channels/user_socket.ex | 23 + .../web/controllers/comment_controller.ex | 0 .../web/controllers/helpers/queries.ex | 10 + .../web/controllers/plugs/require_auth.ex | 20 + .../web/controllers/plugs/set_user.ex | 24 + .../web/controllers/post_controller.ex | 80 + .../web/controllers/session_controller.ex | 48 + react-phoenix/web/gettext.ex | 24 + react-phoenix/web/models/comment.ex | 16 + react-phoenix/web/models/post.ex | 17 + react-phoenix/web/models/user.ex | 25 + react-phoenix/web/router.ex | 35 + react-phoenix/web/static/assets/favicon.ico | Bin 0 -> 1258 bytes .../web/static/assets/images/phoenix.png | Bin 0 -> 13900 bytes react-phoenix/web/static/assets/robots.txt | 5 + react-phoenix/web/static/css/app.css | 38 + react-phoenix/web/static/js/CommentBox.js | 30 + react-phoenix/web/static/js/CommentsList.js | 19 + react-phoenix/web/static/js/app.js | 46 + react-phoenix/web/static/js/socket.js | 0 .../web/templates/layout/_flash.html.eex | 13 + .../web/templates/layout/app.html.eex | 50 + .../web/templates/post/edit.html.eex | 8 + .../web/templates/post/index.html.eex | 21 + react-phoenix/web/templates/post/new.html.eex | 8 + .../web/templates/post/show.html.eex | 8 + react-phoenix/web/views/comment_view.ex | 10 + react-phoenix/web/views/error_helpers.ex | 40 + react-phoenix/web/views/error_view.ex | 17 + react-phoenix/web/views/layout_view.ex | 3 + react-phoenix/web/views/post_view.ex | 3 + react-phoenix/web/web.ex | 82 + 67 files changed, 4716 insertions(+), 7 deletions(-) create mode 100644 react-phoenix/.gitignore create mode 100644 react-phoenix/README.md create mode 100644 react-phoenix/brunch-config.js create mode 100644 react-phoenix/config/config.exs create mode 100644 react-phoenix/config/dev.exs create mode 100644 react-phoenix/config/prod.exs create mode 100644 react-phoenix/config/test.exs create mode 100644 react-phoenix/lib/reddit.ex create mode 100644 react-phoenix/lib/reddit/endpoint.ex create mode 100644 react-phoenix/lib/reddit/repo.ex create mode 100644 react-phoenix/mix.exs create mode 100644 react-phoenix/mix.lock create mode 100644 react-phoenix/package-lock.json create mode 100644 react-phoenix/package.json create mode 100644 react-phoenix/priv/gettext/en/LC_MESSAGES/errors.po create mode 100644 react-phoenix/priv/gettext/errors.pot create mode 100644 react-phoenix/priv/repo/migrations/20160927181632_create_user.exs create mode 100644 react-phoenix/priv/repo/migrations/20160929203558_add_posts.exs create mode 100644 react-phoenix/priv/repo/migrations/20160929203753_comments.exs create mode 100644 react-phoenix/priv/repo/seeds.exs create mode 100644 react-phoenix/test/controllers/post_controller_test.exs create mode 100644 react-phoenix/test/controllers/session_controller_test.exs create mode 100644 react-phoenix/test/models/post_test.exs create mode 100644 react-phoenix/test/models/user_test.exs create mode 100644 react-phoenix/test/support/channel_case.ex create mode 100644 react-phoenix/test/support/conn_case.ex create mode 100644 react-phoenix/test/support/model_case.ex create mode 100644 react-phoenix/test/support/model_helpers.ex create mode 100644 react-phoenix/test/test_helper.exs create mode 100644 react-phoenix/test/views/error_view_test.exs create mode 100644 react-phoenix/test/views/layout_view_test.exs create mode 100644 react-phoenix/test/views/page_view_test.exs create mode 100644 react-phoenix/web/channels/comments_channel.ex create mode 100644 react-phoenix/web/channels/user_socket.ex create mode 100644 react-phoenix/web/controllers/comment_controller.ex create mode 100644 react-phoenix/web/controllers/helpers/queries.ex create mode 100644 react-phoenix/web/controllers/plugs/require_auth.ex create mode 100644 react-phoenix/web/controllers/plugs/set_user.ex create mode 100644 react-phoenix/web/controllers/post_controller.ex create mode 100644 react-phoenix/web/controllers/session_controller.ex create mode 100644 react-phoenix/web/gettext.ex create mode 100644 react-phoenix/web/models/comment.ex create mode 100644 react-phoenix/web/models/post.ex create mode 100644 react-phoenix/web/models/user.ex create mode 100644 react-phoenix/web/router.ex create mode 100644 react-phoenix/web/static/assets/favicon.ico create mode 100644 react-phoenix/web/static/assets/images/phoenix.png create mode 100644 react-phoenix/web/static/assets/robots.txt create mode 100644 react-phoenix/web/static/css/app.css create mode 100644 react-phoenix/web/static/js/CommentBox.js create mode 100644 react-phoenix/web/static/js/CommentsList.js create mode 100644 react-phoenix/web/static/js/app.js create mode 100644 react-phoenix/web/static/js/socket.js create mode 100644 react-phoenix/web/templates/layout/_flash.html.eex create mode 100644 react-phoenix/web/templates/layout/app.html.eex create mode 100644 react-phoenix/web/templates/post/edit.html.eex create mode 100644 react-phoenix/web/templates/post/index.html.eex create mode 100644 react-phoenix/web/templates/post/new.html.eex create mode 100644 react-phoenix/web/templates/post/show.html.eex create mode 100644 react-phoenix/web/views/comment_view.ex create mode 100644 react-phoenix/web/views/error_helpers.ex create mode 100644 react-phoenix/web/views/error_view.ex create mode 100644 react-phoenix/web/views/layout_view.ex create mode 100644 react-phoenix/web/views/post_view.ex create mode 100644 react-phoenix/web/web.ex diff --git a/discuss/mix.exs b/discuss/mix.exs index c25fe2b..4c62c37 100644 --- a/discuss/mix.exs +++ b/discuss/mix.exs @@ -30,7 +30,7 @@ defmodule Discuss.Mixfile do # # Type `mix help deps` for examples and options. defp deps do - [{:phoenix, "~> 1.2.1"}, + [{:phoenix, "~> 1.2.5"}, {:phoenix_pubsub, "~> 1.0"}, {:phoenix_ecto, "~> 3.0"}, {:postgrex, ">= 0.0.0"}, diff --git a/discuss/mix.lock b/discuss/mix.lock index 4831ac3..5f1d652 100644 --- a/discuss/mix.lock +++ b/discuss/mix.lock @@ -1,6 +1,6 @@ %{"certifi": {:hex, :certifi, "0.7.0", "861a57f3808f7eb0c2d1802afeaae0fa5de813b0df0979153cbafcd853ababaf", [:rebar3], []}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []}, - "cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:rebar, :make], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]}, + "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, "db_connection": {:hex, :db_connection, "1.0.0", "63c03e520d54886a66104d34e32397ba960db6e74b596ce221592c07d6a40d8d", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]}, "decimal": {:hex, :decimal, "1.2.0", "462960fd71af282e570f7b477f6be56bf8968e68277d4d0b641a635269bf4b0d", [:mix], []}, @@ -11,20 +11,20 @@ "httpoison": {:hex, :httpoison, "0.9.2", "a211a8e87403a043c41218e64df250d321f236ac57f786c6a0ccf3e9e817c819", [:mix], [{:hackney, "~> 1.6.0", [hex: :hackney, optional: false]}]}, "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, - "mime": {:hex, :mime, "1.0.1", "05c393850524767d13a53627df71beeebb016205eb43bfbd92d14d24ec7a1b51", [:mix], []}, + "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], [], "hexpm"}, "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, "mimetype_parser": {:hex, :mimetype_parser, "0.1.2", "221d2d3f727e89d80de5e1610fc2ce444514aa56f873da1b8fc9c033143e5d6a", [:mix], []}, "oauth2": {:hex, :oauth2, "0.6.0", "f3967aba3f358e64fb70046f659f04ea753d7a407ce032046dd048fc2e2e4be3", [:mix], [{:httpoison, "~> 0.7", [hex: :httpoison, optional: false]}, {:mimetype_parser, "~> 0.1", [hex: :mimetype_parser, optional: false]}, {:poison, "~> 1.3 or ~> 2.0", [hex: :poison, optional: false]}]}, - "phoenix": {:hex, :phoenix, "1.2.1", "6dc592249ab73c67575769765b66ad164ad25d83defa3492dc6ae269bd2a68ab", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.1", [hex: :plug, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}]}, + "phoenix": {:hex, :phoenix, "1.2.5", "dbc45a5f8fb522aaa815b003f2d5598bc45e23102ae3e265768848ab23eafcb7", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.2.4 or ~> 1.1.8 or ~> 1.0.5", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix_ecto": {:hex, :phoenix_ecto, "3.0.1", "42eb486ef732cf209d0a353e791806721f33ff40beab0a86f02070a5649ed00a", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}, {:phoenix_html, "~> 2.6", [hex: :phoenix_html, optional: true]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]}, "phoenix_html": {:hex, :phoenix_html, "2.7.0", "19e12e2044340c2e43df206a06d059677c59ea1868bd1c35165438d592cd420b", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.5", "829218c4152ba1e9848e2bf8e161fcde6b4ec679a516259442561d21fde68d0b", [:mix], [{:fs, "~> 0.9.1", [hex: :fs, optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2-rc", [hex: :phoenix, optional: false]}]}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.1", "c10ddf6237007c804bf2b8f3c4d5b99009b42eca3a0dfac04ea2d8001186056a", [:mix], []}, - "plug": {:hex, :plug, "1.2.2", "cfbda521b54c92ab8ddffb173fbaabed8d8fc94bec07cd9bb58a84c1c501b0bd", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [:mix], [], "hexpm"}, + "plug": {:hex, :plug, "1.3.5", "7503bfcd7091df2a9761ef8cecea666d1f2cc454cbbaf0afa0b6e259203b7031", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, "poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [:mix], []}, "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], []}, "postgrex": {:hex, :postgrex, "0.12.1", "2f8b46cb3a44dcd42f42938abedbfffe7e103ba4ce810ccbeee8dcf27ca0fb06", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}, {:db_connection, "~> 1.0-rc.4", [hex: :db_connection, optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}]}, - "ranch": {:hex, :ranch, "1.2.1", "a6fb992c10f2187b46ffd17ce398ddf8a54f691b81768f9ef5f461ea7e28c762", [:make], []}, + "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], [], "hexpm"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:rebar, :make], []}, "ueberauth": {:hex, :ueberauth, "0.4.0", "bc72d5e5a7bdcbfcf28a756e34630816edabc926303bdce7e171f7ac7ffa4f91", [:mix], [{:plug, "~> 1.2", [hex: :plug, optional: false]}]}, "ueberauth_github": {:hex, :ueberauth_github, "0.4.0", "3fe9a20b88c3379bc1eed55f43a6cbf37b4c5e088d565abd07382dc1c99230a2", [:mix], [{:oauth2, "0.6.0", [hex: :oauth2, optional: false]}, {:ueberauth, "~> 0.4", [hex: :ueberauth, optional: false]}]}} diff --git a/react-phoenix/.gitignore b/react-phoenix/.gitignore new file mode 100644 index 0000000..19fe3da --- /dev/null +++ b/react-phoenix/.gitignore @@ -0,0 +1,24 @@ +# App artifacts +/_build +/db +/deps +/*.ez + +# Generated on crash by the VM +erl_crash.dump + +# Static artifacts +/node_modules + +# Since we are building assets from web/static, +# we ignore priv/static. You may want to comment +# this depending on your deployment strategy. +/priv/static/ + +# The config/prod.secret.exs file by default contains sensitive +# data and you should not commit it into version control. +# +# Alternatively, you may comment the line below and commit the +# secrets file as long as you replace its contents by environment +# variables. +/config/prod.secret.exs diff --git a/react-phoenix/README.md b/react-phoenix/README.md new file mode 100644 index 0000000..ffd9e44 --- /dev/null +++ b/react-phoenix/README.md @@ -0,0 +1,20 @@ +# Reddit + +To start your Phoenix app: + + * Install dependencies with `mix deps.get` + * Create and migrate your database with `mix ecto.create && mix ecto.migrate` + * Install Node.js dependencies with `npm install` + * Start Phoenix endpoint with `mix phoenix.server` + +Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. + +Ready to run in production? Please [check our deployment guides](http://www.phoenixframework.org/docs/deployment). + +## Learn more + + * Official website: http://www.phoenixframework.org/ + * Guides: http://phoenixframework.org/docs/overview + * Docs: https://hexdocs.pm/phoenix + * Mailing list: http://groups.google.com/group/phoenix-talk + * Source: https://github.com/phoenixframework/phoenix diff --git a/react-phoenix/brunch-config.js b/react-phoenix/brunch-config.js new file mode 100644 index 0000000..6448faa --- /dev/null +++ b/react-phoenix/brunch-config.js @@ -0,0 +1,70 @@ +exports.config = { + // See http://brunch.io/#documentation for docs. + files: { + javascripts: { + joinTo: "js/app.js" + + // To use a separate vendor.js bundle, specify two files path + // http://brunch.io/docs/config#-files- + // joinTo: { + // "js/app.js": /^(web\/static\/js)/, + // "js/vendor.js": /^(web\/static\/vendor)|(deps)/ + // } + // + // To change the order of concatenation of files, explicitly mention here + // order: { + // before: [ + // "web/static/vendor/js/jquery-2.1.1.js", + // "web/static/vendor/js/bootstrap.min.js" + // ] + // } + }, + stylesheets: { + joinTo: "css/app.css", + order: { + after: ["web/static/css/app.css"] // concat app.css last + } + }, + templates: { + joinTo: "js/app.js" + } + }, + + conventions: { + // This option sets where we should place non-css and non-js assets in. + // By default, we set this to "/web/static/assets". Files in this directory + // will be copied to `paths.public`, which is "priv/static" by default. + assets: /^(web\/static\/assets)/ + }, + + // Phoenix paths configuration + paths: { + // Dependencies and current project directories to watch + watched: [ + "web/static", + "test/static" + ], + + // Where to compile files to + public: "priv/static" + }, + + // Configure your plugins + plugins: { + babel: { + // Do not use ES6 compiler in vendor code + ignore: [/web\/static\/vendor/], + presets: ['es2015', 'es2016', 'react'] + } + }, + + modules: { + autoRequire: { + "js/app.js": ["web/static/js/app"] + } + }, + + npm: { + enabled: true + } +}; diff --git a/react-phoenix/config/config.exs b/react-phoenix/config/config.exs new file mode 100644 index 0000000..c679d57 --- /dev/null +++ b/react-phoenix/config/config.exs @@ -0,0 +1,36 @@ +# This file is responsible for configuring your application +# and its dependencies with the aid of the Mix.Config module. +# +# This configuration file is loaded before any dependency and +# is restricted to this project. +use Mix.Config + +# General application configuration +config :reddit, + ecto_repos: [Reddit.Repo] + +# Configures the endpoint +config :reddit, Reddit.Endpoint, + url: [host: "localhost"], + secret_key_base: "jibgk00xwYBxa0rw2+c5j88L6yzuCRHiQIJCTd3ntJk/nOJCT+lcEMuK9RErgEHW", + render_errors: [view: Reddit.ErrorView, accepts: ~w(html json)], + pubsub: [name: Reddit.PubSub, + adapter: Phoenix.PubSub.PG2] + +# Configures Elixir's Logger +config :logger, :console, + format: "$time $metadata[$level] $message\n", + metadata: [:request_id] + +# Import environment specific config. This must remain at the bottom +# of this file so it overrides the configuration defined above. +import_config "#{Mix.env}.exs" + +config :ueberauth, Ueberauth, + providers: [ + github: { Ueberauth.Strategy.Github, []} + ] + +config :ueberauth, Ueberauth.Strategy.Github.OAuth, + client_id: "ebf47114a980c0dcc677", + client_secret: "5dfe7d3f66f4b467fd878e782dfbe94f8d0b7bde" diff --git a/react-phoenix/config/dev.exs b/react-phoenix/config/dev.exs new file mode 100644 index 0000000..12ca856 --- /dev/null +++ b/react-phoenix/config/dev.exs @@ -0,0 +1,43 @@ +use Mix.Config + +# For development, we disable any cache and enable +# debugging and code reloading. +# +# The watchers configuration can be used to run external +# watchers to your application. For example, we use it +# with brunch.io to recompile .js and .css sources. +config :reddit, Reddit.Endpoint, + http: [port: 4000], + debug_errors: true, + code_reloader: true, + check_origin: false, + watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin", + cd: Path.expand("../", __DIR__)]] + + +# Watch static and templates for browser reloading. +config :reddit, Reddit.Endpoint, + live_reload: [ + patterns: [ + ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$}, + ~r{priv/gettext/.*(po)$}, + ~r{web/views/.*(ex)$}, + ~r{web/templates/.*(eex)$} + ] + ] + +# Do not include metadata nor timestamps in development logs +config :logger, :console, format: "[$level] $message\n" + +# Set a higher stacktrace during development. Avoid configuring such +# in production as building large stacktraces may be expensive. +config :phoenix, :stacktrace_depth, 20 + +# Configure your database +config :reddit, Reddit.Repo, + adapter: Ecto.Adapters.Postgres, + username: "stephengrider", + password: "", + database: "reddit_dev", + hostname: "localhost", + pool_size: 10 diff --git a/react-phoenix/config/prod.exs b/react-phoenix/config/prod.exs new file mode 100644 index 0000000..5f9644b --- /dev/null +++ b/react-phoenix/config/prod.exs @@ -0,0 +1,61 @@ +use Mix.Config + +# For production, we configure the host to read the PORT +# from the system environment. Therefore, you will need +# to set PORT=80 before running your server. +# +# You should also configure the url host to something +# meaningful, we use this information when generating URLs. +# +# Finally, we also include the path to a manifest +# containing the digested version of static files. This +# manifest is generated by the mix phoenix.digest task +# which you typically run after static files are built. +config :reddit, Reddit.Endpoint, + http: [port: {:system, "PORT"}], + url: [host: "example.com", port: 80], + cache_static_manifest: "priv/static/manifest.json" + +# Do not print debug messages in production +config :logger, level: :info + +# ## SSL Support +# +# To get SSL working, you will need to add the `https` key +# to the previous section and set your `:url` port to 443: +# +# config :reddit, Reddit.Endpoint, +# ... +# url: [host: "example.com", port: 443], +# https: [port: 443, +# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), +# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")] +# +# Where those two env variables return an absolute path to +# the key and cert in disk or a relative path inside priv, +# for example "priv/ssl/server.key". +# +# We also recommend setting `force_ssl`, ensuring no data is +# ever sent via http, always redirecting to https: +# +# config :reddit, Reddit.Endpoint, +# force_ssl: [hsts: true] +# +# Check `Plug.SSL` for all available options in `force_ssl`. + +# ## Using releases +# +# If you are doing OTP releases, you need to instruct Phoenix +# to start the server for all endpoints: +# +# config :phoenix, :serve_endpoints, true +# +# Alternatively, you can configure exactly which server to +# start per endpoint: +# +# config :reddit, Reddit.Endpoint, server: true +# + +# Finally import the config/prod.secret.exs +# which should be versioned separately. +import_config "prod.secret.exs" diff --git a/react-phoenix/config/test.exs b/react-phoenix/config/test.exs new file mode 100644 index 0000000..2b8843a --- /dev/null +++ b/react-phoenix/config/test.exs @@ -0,0 +1,19 @@ +use Mix.Config + +# We don't run a server during test. If one is required, +# you can enable the server option below. +config :reddit, Reddit.Endpoint, + http: [port: 4001], + server: false + +# Print only warnings and errors during test +config :logger, level: :warn + +# Configure your database +config :reddit, Reddit.Repo, + adapter: Ecto.Adapters.Postgres, + username: "postgres", + password: "postgres", + database: "reddit_test", + hostname: "localhost", + pool: Ecto.Adapters.SQL.Sandbox diff --git a/react-phoenix/lib/reddit.ex b/react-phoenix/lib/reddit.ex new file mode 100644 index 0000000..b93be8b --- /dev/null +++ b/react-phoenix/lib/reddit.ex @@ -0,0 +1,31 @@ +defmodule Reddit do + use Application + + # See http://elixir-lang.org/docs/stable/elixir/Application.html + # for more information on OTP Applications + def start(_type, _args) do + import Supervisor.Spec + + # Define workers and child supervisors to be supervised + children = [ + # Start the Ecto repository + supervisor(Reddit.Repo, []), + # Start the endpoint when the application starts + supervisor(Reddit.Endpoint, []), + # Start your own worker by calling: Reddit.Worker.start_link(arg1, arg2, arg3) + # worker(Reddit.Worker, [arg1, arg2, arg3]), + ] + + # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html + # for other strategies and supported options + opts = [strategy: :one_for_one, name: Reddit.Supervisor] + Supervisor.start_link(children, opts) + end + + # Tell Phoenix to update the endpoint configuration + # whenever the application is updated. + def config_change(changed, _new, removed) do + Reddit.Endpoint.config_change(changed, removed) + :ok + end +end diff --git a/react-phoenix/lib/reddit/endpoint.ex b/react-phoenix/lib/reddit/endpoint.ex new file mode 100644 index 0000000..fe60aaa --- /dev/null +++ b/react-phoenix/lib/reddit/endpoint.ex @@ -0,0 +1,42 @@ +defmodule Reddit.Endpoint do + use Phoenix.Endpoint, otp_app: :reddit + + socket "/socket", Reddit.UserSocket + + # Serve at "/" the static files from "priv/static" directory. + # + # You should set gzip to true if you are running phoenix.digest + # when deploying your static files in production. + plug Plug.Static, + at: "/", from: :reddit, gzip: false, + only: ~w(css fonts images js favicon.ico robots.txt) + + # Code reloading can be explicitly enabled under the + # :code_reloader configuration of your endpoint. + if code_reloading? do + socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket + plug Phoenix.LiveReloader + plug Phoenix.CodeReloader + end + + plug Plug.RequestId + plug Plug.Logger + + plug Plug.Parsers, + parsers: [:urlencoded, :multipart, :json], + pass: ["*/*"], + json_decoder: Poison + + plug Plug.MethodOverride + plug Plug.Head + + # The session will be stored in the cookie and signed, + # this means its contents can be read but not tampered with. + # Set :encryption_salt if you would also like to encrypt it. + plug Plug.Session, + store: :cookie, + key: "_reddit_key", + signing_salt: "dtbSiNgX" + + plug Reddit.Router +end diff --git a/react-phoenix/lib/reddit/repo.ex b/react-phoenix/lib/reddit/repo.ex new file mode 100644 index 0000000..d41f915 --- /dev/null +++ b/react-phoenix/lib/reddit/repo.ex @@ -0,0 +1,3 @@ +defmodule Reddit.Repo do + use Ecto.Repo, otp_app: :reddit +end diff --git a/react-phoenix/mix.exs b/react-phoenix/mix.exs new file mode 100644 index 0000000..65961b4 --- /dev/null +++ b/react-phoenix/mix.exs @@ -0,0 +1,59 @@ +defmodule Reddit.Mixfile do + use Mix.Project + + def project do + [app: :reddit, + version: "0.0.1", + elixir: "~> 1.2", + elixirc_paths: elixirc_paths(Mix.env), + compilers: [:phoenix, :gettext] ++ Mix.compilers, + build_embedded: Mix.env == :prod, + start_permanent: Mix.env == :prod, + aliases: aliases(), + deps: deps()] + end + + # Configuration for the OTP application. + # + # Type `mix help compile.app` for more information. + def application do + [mod: {Reddit, []}, + applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext, + :phoenix_ecto, :postgrex, :ueberauth, :ueberauth_github]] + end + + # Specifies which paths to compile per environment. + defp elixirc_paths(:test), do: ["lib", "web", "test/support"] + defp elixirc_paths(_), do: ["lib", "web"] + + # Specifies your project dependencies. + # + # Type `mix help deps` for examples and options. + defp deps do + [ + {:phoenix, "~> 1.2.1"}, + {:phoenix_pubsub, "~> 1.0"}, + {:phoenix_ecto, "~> 3.0"}, + {:postgrex, ">= 0.0.0"}, + {:phoenix_html, "~> 2.6"}, + {:phoenix_live_reload, "~> 1.0", only: :dev}, + {:gettext, "~> 0.11"}, + {:cowboy, "~> 1.0"}, + {:mix_test_watch, "~> 0.2", only: :dev}, + {:ueberauth, "~> 0.3"}, + {:ueberauth_github, "~> 0.4"} + ] + end + + # Aliases are shortcuts or tasks specific to the current project. + # For example, to create, migrate and run the seeds file at once: + # + # $ mix ecto.setup + # + # See the documentation for `Mix` for more info on aliases. + defp aliases do + ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], + "ecto.reset": ["ecto.drop", "ecto.setup"], + "test": ["ecto.create --quiet", "ecto.migrate", "test"]] + end +end diff --git a/react-phoenix/mix.lock b/react-phoenix/mix.lock new file mode 100644 index 0000000..d50a911 --- /dev/null +++ b/react-phoenix/mix.lock @@ -0,0 +1,31 @@ +%{"certifi": {:hex, :certifi, "0.4.0", "a7966efb868b179023618d29a407548f70c52466bf1849b9e8ebd0e34b7ea11f", [:rebar3], []}, + "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []}, + "cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:rebar, :make], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]}, + "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, + "db_connection": {:hex, :db_connection, "1.0.0-rc.5", "1d9ab6e01387bdf2de7a16c56866971f7c2f75aea7c69cae2a0346e4b537ae0d", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0.0-beta.3", [hex: :sbroker, optional: true]}]}, + "decimal": {:hex, :decimal, "1.2.0", "462960fd71af282e570f7b477f6be56bf8968e68277d4d0b641a635269bf4b0d", [:mix], []}, + "ecto": {:hex, :ecto, "2.0.5", "7f4c79ac41ffba1a4c032b69d7045489f0069c256de606523c65d9f8188e502d", [:mix], [{:db_connection, "~> 1.0-rc.4", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.1.2 or ~> 1.2", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.7.7", [hex: :mariaex, optional: true]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.12.0", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}]}, + "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []}, + "gettext": {:hex, :gettext, "0.11.0", "80c1dd42d270482418fa158ec5ba073d2980e3718bacad86f3d4ad71d5667679", [:mix], []}, + "hackney": {:hex, :hackney, "1.6.1", "ddd22d42db2b50e6a155439c8811b8f6df61a4395de10509714ad2751c6da817", [:rebar3], [{:certifi, "0.4.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.0", [hex: :ssl_verify_fun, optional: false]}]}, + "httpoison": {:hex, :httpoison, "0.9.2", "a211a8e87403a043c41218e64df250d321f236ac57f786c6a0ccf3e9e817c819", [:mix], [{:hackney, "~> 1.6.0", [hex: :hackney, optional: false]}]}, + "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, + "mime": {:hex, :mime, "1.0.1", "05c393850524767d13a53627df71beeebb016205eb43bfbd92d14d24ec7a1b51", [:mix], []}, + "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, + "mimetype_parser": {:hex, :mimetype_parser, "0.1.2", "221d2d3f727e89d80de5e1610fc2ce444514aa56f873da1b8fc9c033143e5d6a", [:mix], []}, + "mix_test_watch": {:hex, :mix_test_watch, "0.2.6", "9fcc2b1b89d1594c4a8300959c19d50da2f0ff13642c8f681692a6e507f92cab", [:mix], [{:fs, "~> 0.9.1", [hex: :fs, optional: false]}]}, + "oauth2": {:hex, :oauth2, "0.6.0", "f3967aba3f358e64fb70046f659f04ea753d7a407ce032046dd048fc2e2e4be3", [:mix], [{:httpoison, "~> 0.7", [hex: :httpoison, optional: false]}, {:mimetype_parser, "~> 0.1", [hex: :mimetype_parser, optional: false]}, {:poison, "~> 1.3 or ~> 2.0", [hex: :poison, optional: false]}]}, + "phoenix": {:hex, :phoenix, "1.2.1", "6dc592249ab73c67575769765b66ad164ad25d83defa3492dc6ae269bd2a68ab", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.1", [hex: :plug, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}]}, + "phoenix_ecto": {:hex, :phoenix_ecto, "3.0.1", "42eb486ef732cf209d0a353e791806721f33ff40beab0a86f02070a5649ed00a", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}, {:phoenix_html, "~> 2.6", [hex: :phoenix_html, optional: true]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]}, + "phoenix_html": {:hex, :phoenix_html, "2.7.0", "19e12e2044340c2e43df206a06d059677c59ea1868bd1c35165438d592cd420b", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]}, + "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.5", "829218c4152ba1e9848e2bf8e161fcde6b4ec679a516259442561d21fde68d0b", [:mix], [{:fs, "~> 0.9.1", [hex: :fs, optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2-rc", [hex: :phoenix, optional: false]}]}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.0", "c31af4be22afeeebfaf246592778c8c840e5a1ddc7ca87610c41ccfb160c2c57", [:mix], []}, + "plug": {:hex, :plug, "1.2.0", "496bef96634a49d7803ab2671482f0c5ce9ce0b7b9bc25bc0ae8e09859dd2004", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]}, + "poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [:mix], []}, + "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], []}, + "postgrex": {:hex, :postgrex, "0.12.0", "bdeeb4c42768c47c3c92228e66c70357fe9a9384fbc9de06abba774b22dd0635", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}, {:db_connection, "~> 1.0-rc.4", [hex: :db_connection, optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}]}, + "ranch": {:hex, :ranch, "1.2.1", "a6fb992c10f2187b46ffd17ce398ddf8a54f691b81768f9ef5f461ea7e28c762", [:make], []}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.0", "edee20847c42e379bf91261db474ffbe373f8acb56e9079acb6038d4e0bf414f", [:rebar, :make], []}, + "ueberauth": {:hex, :ueberauth, "0.4.0", "bc72d5e5a7bdcbfcf28a756e34630816edabc926303bdce7e171f7ac7ffa4f91", [:mix], [{:plug, "~> 1.2", [hex: :plug, optional: false]}]}, + "ueberauth_github": {:hex, :ueberauth_github, "0.4.0", "3fe9a20b88c3379bc1eed55f43a6cbf37b4c5e088d565abd07382dc1c99230a2", [:mix], [{:oauth2, "0.6.0", [hex: :oauth2, optional: false]}, {:ueberauth, "~> 0.4", [hex: :ueberauth, optional: false]}]}} diff --git a/react-phoenix/package-lock.json b/react-phoenix/package-lock.json new file mode 100644 index 0000000..0e1c8d3 --- /dev/null +++ b/react-phoenix/package-lock.json @@ -0,0 +1,2894 @@ +{ + "lockfileVersion": 1, + "dependencies": { + "accepts": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", + "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", + "dev": true + }, + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "ansicolors": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", + "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", + "dev": true + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true + }, + "anysort": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anysort/-/anysort-1.0.1.tgz", + "integrity": "sha1-NBvV1boUhfZOVa6GXx1FmUtQf8Q=", + "dev": true + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1.js": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", + "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=", + "dev": true + }, + "assert": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.3.0.tgz", + "integrity": "sha1-A5OaYiWCqBLMICMgoLmlbJuBWEk=", + "dev": true + }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "babel-brunch": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/babel-brunch/-/babel-brunch-6.0.6.tgz", + "integrity": "sha1-QVw/3HfZuYKB0uFfPzNSwPSBjG4=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true + }, + "babel-core": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", + "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", + "dev": true + }, + "babel-generator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", + "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", + "dev": true + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true + }, + "babel-helper-builder-react-jsx": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", + "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", + "dev": true + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-flow": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz", + "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=", + "dev": true + }, + "babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", + "dev": true + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", + "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", + "dev": true + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true + }, + "babel-plugin-transform-flow-strip-types": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz", + "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", + "dev": true + }, + "babel-plugin-transform-react-display-name": { + "version": "6.25.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz", + "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", + "dev": true + }, + "babel-plugin-transform-react-jsx": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz", + "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", + "dev": true + }, + "babel-plugin-transform-react-jsx-self": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz", + "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=", + "dev": true + }, + "babel-plugin-transform-react-jsx-source": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz", + "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", + "dev": true + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "dev": true + }, + "babel-preset-es2016": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2016/-/babel-preset-es2016-6.24.1.tgz", + "integrity": "sha1-+QC/k+LrwNJ235uKtZck6/2Vn4s=", + "dev": true + }, + "babel-preset-flow": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz", + "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", + "dev": true + }, + "babel-preset-react": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz", + "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=", + "dev": true + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "dependencies": { + "core-js": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", + "dev": true + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "dependencies": { + "core-js": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", + "dev": true + } + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", + "dev": true + }, + "binary-extensions": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", + "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", + "dev": true + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "bower-config": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/bower-config/-/bower-config-1.4.0.tgz", + "integrity": "sha1-FsOMETX4BxwZ8lk41hsNjL8Y0/E=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=" + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browser-resolve": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", + "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", + "dev": true + }, + "browserify-aes": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.8.tgz", + "integrity": "sha512-WYCMOT/PtGTlpOKFht0YJFYcPy6pLCR98CtWfzK13zoynLlBMvAdEMSRGmgnJCw2M2j/5qxBkinZQFobieM8dQ==", + "dev": true + }, + "browserify-cipher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", + "dev": true + }, + "browserify-des": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", + "dev": true + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true + }, + "brunch": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/brunch/-/brunch-2.7.4.tgz", + "integrity": "sha1-lhs95x/Gsuy7zwi19XhZXAdSdJo=", + "dev": true, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + } + } + }, + "brunch-skeletons": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/brunch-skeletons/-/brunch-skeletons-0.1.6.tgz", + "integrity": "sha512-x5ASPnG5yKyJFxJsCOydNPezP/YEyk4fB8GH9pdQA8sK2tFIO+RR0zzQ34Fs0y6AGcQJvx1FZvNjqW4f3Q5M3w==", + "dev": true + }, + "buffer": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.3.1.tgz", + "integrity": "sha1-DmX9Acw+kVTRUvazyTS1uKG2czw=", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-2.0.0.tgz", + "integrity": "sha1-byIAO6rPADzNKHr+aHIVH93FhXk=", + "dev": true + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true + }, + "check-dependencies": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/check-dependencies/-/check-dependencies-0.12.3.tgz", + "integrity": "sha1-a6ST1CIdvD9pURM1OuRjj3csXHU=", + "dev": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "chokidar": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.4.3.tgz", + "integrity": "sha1-X+czpNmsrqUbJkVLfllVkWPQ27I=", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true + }, + "clean-css": { + "version": "3.4.28", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz", + "integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=", + "dev": true, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dev": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true + } + } + }, + "clean-css-brunch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clean-css-brunch/-/clean-css-brunch-2.0.0.tgz", + "integrity": "sha1-N8J0dsRwigCUzWRsnEZKwZcMSxc=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + } + } + }, + "coffee-script": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.10.0.tgz", + "integrity": "sha1-EpOLz5vhlI+gBvkuDEyegXBRCMA=", + "dev": true + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true + }, + "commonjs-require-definition": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/commonjs-require-definition/-/commonjs-require-definition-0.5.0.tgz", + "integrity": "sha1-CfGLn8+xxQh1JYPf/P1q/g87eJM=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "connect-slashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/connect-slashes/-/connect-slashes-1.3.1.tgz", + "integrity": "sha1-ldYYMND51YU8hojwtfQ5iLGGrDc=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=", + "dev": true + }, + "convert-source-map": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", + "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", + "dev": true + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-ecdh": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", + "dev": true + }, + "create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", + "dev": true + }, + "create-hmac": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", + "dev": true + }, + "create-react-class": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.0.tgz", + "integrity": "sha1-q0SEl8JlZuHilBPogyB9V8/nvtQ=" + }, + "crypto-browserify": { + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", + "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==", + "dev": true + }, + "css-brunch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-brunch/-/css-brunch-2.0.0.tgz", + "integrity": "sha1-pMa+x9f2yPpHaacu6Vz9/FDFQVc=", + "dev": true + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "deep-assign": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deep-assign/-/deep-assign-2.0.0.tgz", + "integrity": "sha1-6+BrHwfwja5ZdiDj3RYi83GhxXI=", + "dev": true + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", + "dev": true + }, + "deppack": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/deppack/-/deppack-0.4.4.tgz", + "integrity": "sha1-QRqphjbAEaGkfTX7RcG/gBC+RiU=", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-file": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-0.1.0.tgz", + "integrity": "sha1-STXe39lIhkjgBrASlWbpOGcR6mM=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true + }, + "detective": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.5.0.tgz", + "integrity": "sha1-blqMaybmx6JUsca210kNmOyR7dE=", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", + "dev": true + }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", + "dev": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "dev": true + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=", + "dev": true + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", + "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=", + "dev": true + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true + }, + "expand-tilde": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz", + "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", + "dev": true + }, + "express": { + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz", + "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=", + "dev": true + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true + }, + "fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", + "dev": true + }, + "fbjs": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.14.tgz", + "integrity": "sha1-0dviviVMNakeCfMfnNUKQLKg7Rw=" + }, + "fcache": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/fcache/-/fcache-0.1.1.tgz", + "integrity": "sha1-KGIQm5L+D2iz0RvAsGYyR1Az05w=", + "dev": true + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fill-range": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", + "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", + "dev": true + }, + "finalhandler": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz", + "integrity": "sha512-16l/r8RgzlXKmFOhZpHBztvye+lAhC5SU7hXavnerC9UfZqZxxXl3BzL8MhffPT3kF61lj9Oav2LKEzh0ei7tg==", + "dev": true + }, + "findup-sync": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.4.3.tgz", + "integrity": "sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI=", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true + }, + "forwarded": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", + "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=", + "dev": true + }, + "fresh": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", + "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=", + "dev": true + }, + "fs-exists-sync": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz", + "integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", + "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==", + "dev": true, + "optional": true, + "dependencies": { + "abbrev": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "ajv": { + "version": "4.11.8", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "bundled": true, + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "assert-plus": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws-sign2": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "aws4": { + "version": "1.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "balanced-match": { + "version": "0.4.2", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "block-stream": { + "version": "0.0.9", + "bundled": true, + "dev": true + }, + "boom": { + "version": "2.10.1", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.7", + "bundled": true, + "dev": true + }, + "buffer-shims": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true + }, + "co": { + "version": "4.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "debug": { + "version": "2.6.8", + "bundled": true, + "dev": true, + "optional": true + }, + "deep-extend": { + "version": "0.4.2", + "bundled": true, + "dev": true, + "optional": true + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "extend": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "extsprintf": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true, + "optional": true + }, + "form-data": { + "version": "2.1.4", + "bundled": true, + "dev": true, + "optional": true + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "fstream": { + "version": "1.0.11", + "bundled": true, + "dev": true + }, + "fstream-ignore": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "glob": { + "version": "7.1.2", + "bundled": true, + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "har-validator": { + "version": "4.2.1", + "bundled": true, + "dev": true, + "optional": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "hawk": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "optional": true + }, + "hoek": { + "version": "2.16.3", + "bundled": true, + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.4", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "jodid25519": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true, + "optional": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "jsonify": { + "version": "0.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "jsprim": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "mime-db": { + "version": "1.27.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.15", + "bundled": true, + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "node-pre-gyp": { + "version": "0.6.36", + "bundled": true, + "dev": true, + "optional": true + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "npmlog": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.4", + "bundled": true, + "dev": true, + "optional": true + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "1.0.7", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true + }, + "qs": { + "version": "6.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.2.9", + "bundled": true, + "dev": true + }, + "request": { + "version": "2.81.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rimraf": { + "version": "2.6.1", + "bundled": true, + "dev": true + }, + "safe-buffer": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "semver": { + "version": "5.3.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sntp": { + "version": "1.0.9", + "bundled": true, + "dev": true, + "optional": true + }, + "sshpk": { + "version": "1.13.0", + "bundled": true, + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "stringstream": { + "version": "0.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "tar-pack": { + "version": "3.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "tough-cookie": { + "version": "2.3.2", + "bundled": true, + "dev": true, + "optional": true + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "verror": { + "version": "1.3.6", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + } + } + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==" + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true + }, + "global-modules": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz", + "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", + "dev": true + }, + "global-prefix": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz", + "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", + "dev": true + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "growl": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz", + "integrity": "sha1-Sy3sjZB+k9szZiTc7AGDUC+MlCg=", + "dev": true + }, + "hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true + }, + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "dev": true + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", + "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", + "dev": true + }, + "hosted-git-info": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.1.5.tgz", + "integrity": "sha1-C6gdkNouJas0ozLm7HeTbhWYEYs=", + "dev": true + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "dev": true + }, + "https-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", + "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.18.tgz", + "integrity": "sha512-sr1ZQph3UwHTR0XftSbK85OvBbxe/abLGzEnPENCQwmHf7sck8Oyu4ob3LgBxWWxRoM+QszeUyl7jbqapu2TqA==" + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", + "dev": true + }, + "init-skeleton": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/init-skeleton/-/init-skeleton-0.9.5.tgz", + "integrity": "sha1-q6I9xFUXI2xUg25Nc27ULy1JTto=", + "dev": true + }, + "invariant": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", + "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", + "dev": true + }, + "ipaddr.js": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", + "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true + }, + "is-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-windows": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", + "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=" + }, + "javascript-brunch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/javascript-brunch/-/javascript-brunch-2.0.0.tgz", + "integrity": "sha1-gx4db6j/XeyysIcgJPGtRHuWPOA=", + "dev": true + }, + "jquery": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-2.2.4.tgz", + "integrity": "sha1-LInWiJterFIqfuoywUUhVZxsvwI=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "loggy": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/loggy/-/loggy-0.3.5.tgz", + "integrity": "sha1-M/EoAbH2Bjlm6nnZtqJduPy8QQc=", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=" + }, + "materialize-css": { + "version": "0.97.8", + "resolved": "https://registry.npmjs.org/materialize-css/-/materialize-css-0.97.8.tgz", + "integrity": "sha1-45ziBWeFsMTpzUhHws3xU+GcR88=" + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "dev": true, + "dependencies": { + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true + } + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micro-promisify": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/micro-promisify/-/micro-promisify-0.1.1.tgz", + "integrity": "sha1-Bx2lkLSVZWDe30qucERynBookC0=", + "dev": true + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true + }, + "miller-rabin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", + "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=", + "dev": true + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", + "dev": true + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", + "dev": true + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==" + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=" + }, + "mout": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mout/-/mout-1.0.0.tgz", + "integrity": "sha1-m98dSvV9ZtR8s1OmM1oygQmOFQE=", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "nan": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=", + "dev": true, + "optional": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "dev": true + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "node-archiver": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/node-archiver/-/node-archiver-0.3.0.tgz", + "integrity": "sha1-ufGv5QBtC98pJgGBgzoHCXi8aUc=" + }, + "node-browser-modules": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/node-browser-modules/-/node-browser-modules-0.1.0.tgz", + "integrity": "sha1-QZPLtw9UJ1ml5ObWsBzLY46Zoaw=", + "dev": true, + "dependencies": { + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "node-fetch": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.2.tgz", + "integrity": "sha512-xZZUq2yDhKMIn/UgG5q//IZSNLJIwW2QxS14CNH5spuiXkITM2pUitjdq58yLSaU7m4M0wBNaM2Gh/ggY4YJig==" + }, + "normalize-git-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/normalize-git-url/-/normalize-git-url-3.0.2.tgz", + "integrity": "sha1-jl8Uvgva7bc+ByADEKpBbCc1D8Q=", + "dev": true + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=" + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true + }, + "os-browserify": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", + "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "dev": true + }, + "parse-asn1": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "dev": true + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "parseurl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "pbkdf2": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.13.tgz", + "integrity": "sha512-+dCHxDH+djNtjgWmvVC/my3SYBAKpKNqKSjLkp+GtWWYe4XPE+e/PSD2aCanlEZZnqPk2uekTKNC/ccbwd2X2Q==", + "dev": true + }, + "phoenix": { + "version": "file:deps/phoenix" + }, + "phoenix_html": { + "version": "file:deps/phoenix_html" + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "private": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", + "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==" + }, + "prop-types": { + "version": "15.5.10", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.5.10.tgz", + "integrity": "sha1-J5ffwxJhguOpXj37suiT3ddFYVQ=" + }, + "proxy-addr": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", + "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "pushserve": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pushserve/-/pushserve-1.0.2.tgz", + "integrity": "sha1-/fuAOTnKM3UtOPLRqpdawBqTJT8=", + "dev": true + }, + "qs": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", + "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "quickly-copy-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quickly-copy-file/-/quickly-copy-file-1.0.0.tgz", + "integrity": "sha1-n4/wZiMFEO50IrASFHKwk6hpCFk=", + "dev": true + }, + "randomatic": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", + "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", + "dev": true, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true + } + } + }, + "randombytes": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", + "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", + "dev": true + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "react": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react/-/react-15.6.1.tgz", + "integrity": "sha1-uqhDTsZ4C96ZfNw4C3nNM7ljk98=" + }, + "react-dom": { + "version": "15.6.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.1.tgz", + "integrity": "sha1-LLDtQZEDjlPCCes6eaI+Kkz5lHA=" + }, + "read-components": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/read-components/-/read-components-0.7.0.tgz", + "integrity": "sha1-d9znrctypRQkDEemdbm896NQndk=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true + }, + "readdirp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", + "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "dev": true + }, + "regenerate": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz", + "integrity": "sha1-0ZQcZ7rUN+G+dkM63Vs4X5WxkmA=", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", + "dev": true + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "resolve-dir": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz", + "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=" + }, + "ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "dev": true + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, + "send": { + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz", + "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=", + "dev": true + }, + "serve-brunch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/serve-brunch/-/serve-brunch-0.1.2.tgz", + "integrity": "sha1-F55xN0j7qT5lCJLSV4gijTNr3Dw=", + "dev": true + }, + "serve-static": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz", + "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=", + "dev": true + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", + "dev": true + }, + "sha.js": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", + "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=", + "dev": true + }, + "since-app-start": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/since-app-start/-/since-app-start-0.1.0.tgz", + "integrity": "sha1-SHUBUCD5D8q4/l3jMi+pWdVAa0Q=", + "dev": true + }, + "skemata": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/skemata/-/skemata-0.1.2.tgz", + "integrity": "sha1-88UhAptnvm4IJfDfhn7peRbeRTA=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.17.tgz", + "integrity": "sha512-30c1Ch8FSjV0FwC253iftbbj0dU/OXoSg1LAEGZJUlGgjTNj6cu+DVqJWWIZJY5RXLWV4eFtR+4ouo0VIOYOTg==", + "dev": true + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "dev": true + }, + "stream-http": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.1.1.tgz", + "integrity": "sha1-O4gDA7q+A21va0MSfU3Nb4iT4ds=", + "dev": true + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=" + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "true-case-path": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.2.tgz", + "integrity": "sha1-fskRMJJHZsf1c74wIMNPj9/QDWI=", + "dev": true, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "dev": true + } + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.14", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz", + "integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o=" + }, + "uglify-js": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.6.4.tgz", + "integrity": "sha1-ZeovswWck5RpLxX+2HwrNsFrmt8=", + "dev": true + }, + "uglify-js-brunch": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uglify-js-brunch/-/uglify-js-brunch-2.0.1.tgz", + "integrity": "sha1-v1W6KN9eTuUIO/fjlk2I60qryhM=", + "dev": true + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "untildify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz", + "integrity": "sha1-F+soB5h/dpUunASF/DEdBqgmouA=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=", + "dev": true + }, + "vary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", + "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=", + "dev": true + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true + }, + "whatwg-fetch": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", + "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" + }, + "which": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true + } + } +} diff --git a/react-phoenix/package.json b/react-phoenix/package.json new file mode 100644 index 0000000..2b208a9 --- /dev/null +++ b/react-phoenix/package.json @@ -0,0 +1,24 @@ +{ + "repository": {}, + "license": "MIT", + "scripts": { + "deploy": "brunch build --production", + "watch": "brunch watch --stdin" + }, + "dependencies": { + "materialize-css": "^0.97.7", + "phoenix": "file:deps/phoenix", + "phoenix_html": "file:deps/phoenix_html", + "react": "^15.3.2", + "react-dom": "^15.3.2" + }, + "devDependencies": { + "babel-brunch": "~6.0.0", + "babel-preset-react": "^6.16.0", + "brunch": "2.7.4", + "clean-css-brunch": "~2.0.0", + "css-brunch": "~2.0.0", + "javascript-brunch": "~2.0.0", + "uglify-js-brunch": "~2.0.1" + } +} diff --git a/react-phoenix/priv/gettext/en/LC_MESSAGES/errors.po b/react-phoenix/priv/gettext/en/LC_MESSAGES/errors.po new file mode 100644 index 0000000..087d374 --- /dev/null +++ b/react-phoenix/priv/gettext/en/LC_MESSAGES/errors.po @@ -0,0 +1,93 @@ +## `msgid`s in this file come from POT (.pot) files. +## +## Do not add, change, or remove `msgid`s manually here as +## they're tied to the ones in the corresponding POT file +## (with the same domain). +## +## Use `mix gettext.extract --merge` or `mix gettext.merge` +## to merge POT files into PO files. +msgid "" +msgstr "" +"Language: en\n" + +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated to this entry" +msgstr "" + +msgid "are still associated to this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" diff --git a/react-phoenix/priv/gettext/errors.pot b/react-phoenix/priv/gettext/errors.pot new file mode 100644 index 0000000..a228957 --- /dev/null +++ b/react-phoenix/priv/gettext/errors.pot @@ -0,0 +1,91 @@ +## This file is a PO Template file. +## +## `msgid`s here are often extracted from source code. +## Add new translations manually only if they're dynamic +## translations that can't be statically extracted. +## +## Run `mix gettext.extract` to bring this file up to +## date. Leave `msgstr`s empty as changing them here as no +## effect: edit them in PO (`.po`) files instead. + +## From Ecto.Changeset.cast/4 +msgid "can't be blank" +msgstr "" + +## From Ecto.Changeset.unique_constraint/3 +msgid "has already been taken" +msgstr "" + +## From Ecto.Changeset.put_change/3 +msgid "is invalid" +msgstr "" + +## From Ecto.Changeset.validate_format/3 +msgid "has invalid format" +msgstr "" + +## From Ecto.Changeset.validate_subset/3 +msgid "has an invalid entry" +msgstr "" + +## From Ecto.Changeset.validate_exclusion/3 +msgid "is reserved" +msgstr "" + +## From Ecto.Changeset.validate_confirmation/3 +msgid "does not match confirmation" +msgstr "" + +## From Ecto.Changeset.no_assoc_constraint/3 +msgid "is still associated to this entry" +msgstr "" + +msgid "are still associated to this entry" +msgstr "" + +## From Ecto.Changeset.validate_length/3 +msgid "should be %{count} character(s)" +msgid_plural "should be %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have %{count} item(s)" +msgid_plural "should have %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at least %{count} character(s)" +msgid_plural "should be at least %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at least %{count} item(s)" +msgid_plural "should have at least %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should be at most %{count} character(s)" +msgid_plural "should be at most %{count} character(s)" +msgstr[0] "" +msgstr[1] "" + +msgid "should have at most %{count} item(s)" +msgid_plural "should have at most %{count} item(s)" +msgstr[0] "" +msgstr[1] "" + +## From Ecto.Changeset.validate_number/3 +msgid "must be less than %{number}" +msgstr "" + +msgid "must be greater than %{number}" +msgstr "" + +msgid "must be less than or equal to %{number}" +msgstr "" + +msgid "must be greater than or equal to %{number}" +msgstr "" + +msgid "must be equal to %{number}" +msgstr "" diff --git a/react-phoenix/priv/repo/migrations/20160927181632_create_user.exs b/react-phoenix/priv/repo/migrations/20160927181632_create_user.exs new file mode 100644 index 0000000..ed3c7f4 --- /dev/null +++ b/react-phoenix/priv/repo/migrations/20160927181632_create_user.exs @@ -0,0 +1,14 @@ +defmodule Reddit.Repo.Migrations.CreateUser do + use Ecto.Migration + + def change do + create table(:users) do + add :email, :string + add :provider, :string + add :token, :string + + timestamps() + end + + end +end diff --git a/react-phoenix/priv/repo/migrations/20160929203558_add_posts.exs b/react-phoenix/priv/repo/migrations/20160929203558_add_posts.exs new file mode 100644 index 0000000..7becddf --- /dev/null +++ b/react-phoenix/priv/repo/migrations/20160929203558_add_posts.exs @@ -0,0 +1,12 @@ +defmodule Reddit.Repo.Migrations.AddPosts do + use Ecto.Migration + + def change do + create table(:posts) do + add :title, :string + add :user_id, references(:users) + + timestamps + end + end +end diff --git a/react-phoenix/priv/repo/migrations/20160929203753_comments.exs b/react-phoenix/priv/repo/migrations/20160929203753_comments.exs new file mode 100644 index 0000000..928f236 --- /dev/null +++ b/react-phoenix/priv/repo/migrations/20160929203753_comments.exs @@ -0,0 +1,13 @@ +defmodule Reddit.Repo.Migrations.Comments do + use Ecto.Migration + + def change do + create table(:comments) do + add :content, :string + add :user_id, references(:users) + add :post_id, references(:posts) + + timestamps + end + end +end diff --git a/react-phoenix/priv/repo/seeds.exs b/react-phoenix/priv/repo/seeds.exs new file mode 100644 index 0000000..df65a51 --- /dev/null +++ b/react-phoenix/priv/repo/seeds.exs @@ -0,0 +1,11 @@ +# Script for populating the database. You can run it as: +# +# mix run priv/repo/seeds.exs +# +# Inside the script, you can read and write to any of your +# repositories directly: +# +# Reddit.Repo.insert!(%Reddit.SomeModel{}) +# +# We recommend using the bang functions (`insert!`, `update!` +# and so on) as they will fail if something goes wrong. diff --git a/react-phoenix/test/controllers/post_controller_test.exs b/react-phoenix/test/controllers/post_controller_test.exs new file mode 100644 index 0000000..97f2708 --- /dev/null +++ b/react-phoenix/test/controllers/post_controller_test.exs @@ -0,0 +1,58 @@ +defmodule Reddit.PostControllerTest do + use Reddit.ConnCase + + alias Reddit.{Post} + @valid_attrs %{title: "some content"} + @invalid_attrs %{} + + setup %{conn: conn} do + user = create_user + conn = assign(conn, :user, user) + + post = + user + |> build_assoc(:posts) + |> Repo.insert! + + {:ok, %{conn: conn, post: post, user: user}} + end + + test "renders form for new resources", %{conn: conn} do + conn = get conn, post_path(conn, :new) + assert html_response(conn, 200) =~ "Create Post" + end + + test "creates resource when data is valid", %{conn: conn} do + post conn, post_path(conn, :create), post: @valid_attrs + assert Repo.get_by(Post, @valid_attrs) + end + + test "does not create resource and renders errors when data is invalid", %{conn: conn} do + post_count = length(Repo.all(Post)) + + post conn, post_path(conn, :create), post: @invalid_attrs + assert post_count == length(Repo.all(Post)) + end + + test "renders form for editing chosen resource", %{conn: conn, post: post} do + conn = get conn, post_path(conn, :edit, post) + assert html_response(conn, 200) =~ "Create Post" + end + + test "updates chosen resource and redirects when data is valid", %{conn: conn, post: post} do + put conn, post_path(conn, :update, post), post: @valid_attrs + assert Repo.get_by(Post, @valid_attrs) + end + + test "redirects when a user who doesnt own a post edits it", %{conn: conn} do + post = Repo.insert! %Post{} + conn = put conn, post_path(conn, :update, post), post: @valid_attrs + assert html_response(conn, 302) + end + + test "deletes chosen resource", %{conn: conn, post: post} do + conn = delete conn, post_path(conn, :delete, post) + assert redirected_to(conn) == post_path(conn, :index) + refute Repo.get(Post, post.id) + end +end diff --git a/react-phoenix/test/controllers/session_controller_test.exs b/react-phoenix/test/controllers/session_controller_test.exs new file mode 100644 index 0000000..e4848d7 --- /dev/null +++ b/react-phoenix/test/controllers/session_controller_test.exs @@ -0,0 +1,48 @@ +defmodule Reddit.SessionControllerTest do + use Reddit.ConnCase + alias Reddit.SessionController + alias Reddit.User + + @auth %{ + info: %{email: "test@test.com"}, + credentials: %{token: "123"} + } + + def signup(conn) do + conn + |> bypass_through(Reddit.Router, :browser) + |> get("/") + |> assign(:ueberauth_auth, @auth) + |> SessionController.callback(%{"provider" => "github"}) + end + + test "signing in creates a new account", %{conn: conn} do + user_count = length(Repo.all(User)) + signup(conn) + + assert user_count + 1 == length(Repo.all(User)) + end + + test "signing in creates a session", %{conn: conn} do + conn = signup(conn) + assert get_session(conn, :user_id) + end + + test "signing out redirects to index", %{conn: conn} do + conn = signup(conn) + conn = get conn, session_path(conn, :signout) + + assert redirected_to(conn) == "/" + end + + test "oauthing twice doesnt create two accounts", %{conn: conn} do + user_count = length(Repo.all(User)) + conn = signup(conn) + conn = get conn, session_path(conn, :signout) + conn + |> recycle + |> signup + + assert user_count + 1 == length(Repo.all(User)) + end +end diff --git a/react-phoenix/test/models/post_test.exs b/react-phoenix/test/models/post_test.exs new file mode 100644 index 0000000..31cceb1 --- /dev/null +++ b/react-phoenix/test/models/post_test.exs @@ -0,0 +1,18 @@ +defmodule Reddit.PostTest do + use Reddit.ModelCase + + alias Reddit.Post + + @valid_attrs %{title: "some content"} + @invalid_attrs %{} + + test "changeset with valid attributes" do + changeset = Post.changeset(%Post{}, @valid_attrs) + assert changeset.valid? + end + + test "changeset with invalid attributes" do + changeset = Post.changeset(%Post{}, @invalid_attrs) + refute changeset.valid? + end +end diff --git a/react-phoenix/test/models/user_test.exs b/react-phoenix/test/models/user_test.exs new file mode 100644 index 0000000..2c1d298 --- /dev/null +++ b/react-phoenix/test/models/user_test.exs @@ -0,0 +1,18 @@ +defmodule Reddit.UserTest do + use Reddit.ModelCase + + alias Reddit.User + + @valid_attrs %{provider: "some content", token: "some content", email: "some content"} + @invalid_attrs %{} + + test "changeset with valid attributes" do + changeset = User.changeset(%User{}, @valid_attrs) + assert changeset.valid? + end + + test "changeset with invalid attributes" do + changeset = User.changeset(%User{}, @invalid_attrs) + refute changeset.valid? + end +end diff --git a/react-phoenix/test/support/channel_case.ex b/react-phoenix/test/support/channel_case.ex new file mode 100644 index 0000000..1bc6fdb --- /dev/null +++ b/react-phoenix/test/support/channel_case.ex @@ -0,0 +1,43 @@ +defmodule Reddit.ChannelCase do + @moduledoc """ + This module defines the test case to be used by + channel tests. + + Such tests rely on `Phoenix.ChannelTest` and also + import other functionality to make it easier + to build and query models. + + Finally, if the test case interacts with the database, + it cannot be async. For this reason, every test runs + inside a transaction which is reset at the beginning + of the test unless the test case is marked as async. + """ + + use ExUnit.CaseTemplate + + using do + quote do + # Import conveniences for testing with channels + use Phoenix.ChannelTest + + alias Reddit.Repo + import Ecto + import Ecto.Changeset + import Ecto.Query + + + # The default endpoint for testing + @endpoint Reddit.Endpoint + end + end + + setup tags do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Reddit.Repo) + + unless tags[:async] do + Ecto.Adapters.SQL.Sandbox.mode(Reddit.Repo, {:shared, self()}) + end + + :ok + end +end diff --git a/react-phoenix/test/support/conn_case.ex b/react-phoenix/test/support/conn_case.ex new file mode 100644 index 0000000..dd6ff5d --- /dev/null +++ b/react-phoenix/test/support/conn_case.ex @@ -0,0 +1,45 @@ +defmodule Reddit.ConnCase do + @moduledoc """ + This module defines the test case to be used by + tests that require setting up a connection. + + Such tests rely on `Phoenix.ConnTest` and also + import other functionality to make it easier + to build and query models. + + Finally, if the test case interacts with the database, + it cannot be async. For this reason, every test runs + inside a transaction which is reset at the beginning + of the test unless the test case is marked as async. + """ + + use ExUnit.CaseTemplate + + using do + quote do + # Import conveniences for testing with connections + use Phoenix.ConnTest + + alias Reddit.Repo + import Ecto + import Ecto.Changeset + import Ecto.Query + + import Reddit.Router.Helpers + import Reddit.ModelHelpers + + # The default endpoint for testing + @endpoint Reddit.Endpoint + end + end + + setup tags do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Reddit.Repo) + + unless tags[:async] do + Ecto.Adapters.SQL.Sandbox.mode(Reddit.Repo, {:shared, self()}) + end + + {:ok, conn: Phoenix.ConnTest.build_conn()} + end +end diff --git a/react-phoenix/test/support/model_case.ex b/react-phoenix/test/support/model_case.ex new file mode 100644 index 0000000..88dea87 --- /dev/null +++ b/react-phoenix/test/support/model_case.ex @@ -0,0 +1,65 @@ +defmodule Reddit.ModelCase do + @moduledoc """ + This module defines the test case to be used by + model tests. + + You may define functions here to be used as helpers in + your model tests. See `errors_on/2`'s definition as reference. + + Finally, if the test case interacts with the database, + it cannot be async. For this reason, every test runs + inside a transaction which is reset at the beginning + of the test unless the test case is marked as async. + """ + + use ExUnit.CaseTemplate + + using do + quote do + alias Reddit.Repo + + import Ecto + import Ecto.Changeset + import Ecto.Query + import Reddit.ModelCase + end + end + + setup tags do + :ok = Ecto.Adapters.SQL.Sandbox.checkout(Reddit.Repo) + + unless tags[:async] do + Ecto.Adapters.SQL.Sandbox.mode(Reddit.Repo, {:shared, self()}) + end + + :ok + end + + @doc """ + Helper for returning list of errors in a struct when given certain data. + + ## Examples + + Given a User schema that lists `:name` as a required field and validates + `:password` to be safe, it would return: + + iex> errors_on(%User{}, %{password: "password"}) + [password: "is unsafe", name: "is blank"] + + You could then write your assertion like: + + assert {:password, "is unsafe"} in errors_on(%User{}, %{password: "password"}) + + You can also create the changeset manually and retrieve the errors + field directly: + + iex> changeset = User.changeset(%User{}, password: "password") + iex> {:password, "is unsafe"} in changeset.errors + true + """ + def errors_on(struct, data) do + struct.__struct__.changeset(struct, data) + |> Ecto.Changeset.traverse_errors(&Reddit.ErrorHelpers.translate_error/1) + |> Enum.flat_map(fn {key, errors} -> for msg <- errors, do: {key, msg} end) + end +end diff --git a/react-phoenix/test/support/model_helpers.ex b/react-phoenix/test/support/model_helpers.ex new file mode 100644 index 0000000..a1b21e9 --- /dev/null +++ b/react-phoenix/test/support/model_helpers.ex @@ -0,0 +1,31 @@ +defmodule Reddit.ModelHelpers do + import Plug.Conn, only: [assign: 3, put_session: 3] + alias Reddit.User + alias Reddit.Repo + + # def login_user(conn, params \\ %{}) do + # {:ok, user} = create_user(params) + # + # conn = + # conn + # |> put_session(:user_id, user.id) + # |> assign(:user, user) + # + # {conn, user} + # end + + def create_user(params \\ %{}) do + params = Map.merge(%{ + email: "test@test.com", + provider: "github", + token: "12345" + }, params) + + {:ok, user} = + %User{} + |> User.changeset(params) + |> Repo.insert + + user + end +end diff --git a/react-phoenix/test/test_helper.exs b/react-phoenix/test/test_helper.exs new file mode 100644 index 0000000..3857a2e --- /dev/null +++ b/react-phoenix/test/test_helper.exs @@ -0,0 +1,4 @@ +ExUnit.start + +Ecto.Adapters.SQL.Sandbox.mode(Reddit.Repo, :manual) + diff --git a/react-phoenix/test/views/error_view_test.exs b/react-phoenix/test/views/error_view_test.exs new file mode 100644 index 0000000..c7ce8c1 --- /dev/null +++ b/react-phoenix/test/views/error_view_test.exs @@ -0,0 +1,21 @@ +defmodule Reddit.ErrorViewTest do + use Reddit.ConnCase, async: true + + # Bring render/3 and render_to_string/3 for testing custom views + import Phoenix.View + + test "renders 404.html" do + assert render_to_string(Reddit.ErrorView, "404.html", []) == + "Page not found" + end + + test "render 500.html" do + assert render_to_string(Reddit.ErrorView, "500.html", []) == + "Internal server error" + end + + test "render any other" do + assert render_to_string(Reddit.ErrorView, "505.html", []) == + "Internal server error" + end +end diff --git a/react-phoenix/test/views/layout_view_test.exs b/react-phoenix/test/views/layout_view_test.exs new file mode 100644 index 0000000..732b42c --- /dev/null +++ b/react-phoenix/test/views/layout_view_test.exs @@ -0,0 +1,3 @@ +defmodule Reddit.LayoutViewTest do + use Reddit.ConnCase, async: true +end diff --git a/react-phoenix/test/views/page_view_test.exs b/react-phoenix/test/views/page_view_test.exs new file mode 100644 index 0000000..616ef94 --- /dev/null +++ b/react-phoenix/test/views/page_view_test.exs @@ -0,0 +1,3 @@ +defmodule Reddit.PageViewTest do + use Reddit.ConnCase, async: true +end diff --git a/react-phoenix/web/channels/comments_channel.ex b/react-phoenix/web/channels/comments_channel.ex new file mode 100644 index 0000000..4a7513c --- /dev/null +++ b/react-phoenix/web/channels/comments_channel.ex @@ -0,0 +1,38 @@ +defmodule Reddit.CommentsChannel do + use Reddit.Web, :channel + alias Reddit.{Post, Comment, User} + + def join("comments:" <> post_id, _params, socket) do + post_id = String.to_integer(post_id) + post = + Post + |> Repo.get(post_id) + |> Repo.preload(:comments) + + comments = render_comments(post.comments) + + {:ok, %{comments: comments}, assign(socket, :post, post)} + end + + def handle_in("comment:add", %{"content" => content}, socket) do + user = Repo.get(User, socket.assigns.user_id) + + changeset = + socket.assigns.post + |> build_assoc(:comments) + |> Comment.changeset(%{content: content}) + + case Repo.insert(changeset) do + {:ok, comment} -> + broadcast! socket, "comments:#{socket.assigns.post.id}:new", + %{comments: render_comments([comment])} + {:reply, :ok, socket} + {:error, _reason} -> + {:reply, {:error, %{errors: changeset}}, socket} + end + end + + defp render_comments(comments) do + Phoenix.View.render_many(comments, Reddit.CommentView, "comments.json") + end +end diff --git a/react-phoenix/web/channels/user_socket.ex b/react-phoenix/web/channels/user_socket.ex new file mode 100644 index 0000000..ca4869b --- /dev/null +++ b/react-phoenix/web/channels/user_socket.ex @@ -0,0 +1,23 @@ +defmodule Reddit.UserSocket do + use Phoenix.Socket + + ## Channels + channel "comments:*", Reddit.CommentsChannel + + transport :websocket, Phoenix.Transports.WebSocket + + @max_age 2 * 7 * 24 * 60 * 60 + + def connect(%{"auth_token" => token}, socket) do + token = Phoenix.Token.verify(socket, "user_socket", token) + + case token do + {:ok, user_id} -> + {:ok, assign(socket, :user_id, user_id)} + {:error, _error} -> + :error + end + end + + def id(_socket), do: nil +end diff --git a/react-phoenix/web/controllers/comment_controller.ex b/react-phoenix/web/controllers/comment_controller.ex new file mode 100644 index 0000000..e69de29 diff --git a/react-phoenix/web/controllers/helpers/queries.ex b/react-phoenix/web/controllers/helpers/queries.ex new file mode 100644 index 0000000..cab38b6 --- /dev/null +++ b/react-phoenix/web/controllers/helpers/queries.ex @@ -0,0 +1,10 @@ +defmodule Reddit.Queries do + import Ecto.Query + alias Reddit.{Topic, Post, User, Repo} + + def topics_for_posts(topic_id) do + topic = Topic + |> Repo.get(topic_id) + |> Repo.preload(:posts) + end +end diff --git a/react-phoenix/web/controllers/plugs/require_auth.ex b/react-phoenix/web/controllers/plugs/require_auth.ex new file mode 100644 index 0000000..4462dbc --- /dev/null +++ b/react-phoenix/web/controllers/plugs/require_auth.ex @@ -0,0 +1,20 @@ +defmodule Reddit.Plugs.RequireAuth do + import Phoenix.Controller, only: [put_flash: 3, redirect: 2] + import Plug.Conn + + alias Reddit.Router.Helpers + + def init(_params) do + end + + def call(conn, _params) do + if conn.assigns.user do + conn + else + conn + |> put_flash(:error, "You must be logged in to do that") + |> redirect(to: Helpers.post_path(conn, :index)) + |> halt() + end + end +end diff --git a/react-phoenix/web/controllers/plugs/set_user.ex b/react-phoenix/web/controllers/plugs/set_user.ex new file mode 100644 index 0000000..a5e5a53 --- /dev/null +++ b/react-phoenix/web/controllers/plugs/set_user.ex @@ -0,0 +1,24 @@ +defmodule Reddit.Plugs.SetUser do + import Plug.Conn + import Phoenix.Controller + + alias Reddit.Repo + alias Reddit.User + alias Elixgur.Router.Helpers + + def init(_params) do + end + + def call(conn, _params) do + user_id = get_session(conn, :user_id) + + cond do + conn.assigns[:user] -> + conn + user = user_id && Repo.get(User, user_id) -> + assign(conn, :user, user) + true -> + assign(conn, :user, nil) + end + end +end diff --git a/react-phoenix/web/controllers/post_controller.ex b/react-phoenix/web/controllers/post_controller.ex new file mode 100644 index 0000000..4ed30a6 --- /dev/null +++ b/react-phoenix/web/controllers/post_controller.ex @@ -0,0 +1,80 @@ +defmodule Reddit.PostController do + use Reddit.Web, :controller + alias Reddit.{Post} + + plug Reddit.Plugs.RequireAuth when action in [:new, :create] + plug :check_post_owner when action in [:update, :edit, :delete] + + def index(conn, _params) do + posts = Repo.all(Post) + render conn, "index.html", posts: posts + end + + def show(conn, %{"id" => post_id}) do + post = Repo.get!(Post, post_id) + render conn, "show.html", post: post + end + + def new(conn, _params) do + changeset = %Post{} |> Post.changeset(%{}) + + render conn, "new.html", changeset: changeset + end + + def create(conn, %{"post" => post}) do + changeset = conn.assigns.user + |> build_assoc(:posts) + |> Post.changeset(post) + + case Repo.insert(changeset) do + {:ok, post} -> + conn + |> put_flash(:info, "Post Created") + |> redirect(to: post_path(conn, :show, post)) + {:error, changeset} -> + render(conn, "new.html", changeset: changeset) + end + end + + def edit(conn, %{"id" => post_id}) do + post = Repo.get(Post, post_id) + changeset = post |> Post.changeset + + render conn, "edit.html", post: post, changeset: changeset + end + + def update(conn, %{"post" => post, "id" => post_id}) do + changeset = Repo.get(Post, post_id) |> Post.changeset(post) + + case Repo.update(changeset) do + {:ok, post} -> + conn + |> put_flash(:info, "Post Updated") + |> redirect(to: post_path(conn, :show, post)) + {:error, changeset} -> + render conn, "edit.html", changeset: changeset + end + end + + def delete(conn, %{"id" => post_id}) do + Repo.get!(Post, post_id) |> Repo.delete! + + conn + |> put_flash(:info, "Post Deleted") + |> redirect(to: post_path(conn, :index)) + end + + def check_post_owner(conn, _params) do + %{params: %{"id" => post_id}} = conn + + case Repo.get(Post, post_id).user_id == conn.assigns.user.id do + true -> + conn + false -> + conn + |> put_flash(:error, "You cannot edit that") + |> redirect(to: post_path(conn, :index)) + |> halt() + end + end +end diff --git a/react-phoenix/web/controllers/session_controller.ex b/react-phoenix/web/controllers/session_controller.ex new file mode 100644 index 0000000..9c4a215 --- /dev/null +++ b/react-phoenix/web/controllers/session_controller.ex @@ -0,0 +1,48 @@ +defmodule Reddit.SessionController do + use Reddit.Web, :controller + plug Ueberauth + + alias Reddit.User + + def callback(%{assigns: %{ ueberauth_failure: failure }}= conn, _params) do + IO.inspect(failure) + + conn + |> redirect(post_path(conn, :index)) + end + + def callback(%{assigns: %{ ueberauth_auth: auth }} = conn, %{"provider" => "github"}) do + changeset = User.changeset(%User{}, User.attrs_from_github(auth)) + + signin(conn, changeset) + end + + def signout(conn, _params) do + conn + |> configure_session(drop: true) + |> redirect(to: post_path(conn, :index)) + end + + defp signin(conn, changeset) do + case insert_or_update_user(changeset) do + {:ok, user} -> + conn + |> put_flash(:info, "Welcome back") + |> put_session(:user_id, user.id) + |> redirect(to: post_path(conn, :index)) + {:error, _reason} -> + conn + |> put_flash("Error signing in", :error) + |> redirect(to: post_path(conn, :index)) + end + end + + defp insert_or_update_user(changeset) do + case Repo.get_by(User, email: changeset.changes.email) do + nil -> + Repo.insert(changeset) + user -> + {:ok, user} + end + end +end diff --git a/react-phoenix/web/gettext.ex b/react-phoenix/web/gettext.ex new file mode 100644 index 0000000..3d3a170 --- /dev/null +++ b/react-phoenix/web/gettext.ex @@ -0,0 +1,24 @@ +defmodule Reddit.Gettext do + @moduledoc """ + A module providing Internationalization with a gettext-based API. + + By using [Gettext](https://hexdocs.pm/gettext), + your module gains a set of macros for translations, for example: + + import Reddit.Gettext + + # Simple translation + gettext "Here is the string to translate" + + # Plural translation + ngettext "Here is the string to translate", + "Here are the strings to translate", + 3 + + # Domain-based translation + dgettext "errors", "Here is the error message to translate" + + See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. + """ + use Gettext, otp_app: :reddit +end diff --git a/react-phoenix/web/models/comment.ex b/react-phoenix/web/models/comment.ex new file mode 100644 index 0000000..04ca0b3 --- /dev/null +++ b/react-phoenix/web/models/comment.ex @@ -0,0 +1,16 @@ +defmodule Reddit.Comment do + use Reddit.Web, :model + + schema "comments" do + field :content, :string + belongs_to :user, Reddit.User + belongs_to :post, Reddit.Post + + timestamps() + end + + def changeset(struct, params \\ %{}) do + struct + |> cast(params, [:content]) + end +end diff --git a/react-phoenix/web/models/post.ex b/react-phoenix/web/models/post.ex new file mode 100644 index 0000000..7e1a100 --- /dev/null +++ b/react-phoenix/web/models/post.ex @@ -0,0 +1,17 @@ +defmodule Reddit.Post do + use Reddit.Web, :model + + schema "posts" do + field :title, :string + belongs_to :user, Reddit.User + has_many :comments, Reddit.Comment + + timestamps() + end + + def changeset(struct, params \\ %{}) do + struct + |> cast(params, [:title]) + |> validate_required([:title]) + end +end diff --git a/react-phoenix/web/models/user.ex b/react-phoenix/web/models/user.ex new file mode 100644 index 0000000..e674b4f --- /dev/null +++ b/react-phoenix/web/models/user.ex @@ -0,0 +1,25 @@ +defmodule Reddit.User do + use Reddit.Web, :model + + schema "users" do + field :email, :string + field :provider, :string + field :token, :string + has_many :posts, Reddit.Post + + timestamps() + end + + def attrs_from_github(auth) do + %{email: auth.info.email, token: auth.credentials.token, provider: "github"} + end + + @doc """ + Builds a changeset based on the `struct` and `params`. + """ + def changeset(struct, params \\ %{}) do + struct + |> cast(params, [:email, :provider, :token]) + |> validate_required([:email, :provider, :token]) + end +end diff --git a/react-phoenix/web/router.ex b/react-phoenix/web/router.ex new file mode 100644 index 0000000..b8b0471 --- /dev/null +++ b/react-phoenix/web/router.ex @@ -0,0 +1,35 @@ +defmodule Reddit.Router do + use Reddit.Web, :router + + pipeline :browser do + plug :accepts, ["html"] + plug :fetch_session + plug :fetch_flash + plug :protect_from_forgery + plug :put_secure_browser_headers + plug Reddit.Plugs.SetUser + end + + pipeline :api do + plug :accepts, ["json"] + end + + scope "/", Reddit do + pipe_through :browser # Use the default browser stack + + resources "/", PostController + end + + scope "/auth", Reddit do + pipe_through :browser + + get "/signout", SessionController, :signout + get "/:provider", SessionController, :request + get "/:provider/callback", SessionController, :callback + end + + # Other scopes may use custom stacks. + # scope "/api", Reddit do + # pipe_through :api + # end +end diff --git a/react-phoenix/web/static/assets/favicon.ico b/react-phoenix/web/static/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..73de524aaadcf60fbe9d32881db0aa86b58b5cb9 GIT binary patch literal 1258 zcmbtUO>fgM7{=qN=;Mz_82;lvPEdVaxv-<-&=sZLwab?3I zBP>U*&(Hv<5n@9ZQ$vhg#|u$Zmtq8BV;+W*7(?jOx-{r?#TE&$Sdq77MbdJjD5`-q zMm_z(jLv3t>5NhzK{%aG(Yudfpjd3AFdKe2U7&zdepTe>^s(@!&0X8TJ`h+-I?84Ml# literal 0 HcmV?d00001 diff --git a/react-phoenix/web/static/assets/images/phoenix.png b/react-phoenix/web/static/assets/images/phoenix.png new file mode 100644 index 0000000000000000000000000000000000000000..9c81075f63d2151e6f40e9aa66f665749a87cc6a GIT binary patch literal 13900 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 UWxWXg`G1#+f~I_}taaG`2PLnS&Hw-a literal 0 HcmV?d00001 diff --git a/react-phoenix/web/static/assets/robots.txt b/react-phoenix/web/static/assets/robots.txt new file mode 100644 index 0000000..3c9c7c0 --- /dev/null +++ b/react-phoenix/web/static/assets/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-agent: * +# Disallow: / diff --git a/react-phoenix/web/static/css/app.css b/react-phoenix/web/static/css/app.css new file mode 100644 index 0000000..94ce6d2 --- /dev/null +++ b/react-phoenix/web/static/css/app.css @@ -0,0 +1,38 @@ +/* This file is for your main application css. */ + +.has-error { + color: red; + position: absolute; + right: 0px; + top: 18px; +} + +.right { + display: flex; +} + +.right a { + margin-right: 10px; +} + +.notification { + position: absolute !important; + height: 50px; + width: 100px; + bottom: 20px; + left: 20px; + text-align: center; +} + + +.help-block { + color: red; + text-transform: capitalize; + position: absolute; + top: 35px; +} + +.form-group { + position: relative; + margin-bottom: 25px; +} diff --git a/react-phoenix/web/static/js/CommentBox.js b/react-phoenix/web/static/js/CommentBox.js new file mode 100644 index 0000000..5f7e93c --- /dev/null +++ b/react-phoenix/web/static/js/CommentBox.js @@ -0,0 +1,30 @@ +import React, { Component } from 'react'; + +class CommentBox extends Component { + constructor(props) { + super(props); + + this.state = { text: '' }; + } + + onSubmit(event) { + event.preventDefault(); + + this.props.onCommentCreate(this.state.text); + this.setState({ text: '' }); + } + + render() { + return ( +
      +