├── lib
└── talon_example
│ ├── web
│ ├── templates
│ │ ├── talon
│ │ │ └── admin_lte
│ │ │ │ ├── blog_view.ex
│ │ │ │ ├── post_view.ex
│ │ │ │ ├── user_view.ex
│ │ │ │ ├── layout
│ │ │ │ ├── nav_resource_link.html.slim
│ │ │ │ ├── nav_action_link.html.slim
│ │ │ │ ├── sidebar.html.slim
│ │ │ │ └── app.html.slim
│ │ │ │ ├── blog
│ │ │ │ ├── edit.html.slim
│ │ │ │ ├── new.html.slim
│ │ │ │ ├── show.html.slim
│ │ │ │ ├── form.html.slim
│ │ │ │ └── index.html.slim
│ │ │ │ ├── post
│ │ │ │ ├── edit.html.slim
│ │ │ │ ├── new.html.slim
│ │ │ │ ├── show.html.slim
│ │ │ │ ├── form.html.slim
│ │ │ │ └── index.html.slim
│ │ │ │ ├── user
│ │ │ │ ├── edit.html.slim
│ │ │ │ ├── new.html.slim
│ │ │ │ ├── show.html.slim
│ │ │ │ ├── form.html.slim
│ │ │ │ └── index.html.slim
│ │ │ │ ├── generators
│ │ │ │ ├── edit.html.eex
│ │ │ │ ├── new.html.eex
│ │ │ │ ├── show.html.eex
│ │ │ │ ├── form.html.eex
│ │ │ │ └── index.html.eex
│ │ │ │ └── components
│ │ │ │ └── datatable
│ │ │ │ ├── table_body.html.slim
│ │ │ │ └── datatable.html.slim
│ │ ├── page
│ │ │ └── index.html.eex
│ │ └── layout
│ │ │ └── app.html.eex
│ ├── views
│ │ ├── page_view.ex
│ │ ├── layout_view.ex
│ │ ├── talon
│ │ │ └── admin_lte
│ │ │ │ ├── blog_view.ex
│ │ │ │ ├── layout_view.ex
│ │ │ │ ├── post_view.ex
│ │ │ │ ├── user_view.ex
│ │ │ │ └── components
│ │ │ │ ├── datatable_view.ex
│ │ │ │ └── paginate_view.ex
│ │ ├── error_view.ex
│ │ └── error_helpers.ex
│ ├── controllers
│ │ ├── page_controller.ex
│ │ └── talon
│ │ │ └── talon_resource_controller.ex
│ ├── router.ex
│ ├── gettext.ex
│ ├── talon_messages.ex
│ ├── channels
│ │ └── user_socket.ex
│ ├── endpoint.ex
│ ├── web.ex
│ └── talon_web.ex
│ ├── models
│ ├── schema
│ │ ├── base.ex
│ │ ├── blog.ex
│ │ ├── post.ex
│ │ └── user.ex
│ ├── user.ex
│ ├── blog.ex
│ ├── post.ex
│ └── base.ex
│ ├── repo.ex
│ ├── application.ex
│ └── talon
│ ├── blog.ex
│ ├── post.ex
│ ├── user.ex
│ └── talon.ex
├── assets
├── css
│ ├── app.css
│ └── talon
│ │ └── admin-lte
│ │ └── talon.css
├── static
│ ├── favicon.ico
│ ├── images
│ │ ├── phoenix.png
│ │ └── talon
│ │ │ └── admin_lte
│ │ │ └── orderable.png
│ └── robots.txt
├── package.json
├── js
│ ├── talon
│ │ └── admin-lte
│ │ │ └── talon.js
│ ├── app.js
│ └── socket.js
├── brunch-config.js
└── vendor
│ └── talon
│ └── admin-lte
│ ├── dist
│ └── js
│ │ └── app.min.js
│ ├── plugins
│ └── sweetalert
│ │ └── dist
│ │ ├── sweetalert.min.js
│ │ └── sweetalert.css
│ └── bootstrap
│ └── js
│ └── bootstrap.min.js
├── test
├── test_helper.exs
├── talon_example
│ ├── web
│ │ ├── views
│ │ │ ├── page_view_test.exs
│ │ │ ├── layout_view_test.exs
│ │ │ └── error_view_test.exs
│ │ └── controllers
│ │ │ └── page_controller_test.exs
│ └── blogs
│ │ └── blogs_test.exs
└── support
│ ├── channel_case.ex
│ ├── conn_case.ex
│ └── data_case.ex
├── priv
├── repo
│ ├── migrations
│ │ ├── 20170530002211_create_blogs_blog.exs
│ │ ├── 20170530002414_create_users.exs
│ │ └── 20170530002249_create_blogs_post.exs
│ └── seeds.exs
└── gettext
│ ├── en
│ └── LC_MESSAGES
│ │ └── errors.po
│ └── errors.pot
├── config
├── talon.exs
├── test.exs
├── config.exs
├── dev.exs
└── prod.exs
├── .gitignore
├── README.md
├── mix.exs
└── mix.lock
/lib/talon_example/web/templates/talon/admin_lte/blog_view.ex:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/post_view.ex:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/user_view.ex:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/css/app.css:
--------------------------------------------------------------------------------
1 | /* This file is for your main application css. */
--------------------------------------------------------------------------------
/assets/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smpallen99/talon_example/master/assets/static/favicon.ico
--------------------------------------------------------------------------------
/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
3 | Ecto.Adapters.SQL.Sandbox.mode(TalonExample.Repo, :manual)
4 |
5 |
--------------------------------------------------------------------------------
/assets/static/images/phoenix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smpallen99/talon_example/master/assets/static/images/phoenix.png
--------------------------------------------------------------------------------
/lib/talon_example/web/views/page_view.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.PageView do
2 | use TalonExample.Web, :view
3 | end
4 |
--------------------------------------------------------------------------------
/lib/talon_example/web/views/layout_view.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.LayoutView do
2 | use TalonExample.Web, :view
3 | end
4 |
--------------------------------------------------------------------------------
/test/talon_example/web/views/page_view_test.exs:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.PageViewTest do
2 | use TalonExample.Web.ConnCase, async: true
3 | end
4 |
--------------------------------------------------------------------------------
/test/talon_example/web/views/layout_view_test.exs:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.LayoutViewTest do
2 | use TalonExample.Web.ConnCase, async: true
3 | end
4 |
--------------------------------------------------------------------------------
/assets/static/images/talon/admin_lte/orderable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smpallen99/talon_example/master/assets/static/images/talon/admin_lte/orderable.png
--------------------------------------------------------------------------------
/lib/talon_example/web/views/talon/admin_lte/blog_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AdminLte.Web.BlogView do
2 | use Talon.Web, which: :view, theme: "admin_lte", module: AdminLte.Web
3 |
4 | end
5 |
--------------------------------------------------------------------------------
/lib/talon_example/web/views/talon/admin_lte/layout_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AdminLte.Web.LayoutView do
2 | use Talon.Web, which: :view, theme: "admin_lte", module: AdminLte.Web
3 |
4 | end
5 |
--------------------------------------------------------------------------------
/lib/talon_example/web/views/talon/admin_lte/post_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AdminLte.Web.PostView do
2 | use Talon.Web, which: :view, theme: "admin_lte", module: AdminLte.Web
3 |
4 | end
5 |
--------------------------------------------------------------------------------
/lib/talon_example/web/views/talon/admin_lte/user_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AdminLte.Web.UserView do
2 | use Talon.Web, which: :view, theme: "admin_lte", module: AdminLte.Web
3 |
4 | end
5 |
--------------------------------------------------------------------------------
/lib/talon_example/web/controllers/page_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.PageController do
2 | use TalonExample.Web, :controller
3 |
4 | def index(conn, _params) do
5 | render conn, "index.html"
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/layout/nav_resource_link.html.slim:
--------------------------------------------------------------------------------
1 | - {name, path} = @resource_path
2 | li
3 | = link to: path do
4 | i.nav-label.label.label-info
5 | = String.first name
6 | span = name
7 |
--------------------------------------------------------------------------------
/assets/static/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/lib/talon_example/web/views/talon/admin_lte/components/datatable_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AdminLte.Web.DatatableView do
2 | use Talon.Web, which: :component_view, theme: "admin_lte", module: AdminLte.Web
3 | use Talon.Components.Datatable, __MODULE__
4 |
5 | end
6 |
--------------------------------------------------------------------------------
/lib/talon_example/models/schema/base.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Models.Schema.Base do
2 | defmacro __using__(_) do
3 | quote do
4 | use Ecto.Schema
5 | import Ecto.{Query, Changeset}
6 | alias TalonExample.Repo
7 | end
8 | end
9 | end
10 |
11 |
--------------------------------------------------------------------------------
/test/talon_example/web/controllers/page_controller_test.exs:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.PageControllerTest do
2 | use TalonExample.Web.ConnCase
3 |
4 | test "GET /", %{conn: conn} do
5 | conn = get conn, "/"
6 | assert html_response(conn, 200) =~ "Welcome to Phoenix!"
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170530002211_create_blogs_blog.exs:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Repo.Migrations.CreateTalonExample.Blogs.Blog do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:blogs_blogs) do
6 | add :name, :string
7 |
8 | timestamps()
9 | end
10 |
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170530002414_create_users.exs:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Repo.Migrations.CreateUsers do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:users) do
6 | add :name, :string
7 | add :email, :string
8 |
9 | timestamps()
10 | end
11 | create unique_index(:users, [:email])
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/talon_example/models/user.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Models.User do
2 | use TalonExample.Models.Base, schema: TalonExample.Models.Schema.User
3 |
4 | alias __MODULE__, as: User
5 |
6 | def get_by_email(email) do
7 | Repo.get_by(User, email: email)
8 | end
9 |
10 | def preload(user, preloads) do
11 | Repo.preload(user, preloads)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/talon_example/models/blog.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Models.Blog do
2 | use TalonExample.Models.Base, schema: TalonExample.Models.Schema.Blog
3 |
4 | alias __MODULE__, as: Blog
5 |
6 | # def get_by_email(email) do
7 | # Repo.get_by(User, email: email)
8 | # end
9 |
10 | def preload(blog, preloads) do
11 | Repo.preload(blog, preloads)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/talon_example/models/post.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Models.Post do
2 | use TalonExample.Models.Base, schema: TalonExample.Models.Schema.Post
3 |
4 | alias __MODULE__, as: Post
5 |
6 | # def get_by_email(email) do
7 | # Repo.get_by(User, email: email)
8 | # end
9 |
10 | def preload(post, preloads) do
11 | Repo.preload(post, preloads)
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/talon_example/repo.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Repo do
2 | use Ecto.Repo, otp_app: :talon_example
3 | use Scrivener, page_size: 15 # <--- add this
4 | @doc """
5 | Dynamically loads the repository url from the
6 | DATABASE_URL environment variable.
7 | """
8 | def init(_, opts) do
9 | {:ok, Keyword.put(opts, :url, System.get_env("DATABASE_URL"))}
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/config/talon.exs:
--------------------------------------------------------------------------------
1 | use Mix.Config
2 |
3 | config :talon, resources: [
4 | TalonExample.Talon.Models.User,
5 | TalonExample.Talon.Models.Blog,
6 | TalonExample.Talon.Models.Post,
7 | ],
8 | module: TalonExample,
9 | messages_backend: TalonExample.Web.Gettext,
10 | theme: "admin_lte",
11 |
12 | web_namespace: Web,
13 |
14 | schema_adapter: Talon.Schema.Adapters.Ecto
15 |
--------------------------------------------------------------------------------
/priv/repo/seeds.exs:
--------------------------------------------------------------------------------
1 | # Script for populating the database. You can run it as:
2 | #
3 | # mix run priv/repo/seeds.exs
4 | #
5 | # Inside the script, you can read and write to any of your
6 | # repositories directly:
7 | #
8 | # TalonExample.Repo.insert!(%TalonExample.SomeSchema{})
9 | #
10 | # We recommend using the bang functions (`insert!`, `update!`
11 | # and so on) as they will fail if something goes wrong.
12 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/layout/nav_action_link.html.slim:
--------------------------------------------------------------------------------
1 | elixir:
2 | {action, name, path} = @action_path
3 | {i_class, class} = case action do
4 | :new -> {"fa fa-plus-square", ""}
5 | :edit -> {"fa fa-edit", ""}
6 | :delete -> {"fa fa-minus-square", "delete-link"}
7 | _ -> ""
8 | end
9 | li
10 | = link to: path, class: class do
11 | i class="#{i_class}"
12 | span = name
13 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/blog/edit.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | = if @changeset.action do
5 | .alert.alert-danger
6 | p Oops, something went wrong! Please check the errors below.
7 | .box.box-info
8 | = render "form.html", changeset: @changeset, resource: @resource, action: Talon.Utils.talon_resource_path(@resource, :update), talon: @conn.assigns[:talon] || %{}
9 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/post/edit.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | = if @changeset.action do
5 | .alert.alert-danger
6 | p Oops, something went wrong! Please check the errors below.
7 | .box.box-info
8 | = render "form.html", changeset: @changeset, resource: @resource, action: Talon.Utils.talon_resource_path(@resource, :update), talon: @conn.assigns[:talon] || %{}
9 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/user/edit.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | = if @changeset.action do
5 | .alert.alert-danger
6 | p Oops, something went wrong! Please check the errors below.
7 | .box.box-info
8 | = render "form.html", changeset: @changeset, resource: @resource, action: Talon.Utils.talon_resource_path(@resource, :update), talon: @conn.assigns[:talon] || %{}
9 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/generators/edit.html.eex:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | = if @changeset.action do
5 | .alert.alert-danger
6 | p Oops, something went wrong! Please check the errors below.
7 | .box.box-info
8 | = render "form.html", changeset: @changeset, resource: @resource, action: Talon.Utils.talon_resource_path(@resource, :update), talon: @conn.assigns[:talon] || %{}
9 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/blog/new.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | = if @changeset.action do
5 | .alert.alert-danger
6 | p Oops, something went wrong! Please check the errors below.
7 | .box.box-info
8 | = render "form.html", changeset: @changeset, resource: @resource, action: Talon.Utils.talon_resource_path(@resource.__struct__, :create), talon: @conn.assigns[:talon] || %{}
9 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/post/new.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | = if @changeset.action do
5 | .alert.alert-danger
6 | p Oops, something went wrong! Please check the errors below.
7 | .box.box-info
8 | = render "form.html", changeset: @changeset, resource: @resource, action: Talon.Utils.talon_resource_path(@resource.__struct__, :create), talon: @conn.assigns[:talon] || %{}
9 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/user/new.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | = if @changeset.action do
5 | .alert.alert-danger
6 | p Oops, something went wrong! Please check the errors below.
7 | .box.box-info
8 | = render "form.html", changeset: @changeset, resource: @resource, action: Talon.Utils.talon_resource_path(@resource.__struct__, :create), talon: @conn.assigns[:talon] || %{}
9 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/generators/new.html.eex:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | = if @changeset.action do
5 | .alert.alert-danger
6 | p Oops, something went wrong! Please check the errors below.
7 | .box.box-info
8 | = render "form.html", changeset: @changeset, resource: @resource, action: Talon.Utils.talon_resource_path(@resource.__struct__, :create), talon: @conn.assigns[:talon] || %{}
9 |
--------------------------------------------------------------------------------
/priv/repo/migrations/20170530002249_create_blogs_post.exs:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Repo.Migrations.CreateTalonExample.Blogs.Post do
2 | use Ecto.Migration
3 |
4 | def change do
5 | create table(:blogs_posts) do
6 | add :title, :string
7 | add :body, :text
8 | add :blog_id, references(:blogs_blogs, on_delete: :delete_all)
9 |
10 | timestamps()
11 | end
12 |
13 | create index(:blogs_posts, [:blog_id])
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/assets/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "repository": {},
3 | "license": "MIT",
4 | "scripts": {
5 | "deploy": "brunch build --production",
6 | "watch": "brunch watch --stdin"
7 | },
8 | "dependencies": {
9 | "phoenix": "file:../deps/phoenix",
10 | "phoenix_html": "file:../deps/phoenix_html"
11 | },
12 | "devDependencies": {
13 | "babel-brunch": "6.0.6",
14 | "brunch": "2.10.7",
15 | "clean-css-brunch": "2.10.0",
16 | "uglify-js-brunch": "2.1.1"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lib/talon_example/web/views/error_view.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.ErrorView do
2 | use TalonExample.Web, :view
3 |
4 | def render("404.html", _assigns) do
5 | "Page not found"
6 | end
7 |
8 | def render("500.html", _assigns) do
9 | "Internal server error"
10 | end
11 |
12 | # In case no render clause matches or no
13 | # template is found, let's render it as 500
14 | def template_not_found(_template, assigns) do
15 | render "500.html", assigns
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/talon_example/models/schema/blog.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Models.Schema.Blog do
2 | defmacro __using__(_) do
3 | quote do
4 | use TalonExample.Models.Schema.Base
5 |
6 | schema "blogs_blogs" do
7 | field :name, :string
8 | has_many :posts, TalonExample.Models.Post
9 |
10 | timestamps()
11 | end
12 |
13 | @doc false
14 | def changeset(model, attrs \\ %{}) do
15 | model
16 | |> cast(attrs, [:name])
17 | |> validate_required([:name])
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/assets/js/talon/admin-lte/talon.js:
--------------------------------------------------------------------------------
1 | $('body').on('click', '.delete-link', function(e) {
2 | e.preventDefault()
3 | e.stopPropagation()
4 | var url = $(this).attr('href');
5 | console.log('url', url)
6 | swal({
7 | title: "Are you sure?",
8 | text: "The record will be permanently deleted!",
9 | type: "warning",
10 | showCancelButton: true,
11 | confirmButtonColor: "#DD6B55",
12 | confirmButtonText: "Yes, delete it!",
13 | closeOnConfirm: false,
14 | }, function(){
15 | $('#delete-form form').attr('action', url).submit()
16 | });
17 | })
18 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/blog/show.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | .box
5 | .box-header.with-border
6 | .box-title Blogs
7 | .box-body.table-responsive.no-padding
8 | .panel_contents
9 | table.table.table-hover
10 | tbody
11 |
12 | - {name, value} = Talon.View.talon_resource(@conn).get_schema_field(:show, @resource, :name)
13 | tr
14 | th= name
15 | td= value
16 |
17 |
--------------------------------------------------------------------------------
/config/test.exs:
--------------------------------------------------------------------------------
1 | use Mix.Config
2 |
3 | # We don't run a server during test. If one is required,
4 | # you can enable the server option below.
5 | config :talon_example, TalonExample.Web.Endpoint,
6 | http: [port: 4001],
7 | server: false
8 |
9 | # Print only warnings and errors during test
10 | config :logger, level: :warn
11 |
12 | # Configure your database
13 | config :talon_example, TalonExample.Repo,
14 | adapter: Ecto.Adapters.Postgres,
15 | username: "postgres",
16 | password: "postgres",
17 | database: "talon_example_test",
18 | hostname: "localhost",
19 | pool: Ecto.Adapters.SQL.Sandbox
20 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/blog/form.html.slim:
--------------------------------------------------------------------------------
1 | .box-header.with-border
2 | .box-title = Elixir.TalonExample.Talon.Models.Blog.form_card_title(@resource)
3 | = form_for @changeset, @action, [class: "form-horizontal"], fn f ->
4 | .box-body
5 | - a = {f, @talon}
6 |
7 | .form-group
8 | = label f, :name, class: "col-sm-2 control-label"
9 | .col-sm-10
10 | = Talon.Form.input_builder a, :name, class: "form-control", place_holder: "name"
11 | = error_tag f, :name
12 |
13 | .box-footer
14 | = submit "Submit", class: "btn btn-primary"
15 |
--------------------------------------------------------------------------------
/lib/talon_example/models/schema/post.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Models.Schema.Post do
2 | defmacro __using__(_) do
3 | quote do
4 | use TalonExample.Models.Schema.Base
5 |
6 | schema "blogs_posts" do
7 | field :title, :string
8 | field :body, :string
9 | # field :blog_id, :id
10 | belongs_to :blog, TalonExample.Models.Blog
11 |
12 | timestamps()
13 | end
14 |
15 | def changeset(model, params \\ %{}) do
16 | model
17 | |> cast(params, [:body, :title, :blog_id])
18 | |> validate_required([:body, :title, :blog_id])
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/talon_example/models/schema/user.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Models.Schema.User do
2 | defmacro __using__(_) do
3 | quote do
4 | use TalonExample.Models.Schema.Base
5 | # use Coherence.Schema
6 |
7 | schema "users" do
8 | field :name, :string
9 | field :email, :string
10 |
11 | timestamps()
12 | end
13 |
14 | def changeset(model, params \\ %{}) do
15 | model
16 | |> cast(params, [:name, :email])
17 | |> validate_required([:name, :email])
18 | |> validate_format(:email, ~r/@/)
19 | |> unique_constraint(:email)
20 | end
21 |
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/assets/css/talon/admin-lte/talon.css:
--------------------------------------------------------------------------------
1 |
2 | .index-actions button.action-edit,
3 | .index-actions button.action-delete {
4 | margin-left: 2px;
5 | }
6 |
7 | .help-block {
8 | color: #dd4b39 !important;
9 | font-weight: 600;
10 | }
11 | table.index_table th.sortable a {
12 | background: url("/images/talon/admin_lte/orderable.png") no-repeat 0 4px;
13 | padding-left: 13px;
14 | }
15 | table.index_table th.sorted-asc a {
16 | background-position: 0 -27px;
17 | }
18 | table.index_table th.sorted-desc a {
19 | background-position: 0 -56px;
20 | }
21 | table.index_table th.selectable {
22 | width: 25px;
23 | }
24 | table.index_table th.actions {
25 | width: 90px;
26 | }
27 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/layout/sidebar.html.slim:
--------------------------------------------------------------------------------
1 | aside.main-sidebar
2 | section.sidebar style="height: auto;"
3 | ul.sidebar-menu
4 | li.header.text-uppercase Resources
5 | = if @talon_resource do
6 | = for resource_path <- @talon_resource.resource_paths(@conn.assigns.talon) do
7 | = render "nav_resource_link.html", conn: @conn, resource_path: resource_path
8 | li.header.text-uppercase Actions
9 | = if @talon_resource do
10 | = for action_path <- @talon_resource.nav_action_links(Phoenix.Controller.action_name(@conn), @conn.assigns.resource) do
11 | = render "nav_action_link.html", conn: @conn, action_path: action_path
12 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/generators/show.html.eex:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | .box
5 | .box-header.with-border
6 | .box-title <%= @talon_resource.index_card_title() %>
7 | .box-body.table-responsive.no-padding
8 | .panel_contents
9 | table.table.table-hover
10 | tbody
11 | <%= for name <- @talon_resource.display_schema_columns(:show) do %>
12 | - {name, value} = Talon.View.talon_resource(@conn).get_schema_field(:show, @resource, <%= ":#{name}" %>)
13 | tr
14 | th= name
15 | td= value
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/generators/form.html.eex:
--------------------------------------------------------------------------------
1 | .box-header.with-border
2 | .box-title = <%= @talon_resource %>.form_card_title(@resource)
3 | = form_for @changeset, @action, [class: "form-horizontal"], fn f ->
4 | .box-body
5 | - a = {f, @talon}
6 | <%= for name <- @talon_resource.display_schema_columns(:form) do %>
7 | .form-group
8 | = label f, <%= ":#{name}" %>, class: "col-sm-2 control-label"
9 | .col-sm-10
10 | = Talon.Form.input_builder a, <%= ":#{name}" %>, class: "form-control", place_holder: "<%= name %>"
11 | = error_tag f, <%= ":#{name}" %>
12 | <% end %>
13 | .box-footer
14 | = submit "Submit", class: "btn btn-primary"
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # App artifacts
2 | /_build
3 | /db
4 | /deps
5 | /*.ez
6 |
7 | # Generated on crash by the VM
8 | erl_crash.dump
9 |
10 | # Generated on crash by NPM
11 | npm-debug.log
12 |
13 | # Static artifacts
14 | /assets/node_modules
15 |
16 | # Since we are building assets from assets/,
17 | # we ignore priv/static. You may want to comment
18 | # this depending on your deployment strategy.
19 | /priv/static/
20 |
21 | # Files matching config/*.secret.exs pattern contain sensitive
22 | # data and you should not commit them into version control.
23 | #
24 | # Alternatively, you may comment the line below and commit the
25 | # secrets files as long as you replace their contents by environment
26 | # variables.
27 | /config/*.secret.exs
--------------------------------------------------------------------------------
/test/talon_example/web/views/error_view_test.exs:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.ErrorViewTest do
2 | use TalonExample.Web.ConnCase, async: true
3 |
4 | # Bring render/3 and render_to_string/3 for testing custom views
5 | import Phoenix.View
6 |
7 | test "renders 404.html" do
8 | assert render_to_string(TalonExample.Web.ErrorView, "404.html", []) ==
9 | "Page not found"
10 | end
11 |
12 | test "render 500.html" do
13 | assert render_to_string(TalonExample.Web.ErrorView, "500.html", []) ==
14 | "Internal server error"
15 | end
16 |
17 | test "render any other" do
18 | assert render_to_string(TalonExample.Web.ErrorView, "505.html", []) ==
19 | "Internal server error"
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/assets/js/app.js:
--------------------------------------------------------------------------------
1 | // Brunch automatically concatenates all files in your
2 | // watched paths. Those paths can be configured at
3 | // config.paths.watched in "brunch-config.js".
4 | //
5 | // However, those files will only be executed if
6 | // explicitly imported. The only exception are files
7 | // in vendor, which are never wrapped in imports and
8 | // therefore are always executed.
9 |
10 | // Import dependencies
11 | //
12 | // If you no longer want to use a dependency, remember
13 | // to also remove its path from "config.paths.watched".
14 | import "phoenix_html"
15 |
16 | // Import local files
17 | //
18 | // Local files can be imported directly using relative
19 | // paths "./socket" or full ones "web/static/js/socket".
20 |
21 | // import socket from "./socket"
22 |
--------------------------------------------------------------------------------
/lib/talon_example/web/views/talon/admin_lte/components/paginate_view.ex:
--------------------------------------------------------------------------------
1 | defmodule AdminLte.Web.PaginateView do
2 | use Talon.Web, which: :component_view, theme: "admin_lte", module: AdminLte.Web
3 |
4 | import Talon.Components.Paginate
5 | import Talon.Utils, only: [to_integer: 1]
6 |
7 | def paginate(%{params: params} = conn) do
8 | page_number = to_integer(params["page"] || 1)
9 | page = conn.assigns[:page]
10 | model_name =
11 | conn.assigns[:resource]
12 | |> Module.split
13 | |> List.last
14 | link = Talon.Utils.talon_resource_path conn.assigns[:resource], :index, [[order: nil]]
15 |
16 | paginate(link, page_number, page.page_size, page.total_pages, page.total_entries, model_name, show_information: true)
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/user/show.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | .box
5 | .box-header.with-border
6 | .box-title Users
7 | .box-body.table-responsive.no-padding
8 | .panel_contents
9 | table.table.table-hover
10 | tbody
11 |
12 | - {name, value} = Talon.View.talon_resource(@conn).get_schema_field(:show, @resource, :name)
13 | tr
14 | th= name
15 | td= value
16 |
17 | - {name, value} = Talon.View.talon_resource(@conn).get_schema_field(:show, @resource, :email)
18 | tr
19 | th= name
20 | td= value
21 |
22 |
--------------------------------------------------------------------------------
/lib/talon_example/models/base.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Models.Base do
2 | defmacro __using__(opts) do
3 | schema = Keyword.fetch!(opts, :schema)
4 |
5 | quote do
6 | use unquote(schema)
7 | alias TalonExample.Repo
8 |
9 | def list do
10 | Repo.all(__MODULE__)
11 | end
12 |
13 | def get!(id), do: Repo.get!(__MODULE__, id)
14 |
15 | def create(attrs \\ %{}) do
16 | %__MODULE__{}
17 | |> __MODULE__.changeset(attrs)
18 | |> Repo.insert()
19 | end
20 |
21 | def update(%__MODULE__{} = model, attrs) do
22 | model
23 | |> __MODULE__.changeset(attrs)
24 | |> Repo.update()
25 | end
26 |
27 | def delete(%__MODULE__{} = model) do
28 | Repo.delete(model)
29 | end
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/talon_example/web/controllers/talon/talon_resource_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.TalonResourceController do
2 | use TalonExample.Web, :controller
3 | use Talon.Controller, repo: TalonExample.Repo, context: TalonExample.Talon
4 |
5 | plug Talon.Plug.TalonResource
6 | plug Talon.Plug.LoadResource
7 | plug Talon.Plug.LoadAssociations
8 | plug Talon.Plug.LoadAssociatedCollections when action in [:new, :edit]
9 | plug Talon.Plug.Theme
10 | plug Talon.Plug.Layout
11 | plug Talon.Plug.View
12 |
13 | # Each of the controller actions can be overridden in this module
14 |
15 | # Override the show action
16 | # def show(conn, params) do
17 | # IO.inspect params, label: params
18 | # conn
19 | # |> assign(:something, "something")
20 | # |> super(paams)
21 | # end
22 |
23 | end
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TalonExample
2 |
3 | To start your Phoenix server:
4 |
5 | * Install dependencies with `mix deps.get`
6 | * Create and migrate your database with `mix ecto.create && mix ecto.migrate`
7 | * Install Node.js dependencies with `cd assets && npm install`
8 | * Start Phoenix endpoint with `mix phx.server`
9 |
10 | Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
11 |
12 | Ready to run in production? Please [check our deployment guides](http://www.phoenixframework.org/docs/deployment).
13 |
14 | ## Learn more
15 |
16 | * Official website: http://www.phoenixframework.org/
17 | * Guides: http://phoenixframework.org/docs/overview
18 | * Docs: https://hexdocs.pm/phoenix
19 | * Mailing list: http://groups.google.com/group/phoenix-talk
20 | * Source: https://github.com/phoenixframework/phoenix
21 |
--------------------------------------------------------------------------------
/lib/talon_example/web/router.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.Router do
2 | use TalonExample.Web, :router
3 | use Talon.Router
4 |
5 | pipeline :browser do
6 | plug :accepts, ["html"]
7 | plug :fetch_session
8 | plug :fetch_flash
9 | plug :protect_from_forgery
10 | plug :put_secure_browser_headers
11 | end
12 |
13 | pipeline :api do
14 | plug :accepts, ["json"]
15 | end
16 |
17 | # your app's routes
18 | scope "/talon", TalonExample.Web do
19 | pipe_through :browser
20 | talon_routes()
21 | end
22 |
23 | scope "/", TalonExample.Web do
24 | pipe_through :browser # Use the default browser stack
25 |
26 | get "/", PageController, :index
27 | end
28 |
29 | # Other scopes may use custom stacks.
30 | # scope "/api", TalonExample.Web do
31 | # pipe_through :api
32 | # end
33 | end
34 |
--------------------------------------------------------------------------------
/lib/talon_example/web/gettext.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.Gettext do
2 | @moduledoc """
3 | A module providing Internationalization with a gettext-based API.
4 |
5 | By using [Gettext](https://hexdocs.pm/gettext),
6 | your module gains a set of macros for translations, for example:
7 |
8 | import TalonExample.Web.Gettext
9 |
10 | # Simple translation
11 | gettext "Here is the string to translate"
12 |
13 | # Plural translation
14 | ngettext "Here is the string to translate",
15 | "Here are the strings to translate",
16 | 3
17 |
18 | # Domain-based translation
19 | dgettext "errors", "Here is the error message to translate"
20 |
21 | See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
22 | """
23 | use Gettext, otp_app: :talon_example
24 | end
25 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/user/form.html.slim:
--------------------------------------------------------------------------------
1 | .box-header.with-border
2 | .box-title = Elixir.TalonExample.Talon.Models.User.form_card_title(@resource)
3 | = form_for @changeset, @action, [class: "form-horizontal"], fn f ->
4 | .box-body
5 | - a = {f, @talon}
6 |
7 | .form-group
8 | = label f, :name, class: "col-sm-2 control-label"
9 | .col-sm-10
10 | = Talon.Form.input_builder a, :name, class: "form-control", place_holder: "name"
11 | = error_tag f, :name
12 |
13 | .form-group
14 | = label f, :email, class: "col-sm-2 control-label"
15 | .col-sm-10
16 | = Talon.Form.input_builder a, :email, class: "form-control", place_holder: "email"
17 | = error_tag f, :email
18 |
19 | .box-footer
20 | = submit "Submit", class: "btn btn-primary"
21 |
--------------------------------------------------------------------------------
/lib/talon_example/application.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Application do
2 | use Application
3 |
4 | # See http://elixir-lang.org/docs/stable/elixir/Application.html
5 | # for more information on OTP Applications
6 | def start(_type, _args) do
7 | import Supervisor.Spec
8 |
9 | # Define workers and child supervisors to be supervised
10 | children = [
11 | # Start the Ecto repository
12 | supervisor(TalonExample.Repo, []),
13 | # Start the endpoint when the application starts
14 | supervisor(TalonExample.Web.Endpoint, []),
15 | # Start your own worker by calling: TalonExample.Worker.start_link(arg1, arg2, arg3)
16 | # worker(TalonExample.Worker, [arg1, arg2, arg3]),
17 | ]
18 |
19 | # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
20 | # for other strategies and supported options
21 | opts = [strategy: :one_for_one, name: TalonExample.Supervisor]
22 | Supervisor.start_link(children, opts)
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/components/datatable/table_body.html.slim:
--------------------------------------------------------------------------------
1 | - talon_resource = Talon.View.talon_resource(@conn)
2 | = for resource <- @resources do
3 | tr
4 | td.selctable
5 | .resource_selection_cell
6 | input.collection_selection type="checkbox" name="collection_selection[]"
7 | = for name <- talon_resource.display_schema_columns(:index) do
8 | - {_name, value} = talon_resource.get_schema_field(:index, resource, name)
9 | td= value
10 |
11 | td.text-right.index-actions
12 | button.btn.btn-xs.btn-primary.action-show type="button" href="#{Talon.Utils.talon_resource_path(resource, :show)}"
13 | i.fa.fa-info-circle
14 | button.btn.btn-xs.btn-warning.action-edit type="button" href="#{Talon.Utils.talon_resource_path(resource, :edit)}"
15 | i.fa.fa-pencil-square-o
16 | button.btn.btn-xs.btn-danger.delete-link type="button" href="#{Talon.Utils.talon_resource_path(resource, :delete)}"
17 | i.fa.fa-times
18 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/post/show.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12
4 | .box
5 | .box-header.with-border
6 | .box-title Posts
7 | .box-body.table-responsive.no-padding
8 | .panel_contents
9 | table.table.table-hover
10 | tbody
11 |
12 | - {name, value} = Talon.View.talon_resource(@conn).get_schema_field(:show, @resource, :body)
13 | tr
14 | th= name
15 | td= value
16 |
17 | - {name, value} = Talon.View.talon_resource(@conn).get_schema_field(:show, @resource, :title)
18 | tr
19 | th= name
20 | td= value
21 |
22 | - {name, value} = Talon.View.talon_resource(@conn).get_schema_field(:show, @resource, :blog_id)
23 | tr
24 | th= name
25 | td= value
26 |
27 |
--------------------------------------------------------------------------------
/lib/talon_example/web/talon_messages.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.Talon.Messages do
2 | @moduledoc """
3 | Application facing messages generated by the Talon application.
4 |
5 | This module was created by the talon.new mix task. It contains all the
6 | messages used in the coherence application except those in other generated
7 | files like the view and templates.
8 |
9 | To assist in upgrading Talon, the `Talon.Messages behaviour will
10 | alway contain every message for the current version. This will help in upgrades
11 | to ensure the user had added new the new messages from the current version.
12 | """
13 | @behaviour Talon.Messages
14 |
15 | import TalonExample.Web.Gettext
16 |
17 | # Change this to override the "talon" gettext domain. If you would like
18 | # the talon message to be part of your projects domain change it to "default"
19 | @domain "talon"
20 |
21 | ##################
22 | # Messages
23 |
24 | def are_you_sure_you_want_to_delete_this?, do: dgettext(@domain, "Are you sure you want to delete this?")
25 | end
26 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/post/form.html.slim:
--------------------------------------------------------------------------------
1 | .box-header.with-border
2 | .box-title = Elixir.TalonExample.Talon.Models.Post.form_card_title(@resource)
3 | = form_for @changeset, @action, [class: "form-horizontal"], fn f ->
4 | .box-body
5 | - a = {f, @talon}
6 |
7 | .form-group
8 | = label f, :body, class: "col-sm-2 control-label"
9 | .col-sm-10
10 | = Talon.Form.input_builder a, :body, class: "form-control", place_holder: "body"
11 | = error_tag f, :body
12 |
13 | .form-group
14 | = label f, :title, class: "col-sm-2 control-label"
15 | .col-sm-10
16 | = Talon.Form.input_builder a, :title, class: "form-control", place_holder: "title"
17 | = error_tag f, :title
18 |
19 | .form-group
20 | = label f, :blog_id, class: "col-sm-2 control-label"
21 | .col-sm-10
22 | = Talon.Form.input_builder a, :blog_id, class: "form-control", place_holder: "blog_id"
23 | = error_tag f, :blog_id
24 |
25 | .box-footer
26 | = submit "Submit", class: "btn btn-primary"
27 |
--------------------------------------------------------------------------------
/test/support/channel_case.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.ChannelCase do
2 | @moduledoc """
3 | This module defines the test case to be used by
4 | channel tests.
5 |
6 | Such tests rely on `Phoenix.ChannelTest` and also
7 | import other functionality to make it easier
8 | to build common datastructures and query the data layer.
9 |
10 | Finally, if the test case interacts with the database,
11 | it cannot be async. For this reason, every test runs
12 | inside a transaction which is reset at the beginning
13 | of the test unless the test case is marked as async.
14 | """
15 |
16 | use ExUnit.CaseTemplate
17 |
18 | using do
19 | quote do
20 | # Import conveniences for testing with channels
21 | use Phoenix.ChannelTest
22 |
23 | # The default endpoint for testing
24 | @endpoint TalonExample.Web.Endpoint
25 | end
26 | end
27 |
28 |
29 | setup tags do
30 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(TalonExample.Repo)
31 | unless tags[:async] do
32 | Ecto.Adapters.SQL.Sandbox.mode(TalonExample.Repo, {:shared, self()})
33 | end
34 | :ok
35 | end
36 |
37 | end
38 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/page/index.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
<%= gettext "Welcome to %{name}!", name: "Phoenix" %>
3 |
A productive web framework that does not compromise speed and maintainability.
4 |
5 |
6 |
37 |
--------------------------------------------------------------------------------
/config/config.exs:
--------------------------------------------------------------------------------
1 | # This file is responsible for configuring your application
2 | # and its dependencies with the aid of the Mix.Config module.
3 | #
4 | # This configuration file is loaded before any dependency and
5 | # is restricted to this project.
6 | use Mix.Config
7 |
8 | # General application configuration
9 | config :talon_example,
10 | ecto_repos: [TalonExample.Repo]
11 |
12 | # Configures the endpoint
13 | config :talon_example, TalonExample.Web.Endpoint,
14 | url: [host: "localhost"],
15 | secret_key_base: "6ISb2/B7l5W3f1XDn7qTMOxt1lYMYxVOl+MeSMDi0N19NvPKBGLw/Q/eiFC9RS1K",
16 | render_errors: [view: TalonExample.Web.ErrorView, accepts: ~w(html json)],
17 | pubsub: [name: TalonExample.PubSub,
18 | adapter: Phoenix.PubSub.PG2]
19 |
20 | # Configures Elixir's Logger
21 | config :logger, :console,
22 | format: "$time $metadata[$level] $message\n",
23 | metadata: [:request_id]
24 |
25 | # Import environment specific config. This must remain at the bottom
26 | # of this file so it overrides the configuration defined above.
27 | import_config "#{Mix.env}.exs"
28 | config :phoenix, :template_engines,
29 | slim: PhoenixSlime.Engine,
30 | slime: PhoenixSlime.Engine
31 |
32 | import_config "talon.exs"
33 |
34 |
--------------------------------------------------------------------------------
/test/support/conn_case.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.ConnCase do
2 | @moduledoc """
3 | This module defines the test case to be used by
4 | tests that require setting up a connection.
5 |
6 | Such tests rely on `Phoenix.ConnTest` and also
7 | import other functionality to make it easier
8 | to build common datastructures and query the data layer.
9 |
10 | Finally, if the test case interacts with the database,
11 | it cannot be async. For this reason, every test runs
12 | inside a transaction which is reset at the beginning
13 | of the test unless the test case is marked as async.
14 | """
15 |
16 | use ExUnit.CaseTemplate
17 |
18 | using do
19 | quote do
20 | # Import conveniences for testing with connections
21 | use Phoenix.ConnTest
22 | import TalonExample.Web.Router.Helpers
23 |
24 | # The default endpoint for testing
25 | @endpoint TalonExample.Web.Endpoint
26 | end
27 | end
28 |
29 |
30 | setup tags do
31 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(TalonExample.Repo)
32 | unless tags[:async] do
33 | Ecto.Adapters.SQL.Sandbox.mode(TalonExample.Repo, {:shared, self()})
34 | end
35 | {:ok, conn: Phoenix.ConnTest.build_conn()}
36 | end
37 |
38 | end
39 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/layout/app.html.eex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Hello TalonExample!
11 | ">
12 |
13 |
14 |
15 |
16 |
24 |
25 |
<%= get_flash(@conn, :info) %>
26 |
<%= get_flash(@conn, :error) %>
27 |
28 |
29 | <%= render @view_module, @view_template, assigns %>
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/blog/index.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12#datatable-view
4 | = AdminLte.Web.DatatableView.render_table(@conn, @resources)
5 | javascript:
6 |
7 | function search_path(value) {
8 | var route = $('#datatable-body').data('route')
9 | // TODO: this needs to be based on the route scope. Should build this with router builders
10 | return '/talon/' + route + '/search/' + value + '?format.js';
11 | }
12 |
13 | window.onload = function(e) {
14 | $('body').on('click', '.index-actions button.action-show', function(e) {
15 | window.location = $(this).attr('href');
16 | })
17 | $('body').on('click', '.index-actions button.action-edit', function(e) {
18 | window.location = $(this).attr('href');
19 | })
20 | $('body').on('keypress', '#search', function(e) {
21 | var value = $(this).val() + e.key
22 | $.get(search_path(value), function(html) {
23 | $('#datatable-body').html(html)
24 | })
25 | })
26 | $('body').on('keyup', '#search', function(e) {
27 | var value = $(this).val()
28 | if(e.keyCode == 8) {
29 | $.get(search_path(value), function(html) {
30 | $('#datatable-body').html(html)
31 | })
32 | }
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/post/index.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12#datatable-view
4 | = AdminLte.Web.DatatableView.render_table(@conn, @resources)
5 | javascript:
6 |
7 | function search_path(value) {
8 | var route = $('#datatable-body').data('route')
9 | // TODO: this needs to be based on the route scope. Should build this with router builders
10 | return '/talon/' + route + '/search/' + value + '?format.js';
11 | }
12 |
13 | window.onload = function(e) {
14 | $('body').on('click', '.index-actions button.action-show', function(e) {
15 | window.location = $(this).attr('href');
16 | })
17 | $('body').on('click', '.index-actions button.action-edit', function(e) {
18 | window.location = $(this).attr('href');
19 | })
20 | $('body').on('keypress', '#search', function(e) {
21 | var value = $(this).val() + e.key
22 | $.get(search_path(value), function(html) {
23 | $('#datatable-body').html(html)
24 | })
25 | })
26 | $('body').on('keyup', '#search', function(e) {
27 | var value = $(this).val()
28 | if(e.keyCode == 8) {
29 | $.get(search_path(value), function(html) {
30 | $('#datatable-body').html(html)
31 | })
32 | }
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/user/index.html.slim:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12#datatable-view
4 | = AdminLte.Web.DatatableView.render_table(@conn, @resources)
5 | javascript:
6 |
7 | function search_path(value) {
8 | var route = $('#datatable-body').data('route')
9 | // TODO: this needs to be based on the route scope. Should build this with router builders
10 | return '/talon/' + route + '/search/' + value + '?format.js';
11 | }
12 |
13 | window.onload = function(e) {
14 | $('body').on('click', '.index-actions button.action-show', function(e) {
15 | window.location = $(this).attr('href');
16 | })
17 | $('body').on('click', '.index-actions button.action-edit', function(e) {
18 | window.location = $(this).attr('href');
19 | })
20 | $('body').on('keypress', '#search', function(e) {
21 | var value = $(this).val() + e.key
22 | $.get(search_path(value), function(html) {
23 | $('#datatable-body').html(html)
24 | })
25 | })
26 | $('body').on('keyup', '#search', function(e) {
27 | var value = $(this).val()
28 | if(e.keyCode == 8) {
29 | $.get(search_path(value), function(html) {
30 | $('#datatable-body').html(html)
31 | })
32 | }
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/generators/index.html.eex:
--------------------------------------------------------------------------------
1 | .container-fluid
2 | .row
3 | .col-md-12#datatable-view
4 | = AdminLte.Web.DatatableView.render_table(@conn, @resources)
5 | javascript:
6 |
7 | function search_path(value) {
8 | var route = $('#datatable-body').data('route')
9 | // TODO: this needs to be based on the route scope. Should build this with router builders
10 | return '/talon/' + route + '/search/' + value + '?format.js';
11 | }
12 |
13 | window.onload = function(e) {
14 | $('body').on('click', '.index-actions button.action-show', function(e) {
15 | window.location = $(this).attr('href');
16 | })
17 | $('body').on('click', '.index-actions button.action-edit', function(e) {
18 | window.location = $(this).attr('href');
19 | })
20 | $('body').on('keypress', '#search', function(e) {
21 | var value = $(this).val() + e.key
22 | $.get(search_path(value), function(html) {
23 | $('#datatable-body').html(html)
24 | })
25 | })
26 | $('body').on('keyup', '#search', function(e) {
27 | var value = $(this).val()
28 | if(e.keyCode == 8) {
29 | $.get(search_path(value), function(html) {
30 | $('#datatable-body').html(html)
31 | })
32 | }
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/lib/talon_example/web/channels/user_socket.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.UserSocket do
2 | use Phoenix.Socket
3 |
4 | ## Channels
5 | # channel "room:*", TalonExample.Web.RoomChannel
6 |
7 | ## Transports
8 | transport :websocket, Phoenix.Transports.WebSocket
9 | # transport :longpoll, Phoenix.Transports.LongPoll
10 |
11 | # Socket params are passed from the client and can
12 | # be used to verify and authenticate a user. After
13 | # verification, you can put default assigns into
14 | # the socket that will be set for all channels, ie
15 | #
16 | # {:ok, assign(socket, :user_id, verified_user_id)}
17 | #
18 | # To deny connection, return `:error`.
19 | #
20 | # See `Phoenix.Token` documentation for examples in
21 | # performing token verification on connect.
22 | def connect(_params, socket) do
23 | {:ok, socket}
24 | end
25 |
26 | # Socket id's are topics that allow you to identify all sockets for a given user:
27 | #
28 | # def id(socket), do: "user_socket:#{socket.assigns.user_id}"
29 | #
30 | # Would allow you to broadcast a "disconnect" event and terminate
31 | # all active sockets and channels for a given user:
32 | #
33 | # TalonExample.Web.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
34 | #
35 | # Returning `nil` makes this socket anonymous.
36 | def id(_socket), do: nil
37 | end
38 |
--------------------------------------------------------------------------------
/lib/talon_example/web/views/error_helpers.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.ErrorHelpers do
2 | @moduledoc """
3 | Conveniences for translating and building error messages.
4 | """
5 |
6 | use Phoenix.HTML
7 |
8 | @doc """
9 | Generates tag for inlined form input errors.
10 | """
11 | def error_tag(form, field) do
12 | if error = form.errors[field] do
13 | content_tag :span, translate_error(error), class: "help-block"
14 | end
15 | end
16 |
17 | @doc """
18 | Translates an error message using gettext.
19 | """
20 | def translate_error({msg, opts}) do
21 | # Because error messages were defined within Ecto, we must
22 | # call the Gettext module passing our Gettext backend. We
23 | # also use the "errors" domain as translations are placed
24 | # in the errors.po file.
25 | # Ecto will pass the :count keyword if the error message is
26 | # meant to be pluralized.
27 | # On your own code and templates, depending on whether you
28 | # need the message to be pluralized or not, this could be
29 | # written simply as:
30 | #
31 | # dngettext "errors", "1 file", "%{count} files", count
32 | # dgettext "errors", "is invalid"
33 | #
34 | if count = opts[:count] do
35 | Gettext.dngettext(TalonExample.Web.Gettext, "errors", msg, msg, count, opts)
36 | else
37 | Gettext.dgettext(TalonExample.Web.Gettext, "errors", msg, opts)
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/talon_example/talon/blog.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Talon.Models.Blog do
2 | @moduledoc """
3 | Use this file to configure how Talon renders your resource.
4 |
5 | TBD
6 | """
7 | use Talon.Resource, schema: TalonExample.Models.Blog, context: TalonExample.Talon
8 |
9 | # The above is all that is required to use Talon with defaults
10 | # If you would like to customize some of the features, wee the
11 | # commented boilerplate below
12 |
13 | # @doc """
14 | # Prelod the :state and :country associations
15 | # """
16 | # def preload(resource, _action) do
17 | # TalonExample.Repo.preload resource, [:some_association]
18 | # end
19 |
20 | # @doc """
21 | # Override the default index columns
22 | # Add the :id column on the index page and add the
23 | # :id, :updated_at and :inserted_at fields for the
24 | # show page. Use the default for the form page.
25 | # """
26 | # def display_schema_columns(:index) do
27 | # [:id | super(:index)]
28 | # end
29 | # def display_schema_columns(:show) do
30 | # [:id | super(:showw)] ++ [:updated_at, :inserted_at]
31 | # end
32 | # def display_schema_columns(action) do
33 | # super(action)
34 | # end
35 |
36 | # @doc """
37 | # Override the default columm nam redering.
38 | # """
39 | # def render_column_name(_page, :zip_code), do: "Zip"
40 | # def render_column_name(_page, :street_num), do: "Num"
41 | # def render_column_name(page, field) do
42 | # # handle redering for the remaining fields
43 | # super(page, field)
44 | # end
45 |
46 | end
47 |
--------------------------------------------------------------------------------
/lib/talon_example/talon/post.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Talon.Models.Post do
2 | @moduledoc """
3 | Use this file to configure how Talon renders your resource.
4 |
5 | TBD
6 | """
7 | use Talon.Resource, schema: TalonExample.Models.Post, context: TalonExample.Talon
8 |
9 | # The above is all that is required to use Talon with defaults
10 | # If you would like to customize some of the features, wee the
11 | # commented boilerplate below
12 |
13 | # @doc """
14 | # Prelod the :state and :country associations
15 | # """
16 | # def preload(resource, _action) do
17 | # TalonExample.Repo.preload resource, [:some_association]
18 | # end
19 |
20 | # @doc """
21 | # Override the default index columns
22 | # Add the :id column on the index page and add the
23 | # :id, :updated_at and :inserted_at fields for the
24 | # show page. Use the default for the form page.
25 | # """
26 | # def display_schema_columns(:index) do
27 | # [:id | super(:index)]
28 | # end
29 | # def display_schema_columns(:show) do
30 | # [:id | super(:showw)] ++ [:updated_at, :inserted_at]
31 | # end
32 | # def display_schema_columns(action) do
33 | # super(action)
34 | # end
35 |
36 | # @doc """
37 | # Override the default columm nam redering.
38 | # """
39 | # def render_column_name(_page, :zip_code), do: "Zip"
40 | # def render_column_name(_page, :street_num), do: "Num"
41 | # def render_column_name(page, field) do
42 | # # handle redering for the remaining fields
43 | # super(page, field)
44 | # end
45 |
46 | end
47 |
--------------------------------------------------------------------------------
/lib/talon_example/talon/user.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Talon.Models.User do
2 | @moduledoc """
3 | Use this file to configure how Talon renders your resource.
4 |
5 | TBD
6 | """
7 | use Talon.Resource, schema: TalonExample.Models.User, context: TalonExample.Talon
8 |
9 | # The above is all that is required to use Talon with defaults
10 | # If you would like to customize some of the features, wee the
11 | # commented boilerplate below
12 |
13 | # @doc """
14 | # Prelod the :state and :country associations
15 | # """
16 | # def preload(resource, _action) do
17 | # TalonExample.Repo.preload resource, [:some_association]
18 | # end
19 |
20 | # @doc """
21 | # Override the default index columns
22 | # Add the :id column on the index page and add the
23 | # :id, :updated_at and :inserted_at fields for the
24 | # show page. Use the default for the form page.
25 | # """
26 | # def display_schema_columns(:index) do
27 | # [:id | super(:index)]
28 | # end
29 | # def display_schema_columns(:show) do
30 | # [:id | super(:showw)] ++ [:updated_at, :inserted_at]
31 | # end
32 | # def display_schema_columns(action) do
33 | # super(action)
34 | # end
35 |
36 | # @doc """
37 | # Override the default columm nam redering.
38 | # """
39 | # def render_column_name(_page, :zip_code), do: "Zip"
40 | # def render_column_name(_page, :street_num), do: "Num"
41 | # def render_column_name(page, field) do
42 | # # handle redering for the remaining fields
43 | # super(page, field)
44 | # end
45 |
46 | end
47 |
--------------------------------------------------------------------------------
/test/support/data_case.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.DataCase do
2 | @moduledoc """
3 | This module defines the setup for tests requiring
4 | access to the application's data layer.
5 |
6 | You may define functions here to be used as helpers in
7 | your tests.
8 |
9 | Finally, if the test case interacts with the database,
10 | it cannot be async. For this reason, every test runs
11 | inside a transaction which is reset at the beginning
12 | of the test unless the test case is marked as async.
13 | """
14 |
15 | use ExUnit.CaseTemplate
16 |
17 | using do
18 | quote do
19 | alias TalonExample.Repo
20 |
21 | import Ecto
22 | import Ecto.Changeset
23 | import Ecto.Query
24 | import TalonExample.DataCase
25 | end
26 | end
27 |
28 | setup tags do
29 | :ok = Ecto.Adapters.SQL.Sandbox.checkout(TalonExample.Repo)
30 |
31 | unless tags[:async] do
32 | Ecto.Adapters.SQL.Sandbox.mode(TalonExample.Repo, {:shared, self()})
33 | end
34 |
35 | :ok
36 | end
37 |
38 | @doc """
39 | A helper that transform changeset errors to a map of messages.
40 |
41 | assert {:error, changeset} = Accounts.create_user(%{password: "short"})
42 | assert "password is too short" in errors_on(changeset).password
43 | assert %{password: ["password is too short"]} = errors_on(changeset)
44 |
45 | """
46 | def errors_on(changeset) do
47 | Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
48 | Enum.reduce(opts, message, fn {key, value}, acc ->
49 | String.replace(acc, "%{#{key}}", to_string(value))
50 | end)
51 | end)
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Mixfile do
2 | use Mix.Project
3 |
4 | def project do
5 | [app: :talon_example,
6 | version: "0.0.1",
7 | elixir: "~> 1.4",
8 | elixirc_paths: elixirc_paths(Mix.env),
9 | compilers: [:talon, :phoenix, :gettext] ++ Mix.compilers,
10 | start_permanent: Mix.env == :prod,
11 | aliases: aliases(),
12 | deps: deps()]
13 | end
14 |
15 | # Configuration for the OTP application.
16 | #
17 | # Type `mix help compile.app` for more information.
18 | def application do
19 | [mod: {TalonExample.Application, []},
20 | extra_applications: [:logger, :runtime_tools, :talon]]
21 | end
22 |
23 | # Specifies which paths to compile per environment.
24 | defp elixirc_paths(:test), do: ["lib", "test/support"]
25 | defp elixirc_paths(_), do: ["lib"]
26 |
27 | # Specifies your project dependencies.
28 | #
29 | # Type `mix help deps` for examples and options.
30 | defp deps do
31 | [{:phoenix, "~> 1.3.0-rc"},
32 | {:phoenix_pubsub, "~> 1.0"},
33 | {:phoenix_ecto, "~> 3.2"},
34 | {:postgrex, ">= 0.0.0"},
35 | {:phoenix_html, "~> 2.6"},
36 | {:phoenix_live_reload, "~> 1.0", only: :dev},
37 | {:gettext, "~> 0.11"},
38 | {:talon, path: "../talon"},
39 | {:cowboy, "~> 1.0"}]
40 | end
41 |
42 | # Aliases are shortcuts or tasks specific to the current project.
43 | # For example, to create, migrate and run the seeds file at once:
44 | #
45 | # $ mix ecto.setup
46 | #
47 | # See the documentation for `Mix` for more info on aliases.
48 | defp aliases do
49 | ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
50 | "ecto.reset": ["ecto.drop", "ecto.setup"],
51 | "test": ["ecto.create --quiet", "ecto.migrate", "test"]]
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/lib/talon_example/web/endpoint.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web.Endpoint do
2 | use Phoenix.Endpoint, otp_app: :talon_example
3 |
4 | socket "/socket", TalonExample.Web.UserSocket
5 |
6 | # Serve at "/" the static files from "priv/static" directory.
7 | #
8 | # You should set gzip to true if you are running phoenix.digest
9 | # when deploying your static files in production.
10 | plug Plug.Static,
11 | at: "/", from: :talon_example, gzip: false,
12 | only: ~w(css fonts images js favicon.ico robots.txt)
13 |
14 | # Code reloading can be explicitly enabled under the
15 | # :code_reloader configuration of your endpoint.
16 | if code_reloading? do
17 | socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
18 | plug Phoenix.LiveReloader
19 | plug Phoenix.CodeReloader
20 | end
21 |
22 | plug Plug.RequestId
23 | plug Plug.Logger
24 |
25 | plug Plug.Parsers,
26 | parsers: [:urlencoded, :multipart, :json],
27 | pass: ["*/*"],
28 | json_decoder: Poison
29 |
30 | plug Plug.MethodOverride
31 | plug Plug.Head
32 |
33 | # The session will be stored in the cookie and signed,
34 | # this means its contents can be read but not tampered with.
35 | # Set :encryption_salt if you would also like to encrypt it.
36 | plug Plug.Session,
37 | store: :cookie,
38 | key: "_talon_example_key",
39 | signing_salt: "djSnTgfW"
40 |
41 | plug TalonExample.Web.Router
42 |
43 | @doc """
44 | Dynamically loads configuration from the system environment
45 | on startup.
46 |
47 | It receives the endpoint configuration from the config files
48 | and must return the updated configuration.
49 | """
50 | def load_from_system_env(config) do
51 | port = System.get_env("PORT") || raise "expected the PORT environment variable to be set"
52 | {:ok, Keyword.put(config, :http, [:inet6, port: port])}
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/lib/talon_example/web/web.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Web do
2 | @moduledoc """
3 | A module that keeps using definitions for controllers,
4 | views and so on.
5 |
6 | This can be used in your application as:
7 |
8 | use TalonExample.Web, :controller
9 | use TalonExample.Web, :view
10 |
11 | The definitions below will be executed for every view,
12 | controller, etc, so keep them short and clean, focused
13 | on imports, uses and aliases.
14 |
15 | Do NOT define functions inside the quoted expressions
16 | below.
17 | """
18 |
19 | def controller do
20 | quote do
21 | use Phoenix.Controller, namespace: TalonExample.Web
22 | import Plug.Conn
23 | import TalonExample.Web.Router.Helpers
24 | import TalonExample.Web.Gettext
25 | end
26 | end
27 |
28 | def view do
29 | quote do
30 | use Phoenix.View, root: "lib/talon_example/web/templates",
31 | namespace: TalonExample.Web
32 |
33 | # Import convenience functions from controllers
34 | import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
35 |
36 | # Use all HTML functionality (forms, tags, etc)
37 | use Phoenix.HTML
38 |
39 | import TalonExample.Web.Router.Helpers
40 | import TalonExample.Web.ErrorHelpers
41 | import TalonExample.Web.Gettext
42 | end
43 | end
44 |
45 | def router do
46 | quote do
47 | use Phoenix.Router
48 | import Plug.Conn
49 | import Phoenix.Controller
50 | end
51 | end
52 |
53 | def channel do
54 | quote do
55 | use Phoenix.Channel
56 | import TalonExample.Web.Gettext
57 | end
58 | end
59 |
60 | @doc """
61 | When used, dispatch to the appropriate controller/view/etc.
62 | """
63 | defmacro __using__(which) when is_atom(which) do
64 | apply(__MODULE__, which, [])
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/components/datatable/datatable.html.slim:
--------------------------------------------------------------------------------
1 | - talon_resource = Talon.View.talon_resource(@conn)
2 | .box
3 | .box-header
4 | .row
5 | .col-sm-9
6 | .col-sm-3
7 | button#go-search.btn.btn-xs.btn-primary type="button" style="float: right; margin-left: 5px;" Go
8 | input#search type="search" placeholder="Search" style="float: right;"
9 | .box-body.table-responsived.no-padding
10 | .paginated_collection
11 | table#datatables.table.table-hover.table-striped.index_table
12 | thead
13 | tr
14 | th.slectable
15 | .resource_selection_toggle_cell
16 | input.toggle_all#collection_selection_toggle_all type="checkbox" name="collection_selection_toggle_all"
17 | = for name <- talon_resource.display_schema_columns(:index) do
18 | th class="sortable #{sort_column_class(@conn, name)}"
19 | a href="#{column_sort_link(@conn, name)}" = talon_resource.render_column_name(:index, name)
20 | th.actions.disable-sorting.text-right data-orderable="false" Actions
21 | tbody#datatable-body data-route="#{talon_resource.route_name()}"
22 | = render "table_body.html", resources: @resources, conn: @conn
23 | tfoot
24 | tr
25 | th.selectable
26 | .resource_selection_toggle_cell
27 | input.toggle_all#collection_selection_toggle_all type="checkbox" name="collection_selection_toggle_all"
28 | = for name <- talon_resource.display_schema_columns(:index) do
29 | th = talon_resource.render_column_name(:index, name)
30 |
31 | th.text-right data-orderable="false" Actions
32 |
33 | .box-footer.clear-fix
34 | = AdminLte.Web.PaginateView.paginate(@conn)
35 |
--------------------------------------------------------------------------------
/config/dev.exs:
--------------------------------------------------------------------------------
1 | use Mix.Config
2 |
3 | # For development, we disable any cache and enable
4 | # debugging and code reloading.
5 | #
6 | # The watchers configuration can be used to run external
7 | # watchers to your application. For example, we use it
8 | # with brunch.io to recompile .js and .css sources.
9 | config :talon_example, TalonExample.Web.Endpoint,
10 | http: [port: 4000],
11 | debug_errors: true,
12 | code_reloader: true,
13 | check_origin: false,
14 | watchers: [node: ["node_modules/.bin/brunch", "watch", "--stdin",
15 | cd: Path.expand("../assets", __DIR__)]]
16 |
17 | # ## SSL Support
18 | #
19 | # In order to use HTTPS in development, a self-signed
20 | # certificate can be generated by running the following
21 | # command from your terminal:
22 | #
23 | # 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
24 | #
25 | # The `http:` config above can be replaced with:
26 | #
27 | # https: [port: 4000, keyfile: "priv/server.key", certfile: "priv/server.pem"],
28 | #
29 | # If desired, both `http:` and `https:` keys can be
30 | # configured to run both http and https servers on
31 | # different ports.
32 |
33 | # Watch static and templates for browser reloading.
34 | config :talon_example, TalonExample.Web.Endpoint,
35 | live_reload: [
36 | patterns: [
37 | ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},
38 | ~r{priv/gettext/.*(po)$},
39 | ~r{lib/talon_example/web/views/.*(ex)$},
40 | ~r{lib/talon_example/web/templates/.*(eex)$}
41 | ]
42 | ]
43 |
44 | # Do not include metadata nor timestamps in development logs
45 | config :logger, :console, format: "[$level] $message\n"
46 |
47 | # Set a higher stacktrace during development. Avoid configuring such
48 | # in production as building large stacktraces may be expensive.
49 | config :phoenix, :stacktrace_depth, 20
50 |
51 | # Configure your database
52 | config :talon_example, TalonExample.Repo,
53 | adapter: Ecto.Adapters.Postgres,
54 | username: "postgres",
55 | password: "postgres",
56 | database: "talon_example_dev",
57 | hostname: "localhost",
58 | pool_size: 10
59 |
--------------------------------------------------------------------------------
/lib/talon_example/talon/talon.ex:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.Talon do
2 | @moduledoc """
3 | Admmin Endpoint/Context.
4 |
5 | This module contains a number of default functions which are all
6 | overridable. It is also used for namespacing configuration.
7 |
8 | You may create multiple instances of the module if you would like to
9 | have different behaviour for an Talon managed front end an a separate
10 | Talon managed backend.
11 |
12 | If you are using Talon in differnt umbrella apps, create a separate
13 | module (differnt name) for each app that uses Talon.
14 | """
15 | use Talon, otp_app: :talon_example
16 |
17 | # Here is a list of the functions that you can override:
18 |
19 | # @spec base() :: atom
20 | # def base, do: @__base__
21 |
22 | # @spec repo() :: atom
23 | # def repo, do: @__repo__
24 |
25 | # def resource_map, do: @__resource_map__
26 |
27 | # def resources, do: @__resources__
28 |
29 | # def resource_names, do: @__resource_map__ |> Map.keys
30 |
31 | # def schema(resource_name) do
32 | # talon_resource(resource_name).schema()
33 | # end
34 |
35 | # def schema_names do
36 | # resource_names()
37 | # |> Enum.map(fn name ->
38 | # name |> schema |> Module.split |> List.last
39 | # end)
40 | # end
41 |
42 | # def talon_resource(resource_name) when is_binary(resource_name) do
43 | # @__resource_map__[resource_name]
44 | # end
45 | # def talon_resource(struct) when is_atom(struct) do
46 | # @__resource_to_talon__[struct]
47 | # end
48 | # def talon_resource(resource) when is_map(resource) do
49 | # @__resource_to_talon__[resource.__struct__]
50 | # end
51 |
52 | # def resource_schema(resource_name) when is_binary(resource_name) do
53 | # {String.to_atom(resource_name), talon_resource(resource_name)}
54 | # end
55 |
56 | # def controller_action(resource_name) do
57 | # {resource_name, talon_resource} = resource_schema(resource_name)
58 | # schema = talon_resource.schema()
59 | # {schema, resource_name, Application.get_env(:talon, :theme)}
60 | # end
61 |
62 | # def template_path_name(resource_name) do
63 | # @__view_path_names__[resource_name]
64 | # end
65 |
66 |
67 | end
68 |
--------------------------------------------------------------------------------
/lib/talon_example/web/templates/talon/admin_lte/layout/app.html.slim:
--------------------------------------------------------------------------------
1 | doctype html
2 | html lang="en"
3 | head
4 | meta charset="utf-8"
5 | meta content="IE=edge" http-equiv="X-UA-Compatible"
6 | meta content="width=device-width, initial-scale=1" name="viewport"
7 | meta content="" name="description"
8 | meta content="" name="author"
9 | title Talon
10 | link rel="stylesheet" href="#{static_path(@conn, "/css/talon/admin_lte/talon.css")}"
11 | link href="http://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" rel="stylesheet"
12 | link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css"
13 |
14 | body.sidebar-mini.skin-purple.hold-transition.sidebar-mini
15 | #delete-form style="display: none"
16 | = link "", to: "", method: :delete, data: [confirm: "are you sure?"]
17 | .wrapper
18 | header.main-header
19 | a href="#" class="logo"
20 | span.logo-mini = raw "T F"
21 | span.logo-lg = raw "T alon"
22 | nav.navbar.navbar-static-top
23 | a.sidebar-toggle href="#" data-toggle="offcanvas" role="button"
24 | span.sr-only "Toggle navigation"
25 | .navbar-custom-menu
26 | ul.nav.navbar-nav
27 | li.dropdown.messages-menu
28 | = render "sidebar.html", conn: @conn, talon_resource: Talon.View.talon_resource(@conn)
29 | .content-wrapper style="min-height: 916px;"
30 | / TODO: Move this to a partial
31 | section.content-header#title_bar style="padding-left: 30px;"
32 | h1#page_title= Talon.View.talon_resource(@conn).index_card_title()
33 | ol.breadcrumb
34 | li
35 | a href="/talon"
36 | i.fa.fa-dashboard
37 | = "talon"
38 | section.content = render @view_module, @view_template, assigns
39 |
40 | footer.main-footer
41 |
42 | script src="#{static_path(@conn, "/js/talon/admin_lte/jquery-2.2.3.min.js")}"
43 | script src="#{static_path(@conn, "/js/talon/admin_lte/bootstrap.min.js")}"
44 | script src="#{static_path(@conn, "/js/talon/admin_lte/app.min.js")}"
45 | script src="#{static_path(@conn, "/js/talon/admin_lte/sweetalert.min.js")}"
46 | script src="#{static_path(@conn, "/js/app.js")}"
47 |
--------------------------------------------------------------------------------
/assets/js/socket.js:
--------------------------------------------------------------------------------
1 | // NOTE: The contents of this file will only be executed if
2 | // you uncomment its entry in "assets/js/app.js".
3 |
4 | // To use Phoenix channels, the first step is to import Socket
5 | // and connect at the socket path in "lib/web/endpoint.ex":
6 | import {Socket} from "phoenix"
7 |
8 | let socket = new Socket("/socket", {params: {token: window.userToken}})
9 |
10 | // When you connect, you'll often need to authenticate the client.
11 | // For example, imagine you have an authentication plug, `MyAuth`,
12 | // which authenticates the session and assigns a `:current_user`.
13 | // If the current user exists you can assign the user's token in
14 | // the connection for use in the layout.
15 | //
16 | // In your "lib/web/router.ex":
17 | //
18 | // pipeline :browser do
19 | // ...
20 | // plug MyAuth
21 | // plug :put_user_token
22 | // end
23 | //
24 | // defp put_user_token(conn, _) do
25 | // if current_user = conn.assigns[:current_user] do
26 | // token = Phoenix.Token.sign(conn, "user socket", current_user.id)
27 | // assign(conn, :user_token, token)
28 | // else
29 | // conn
30 | // end
31 | // end
32 | //
33 | // Now you need to pass this token to JavaScript. You can do so
34 | // inside a script tag in "lib/web/templates/layout/app.html.eex":
35 | //
36 | //
37 | //
38 | // You will need to verify the user token in the "connect/2" function
39 | // in "lib/web/channels/user_socket.ex":
40 | //
41 | // def connect(%{"token" => token}, socket) do
42 | // # max_age: 1209600 is equivalent to two weeks in seconds
43 | // case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do
44 | // {:ok, user_id} ->
45 | // {:ok, assign(socket, :user, user_id)}
46 | // {:error, reason} ->
47 | // :error
48 | // end
49 | // end
50 | //
51 | // Finally, pass the token on connect as below. Or remove it
52 | // from connect if you don't care about authentication.
53 |
54 | socket.connect()
55 |
56 | // Now that you are connected, you can join channels with a topic:
57 | let channel = socket.channel("topic:subtopic", {})
58 | channel.join()
59 | .receive("ok", resp => { console.log("Joined successfully", resp) })
60 | .receive("error", resp => { console.log("Unable to join", resp) })
61 |
62 | export default socket
63 |
--------------------------------------------------------------------------------
/config/prod.exs:
--------------------------------------------------------------------------------
1 | use Mix.Config
2 |
3 | # For production, we often load configuration from external
4 | # sources, such as your system environment. For this reason,
5 | # you won't find the :http configuration below, but set inside
6 | # TalonExample.Web.Endpoint.load_from_system_env/1 dynamically.
7 | # Any dynamic configuration should be moved to such function.
8 | #
9 | # Don't forget to configure the url host to something meaningful,
10 | # Phoenix uses this information when generating URLs.
11 | #
12 | # Finally, we also include the path to a cache manifest
13 | # containing the digested version of static files. This
14 | # manifest is generated by the mix phoenix.digest task
15 | # which you typically run after static files are built.
16 | config :talon_example, TalonExample.Web.Endpoint,
17 | on_init: {TalonExample.Web.Endpoint, :load_from_system_env, []},
18 | url: [host: "example.com", port: 80],
19 | cache_static_manifest: "priv/static/cache_manifest.json"
20 |
21 | # Do not print debug messages in production
22 | config :logger, level: :info
23 |
24 | # ## SSL Support
25 | #
26 | # To get SSL working, you will need to add the `https` key
27 | # to the previous section and set your `:url` port to 443:
28 | #
29 | # config :talon_example, TalonExample.Web.Endpoint,
30 | # ...
31 | # url: [host: "example.com", port: 443],
32 | # https: [:inet6,
33 | # port: 443,
34 | # keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
35 | # certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
36 | #
37 | # Where those two env variables return an absolute path to
38 | # the key and cert in disk or a relative path inside priv,
39 | # for example "priv/ssl/server.key".
40 | #
41 | # We also recommend setting `force_ssl`, ensuring no data is
42 | # ever sent via http, always redirecting to https:
43 | #
44 | # config :talon_example, TalonExample.Web.Endpoint,
45 | # force_ssl: [hsts: true]
46 | #
47 | # Check `Plug.SSL` for all available options in `force_ssl`.
48 |
49 | # ## Using releases
50 | #
51 | # If you are doing OTP releases, you need to instruct Phoenix
52 | # to start the server for all endpoints:
53 | #
54 | # config :phoenix, :serve_endpoints, true
55 | #
56 | # Alternatively, you can configure exactly which server to
57 | # start per endpoint:
58 | #
59 | # config :talon_example, TalonExample.Web.Endpoint, server: true
60 | #
61 |
62 | # Finally import the config/prod.secret.exs
63 | # which should be versioned separately.
64 | import_config "prod.secret.exs"
65 |
--------------------------------------------------------------------------------
/priv/gettext/en/LC_MESSAGES/errors.po:
--------------------------------------------------------------------------------
1 | ## `msgid`s in this file come from POT (.pot) files.
2 | ##
3 | ## Do not add, change, or remove `msgid`s manually here as
4 | ## they're tied to the ones in the corresponding POT file
5 | ## (with the same domain).
6 | ##
7 | ## Use `mix gettext.extract --merge` or `mix gettext.merge`
8 | ## to merge POT files into PO files.
9 | msgid ""
10 | msgstr ""
11 | "Language: en\n"
12 |
13 | ## From Ecto.Changeset.cast/4
14 | msgid "can't be blank"
15 | msgstr ""
16 |
17 | ## From Ecto.Changeset.unique_constraint/3
18 | msgid "has already been taken"
19 | msgstr ""
20 |
21 | ## From Ecto.Changeset.put_change/3
22 | msgid "is invalid"
23 | msgstr ""
24 |
25 | ## From Ecto.Changeset.validate_format/3
26 | msgid "has invalid format"
27 | msgstr ""
28 |
29 | ## From Ecto.Changeset.validate_subset/3
30 | msgid "has an invalid entry"
31 | msgstr ""
32 |
33 | ## From Ecto.Changeset.validate_exclusion/3
34 | msgid "is reserved"
35 | msgstr ""
36 |
37 | ## From Ecto.Changeset.validate_confirmation/3
38 | msgid "does not match confirmation"
39 | msgstr ""
40 |
41 | ## From Ecto.Changeset.no_assoc_constraint/3
42 | msgid "is still associated with this entry"
43 | msgstr ""
44 |
45 | msgid "are still associated with this entry"
46 | msgstr ""
47 |
48 | ## From Ecto.Changeset.validate_length/3
49 | msgid "should be %{count} character(s)"
50 | msgid_plural "should be %{count} character(s)"
51 | msgstr[0] ""
52 | msgstr[1] ""
53 |
54 | msgid "should have %{count} item(s)"
55 | msgid_plural "should have %{count} item(s)"
56 | msgstr[0] ""
57 | msgstr[1] ""
58 |
59 | msgid "should be at least %{count} character(s)"
60 | msgid_plural "should be at least %{count} character(s)"
61 | msgstr[0] ""
62 | msgstr[1] ""
63 |
64 | msgid "should have at least %{count} item(s)"
65 | msgid_plural "should have at least %{count} item(s)"
66 | msgstr[0] ""
67 | msgstr[1] ""
68 |
69 | msgid "should be at most %{count} character(s)"
70 | msgid_plural "should be at most %{count} character(s)"
71 | msgstr[0] ""
72 | msgstr[1] ""
73 |
74 | msgid "should have at most %{count} item(s)"
75 | msgid_plural "should have at most %{count} item(s)"
76 | msgstr[0] ""
77 | msgstr[1] ""
78 |
79 | ## From Ecto.Changeset.validate_number/3
80 | msgid "must be less than %{number}"
81 | msgstr ""
82 |
83 | msgid "must be greater than %{number}"
84 | msgstr ""
85 |
86 | msgid "must be less than or equal to %{number}"
87 | msgstr ""
88 |
89 | msgid "must be greater than or equal to %{number}"
90 | msgstr ""
91 |
92 | msgid "must be equal to %{number}"
93 | msgstr ""
94 |
--------------------------------------------------------------------------------
/priv/gettext/errors.pot:
--------------------------------------------------------------------------------
1 | ## This file is a PO Template file.
2 | ##
3 | ## `msgid`s here are often extracted from source code.
4 | ## Add new translations manually only if they're dynamic
5 | ## translations that can't be statically extracted.
6 | ##
7 | ## Run `mix gettext.extract` to bring this file up to
8 | ## date. Leave `msgstr`s empty as changing them here as no
9 | ## effect: edit them in PO (`.po`) files instead.
10 |
11 | ## From Ecto.Changeset.cast/4
12 | msgid "can't be blank"
13 | msgstr ""
14 |
15 | ## From Ecto.Changeset.unique_constraint/3
16 | msgid "has already been taken"
17 | msgstr ""
18 |
19 | ## From Ecto.Changeset.put_change/3
20 | msgid "is invalid"
21 | msgstr ""
22 |
23 | ## From Ecto.Changeset.validate_format/3
24 | msgid "has invalid format"
25 | msgstr ""
26 |
27 | ## From Ecto.Changeset.validate_subset/3
28 | msgid "has an invalid entry"
29 | msgstr ""
30 |
31 | ## From Ecto.Changeset.validate_exclusion/3
32 | msgid "is reserved"
33 | msgstr ""
34 |
35 | ## From Ecto.Changeset.validate_confirmation/3
36 | msgid "does not match confirmation"
37 | msgstr ""
38 |
39 | ## From Ecto.Changeset.no_assoc_constraint/3
40 | msgid "is still associated with this entry"
41 | msgstr ""
42 |
43 | msgid "are still associated with this entry"
44 | msgstr ""
45 |
46 | ## From Ecto.Changeset.validate_length/3
47 | msgid "should be %{count} character(s)"
48 | msgid_plural "should be %{count} character(s)"
49 | msgstr[0] ""
50 | msgstr[1] ""
51 |
52 | msgid "should have %{count} item(s)"
53 | msgid_plural "should have %{count} item(s)"
54 | msgstr[0] ""
55 | msgstr[1] ""
56 |
57 | msgid "should be at least %{count} character(s)"
58 | msgid_plural "should be at least %{count} character(s)"
59 | msgstr[0] ""
60 | msgstr[1] ""
61 |
62 | msgid "should have at least %{count} item(s)"
63 | msgid_plural "should have at least %{count} item(s)"
64 | msgstr[0] ""
65 | msgstr[1] ""
66 |
67 | msgid "should be at most %{count} character(s)"
68 | msgid_plural "should be at most %{count} character(s)"
69 | msgstr[0] ""
70 | msgstr[1] ""
71 |
72 | msgid "should have at most %{count} item(s)"
73 | msgid_plural "should have at most %{count} item(s)"
74 | msgstr[0] ""
75 | msgstr[1] ""
76 |
77 | ## From Ecto.Changeset.validate_number/3
78 | msgid "must be less than %{number}"
79 | msgstr ""
80 |
81 | msgid "must be greater than %{number}"
82 | msgstr ""
83 |
84 | msgid "must be less than or equal to %{number}"
85 | msgstr ""
86 |
87 | msgid "must be greater than or equal to %{number}"
88 | msgstr ""
89 |
90 | msgid "must be equal to %{number}"
91 | msgstr ""
92 |
--------------------------------------------------------------------------------
/lib/talon_example/web/talon_web.ex:
--------------------------------------------------------------------------------
1 | defmodule Talon.Web do
2 | @moduledoc """
3 | A module defining __using__ hooks for controllers,
4 | views and so on.
5 |
6 | This can be used in your application as:
7 |
8 | use Talon.Web, :controller
9 | use Talon.Web, :view
10 |
11 | The definitions below will be executed for every view,
12 | controller, etc, so keep them short and clean, focused
13 | on imports, uses and aliases.
14 |
15 | Do NOT define functions inside the quoted expressions
16 | below.
17 | """
18 |
19 | def model(_) do
20 | quote do
21 | use Ecto.Schema
22 |
23 | import Ecto
24 | import Ecto.Changeset
25 | import Ecto.Query
26 |
27 | def all, do: TalonExample.Repo.all(__MODULE__)
28 | end
29 | end
30 |
31 | def controller(_) do
32 | quote do
33 | use Phoenix.Controller
34 |
35 | alias TalonExample.Repo
36 | import Ecto
37 | import Ecto.Query
38 |
39 | import TalonExample.Web.Router.Helpers
40 | import TalonExample.Web.Gettext
41 | end
42 | end
43 |
44 | def view(opts) do
45 | quote do
46 | opts = unquote(opts)
47 | use Phoenix.View, root: "lib/talon_example/web/templates/talon/#{opts[:theme]}", namespace: opts[:module]
48 |
49 | # Import convenience functions from controllers
50 | import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
51 |
52 | # Use all HTML functionality (forms, tags, etc)
53 | use Phoenix.HTML
54 |
55 | import TalonExample.Web.Router.Helpers
56 | import TalonExample.Web.ErrorHelpers
57 | import TalonExample.Web.Gettext
58 | end
59 | end
60 |
61 | def component_view(opts) do
62 | quote do
63 | opts = unquote(opts)
64 | use Phoenix.View, root: "lib/talon_example/web/templates/talon/#{opts[:theme]}/components", namespace: opts[:module]
65 |
66 | # Import convenience functions from controllers
67 | import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
68 |
69 | # Use all HTML functionality (forms, tags, etc)
70 | use Phoenix.HTML
71 |
72 | import TalonExample.Web.Router.Helpers
73 | import TalonExample.Web.ErrorHelpers
74 | import TalonExample.Web.Gettext
75 | end
76 | end
77 |
78 | def router(_) do
79 | quote do
80 | use Phoenix.Router
81 | end
82 | end
83 |
84 | def channel(_) do
85 | quote do
86 | use Phoenix.Channel
87 |
88 | alias TalonExample.Repo
89 | import Ecto
90 | import Ecto.Query
91 | import TalonExample.Web.Gettext
92 | end
93 | end
94 |
95 | @doc """
96 | When used, dispatch to the appropriate controller/view/etc.
97 | """
98 | defmacro __using__(which) when is_atom(which) do
99 | apply(__MODULE__, which, [])
100 | end
101 | defmacro __using__(opts) when is_list(opts) do
102 | apply(__MODULE__, opts[:which], [opts])
103 | end
104 | end
105 |
--------------------------------------------------------------------------------
/assets/brunch-config.js:
--------------------------------------------------------------------------------
1 | exports.config = {
2 | // See http://brunch.io/#documentation for docs.
3 | files: {
4 | javascripts: {
5 | joinTo: {
6 | 'js/app.js': /^(js)|(node_modules)/,
7 | 'js/talon/admin_lte/jquery-2.2.3.min.js': 'vendor/talon/admin-lte/plugins/jQuery/jquery-2.2.3.min.js',
8 | 'js/talon/admin_lte/bootstrap.min.js': 'vendor/talon/admin-lte/bootstrap/js/bootstrap.min.js',
9 | 'js/talon/admin_lte/app.min.js': 'vendor/talon/admin-lte/dist/js/app.min.js',
10 | 'js/talon/admin_lte/sweetalert.min.js': 'vendor/talon/admin-lte/plugins/sweetalert/dist/sweetalert.min.js'
11 | }
12 | },
13 | stylesheets: {
14 | joinTo: {
15 | 'css/app.css': /^(css)/,
16 | 'css/talon/admin_lte/talon.css': [
17 | 'css/talon/admin-lte/talon.css',
18 | 'vendor/talon/admin-lte/dist/css/skins/all-skins.css',
19 | 'vendor/talon/admin-lte/bootstrap/css/bootstrap.min.css',
20 | 'vendor/talon/admin-lte/dist/css/AdminLTE.min.css',
21 | 'vendor/talon/admin-lte/plugins/sweetalert/dist/sweetalert.css'
22 | ]
23 | }
24 | },
25 | templates: {
26 | joinTo: "js/app.js"
27 | }
28 | },
29 |
30 | conventions: {
31 | // This option sets where we should place non-css and non-js assets in.
32 | // By default, we set this to "/assets/static". Files in this directory
33 | // will be copied to `paths.public`, which is "priv/static" by default.
34 | assets: /^(static)/
35 | },
36 |
37 | // Phoenix paths configuration
38 | paths: {
39 | // Dependencies and current project directories to watch
40 | watched: ["static", "css", "js", "vendor"],
41 | // Where to compile files to
42 | public: "../priv/static"
43 | },
44 |
45 | // Configure your plugins
46 | plugins: {
47 | babel: {
48 | // Do not use ES6 compiler in vendor code
49 | ignore: [/vendor/]
50 | }
51 | },
52 |
53 | modules: {
54 | autoRequire: {
55 | "js/app.js": ["js/app", "js/talon/admin-lte/talon"]
56 | }
57 | },
58 |
59 | npm: {
60 | enabled: true
61 | }
62 | };
63 |
64 | // To add the Talon generated assets to your brunch build, do the following:
65 | //
66 | // Replace
67 | //
68 | // javascripts: {
69 | // joinTo: "js/app.js"
70 | // },
71 | //
72 | // With
73 | //
74 | // javascripts: {
75 | // joinTo: {
76 | // 'js/app.js': /^(js)|(node_modules)/,
77 | // 'js/talon/admin_lte/jquery-2.2.3.min.js': 'vendor/talon/admin-lte/plugins/jQuery/jquery-2.2.3.min.js',
78 | // 'js/talon/admin_lte/bootstrap.min.js': 'vendor/talon/admin-lte/bootstrap/js/bootstrap.min.js',
79 | // 'js/talon/admin_lte/app.min.js': 'vendor/talon/admin-lte/dist/js/app.min.js',
80 | // 'js/talon/admin_lte/sweetalert.min.js': 'vendor/talon/admin-lte/plugins/sweetalert/dist/sweetalert.min.js'
81 | // }
82 | // },
83 | //
84 | // Replace
85 | //
86 | // stylesheets: {
87 | // joinTo: "css/app.css"
88 | // },
89 | //
90 | // With
91 | //
92 | // stylesheets: {
93 | // joinTo: {
94 | // 'css/app.css': /^(css)/,
95 | // 'css/talon/admin_lte/talon.css': [
96 | // 'css/talon/admin-lte/talon.css',
97 | // 'vendor/talon/admin-lte/dist/css/skins/all-skins.css',
98 | // 'vendor/talon/admin-lte/bootstrap/css/bootstrap.min.css',
99 | // 'vendor/talon/admin-lte/dist/css/AdminLTE.min.css',
100 | // 'vendor/talon/admin-lte/plugins/sweetalert/dist/sweetalert.css'
101 | // ]
102 | // }
103 | // },
104 | //
105 | // Replace
106 | //
107 | // autoRequire: {
108 | // "js/app.js": ["js/app"]
109 | // }
110 | //
111 | // With
112 | //
113 | // autoRequire: {
114 | // "js/app.js": ["js/app", "js/talon/admin-lte/talon"]
115 | // }
116 |
117 |
--------------------------------------------------------------------------------
/test/talon_example/blogs/blogs_test.exs:
--------------------------------------------------------------------------------
1 | defmodule TalonExample.BlogsTest do
2 | use TalonExample.DataCase
3 |
4 | alias TalonExample.Blogs
5 |
6 | describe "blogs" do
7 | alias TalonExample.Blogs.Blog
8 |
9 | @valid_attrs %{name: "some name"}
10 | @update_attrs %{name: "some updated name"}
11 | @invalid_attrs %{name: nil}
12 |
13 | def blog_fixture(attrs \\ %{}) do
14 | {:ok, blog} =
15 | attrs
16 | |> Enum.into(@valid_attrs)
17 | |> Blogs.create_blog()
18 |
19 | blog
20 | end
21 |
22 | test "list_blogs/0 returns all blogs" do
23 | blog = blog_fixture()
24 | assert Blogs.list_blogs() == [blog]
25 | end
26 |
27 | test "get_blog!/1 returns the blog with given id" do
28 | blog = blog_fixture()
29 | assert Blogs.get_blog!(blog.id) == blog
30 | end
31 |
32 | test "create_blog/1 with valid data creates a blog" do
33 | assert {:ok, %Blog{} = blog} = Blogs.create_blog(@valid_attrs)
34 | assert blog.name == "some name"
35 | end
36 |
37 | test "create_blog/1 with invalid data returns error changeset" do
38 | assert {:error, %Ecto.Changeset{}} = Blogs.create_blog(@invalid_attrs)
39 | end
40 |
41 | test "update_blog/2 with valid data updates the blog" do
42 | blog = blog_fixture()
43 | assert {:ok, blog} = Blogs.update_blog(blog, @update_attrs)
44 | assert %Blog{} = blog
45 | assert blog.name == "some updated name"
46 | end
47 |
48 | test "update_blog/2 with invalid data returns error changeset" do
49 | blog = blog_fixture()
50 | assert {:error, %Ecto.Changeset{}} = Blogs.update_blog(blog, @invalid_attrs)
51 | assert blog == Blogs.get_blog!(blog.id)
52 | end
53 |
54 | test "delete_blog/1 deletes the blog" do
55 | blog = blog_fixture()
56 | assert {:ok, %Blog{}} = Blogs.delete_blog(blog)
57 | assert_raise Ecto.NoResultsError, fn -> Blogs.get_blog!(blog.id) end
58 | end
59 |
60 | test "change_blog/1 returns a blog changeset" do
61 | blog = blog_fixture()
62 | assert %Ecto.Changeset{} = Blogs.change_blog(blog)
63 | end
64 | end
65 |
66 | describe "posts" do
67 | alias TalonExample.Blogs.Post
68 |
69 | @valid_attrs %{body: "some body", title: "some title"}
70 | @update_attrs %{body: "some updated body", title: "some updated title"}
71 | @invalid_attrs %{body: nil, title: nil}
72 |
73 | def post_fixture(attrs \\ %{}) do
74 | {:ok, post} =
75 | attrs
76 | |> Enum.into(@valid_attrs)
77 | |> Blogs.create_post()
78 |
79 | post
80 | end
81 |
82 | test "list_posts/0 returns all posts" do
83 | post = post_fixture()
84 | assert Blogs.list_posts() == [post]
85 | end
86 |
87 | test "get_post!/1 returns the post with given id" do
88 | post = post_fixture()
89 | assert Blogs.get_post!(post.id) == post
90 | end
91 |
92 | test "create_post/1 with valid data creates a post" do
93 | assert {:ok, %Post{} = post} = Blogs.create_post(@valid_attrs)
94 | assert post.body == "some body"
95 | assert post.title == "some title"
96 | end
97 |
98 | test "create_post/1 with invalid data returns error changeset" do
99 | assert {:error, %Ecto.Changeset{}} = Blogs.create_post(@invalid_attrs)
100 | end
101 |
102 | test "update_post/2 with valid data updates the post" do
103 | post = post_fixture()
104 | assert {:ok, post} = Blogs.update_post(post, @update_attrs)
105 | assert %Post{} = post
106 | assert post.body == "some updated body"
107 | assert post.title == "some updated title"
108 | end
109 |
110 | test "update_post/2 with invalid data returns error changeset" do
111 | post = post_fixture()
112 | assert {:error, %Ecto.Changeset{}} = Blogs.update_post(post, @invalid_attrs)
113 | assert post == Blogs.get_post!(post.id)
114 | end
115 |
116 | test "delete_post/1 deletes the post" do
117 | post = post_fixture()
118 | assert {:ok, %Post{}} = Blogs.delete_post(post)
119 | assert_raise Ecto.NoResultsError, fn -> Blogs.get_post!(post.id) end
120 | end
121 |
122 | test "change_post/1 returns a post changeset" do
123 | post = post_fixture()
124 | assert %Ecto.Changeset{} = Blogs.change_post(post)
125 | end
126 | end
127 | end
128 |
--------------------------------------------------------------------------------
/mix.lock:
--------------------------------------------------------------------------------
1 | %{"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []},
2 | "cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, optional: false]}]},
3 | "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
4 | "db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]},
5 | "decimal": {:hex, :decimal, "1.3.1", "157b3cedb2bfcb5359372a7766dd7a41091ad34578296e951f58a946fcab49c6", [:mix], []},
6 | "ecto": {:hex, :ecto, "2.1.4", "d1ba932813ec0e0d9db481ef2c17777f1cefb11fc90fa7c142ff354972dfba7e", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]},
7 | "ecto_talon": {:git, "https://github.com/talonframework/ecto_talon.git", "a56dae3dce4e816078947c3fc5c263c08fc13746", []},
8 | "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []},
9 | "gettext": {:hex, :gettext, "0.13.1", "5e0daf4e7636d771c4c71ad5f3f53ba09a9ae5c250e1ab9c42ba9edccc476263", [:mix], []},
10 | "inflex": {:hex, :inflex, "1.8.1", "9fa9684ff1a872eab7415c0be500cc1b7782f28da6ed75423081e75f92831b1c", [:mix], []},
11 | "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []},
12 | "phoenix": {:hex, :phoenix, "1.3.0-rc.2", "53104ada25ba85fe160268c0dc826fe038bc074293730b4522fb9aca28d8aa13", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.3.2 or ~> 1.4", [hex: :plug, optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, optional: false]}]},
13 | "phoenix_ecto": {:hex, :phoenix_ecto, "3.2.3", "450c749876ff1de4a78fdb305a142a76817c77a1cd79aeca29e5fc9a6c630b26", [:mix], [{:ecto, "~> 2.1", [hex: :ecto, optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, optional: true]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]},
14 | "phoenix_html": {:hex, :phoenix_html, "2.9.3", "1b5a2122cbf743aa242f54dced8a4f1cc778b8bd304f4b4c0043a6250c58e258", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]},
15 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.8", "4333f9c74190f485a74866beff2f9304f069d53f047f5fbb0fb8d1ee4c495f73", [:mix], [{:fs, "~> 0.9.1", [hex: :fs, optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2-rc", [hex: :phoenix, optional: false]}]},
16 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.1", "c10ddf6237007c804bf2b8f3c4d5b99009b42eca3a0dfac04ea2d8001186056a", [:mix], []},
17 | "phoenix_slime": {:git, "https://github.com/slime-lang/phoenix_slime.git", "da5b9dc54e765584a8235fdb08c5f4a13f6e9817", []},
18 | "plug": {:hex, :plug, "1.3.5", "7503bfcd7091df2a9761ef8cecea666d1f2cc454cbbaf0afa0b6e259203b7031", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]},
19 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []},
20 | "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], []},
21 | "postgrex": {:hex, :postgrex, "0.13.2", "2b88168fc6a5456a27bfb54ccf0ba4025d274841a7a3af5e5deb1b755d95154e", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}]},
22 | "ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [:rebar3], []},
23 | "scrivener": {:hex, :scrivener, "2.3.0", "16b1d744202d47233798205447b35592d96a209241c566304f84ddef63c718b2", [:mix], []},
24 | "scrivener_ecto": {:hex, :scrivener_ecto, "1.2.2", "811e4016dc19836a12c746989d2b9b495c3a9e739a2531c4b58226219109ff51", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}, {:postgrex, "~> 0.11.0 or ~> 0.12.0 or ~> 0.13.0", [hex: :postgrex, optional: true]}, {:scrivener, "~> 2.3", [hex: :scrivener, optional: false]}]},
25 | "slime": {:hex, :slime, "0.16.0", "4f9c677ca37b2817cd10422ecb42c524fe904d3630acf242b81dfe189900272a", [:mix], []}}
26 |
--------------------------------------------------------------------------------
/assets/vendor/talon/admin-lte/dist/js/app.min.js:
--------------------------------------------------------------------------------
1 | /*! AdminLTE app.js
2 | * ================
3 | * Main JS application file for AdminLTE v2. This file
4 | * should be included in all pages. It controls some layout
5 | * options and implements exclusive AdminLTE plugins.
6 | *
7 | * @Author Almsaeed Studio
8 | * @Support
9 | * @Email
10 | * @version 2.3.8
11 | * @license MIT
12 | */
13 | function _init(){"use strict";$.AdminLTE.layout={activate:function(){var a=this;a.fix(),a.fixSidebar(),$("body, html, .wrapper").css("height","auto"),$(window,".wrapper").resize(function(){a.fix(),a.fixSidebar()})},fix:function(){$(".layout-boxed > .wrapper").css("overflow","hidden");var a=$(".main-footer").outerHeight()||0,b=$(".main-header").outerHeight()+a,c=$(window).height(),d=$(".sidebar").height()||0;if($("body").hasClass("fixed"))$(".content-wrapper, .right-side").css("min-height",c-a);else{var e;c>=d?($(".content-wrapper, .right-side").css("min-height",c-b),e=c-b):($(".content-wrapper, .right-side").css("min-height",d),e=d);var f=$($.AdminLTE.options.controlSidebarOptions.selector);"undefined"!=typeof f&&f.height()>e&&$(".content-wrapper, .right-side").css("min-height",f.height())}},fixSidebar:function(){return $("body").hasClass("fixed")?("undefined"==typeof $.fn.slimScroll&&window.console&&window.console.error("Error: the fixed layout requires the slimscroll plugin!"),void($.AdminLTE.options.sidebarSlimScroll&&"undefined"!=typeof $.fn.slimScroll&&($(".sidebar").slimScroll({destroy:!0}).height("auto"),$(".sidebar").slimScroll({height:$(window).height()-$(".main-header").height()+"px",color:"rgba(0,0,0,0.2)",size:"3px"})))):void("undefined"!=typeof $.fn.slimScroll&&$(".sidebar").slimScroll({destroy:!0}).height("auto"))}},$.AdminLTE.pushMenu={activate:function(a){var b=$.AdminLTE.options.screenSizes;$(document).on("click",a,function(a){a.preventDefault(),$(window).width()>b.sm-1?$("body").hasClass("sidebar-collapse")?$("body").removeClass("sidebar-collapse").trigger("expanded.pushMenu"):$("body").addClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").hasClass("sidebar-open")?$("body").removeClass("sidebar-open").removeClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").addClass("sidebar-open").trigger("expanded.pushMenu")}),$(".content-wrapper").click(function(){$(window).width()<=b.sm-1&&$("body").hasClass("sidebar-open")&&$("body").removeClass("sidebar-open")}),($.AdminLTE.options.sidebarExpandOnHover||$("body").hasClass("fixed")&&$("body").hasClass("sidebar-mini"))&&this.expandOnHover()},expandOnHover:function(){var a=this,b=$.AdminLTE.options.screenSizes.sm-1;$(".main-sidebar").hover(function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-collapse")&&$(window).width()>b&&a.expand()},function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-expanded-on-hover")&&$(window).width()>b&&a.collapse()})},expand:function(){$("body").removeClass("sidebar-collapse").addClass("sidebar-expanded-on-hover")},collapse:function(){$("body").hasClass("sidebar-expanded-on-hover")&&$("body").removeClass("sidebar-expanded-on-hover").addClass("sidebar-collapse")}},$.AdminLTE.tree=function(a){var b=this,c=$.AdminLTE.options.animationSpeed;$(document).off("click",a+" li a").on("click",a+" li a",function(a){var d=$(this),e=d.next();if(e.is(".treeview-menu")&&e.is(":visible")&&!$("body").hasClass("sidebar-collapse"))e.slideUp(c,function(){e.removeClass("menu-open")}),e.parent("li").removeClass("active");else if(e.is(".treeview-menu")&&!e.is(":visible")){var f=d.parents("ul").first(),g=f.find("ul:visible").slideUp(c);g.removeClass("menu-open");var h=d.parent("li");e.slideDown(c,function(){e.addClass("menu-open"),f.find("li.active").removeClass("active"),h.addClass("active"),b.layout.fix()})}e.is(".treeview-menu")&&a.preventDefault()})},$.AdminLTE.controlSidebar={activate:function(){var a=this,b=$.AdminLTE.options.controlSidebarOptions,c=$(b.selector),d=$(b.toggleBtnSelector);d.on("click",function(d){d.preventDefault(),c.hasClass("control-sidebar-open")||$("body").hasClass("control-sidebar-open")?a.close(c,b.slide):a.open(c,b.slide)});var e=$(".control-sidebar-bg");a._fix(e),$("body").hasClass("fixed")?a._fixForFixed(c):$(".content-wrapper, .right-side").height() .box-body, > .box-footer, > form >.box-body, > form > .box-footer");c.hasClass("collapsed-box")?(a.children(":first").removeClass(b.icons.open).addClass(b.icons.collapse),d.slideDown(b.animationSpeed,function(){c.removeClass("collapsed-box")})):(a.children(":first").removeClass(b.icons.collapse).addClass(b.icons.open),d.slideUp(b.animationSpeed,function(){c.addClass("collapsed-box")}))},remove:function(a){var b=a.parents(".box").first();b.slideUp(this.animationSpeed)}}}if("undefined"==typeof jQuery)throw new Error("AdminLTE requires jQuery");$.AdminLTE={},$.AdminLTE.options={navbarMenuSlimscroll:!0,navbarMenuSlimscrollWidth:"3px",navbarMenuHeight:"200px",animationSpeed:500,sidebarToggleSelector:"[data-toggle='offcanvas']",sidebarPushMenu:!0,sidebarSlimScroll:!0,sidebarExpandOnHover:!1,enableBoxRefresh:!0,enableBSToppltip:!0,BSTooltipSelector:"[data-toggle='tooltip']",enableFastclick:!1,enableControlTreeView:!0,enableControlSidebar:!0,controlSidebarOptions:{toggleBtnSelector:"[data-toggle='control-sidebar']",selector:".control-sidebar",slide:!0},enableBoxWidget:!0,boxWidgetOptions:{boxWidgetIcons:{collapse:"fa-minus",open:"fa-plus",remove:"fa-times"},boxWidgetSelectors:{remove:'[data-widget="remove"]',collapse:'[data-widget="collapse"]'}},directChat:{enable:!0,contactToggleSelector:'[data-widget="chat-pane-toggle"]'},colors:{lightBlue:"#3c8dbc",red:"#f56954",green:"#00a65a",aqua:"#00c0ef",yellow:"#f39c12",blue:"#0073b7",navy:"#001F3F",teal:"#39CCCC",olive:"#3D9970",lime:"#01FF70",orange:"#FF851B",fuchsia:"#F012BE",purple:"#8E24AA",maroon:"#D81B60",black:"#222222",gray:"#d2d6de"},screenSizes:{xs:480,sm:768,md:992,lg:1200}},$(function(){"use strict";$("body").removeClass("hold-transition"),"undefined"!=typeof AdminLTEOptions&&$.extend(!0,$.AdminLTE.options,AdminLTEOptions);var a=$.AdminLTE.options;_init(),$.AdminLTE.layout.activate(),a.enableControlTreeView&&$.AdminLTE.tree(".sidebar"),a.enableControlSidebar&&$.AdminLTE.controlSidebar.activate(),a.navbarMenuSlimscroll&&"undefined"!=typeof $.fn.slimscroll&&$(".navbar .menu").slimscroll({height:a.navbarMenuHeight,alwaysVisible:!1,size:a.navbarMenuSlimscrollWidth}).css("width","100%"),a.sidebarPushMenu&&$.AdminLTE.pushMenu.activate(a.sidebarToggleSelector),a.enableBSToppltip&&$("body").tooltip({selector:a.BSTooltipSelector,container:"body"}),a.enableBoxWidget&&$.AdminLTE.boxWidget.activate(),a.enableFastclick&&"undefined"!=typeof FastClick&&FastClick.attach(document.body),a.directChat.enable&&$(document).on("click",a.directChat.contactToggleSelector,function(){var a=$(this).parents(".direct-chat").first();a.toggleClass("direct-chat-contacts-open")}),$('.btn-group[data-toggle="btn-toggle"]').each(function(){var a=$(this);$(this).find(".btn").on("click",function(b){a.find(".btn.active").removeClass("active"),$(this).addClass("active"),b.preventDefault()})})}),function(a){"use strict";a.fn.boxRefresh=function(b){function c(a){a.append(f),e.onLoadStart.call(a)}function d(a){a.find(f).remove(),e.onLoadDone.call(a)}var e=a.extend({trigger:".refresh-btn",source:"",onLoadStart:function(a){return a},onLoadDone:function(a){return a}},b),f=a('');return this.each(function(){if(""===e.source)return void(window.console&&window.console.log("Please specify a source first - boxRefresh()"));var b=a(this),f=b.find(e.trigger).first();f.on("click",function(a){a.preventDefault(),c(b),b.find(".box-body").load(e.source,function(){d(b)})})})}}(jQuery),function(a){"use strict";a.fn.activateBox=function(){a.AdminLTE.boxWidget.activate(this)},a.fn.toggleBox=function(){var b=a(a.AdminLTE.boxWidget.selectors.collapse,this);a.AdminLTE.boxWidget.collapse(b)},a.fn.removeBox=function(){var b=a(a.AdminLTE.boxWidget.selectors.remove,this);a.AdminLTE.boxWidget.remove(b)}}(jQuery),function(a){"use strict";a.fn.todolist=function(b){var c=a.extend({onCheck:function(a){return a},onUncheck:function(a){return a}},b);return this.each(function(){"undefined"!=typeof a.fn.iCheck?(a("input",this).on("ifChecked",function(){var b=a(this).parents("li").first();b.toggleClass("done"),c.onCheck.call(b)}),a("input",this).on("ifUnchecked",function(){var b=a(this).parents("li").first();b.toggleClass("done"),c.onUncheck.call(b)})):a("input",this).on("change",function(){var b=a(this).parents("li").first();b.toggleClass("done"),a("input",b).is(":checked")?c.onCheck.call(b):c.onUncheck.call(b)})})}}(jQuery);
--------------------------------------------------------------------------------
/assets/vendor/talon/admin-lte/plugins/sweetalert/dist/sweetalert.min.js:
--------------------------------------------------------------------------------
1 | !function(e,t,n){"use strict";!function o(e,t,n){function a(s,l){if(!t[s]){if(!e[s]){var i="function"==typeof require&&require;if(!l&&i)return i(s,!0);if(r)return r(s,!0);var u=new Error("Cannot find module '"+s+"'");throw u.code="MODULE_NOT_FOUND",u}var c=t[s]={exports:{}};e[s][0].call(c.exports,function(t){var n=e[s][1][t];return a(n?n:t)},c,c.exports,o,e,t,n)}return t[s].exports}for(var r="function"==typeof require&&require,s=0;s=0;)n=n.replace(" "+t+" "," ");e.className=n.replace(/^\s+|\s+$/g,"")}},i=function(e){var n=t.createElement("div");return n.appendChild(t.createTextNode(e)),n.innerHTML},u=function(e){e.style.opacity="",e.style.display="block"},c=function(e){if(e&&!e.length)return u(e);for(var t=0;t0?setTimeout(a,t):e.style.display="none"};o()},b=function(n){if("function"==typeof MouseEvent){var o=new MouseEvent("click",{view:e,bubbles:!1,cancelable:!0});n.dispatchEvent(o)}else if(t.createEvent){var a=t.createEvent("MouseEvents");a.initEvent("click",!1,!1),n.dispatchEvent(a)}else t.createEventObject?n.fireEvent("onclick"):"function"==typeof n.onclick&&n.onclick()},h=function(t){"function"==typeof t.stopPropagation?(t.stopPropagation(),t.preventDefault()):e.event&&e.event.hasOwnProperty("cancelBubble")&&(e.event.cancelBubble=!0)};a.hasClass=r,a.addClass=s,a.removeClass=l,a.escapeHtml=i,a._show=u,a.show=c,a._hide=d,a.hide=f,a.isDescendant=p,a.getTopMargin=m,a.fadeIn=v,a.fadeOut=y,a.fireClick=b,a.stopEventPropagation=h},{}],5:[function(t,o,a){Object.defineProperty(a,"__esModule",{value:!0});var r=t("./handle-dom"),s=t("./handle-swal-dom"),l=function(t,o,a){var l=t||e.event,i=l.keyCode||l.which,u=a.querySelector("button.confirm"),c=a.querySelector("button.cancel"),d=a.querySelectorAll("button[tabindex]");if(-1!==[9,13,32,27].indexOf(i)){for(var f=l.target||l.srcElement,p=-1,m=0;m"),i.innerHTML=e.html?e.text:(0,s.escapeHtml)(e.text||"").split("\n").join(" "),e.text&&(0,s.show)(i),e.customClass)(0,s.addClass)(t,e.customClass),t.setAttribute("data-custom-class",e.customClass);else{var d=t.getAttribute("data-custom-class");(0,s.removeClass)(t,d),t.setAttribute("data-custom-class","")}if((0,s.hide)(t.querySelectorAll(".sa-icon")),e.type&&!(0,a.isIE8)()){var f=function(){for(var o=!1,a=0;ao;o++)n=parseInt(e.substr(2*o,2),16),n=Math.round(Math.min(Math.max(0,n+n*t),255)).toString(16),a+=("00"+n).substr(n.length);return a};o.extend=a,o.hexToRgb=r,o.isIE8=s,o.logStr=l,o.colorLuminance=i},{}]},{},[1]),"function"==typeof define&&define.amd?define(function(){return sweetAlert}):"undefined"!=typeof module&&module.exports&&(module.exports=sweetAlert)}(window,document);
2 |
--------------------------------------------------------------------------------
/assets/vendor/talon/admin-lte/plugins/sweetalert/dist/sweetalert.css:
--------------------------------------------------------------------------------
1 | body.stop-scrolling {
2 | height: 100%;
3 | overflow: hidden; }
4 |
5 | .sweet-overlay {
6 | background-color: black;
7 | /* IE8 */
8 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
9 | /* IE8 */
10 | background-color: rgba(0, 0, 0, 0.4);
11 | position: fixed;
12 | left: 0;
13 | right: 0;
14 | top: 0;
15 | bottom: 0;
16 | display: none;
17 | z-index: 10000; }
18 |
19 | .sweet-alert {
20 | background-color: white;
21 | font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
22 | width: 478px;
23 | padding: 17px;
24 | border-radius: 5px;
25 | text-align: center;
26 | position: fixed;
27 | left: 50%;
28 | top: 50%;
29 | margin-left: -256px;
30 | margin-top: -200px;
31 | overflow: hidden;
32 | display: none;
33 | z-index: 99999; }
34 | @media all and (max-width: 540px) {
35 | .sweet-alert {
36 | width: auto;
37 | margin-left: 0;
38 | margin-right: 0;
39 | left: 15px;
40 | right: 15px; } }
41 | .sweet-alert h2 {
42 | color: #575757;
43 | font-size: 30px;
44 | text-align: center;
45 | font-weight: 600;
46 | text-transform: none;
47 | position: relative;
48 | margin: 25px 0;
49 | padding: 0;
50 | line-height: 40px;
51 | display: block; }
52 | .sweet-alert p {
53 | color: #797979;
54 | font-size: 16px;
55 | text-align: center;
56 | font-weight: 300;
57 | position: relative;
58 | text-align: inherit;
59 | float: none;
60 | margin: 0;
61 | padding: 0;
62 | line-height: normal; }
63 | .sweet-alert fieldset {
64 | border: none;
65 | position: relative; }
66 | .sweet-alert .sa-error-container {
67 | background-color: #f1f1f1;
68 | margin-left: -17px;
69 | margin-right: -17px;
70 | overflow: hidden;
71 | padding: 0 10px;
72 | max-height: 0;
73 | webkit-transition: padding 0.15s, max-height 0.15s;
74 | transition: padding 0.15s, max-height 0.15s; }
75 | .sweet-alert .sa-error-container.show {
76 | padding: 10px 0;
77 | max-height: 100px;
78 | webkit-transition: padding 0.2s, max-height 0.2s;
79 | transition: padding 0.25s, max-height 0.25s; }
80 | .sweet-alert .sa-error-container .icon {
81 | display: inline-block;
82 | width: 24px;
83 | height: 24px;
84 | border-radius: 50%;
85 | background-color: #ea7d7d;
86 | color: white;
87 | line-height: 24px;
88 | text-align: center;
89 | margin-right: 3px; }
90 | .sweet-alert .sa-error-container p {
91 | display: inline-block; }
92 | .sweet-alert .sa-input-error {
93 | position: absolute;
94 | top: 29px;
95 | right: 26px;
96 | width: 20px;
97 | height: 20px;
98 | opacity: 0;
99 | -webkit-transform: scale(0.5);
100 | transform: scale(0.5);
101 | -webkit-transform-origin: 50% 50%;
102 | transform-origin: 50% 50%;
103 | -webkit-transition: all 0.1s;
104 | transition: all 0.1s; }
105 | .sweet-alert .sa-input-error::before, .sweet-alert .sa-input-error::after {
106 | content: "";
107 | width: 20px;
108 | height: 6px;
109 | background-color: #f06e57;
110 | border-radius: 3px;
111 | position: absolute;
112 | top: 50%;
113 | margin-top: -4px;
114 | left: 50%;
115 | margin-left: -9px; }
116 | .sweet-alert .sa-input-error::before {
117 | -webkit-transform: rotate(-45deg);
118 | transform: rotate(-45deg); }
119 | .sweet-alert .sa-input-error::after {
120 | -webkit-transform: rotate(45deg);
121 | transform: rotate(45deg); }
122 | .sweet-alert .sa-input-error.show {
123 | opacity: 1;
124 | -webkit-transform: scale(1);
125 | transform: scale(1); }
126 | .sweet-alert input {
127 | width: 100%;
128 | box-sizing: border-box;
129 | border-radius: 3px;
130 | border: 1px solid #d7d7d7;
131 | height: 43px;
132 | margin-top: 10px;
133 | margin-bottom: 17px;
134 | font-size: 18px;
135 | box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.06);
136 | padding: 0 12px;
137 | display: none;
138 | -webkit-transition: all 0.3s;
139 | transition: all 0.3s; }
140 | .sweet-alert input:focus {
141 | outline: none;
142 | box-shadow: 0px 0px 3px #c4e6f5;
143 | border: 1px solid #b4dbed; }
144 | .sweet-alert input:focus::-moz-placeholder {
145 | transition: opacity 0.3s 0.03s ease;
146 | opacity: 0.5; }
147 | .sweet-alert input:focus:-ms-input-placeholder {
148 | transition: opacity 0.3s 0.03s ease;
149 | opacity: 0.5; }
150 | .sweet-alert input:focus::-webkit-input-placeholder {
151 | transition: opacity 0.3s 0.03s ease;
152 | opacity: 0.5; }
153 | .sweet-alert input::-moz-placeholder {
154 | color: #bdbdbd; }
155 | .sweet-alert input::-ms-clear {
156 | display: none; }
157 | .sweet-alert input:-ms-input-placeholder {
158 | color: #bdbdbd; }
159 | .sweet-alert input::-webkit-input-placeholder {
160 | color: #bdbdbd; }
161 | .sweet-alert.show-input input {
162 | display: block; }
163 | .sweet-alert .sa-confirm-button-container {
164 | display: inline-block;
165 | position: relative; }
166 | .sweet-alert .la-ball-fall {
167 | position: absolute;
168 | left: 50%;
169 | top: 50%;
170 | margin-left: -27px;
171 | margin-top: 4px;
172 | opacity: 0;
173 | visibility: hidden; }
174 | .sweet-alert button {
175 | background-color: #8CD4F5;
176 | color: white;
177 | border: none;
178 | box-shadow: none;
179 | font-size: 17px;
180 | font-weight: 500;
181 | -webkit-border-radius: 4px;
182 | border-radius: 5px;
183 | padding: 10px 32px;
184 | margin: 26px 5px 0 5px;
185 | cursor: pointer; }
186 | .sweet-alert button:focus {
187 | outline: none;
188 | box-shadow: 0 0 2px rgba(128, 179, 235, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.05); }
189 | .sweet-alert button:hover {
190 | background-color: #7ecff4; }
191 | .sweet-alert button:active {
192 | background-color: #5dc2f1; }
193 | .sweet-alert button.cancel {
194 | background-color: #C1C1C1; }
195 | .sweet-alert button.cancel:hover {
196 | background-color: #b9b9b9; }
197 | .sweet-alert button.cancel:active {
198 | background-color: #a8a8a8; }
199 | .sweet-alert button.cancel:focus {
200 | box-shadow: rgba(197, 205, 211, 0.8) 0px 0px 2px, rgba(0, 0, 0, 0.0470588) 0px 0px 0px 1px inset !important; }
201 | .sweet-alert button[disabled] {
202 | opacity: .6;
203 | cursor: default; }
204 | .sweet-alert button.confirm[disabled] {
205 | color: transparent; }
206 | .sweet-alert button.confirm[disabled] ~ .la-ball-fall {
207 | opacity: 1;
208 | visibility: visible;
209 | transition-delay: 0s; }
210 | .sweet-alert button::-moz-focus-inner {
211 | border: 0; }
212 | .sweet-alert[data-has-cancel-button=false] button {
213 | box-shadow: none !important; }
214 | .sweet-alert[data-has-confirm-button=false][data-has-cancel-button=false] {
215 | padding-bottom: 40px; }
216 | .sweet-alert .sa-icon {
217 | width: 80px;
218 | height: 80px;
219 | border: 4px solid gray;
220 | -webkit-border-radius: 40px;
221 | border-radius: 40px;
222 | border-radius: 50%;
223 | margin: 20px auto;
224 | padding: 0;
225 | position: relative;
226 | box-sizing: content-box; }
227 | .sweet-alert .sa-icon.sa-error {
228 | border-color: #F27474; }
229 | .sweet-alert .sa-icon.sa-error .sa-x-mark {
230 | position: relative;
231 | display: block; }
232 | .sweet-alert .sa-icon.sa-error .sa-line {
233 | position: absolute;
234 | height: 5px;
235 | width: 47px;
236 | background-color: #F27474;
237 | display: block;
238 | top: 37px;
239 | border-radius: 2px; }
240 | .sweet-alert .sa-icon.sa-error .sa-line.sa-left {
241 | -webkit-transform: rotate(45deg);
242 | transform: rotate(45deg);
243 | left: 17px; }
244 | .sweet-alert .sa-icon.sa-error .sa-line.sa-right {
245 | -webkit-transform: rotate(-45deg);
246 | transform: rotate(-45deg);
247 | right: 16px; }
248 | .sweet-alert .sa-icon.sa-warning {
249 | border-color: #F8BB86; }
250 | .sweet-alert .sa-icon.sa-warning .sa-body {
251 | position: absolute;
252 | width: 5px;
253 | height: 47px;
254 | left: 50%;
255 | top: 10px;
256 | -webkit-border-radius: 2px;
257 | border-radius: 2px;
258 | margin-left: -2px;
259 | background-color: #F8BB86; }
260 | .sweet-alert .sa-icon.sa-warning .sa-dot {
261 | position: absolute;
262 | width: 7px;
263 | height: 7px;
264 | -webkit-border-radius: 50%;
265 | border-radius: 50%;
266 | margin-left: -3px;
267 | left: 50%;
268 | bottom: 10px;
269 | background-color: #F8BB86; }
270 | .sweet-alert .sa-icon.sa-info {
271 | border-color: #C9DAE1; }
272 | .sweet-alert .sa-icon.sa-info::before {
273 | content: "";
274 | position: absolute;
275 | width: 5px;
276 | height: 29px;
277 | left: 50%;
278 | bottom: 17px;
279 | border-radius: 2px;
280 | margin-left: -2px;
281 | background-color: #C9DAE1; }
282 | .sweet-alert .sa-icon.sa-info::after {
283 | content: "";
284 | position: absolute;
285 | width: 7px;
286 | height: 7px;
287 | border-radius: 50%;
288 | margin-left: -3px;
289 | top: 19px;
290 | background-color: #C9DAE1;
291 | left: 50%; }
292 | .sweet-alert .sa-icon.sa-success {
293 | border-color: #A5DC86; }
294 | .sweet-alert .sa-icon.sa-success::before, .sweet-alert .sa-icon.sa-success::after {
295 | content: '';
296 | -webkit-border-radius: 40px;
297 | border-radius: 40px;
298 | border-radius: 50%;
299 | position: absolute;
300 | width: 60px;
301 | height: 120px;
302 | background: white;
303 | -webkit-transform: rotate(45deg);
304 | transform: rotate(45deg); }
305 | .sweet-alert .sa-icon.sa-success::before {
306 | -webkit-border-radius: 120px 0 0 120px;
307 | border-radius: 120px 0 0 120px;
308 | top: -7px;
309 | left: -33px;
310 | -webkit-transform: rotate(-45deg);
311 | transform: rotate(-45deg);
312 | -webkit-transform-origin: 60px 60px;
313 | transform-origin: 60px 60px; }
314 | .sweet-alert .sa-icon.sa-success::after {
315 | -webkit-border-radius: 0 120px 120px 0;
316 | border-radius: 0 120px 120px 0;
317 | top: -11px;
318 | left: 30px;
319 | -webkit-transform: rotate(-45deg);
320 | transform: rotate(-45deg);
321 | -webkit-transform-origin: 0px 60px;
322 | transform-origin: 0px 60px; }
323 | .sweet-alert .sa-icon.sa-success .sa-placeholder {
324 | width: 80px;
325 | height: 80px;
326 | border: 4px solid rgba(165, 220, 134, 0.2);
327 | -webkit-border-radius: 40px;
328 | border-radius: 40px;
329 | border-radius: 50%;
330 | box-sizing: content-box;
331 | position: absolute;
332 | left: -4px;
333 | top: -4px;
334 | z-index: 2; }
335 | .sweet-alert .sa-icon.sa-success .sa-fix {
336 | width: 5px;
337 | height: 90px;
338 | background-color: white;
339 | position: absolute;
340 | left: 28px;
341 | top: 8px;
342 | z-index: 1;
343 | -webkit-transform: rotate(-45deg);
344 | transform: rotate(-45deg); }
345 | .sweet-alert .sa-icon.sa-success .sa-line {
346 | height: 5px;
347 | background-color: #A5DC86;
348 | display: block;
349 | border-radius: 2px;
350 | position: absolute;
351 | z-index: 2; }
352 | .sweet-alert .sa-icon.sa-success .sa-line.sa-tip {
353 | width: 25px;
354 | left: 14px;
355 | top: 46px;
356 | -webkit-transform: rotate(45deg);
357 | transform: rotate(45deg); }
358 | .sweet-alert .sa-icon.sa-success .sa-line.sa-long {
359 | width: 47px;
360 | right: 8px;
361 | top: 38px;
362 | -webkit-transform: rotate(-45deg);
363 | transform: rotate(-45deg); }
364 | .sweet-alert .sa-icon.sa-custom {
365 | background-size: contain;
366 | border-radius: 0;
367 | border: none;
368 | background-position: center center;
369 | background-repeat: no-repeat; }
370 |
371 | /*
372 | * Animations
373 | */
374 | @-webkit-keyframes showSweetAlert {
375 | 0% {
376 | transform: scale(0.7);
377 | -webkit-transform: scale(0.7); }
378 | 45% {
379 | transform: scale(1.05);
380 | -webkit-transform: scale(1.05); }
381 | 80% {
382 | transform: scale(0.95);
383 | -webkit-transform: scale(0.95); }
384 | 100% {
385 | transform: scale(1);
386 | -webkit-transform: scale(1); } }
387 |
388 | @keyframes showSweetAlert {
389 | 0% {
390 | transform: scale(0.7);
391 | -webkit-transform: scale(0.7); }
392 | 45% {
393 | transform: scale(1.05);
394 | -webkit-transform: scale(1.05); }
395 | 80% {
396 | transform: scale(0.95);
397 | -webkit-transform: scale(0.95); }
398 | 100% {
399 | transform: scale(1);
400 | -webkit-transform: scale(1); } }
401 |
402 | @-webkit-keyframes hideSweetAlert {
403 | 0% {
404 | transform: scale(1);
405 | -webkit-transform: scale(1); }
406 | 100% {
407 | transform: scale(0.5);
408 | -webkit-transform: scale(0.5); } }
409 |
410 | @keyframes hideSweetAlert {
411 | 0% {
412 | transform: scale(1);
413 | -webkit-transform: scale(1); }
414 | 100% {
415 | transform: scale(0.5);
416 | -webkit-transform: scale(0.5); } }
417 |
418 | @-webkit-keyframes slideFromTop {
419 | 0% {
420 | top: 0%; }
421 | 100% {
422 | top: 50%; } }
423 |
424 | @keyframes slideFromTop {
425 | 0% {
426 | top: 0%; }
427 | 100% {
428 | top: 50%; } }
429 |
430 | @-webkit-keyframes slideToTop {
431 | 0% {
432 | top: 50%; }
433 | 100% {
434 | top: 0%; } }
435 |
436 | @keyframes slideToTop {
437 | 0% {
438 | top: 50%; }
439 | 100% {
440 | top: 0%; } }
441 |
442 | @-webkit-keyframes slideFromBottom {
443 | 0% {
444 | top: 70%; }
445 | 100% {
446 | top: 50%; } }
447 |
448 | @keyframes slideFromBottom {
449 | 0% {
450 | top: 70%; }
451 | 100% {
452 | top: 50%; } }
453 |
454 | @-webkit-keyframes slideToBottom {
455 | 0% {
456 | top: 50%; }
457 | 100% {
458 | top: 70%; } }
459 |
460 | @keyframes slideToBottom {
461 | 0% {
462 | top: 50%; }
463 | 100% {
464 | top: 70%; } }
465 |
466 | .showSweetAlert[data-animation=pop] {
467 | -webkit-animation: showSweetAlert 0.3s;
468 | animation: showSweetAlert 0.3s; }
469 |
470 | .showSweetAlert[data-animation=none] {
471 | -webkit-animation: none;
472 | animation: none; }
473 |
474 | .showSweetAlert[data-animation=slide-from-top] {
475 | -webkit-animation: slideFromTop 0.3s;
476 | animation: slideFromTop 0.3s; }
477 |
478 | .showSweetAlert[data-animation=slide-from-bottom] {
479 | -webkit-animation: slideFromBottom 0.3s;
480 | animation: slideFromBottom 0.3s; }
481 |
482 | .hideSweetAlert[data-animation=pop] {
483 | -webkit-animation: hideSweetAlert 0.2s;
484 | animation: hideSweetAlert 0.2s; }
485 |
486 | .hideSweetAlert[data-animation=none] {
487 | -webkit-animation: none;
488 | animation: none; }
489 |
490 | .hideSweetAlert[data-animation=slide-from-top] {
491 | -webkit-animation: slideToTop 0.4s;
492 | animation: slideToTop 0.4s; }
493 |
494 | .hideSweetAlert[data-animation=slide-from-bottom] {
495 | -webkit-animation: slideToBottom 0.3s;
496 | animation: slideToBottom 0.3s; }
497 |
498 | @-webkit-keyframes animateSuccessTip {
499 | 0% {
500 | width: 0;
501 | left: 1px;
502 | top: 19px; }
503 | 54% {
504 | width: 0;
505 | left: 1px;
506 | top: 19px; }
507 | 70% {
508 | width: 50px;
509 | left: -8px;
510 | top: 37px; }
511 | 84% {
512 | width: 17px;
513 | left: 21px;
514 | top: 48px; }
515 | 100% {
516 | width: 25px;
517 | left: 14px;
518 | top: 45px; } }
519 |
520 | @keyframes animateSuccessTip {
521 | 0% {
522 | width: 0;
523 | left: 1px;
524 | top: 19px; }
525 | 54% {
526 | width: 0;
527 | left: 1px;
528 | top: 19px; }
529 | 70% {
530 | width: 50px;
531 | left: -8px;
532 | top: 37px; }
533 | 84% {
534 | width: 17px;
535 | left: 21px;
536 | top: 48px; }
537 | 100% {
538 | width: 25px;
539 | left: 14px;
540 | top: 45px; } }
541 |
542 | @-webkit-keyframes animateSuccessLong {
543 | 0% {
544 | width: 0;
545 | right: 46px;
546 | top: 54px; }
547 | 65% {
548 | width: 0;
549 | right: 46px;
550 | top: 54px; }
551 | 84% {
552 | width: 55px;
553 | right: 0px;
554 | top: 35px; }
555 | 100% {
556 | width: 47px;
557 | right: 8px;
558 | top: 38px; } }
559 |
560 | @keyframes animateSuccessLong {
561 | 0% {
562 | width: 0;
563 | right: 46px;
564 | top: 54px; }
565 | 65% {
566 | width: 0;
567 | right: 46px;
568 | top: 54px; }
569 | 84% {
570 | width: 55px;
571 | right: 0px;
572 | top: 35px; }
573 | 100% {
574 | width: 47px;
575 | right: 8px;
576 | top: 38px; } }
577 |
578 | @-webkit-keyframes rotatePlaceholder {
579 | 0% {
580 | transform: rotate(-45deg);
581 | -webkit-transform: rotate(-45deg); }
582 | 5% {
583 | transform: rotate(-45deg);
584 | -webkit-transform: rotate(-45deg); }
585 | 12% {
586 | transform: rotate(-405deg);
587 | -webkit-transform: rotate(-405deg); }
588 | 100% {
589 | transform: rotate(-405deg);
590 | -webkit-transform: rotate(-405deg); } }
591 |
592 | @keyframes rotatePlaceholder {
593 | 0% {
594 | transform: rotate(-45deg);
595 | -webkit-transform: rotate(-45deg); }
596 | 5% {
597 | transform: rotate(-45deg);
598 | -webkit-transform: rotate(-45deg); }
599 | 12% {
600 | transform: rotate(-405deg);
601 | -webkit-transform: rotate(-405deg); }
602 | 100% {
603 | transform: rotate(-405deg);
604 | -webkit-transform: rotate(-405deg); } }
605 |
606 | .animateSuccessTip {
607 | -webkit-animation: animateSuccessTip 0.75s;
608 | animation: animateSuccessTip 0.75s; }
609 |
610 | .animateSuccessLong {
611 | -webkit-animation: animateSuccessLong 0.75s;
612 | animation: animateSuccessLong 0.75s; }
613 |
614 | .sa-icon.sa-success.animate::after {
615 | -webkit-animation: rotatePlaceholder 4.25s ease-in;
616 | animation: rotatePlaceholder 4.25s ease-in; }
617 |
618 | @-webkit-keyframes animateErrorIcon {
619 | 0% {
620 | transform: rotateX(100deg);
621 | -webkit-transform: rotateX(100deg);
622 | opacity: 0; }
623 | 100% {
624 | transform: rotateX(0deg);
625 | -webkit-transform: rotateX(0deg);
626 | opacity: 1; } }
627 |
628 | @keyframes animateErrorIcon {
629 | 0% {
630 | transform: rotateX(100deg);
631 | -webkit-transform: rotateX(100deg);
632 | opacity: 0; }
633 | 100% {
634 | transform: rotateX(0deg);
635 | -webkit-transform: rotateX(0deg);
636 | opacity: 1; } }
637 |
638 | .animateErrorIcon {
639 | -webkit-animation: animateErrorIcon 0.5s;
640 | animation: animateErrorIcon 0.5s; }
641 |
642 | @-webkit-keyframes animateXMark {
643 | 0% {
644 | transform: scale(0.4);
645 | -webkit-transform: scale(0.4);
646 | margin-top: 26px;
647 | opacity: 0; }
648 | 50% {
649 | transform: scale(0.4);
650 | -webkit-transform: scale(0.4);
651 | margin-top: 26px;
652 | opacity: 0; }
653 | 80% {
654 | transform: scale(1.15);
655 | -webkit-transform: scale(1.15);
656 | margin-top: -6px; }
657 | 100% {
658 | transform: scale(1);
659 | -webkit-transform: scale(1);
660 | margin-top: 0;
661 | opacity: 1; } }
662 |
663 | @keyframes animateXMark {
664 | 0% {
665 | transform: scale(0.4);
666 | -webkit-transform: scale(0.4);
667 | margin-top: 26px;
668 | opacity: 0; }
669 | 50% {
670 | transform: scale(0.4);
671 | -webkit-transform: scale(0.4);
672 | margin-top: 26px;
673 | opacity: 0; }
674 | 80% {
675 | transform: scale(1.15);
676 | -webkit-transform: scale(1.15);
677 | margin-top: -6px; }
678 | 100% {
679 | transform: scale(1);
680 | -webkit-transform: scale(1);
681 | margin-top: 0;
682 | opacity: 1; } }
683 |
684 | .animateXMark {
685 | -webkit-animation: animateXMark 0.5s;
686 | animation: animateXMark 0.5s; }
687 |
688 | @-webkit-keyframes pulseWarning {
689 | 0% {
690 | border-color: #F8D486; }
691 | 100% {
692 | border-color: #F8BB86; } }
693 |
694 | @keyframes pulseWarning {
695 | 0% {
696 | border-color: #F8D486; }
697 | 100% {
698 | border-color: #F8BB86; } }
699 |
700 | .pulseWarning {
701 | -webkit-animation: pulseWarning 0.75s infinite alternate;
702 | animation: pulseWarning 0.75s infinite alternate; }
703 |
704 | @-webkit-keyframes pulseWarningIns {
705 | 0% {
706 | background-color: #F8D486; }
707 | 100% {
708 | background-color: #F8BB86; } }
709 |
710 | @keyframes pulseWarningIns {
711 | 0% {
712 | background-color: #F8D486; }
713 | 100% {
714 | background-color: #F8BB86; } }
715 |
716 | .pulseWarningIns {
717 | -webkit-animation: pulseWarningIns 0.75s infinite alternate;
718 | animation: pulseWarningIns 0.75s infinite alternate; }
719 |
720 | @-webkit-keyframes rotate-loading {
721 | 0% {
722 | transform: rotate(0deg); }
723 | 100% {
724 | transform: rotate(360deg); } }
725 |
726 | @keyframes rotate-loading {
727 | 0% {
728 | transform: rotate(0deg); }
729 | 100% {
730 | transform: rotate(360deg); } }
731 |
732 | /* Internet Explorer 9 has some special quirks that are fixed here */
733 | /* The icons are not animated. */
734 | /* This file is automatically merged into sweet-alert.min.js through Gulp */
735 | /* Error icon */
736 | .sweet-alert .sa-icon.sa-error .sa-line.sa-left {
737 | -ms-transform: rotate(45deg) \9; }
738 |
739 | .sweet-alert .sa-icon.sa-error .sa-line.sa-right {
740 | -ms-transform: rotate(-45deg) \9; }
741 |
742 | /* Success icon */
743 | .sweet-alert .sa-icon.sa-success {
744 | border-color: transparent\9; }
745 |
746 | .sweet-alert .sa-icon.sa-success .sa-line.sa-tip {
747 | -ms-transform: rotate(45deg) \9; }
748 |
749 | .sweet-alert .sa-icon.sa-success .sa-line.sa-long {
750 | -ms-transform: rotate(-45deg) \9; }
751 |
752 | /*!
753 | * Load Awesome v1.1.0 (http://github.danielcardoso.net/load-awesome/)
754 | * Copyright 2015 Daniel Cardoso <@DanielCardoso>
755 | * Licensed under MIT
756 | */
757 | .la-ball-fall,
758 | .la-ball-fall > div {
759 | position: relative;
760 | -webkit-box-sizing: border-box;
761 | -moz-box-sizing: border-box;
762 | box-sizing: border-box; }
763 |
764 | .la-ball-fall {
765 | display: block;
766 | font-size: 0;
767 | color: #fff; }
768 |
769 | .la-ball-fall.la-dark {
770 | color: #333; }
771 |
772 | .la-ball-fall > div {
773 | display: inline-block;
774 | float: none;
775 | background-color: currentColor;
776 | border: 0 solid currentColor; }
777 |
778 | .la-ball-fall {
779 | width: 54px;
780 | height: 18px; }
781 |
782 | .la-ball-fall > div {
783 | width: 10px;
784 | height: 10px;
785 | margin: 4px;
786 | border-radius: 100%;
787 | opacity: 0;
788 | -webkit-animation: ball-fall 1s ease-in-out infinite;
789 | -moz-animation: ball-fall 1s ease-in-out infinite;
790 | -o-animation: ball-fall 1s ease-in-out infinite;
791 | animation: ball-fall 1s ease-in-out infinite; }
792 |
793 | .la-ball-fall > div:nth-child(1) {
794 | -webkit-animation-delay: -200ms;
795 | -moz-animation-delay: -200ms;
796 | -o-animation-delay: -200ms;
797 | animation-delay: -200ms; }
798 |
799 | .la-ball-fall > div:nth-child(2) {
800 | -webkit-animation-delay: -100ms;
801 | -moz-animation-delay: -100ms;
802 | -o-animation-delay: -100ms;
803 | animation-delay: -100ms; }
804 |
805 | .la-ball-fall > div:nth-child(3) {
806 | -webkit-animation-delay: 0ms;
807 | -moz-animation-delay: 0ms;
808 | -o-animation-delay: 0ms;
809 | animation-delay: 0ms; }
810 |
811 | .la-ball-fall.la-sm {
812 | width: 26px;
813 | height: 8px; }
814 |
815 | .la-ball-fall.la-sm > div {
816 | width: 4px;
817 | height: 4px;
818 | margin: 2px; }
819 |
820 | .la-ball-fall.la-2x {
821 | width: 108px;
822 | height: 36px; }
823 |
824 | .la-ball-fall.la-2x > div {
825 | width: 20px;
826 | height: 20px;
827 | margin: 8px; }
828 |
829 | .la-ball-fall.la-3x {
830 | width: 162px;
831 | height: 54px; }
832 |
833 | .la-ball-fall.la-3x > div {
834 | width: 30px;
835 | height: 30px;
836 | margin: 12px; }
837 |
838 | /*
839 | * Animation
840 | */
841 | @-webkit-keyframes ball-fall {
842 | 0% {
843 | opacity: 0;
844 | -webkit-transform: translateY(-145%);
845 | transform: translateY(-145%); }
846 | 10% {
847 | opacity: .5; }
848 | 20% {
849 | opacity: 1;
850 | -webkit-transform: translateY(0);
851 | transform: translateY(0); }
852 | 80% {
853 | opacity: 1;
854 | -webkit-transform: translateY(0);
855 | transform: translateY(0); }
856 | 90% {
857 | opacity: .5; }
858 | 100% {
859 | opacity: 0;
860 | -webkit-transform: translateY(145%);
861 | transform: translateY(145%); } }
862 |
863 | @-moz-keyframes ball-fall {
864 | 0% {
865 | opacity: 0;
866 | -moz-transform: translateY(-145%);
867 | transform: translateY(-145%); }
868 | 10% {
869 | opacity: .5; }
870 | 20% {
871 | opacity: 1;
872 | -moz-transform: translateY(0);
873 | transform: translateY(0); }
874 | 80% {
875 | opacity: 1;
876 | -moz-transform: translateY(0);
877 | transform: translateY(0); }
878 | 90% {
879 | opacity: .5; }
880 | 100% {
881 | opacity: 0;
882 | -moz-transform: translateY(145%);
883 | transform: translateY(145%); } }
884 |
885 | @-o-keyframes ball-fall {
886 | 0% {
887 | opacity: 0;
888 | -o-transform: translateY(-145%);
889 | transform: translateY(-145%); }
890 | 10% {
891 | opacity: .5; }
892 | 20% {
893 | opacity: 1;
894 | -o-transform: translateY(0);
895 | transform: translateY(0); }
896 | 80% {
897 | opacity: 1;
898 | -o-transform: translateY(0);
899 | transform: translateY(0); }
900 | 90% {
901 | opacity: .5; }
902 | 100% {
903 | opacity: 0;
904 | -o-transform: translateY(145%);
905 | transform: translateY(145%); } }
906 |
907 | @keyframes ball-fall {
908 | 0% {
909 | opacity: 0;
910 | -webkit-transform: translateY(-145%);
911 | -moz-transform: translateY(-145%);
912 | -o-transform: translateY(-145%);
913 | transform: translateY(-145%); }
914 | 10% {
915 | opacity: .5; }
916 | 20% {
917 | opacity: 1;
918 | -webkit-transform: translateY(0);
919 | -moz-transform: translateY(0);
920 | -o-transform: translateY(0);
921 | transform: translateY(0); }
922 | 80% {
923 | opacity: 1;
924 | -webkit-transform: translateY(0);
925 | -moz-transform: translateY(0);
926 | -o-transform: translateY(0);
927 | transform: translateY(0); }
928 | 90% {
929 | opacity: .5; }
930 | 100% {
931 | opacity: 0;
932 | -webkit-transform: translateY(145%);
933 | -moz-transform: translateY(145%);
934 | -o-transform: translateY(145%);
935 | transform: translateY(145%); } }
936 |
--------------------------------------------------------------------------------
/assets/vendor/talon/admin-lte/bootstrap/js/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.7 (http://getbootstrap.com)
3 | * Copyright 2011-2016 Twitter, Inc.
4 | * Licensed under the MIT license
5 | */
6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){if(a(b.target).is(this))return b.handleObj.handler.apply(this,arguments)}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.7",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a("#"===f?[]:f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.7",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c).prop(c,!0)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c).prop(c,!1))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target).closest(".btn");b.call(d,"toggle"),a(c.target).is('input[type="radio"], input[type="checkbox"]')||(c.preventDefault(),d.is("input,button")?d.trigger("focus"):d.find("input:visible,button:visible").first().trigger("focus"))}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.7",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));if(!(a>this.$items.length-1||a<0))return this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){if(!this.sliding)return this.slide("next")},c.prototype.prev=function(){if(!this.sliding)return this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.7",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.7",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);if(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),!c.isInStateTrue())return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null,a.$element=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;!e&&/destroy|hide/.test(b)||(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.7",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.7",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.7",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return e=a-d&&"bottom"},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
--------------------------------------------------------------------------------