├── .gitattributes ├── priv ├── static │ ├── sass │ │ ├── bourbon │ │ │ ├── settings │ │ │ │ ├── _px-to-em.scss │ │ │ │ ├── _asset-pipeline.scss │ │ │ │ └── _prefixer.scss │ │ │ ├── css3 │ │ │ │ ├── _appearance.scss │ │ │ │ ├── _user-select.scss │ │ │ │ ├── _calc.scss │ │ │ │ ├── _hyphens.scss │ │ │ │ ├── _box-sizing.scss │ │ │ │ ├── _filter.scss │ │ │ │ ├── _placeholder.scss │ │ │ │ ├── _perspective.scss │ │ │ │ ├── _backface-visibility.scss │ │ │ │ ├── _image-rendering.scss │ │ │ │ ├── _font-feature-settings.scss │ │ │ │ ├── _hidpi-media-query.scss │ │ │ │ ├── _transform.scss │ │ │ │ ├── _border-radius.scss │ │ │ │ ├── _font-face.scss │ │ │ │ ├── _keyframes.scss │ │ │ │ ├── _columns.scss │ │ │ │ ├── _linear-gradient.scss │ │ │ │ ├── _background-image.scss │ │ │ │ ├── _radial-gradient.scss │ │ │ │ ├── _animation.scss │ │ │ │ ├── _background.scss │ │ │ │ └── _border-image.scss │ │ │ ├── functions │ │ │ │ ├── _golden-ratio.scss │ │ │ │ ├── _strip-units.scss │ │ │ │ ├── _tint-shade.scss │ │ │ │ ├── _assign.scss │ │ │ │ ├── _px-to-rem.scss │ │ │ │ ├── _px-to-em.scss │ │ │ │ ├── _grid-width.scss │ │ │ │ ├── _color-lightness.scss │ │ │ │ ├── _unpack.scss │ │ │ │ ├── _transition-property-name.scss │ │ │ │ ├── _flex-grid.scss │ │ │ │ └── _modular-scale.scss │ │ │ ├── addons │ │ │ │ ├── _ellipsis.scss │ │ │ │ ├── _hide-text.scss │ │ │ │ ├── _word-wrap.scss │ │ │ │ ├── _font-family.scss │ │ │ │ ├── _size.scss │ │ │ │ ├── _clearfix.scss │ │ │ │ ├── _position.scss │ │ │ │ ├── _retina-image.scss │ │ │ │ ├── _prefixer.scss │ │ │ │ └── _timing-functions.scss │ │ │ ├── helpers │ │ │ │ ├── _shape-size-stripper.scss │ │ │ │ ├── _is-num.scss │ │ │ │ ├── _gradient-positions-parser.scss │ │ │ │ ├── _radial-positions-parser.scss │ │ │ │ ├── _convert-units.scss │ │ │ │ ├── _linear-angle-parser.scss │ │ │ │ ├── _render-gradients.scss │ │ │ │ ├── _linear-side-corner-parser.scss │ │ │ │ ├── _linear-gradient-parser.scss │ │ │ │ ├── _str-to-num.scss │ │ │ │ ├── _radial-gradient-parser.scss │ │ │ │ └── _radial-arg-parser.scss │ │ │ └── _bourbon-deprecated-upcoming.scss │ │ ├── base │ │ │ ├── extends │ │ │ │ ├── _clearfix.scss │ │ │ │ ├── _hide-text.scss │ │ │ │ ├── _errors.scss │ │ │ │ ├── _button.scss │ │ │ │ └── _flashes.scss │ │ │ ├── _buttons.scss │ │ │ ├── _grid-settings.scss │ │ │ ├── _tables.scss │ │ │ ├── _lists.scss │ │ │ ├── _base.scss │ │ │ ├── _forms.scss │ │ │ ├── _typography.scss │ │ │ └── _variables.scss │ │ └── neat │ │ │ ├── _neat-helpers.scss │ │ │ ├── grid │ │ │ ├── _box-sizing.scss │ │ │ ├── _fill-parent.scss │ │ │ ├── _pad.scss │ │ │ ├── _display-context.scss │ │ │ ├── _direction-context.scss │ │ │ ├── _outer-container.scss │ │ │ ├── _visual-grid.scss │ │ │ ├── _private.scss │ │ │ ├── _shift.scss │ │ │ └── _row.scss │ │ │ ├── settings │ │ │ ├── _disable-warnings.scss │ │ │ └── _visual-grid.scss │ │ │ ├── _neat.scss │ │ │ └── functions │ │ │ └── _new-breakpoint.scss │ ├── images │ │ ├── favicon.ico │ │ ├── phoenix.png │ │ └── mountains.png │ └── css │ │ ├── flash.css.map │ │ └── flash.css └── repo │ ├── seeds.ex │ └── migrations │ ├── 20150417053013_create_signup.exs │ └── 20150417052223_create_user.exs ├── web ├── static │ ├── css │ │ ├── bourbon │ │ │ ├── settings │ │ │ │ ├── _px-to-em.scss │ │ │ │ ├── _asset-pipeline.scss │ │ │ │ └── _prefixer.scss │ │ │ ├── css3 │ │ │ │ ├── _appearance.scss │ │ │ │ ├── _user-select.scss │ │ │ │ ├── _calc.scss │ │ │ │ ├── _backface-visibility.scss │ │ │ │ ├── _hyphens.scss │ │ │ │ ├── _filter.scss │ │ │ │ ├── _font-feature-settings.scss │ │ │ │ ├── _placeholder.scss │ │ │ │ ├── _perspective.scss │ │ │ │ ├── _image-rendering.scss │ │ │ │ ├── _hidpi-media-query.scss │ │ │ │ ├── _transform.scss │ │ │ │ ├── _font-face.scss │ │ │ │ ├── _text-decoration.scss │ │ │ │ ├── _selection.scss │ │ │ │ ├── _keyframes.scss │ │ │ │ ├── _columns.scss │ │ │ │ ├── _linear-gradient.scss │ │ │ │ ├── _background-image.scss │ │ │ │ ├── _radial-gradient.scss │ │ │ │ ├── _animation.scss │ │ │ │ ├── _background.scss │ │ │ │ └── _border-image.scss │ │ │ ├── functions │ │ │ │ ├── _is-number.scss │ │ │ │ ├── _assign-inputs.scss │ │ │ │ ├── _is-size.scss │ │ │ │ ├── _is-length.scss │ │ │ │ ├── _contains-falsy.scss │ │ │ │ ├── _px-to-rem.scss │ │ │ │ ├── _strip-units.scss │ │ │ │ ├── _px-to-em.scss │ │ │ │ ├── _tint.scss │ │ │ │ ├── _shade.scss │ │ │ │ ├── _contains.scss │ │ │ │ ├── _is-light.scss │ │ │ │ ├── _transition-property-name.scss │ │ │ │ ├── _unpack.scss │ │ │ │ └── _modular-scale.scss │ │ │ ├── helpers │ │ │ │ ├── _shape-size-stripper.scss │ │ │ │ ├── _gradient-positions-parser.scss │ │ │ │ ├── _radial-positions-parser.scss │ │ │ │ ├── _linear-angle-parser.scss │ │ │ │ ├── _convert-units.scss │ │ │ │ ├── _render-gradients.scss │ │ │ │ ├── _linear-side-corner-parser.scss │ │ │ │ ├── _font-source-declaration.scss │ │ │ │ ├── _linear-gradient-parser.scss │ │ │ │ ├── _str-to-num.scss │ │ │ │ ├── _radial-gradient-parser.scss │ │ │ │ └── _radial-arg-parser.scss │ │ │ └── addons │ │ │ │ ├── _clearfix.scss │ │ │ │ ├── _margin.scss │ │ │ │ ├── _border-width.scss │ │ │ │ ├── _padding.scss │ │ │ │ ├── _border-style.scss │ │ │ │ ├── _word-wrap.scss │ │ │ │ ├── _font-stacks.scss │ │ │ │ ├── _border-color.scss │ │ │ │ ├── _ellipsis.scss │ │ │ │ ├── _hide-text.scss │ │ │ │ ├── _retina-image.scss │ │ │ │ ├── _border-radius.scss │ │ │ │ ├── _size.scss │ │ │ │ ├── _position.scss │ │ │ │ ├── _timing-functions.scss │ │ │ │ ├── _prefixer.scss │ │ │ │ └── _buttons.scss │ │ ├── neat │ │ │ ├── _neat-helpers.scss │ │ │ ├── grid │ │ │ │ ├── _box-sizing.scss │ │ │ │ ├── _fill-parent.scss │ │ │ │ ├── _pad.scss │ │ │ │ ├── _display-context.scss │ │ │ │ ├── _direction-context.scss │ │ │ │ ├── _outer-container.scss │ │ │ │ ├── _visual-grid.scss │ │ │ │ ├── _private.scss │ │ │ │ ├── _shift.scss │ │ │ │ └── _row.scss │ │ │ ├── settings │ │ │ │ ├── _disable-warnings.scss │ │ │ │ ├── _visual-grid.scss │ │ │ │ └── _grid.scss │ │ │ ├── _neat.scss │ │ │ └── functions │ │ │ │ └── _new-breakpoint.scss │ │ └── base │ │ │ ├── _base.scss │ │ │ ├── _grid-settings.scss │ │ │ ├── _tables.scss │ │ │ ├── _lists.scss │ │ │ ├── _buttons.scss │ │ │ ├── _flashes.scss │ │ │ ├── _variables.scss │ │ │ ├── _typography.scss │ │ │ └── _forms.scss │ └── js │ │ └── app.js ├── views │ ├── page_view.ex │ ├── user_view.ex │ ├── signup_view.ex │ ├── error_view.ex │ └── layout_view.ex ├── templates │ ├── signup │ │ ├── form.html.eex │ │ └── index.html.eex │ ├── user │ │ ├── form.html.eex │ │ └── login.html.eex │ ├── page │ │ └── index.html.eex │ └── layout │ │ └── app.html.eex ├── controllers │ ├── page_controller.ex │ └── user_controller.ex ├── models │ ├── signup.ex │ ├── queries.ex │ └── user.ex ├── router.ex ├── channels │ └── user_socket.ex └── web.ex ├── lib ├── preview │ ├── repo.ex │ ├── mailer.ex │ ├── endpoint.ex │ └── authenticate.ex └── preview.ex ├── test ├── doc_test.exs ├── controllers │ ├── page_controller_test.exs │ └── user_controller_test.exs ├── test_helper.exs ├── models │ ├── signup_test.exs │ └── user_test.exs ├── support │ ├── model_case.ex │ ├── channel_case.ex │ └── conn_case.ex └── lib │ ├── authenticate_test.exs │ └── csv_helper_test.exs ├── .gitignore ├── package.json ├── .travis.yml ├── config ├── prod.secret.exs ├── test.exs ├── config.exs ├── dev.exs └── prod.exs ├── mix.lock ├── brunch-config.js └── mix.exs /.gitattributes: -------------------------------------------------------------------------------- 1 | priv/static/* linguist-vendored 2 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/settings/_px-to-em.scss: -------------------------------------------------------------------------------- 1 | $em-base: 16px !default; 2 | -------------------------------------------------------------------------------- /web/static/css/bourbon/settings/_px-to-em.scss: -------------------------------------------------------------------------------- 1 | $em-base: 16px !default; 2 | -------------------------------------------------------------------------------- /priv/static/sass/base/extends/_clearfix.scss: -------------------------------------------------------------------------------- 1 | %clearfix { 2 | @include clearfix; 3 | } 4 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/settings/_asset-pipeline.scss: -------------------------------------------------------------------------------- 1 | $asset-pipeline: false !default; 2 | -------------------------------------------------------------------------------- /web/views/page_view.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.PageView do 2 | use Preview.Web, :view 3 | end 4 | -------------------------------------------------------------------------------- /web/views/user_view.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.UserView do 2 | use Preview.Web, :view 3 | end 4 | -------------------------------------------------------------------------------- /lib/preview/repo.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.Repo do 2 | use Ecto.Repo, otp_app: :preview 3 | end 4 | -------------------------------------------------------------------------------- /priv/static/sass/base/extends/_hide-text.scss: -------------------------------------------------------------------------------- 1 | %hide-text { 2 | @include hide-text; 3 | } 4 | -------------------------------------------------------------------------------- /web/views/signup_view.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.SignupView do 2 | use Preview.Web, :view 3 | end 4 | -------------------------------------------------------------------------------- /priv/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutablestate/preview/HEAD/priv/static/images/favicon.ico -------------------------------------------------------------------------------- /priv/static/images/phoenix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutablestate/preview/HEAD/priv/static/images/phoenix.png -------------------------------------------------------------------------------- /priv/static/images/mountains.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mutablestate/preview/HEAD/priv/static/images/mountains.png -------------------------------------------------------------------------------- /test/doc_test.exs: -------------------------------------------------------------------------------- 1 | defmodule DocTest do 2 | use ExUnit.Case, async: true 3 | 4 | doctest Preview 5 | doctest Preview.LayoutView 6 | end 7 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_appearance.scss: -------------------------------------------------------------------------------- 1 | @mixin appearance ($value) { 2 | @include prefixer(appearance, $value, webkit moz ms o spec); 3 | } 4 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_appearance.scss: -------------------------------------------------------------------------------- 1 | @mixin appearance($value) { 2 | @include prefixer(appearance, $value, webkit moz ms o spec); 3 | } 4 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_user-select.scss: -------------------------------------------------------------------------------- 1 | @mixin user-select($arg: none) { 2 | @include prefixer(user-select, $arg, webkit moz ms spec); 3 | } 4 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_user-select.scss: -------------------------------------------------------------------------------- 1 | @mixin user-select($value: none) { 2 | @include prefixer(user-select, $value, webkit moz ms spec); 3 | } 4 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_calc.scss: -------------------------------------------------------------------------------- 1 | @mixin calc($property, $value) { 2 | #{$property}: -webkit-calc(#{$value}); 3 | #{$property}: calc(#{$value}); 4 | } 5 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_golden-ratio.scss: -------------------------------------------------------------------------------- 1 | @function golden-ratio($value, $increment) { 2 | @return modular-scale($value, $increment, $golden) 3 | } 4 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_calc.scss: -------------------------------------------------------------------------------- 1 | @mixin calc($property, $value) { 2 | #{$property}: -webkit-calc(#{$value}); 3 | #{$property}: calc(#{$value}); 4 | } 5 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_backface-visibility.scss: -------------------------------------------------------------------------------- 1 | @mixin backface-visibility($visibility) { 2 | @include prefixer(backface-visibility, $visibility, webkit spec); 3 | } 4 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_hyphens.scss: -------------------------------------------------------------------------------- 1 | @mixin hyphens($hyphenation: none) { 2 | // none | manual | auto 3 | @include prefixer(hyphens, $hyphenation, webkit moz ms spec); 4 | } -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_box-sizing.scss: -------------------------------------------------------------------------------- 1 | @mixin box-sizing ($box) { 2 | // content-box | border-box | inherit 3 | @include prefixer(box-sizing, $box, webkit moz spec); 4 | } 5 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_hyphens.scss: -------------------------------------------------------------------------------- 1 | @mixin hyphens($hyphenation: none) { 2 | // none | manual | auto 3 | @include prefixer(hyphens, $hyphenation, webkit moz ms spec); 4 | } 5 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_strip-units.scss: -------------------------------------------------------------------------------- 1 | // Srtips the units from a value. e.g. 12px -> 12 2 | 3 | @function strip-units($val) { 4 | @return ($val / ($val * 0 + 1)); 5 | } 6 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_filter.scss: -------------------------------------------------------------------------------- 1 | @mixin filter($function: none) { 2 | // [ [ %> 2 | 3 | <%= text_input f, :email, placeholder: "yourname@email.com" %> 4 | 5 | <%= submit "Register" %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /web/templates/user/form.html.eex: -------------------------------------------------------------------------------- 1 | <%= form_for @changeset, @action, fn f -> %> 2 | 3 | <%= text_input f, :email %> 4 | 5 | 6 | <%= password_input f, :password %> 7 | 8 | <%= submit "Submit" %> 9 | <% end %> 10 | -------------------------------------------------------------------------------- /priv/repo/seeds.ex: -------------------------------------------------------------------------------- 1 | # mix run priv/repo/seeds.ex 2 | 3 | user = %{ 4 | email: "yoda@example.com", 5 | password: Comeonin.Bcrypt.hashpwsalt("usetheforce") 6 | } 7 | 8 | new_user = Map.merge(%Preview.User{}, user) 9 | Preview.Repo.insert(new_user) 10 | -------------------------------------------------------------------------------- /priv/repo/migrations/20150417053013_create_signup.exs: -------------------------------------------------------------------------------- 1 | defmodule Preview.Repo.Migrations.CreateSignup do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:signups) do 6 | add :email, :string 7 | 8 | timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/static/sass/neat/grid/_box-sizing.scss: -------------------------------------------------------------------------------- 1 | @if $border-box-sizing == true { 2 | html { // http://bit.ly/1qk2tVR 3 | @include box-sizing(border-box); 4 | } 5 | 6 | * { 7 | &, &:before, &:after { 8 | @include box-sizing(inherit); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_perspective.scss: -------------------------------------------------------------------------------- 1 | @mixin perspective($depth: none) { 2 | // none | 3 | @include prefixer(perspective, $depth, webkit moz spec); 4 | } 5 | 6 | @mixin perspective-origin($value: 50% 50%) { 7 | @include prefixer(perspective-origin, $value, webkit moz spec); 8 | } 9 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_perspective.scss: -------------------------------------------------------------------------------- 1 | @mixin perspective($depth: none) { 2 | // none | 3 | @include prefixer(perspective, $depth, webkit moz spec); 4 | } 5 | 6 | @mixin perspective-origin($value: 50% 50%) { 7 | @include prefixer(perspective-origin, $value, webkit moz spec); 8 | } 9 | -------------------------------------------------------------------------------- /web/static/js/app.js: -------------------------------------------------------------------------------- 1 | import {Socket} from "deps/phoenix/web/static/js/phoenix" 2 | import "deps/phoenix_html/web/static/js/phoenix_html" 3 | 4 | // let socket = new Socket("/ws") 5 | // socket.join("topic:subtopic", {}, chan => { 6 | // }) 7 | 8 | let App = { 9 | } 10 | 11 | export default App 12 | -------------------------------------------------------------------------------- /priv/repo/migrations/20150417052223_create_user.exs: -------------------------------------------------------------------------------- 1 | defmodule Preview.Repo.Migrations.CreateUser do 2 | use Ecto.Migration 3 | 4 | def change do 5 | create table(:users) do 6 | add :email, :string 7 | add :password, :string 8 | 9 | timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /web/static/css/neat/grid/_box-sizing.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | @if $border-box-sizing == true { 4 | html { // http://bit.ly/1qk2tVR 5 | box-sizing: border-box; 6 | } 7 | 8 | * { 9 | &, 10 | &::after, 11 | &::before { 12 | box-sizing: inherit; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /web/controllers/page_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.PageController do 2 | use Preview.Web, :controller 3 | 4 | alias Preview.Signup 5 | 6 | def index(conn, _params) do 7 | changeset = Signup.changeset(%Signup{}) 8 | 9 | render conn, "index.html", changeset: changeset 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_tint-shade.scss: -------------------------------------------------------------------------------- 1 | // Add percentage of white to a color 2 | @function tint($color, $percent){ 3 | @return mix(white, $color, $percent); 4 | } 5 | 6 | // Add percentage of black to a color 7 | @function shade($color, $percent){ 8 | @return mix(black, $color, $percent); 9 | } 10 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_assign.scss: -------------------------------------------------------------------------------- 1 | @function assign-inputs($inputs, $pseudo: null) { 2 | $list : (); 3 | 4 | @each $input in $inputs { 5 | $input: unquote($input); 6 | $input: if($pseudo, $input + ":" + $pseudo, $input); 7 | $list: append($list, $input, comma); 8 | } 9 | 10 | @return $list; 11 | } -------------------------------------------------------------------------------- /priv/static/sass/bourbon/settings/_prefixer.scss: -------------------------------------------------------------------------------- 1 | // Variable settings for /addons/prefixer.scss 2 | $prefix-for-webkit: true !default; 3 | $prefix-for-mozilla: true !default; 4 | $prefix-for-microsoft: true !default; 5 | $prefix-for-opera: true !default; 6 | $prefix-for-spec: true !default; // required for keyframe mixin 7 | -------------------------------------------------------------------------------- /priv/static/sass/neat/settings/_disable-warnings.scss: -------------------------------------------------------------------------------- 1 | /// Disable all deprecation warnings. Defaults to `false`. Set with a `!global` flag. 2 | /// 3 | /// @type Bool 4 | 5 | $disable-warnings: false !default; 6 | 7 | @mixin -neat-warn($message) { 8 | @if $disable-warnings == false { 9 | @warn "#{$message}"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_is-number.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Checks for a valid number. 4 | /// 5 | /// @param {Number} $value 6 | /// 7 | /// @require {function} contains 8 | 9 | @function is-number($value) { 10 | @return contains("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" 0 1 2 3 4 5 6 7 8 9, $value); 11 | } 12 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_assign-inputs.scss: -------------------------------------------------------------------------------- 1 | @function assign-inputs($inputs, $pseudo: null) { 2 | $list: (); 3 | 4 | @each $input in $inputs { 5 | $input: unquote($input); 6 | $input: if($pseudo, $input + ":" + $pseudo, $input); 7 | $list: append($list, $input, comma); 8 | } 9 | 10 | @return $list; 11 | } 12 | -------------------------------------------------------------------------------- /web/static/css/bourbon/settings/_prefixer.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Global variables to enable or disable vendor prefixes 4 | 5 | $prefix-for-webkit: true !default; 6 | $prefix-for-mozilla: true !default; 7 | $prefix-for-microsoft: true !default; 8 | $prefix-for-opera: true !default; 9 | $prefix-for-spec: true !default; 10 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/addons/_font-family.scss: -------------------------------------------------------------------------------- 1 | $georgia: Georgia, Cambria, "Times New Roman", Times, serif; 2 | $helvetica: "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif; 3 | $lucida-grande: "Lucida Grande", Tahoma, Verdana, Arial, sans-serif; 4 | $monospace: "Bitstream Vera Sans Mono", Consolas, Courier, monospace; 5 | $verdana: Verdana, Geneva, sans-serif; 6 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_backface-visibility.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Backface-visibility mixin 3 | //************************************************************************// 4 | @mixin backface-visibility($visibility) { 5 | @include prefixer(backface-visibility, $visibility, webkit spec); 6 | } 7 | -------------------------------------------------------------------------------- /web/static/css/neat/settings/_disable-warnings.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Disable all deprecation warnings. Defaults to `false`. Set with a `!global` flag. 4 | /// 5 | /// @type Bool 6 | 7 | $disable-warnings: false !default; 8 | 9 | @mixin -neat-warn($message) { 10 | @if $disable-warnings == false { 11 | @warn "#{$message}"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_shape-size-stripper.scss: -------------------------------------------------------------------------------- 1 | @function _shape-size-stripper($shape-size) { 2 | $shape-size-spec: null; 3 | @each $value in $shape-size { 4 | @if ($value == "cover") or ($value == "contain") { 5 | $value: null; 6 | } 7 | $shape-size-spec: "#{$shape-size-spec} #{$value}"; 8 | } 9 | @return $shape-size-spec; 10 | } 11 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_shape-size-stripper.scss: -------------------------------------------------------------------------------- 1 | @function _shape-size-stripper($shape-size) { 2 | $shape-size-spec: null; 3 | @each $value in $shape-size { 4 | @if ($value == "cover") or ($value == "contain") { 5 | $value: null; 6 | } 7 | $shape-size-spec: "#{$shape-size-spec} #{$value}"; 8 | } 9 | @return $shape-size-spec; 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mix artifacts 2 | /_build 3 | /deps 4 | /*.ez 5 | 6 | # Generate on crash by the VM 7 | erl_crash.dump 8 | 9 | # Static artifacts 10 | /node_modules 11 | 12 | # Since we are building js and css from web/static, 13 | # we ignore priv/static/{css,js}. You may want to 14 | # comment this depending on your deployment strategy. 15 | /priv/static/css 16 | /priv/static/js 17 | -------------------------------------------------------------------------------- /web/templates/user/login.html.eex: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "repository": { 3 | }, 4 | "dependencies": { 5 | "brunch": "^1.8.5", 6 | "babel-brunch": "^4.0.0", 7 | "clean-css-brunch": ">= 1.0 < 1.8", 8 | "css-brunch": ">= 1.0 < 1.8", 9 | "javascript-brunch": ">= 1.0 < 1.8", 10 | "sass-brunch": "git://github.com/brunch/sass-brunch.git#master", 11 | "uglify-js-brunch": ">= 1.0 < 1.8" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_is-size.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Checks for a valid CSS size. 4 | /// 5 | /// @param {String} $value 6 | /// 7 | /// @require {function} contains 8 | /// @require {function} is-length 9 | 10 | @function is-size($value) { 11 | @return is-length($value) 12 | or contains("fill" "fit-content" "min-content" "max-content", $value); 13 | } 14 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_is-length.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Checks for a valid CSS length. 4 | /// 5 | /// @param {String} $value 6 | 7 | @function is-length($value) { 8 | @return type-of($value) != "null" and (str-slice($value + "", 1, 4) == "calc" 9 | or index(auto inherit initial 0, $value) 10 | or (type-of($value) == "number" and not(unitless($value)))); 11 | } 12 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_is-num.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Helper for linear-gradient-parser 3 | //************************************************************************// 4 | @function _is-num($char) { 5 | $values: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' 0 1 2 3 4 5 6 7 8 9; 6 | $index: index($values, $char); 7 | @return if($index, true, false); 8 | } 9 | -------------------------------------------------------------------------------- /web/static/css/base/_base.scss: -------------------------------------------------------------------------------- 1 | // Bitters 1.0.0 2 | // http://bitters.bourbon.io 3 | // Copyright 2013-2015 thoughtbot, inc. 4 | // MIT License 5 | 6 | @import "variables"; 7 | 8 | // Neat Settings -- uncomment if using Neat -- must be imported before Neat 9 | // @import "grid-settings"; 10 | 11 | @import "buttons"; 12 | @import "forms"; 13 | @import "lists"; 14 | @import "tables"; 15 | @import "typography"; 16 | @import "flashes"; 17 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_image-rendering.scss: -------------------------------------------------------------------------------- 1 | @mixin image-rendering ($mode:auto) { 2 | 3 | @if ($mode == crisp-edges) { 4 | -ms-interpolation-mode: nearest-neighbor; // IE8+ 5 | image-rendering: -moz-crisp-edges; 6 | image-rendering: -o-crisp-edges; 7 | image-rendering: -webkit-optimize-contrast; 8 | image-rendering: crisp-edges; 9 | } 10 | 11 | @else { 12 | image-rendering: $mode; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/addons/_size.scss: -------------------------------------------------------------------------------- 1 | @mixin size($size) { 2 | $height: nth($size, 1); 3 | $width: $height; 4 | 5 | @if length($size) > 1 { 6 | $height: nth($size, 2); 7 | } 8 | 9 | @if $height == auto or (type-of($height) == number and not unitless($height)) { 10 | height: $height; 11 | } 12 | 13 | @if $width == auto or (type-of($width) == number and not unitless($width)) { 14 | width: $width; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_contains-falsy.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Checks if a list does not contains a value. 4 | /// 5 | /// @access private 6 | /// 7 | /// @param {List} $list 8 | /// The list to check against. 9 | /// 10 | /// @return {Bool} 11 | 12 | @function contains-falsy($list) { 13 | @each $item in $list { 14 | @if not $item { 15 | @return true; 16 | } 17 | } 18 | 19 | @return false; 20 | } 21 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_px-to-rem.scss: -------------------------------------------------------------------------------- 1 | // Convert pixels to rems 2 | // eg. for a relational value of 12px write rem(12) 3 | // Assumes $em-base is the font-size of 4 | 5 | @function rem($pxval) { 6 | @if not unitless($pxval) { 7 | $pxval: strip-units($pxval); 8 | } 9 | 10 | $base: $em-base; 11 | @if not unitless($base) { 12 | $base: strip-units($base); 13 | } 14 | @return ($pxval / $base) * 1rem; 15 | } 16 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_image-rendering.scss: -------------------------------------------------------------------------------- 1 | @mixin image-rendering ($mode:auto) { 2 | 3 | @if ($mode == crisp-edges) { 4 | -ms-interpolation-mode: nearest-neighbor; // IE8+ 5 | image-rendering: -moz-crisp-edges; 6 | image-rendering: -o-crisp-edges; 7 | image-rendering: -webkit-optimize-contrast; 8 | image-rendering: crisp-edges; 9 | } 10 | 11 | @else { 12 | image-rendering: $mode; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_px-to-rem.scss: -------------------------------------------------------------------------------- 1 | // Convert pixels to rems 2 | // eg. for a relational value of 12px write rem(12) 3 | // Assumes $em-base is the font-size of 4 | 5 | @function rem($pxval) { 6 | @if not unitless($pxval) { 7 | $pxval: strip-units($pxval); 8 | } 9 | 10 | $base: $em-base; 11 | @if not unitless($base) { 12 | $base: strip-units($base); 13 | } 14 | @return ($pxval / $base) * 1rem; 15 | } 16 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_strip-units.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Strips the unit from a number. 4 | /// 5 | /// @param {Number (With Unit)} $value 6 | /// 7 | /// @example scss - Usage 8 | /// $dimension: strip-units(10em); 9 | /// 10 | /// @example css - CSS Output 11 | /// $dimension: 10; 12 | /// 13 | /// @return {Number (Unitless)} 14 | 15 | @function strip-units($value) { 16 | @return ($value / ($value * 0 + 1)); 17 | } 18 | -------------------------------------------------------------------------------- /web/static/css/base/_grid-settings.scss: -------------------------------------------------------------------------------- 1 | @import "neat-helpers"; // or "../neat/neat-helpers" when not in Rails 2 | 3 | // Neat Overrides 4 | // $column: 90px; 5 | // $gutter: 30px; 6 | // $grid-columns: 12; 7 | // $max-width: em(1088); 8 | 9 | // Neat Breakpoints 10 | $medium-screen: em(640); 11 | $large-screen: em(860); 12 | 13 | $medium-screen-up: new-breakpoint(min-width $medium-screen 4); 14 | $large-screen-up: new-breakpoint(min-width $large-screen 8); 15 | -------------------------------------------------------------------------------- /priv/static/sass/base/_grid-settings.scss: -------------------------------------------------------------------------------- 1 | @import "neat-helpers"; // or "../neat/neat-helpers" when not in Rails 2 | 3 | // Neat Overrides 4 | // $column: 90px; 5 | // $gutter: 30px; 6 | // $grid-columns: 12; 7 | // $max-width: em(1088); 8 | 9 | // Neat Breakpoints 10 | $medium-screen: em(640); 11 | $large-screen: em(860); 12 | 13 | $medium-screen-up: new-breakpoint(min-width $medium-screen 4); 14 | $large-screen-up: new-breakpoint(min-width $large-screen 8); 15 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_px-to-em.scss: -------------------------------------------------------------------------------- 1 | // Convert pixels to ems 2 | // eg. for a relational value of 12px write em(12) when the parent is 16px 3 | // if the parent is another value say 24px write em(12, 24) 4 | 5 | @function em($pxval, $base: $em-base) { 6 | @if not unitless($pxval) { 7 | $pxval: strip-units($pxval); 8 | } 9 | @if not unitless($base) { 10 | $base: strip-units($base); 11 | } 12 | @return ($pxval / $base) * 1em; 13 | } 14 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_px-to-em.scss: -------------------------------------------------------------------------------- 1 | // Convert pixels to ems 2 | // eg. for a relational value of 12px write em(12) when the parent is 16px 3 | // if the parent is another value say 24px write em(12, 24) 4 | 5 | @function em($pxval, $base: $em-base) { 6 | @if not unitless($pxval) { 7 | $pxval: strip-units($pxval); 8 | } 9 | @if not unitless($base) { 10 | $base: strip-units($base); 11 | } 12 | @return ($pxval / $base) * 1em; 13 | } 14 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/_bourbon-deprecated-upcoming.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // These mixins/functions are deprecated 3 | // They will be removed in the next MAJOR version release 4 | //************************************************************************// 5 | @mixin inline-block { 6 | display: inline-block; 7 | @warn "inline-block mixin is deprecated and will be removed in the next major version release"; 8 | } 9 | -------------------------------------------------------------------------------- /web/views/error_view.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.ErrorView do 2 | use Preview.Web, :view 3 | 4 | def render("404.html", _assigns) do 5 | "Page not found - 404" 6 | end 7 | 8 | def render("500.html", _assigns) do 9 | "Server internal error - 500" 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 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | otp_release: 3 | - 17.3 4 | - 17.4 5 | sudo: false 6 | before_install: 7 | - wget http://s3.hex.pm/builds/elixir/v1.0.4.zip 8 | - unzip -d elixir v1.0.4.zip 9 | before_script: 10 | - export PATH=`pwd`/elixir/bin:$PATH 11 | - mix local.hex --force 12 | - mix deps.get --only test 13 | - psql -c 'create database preview_test;' -U postgres 14 | script: 15 | - mix test 16 | after_script: 17 | - mix deps.get --only docs 18 | - MIX_ENV=docs mix inch.report 19 | -------------------------------------------------------------------------------- /priv/static/sass/base/_tables.scss: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: collapse; 3 | margin: ($base-spacing / 2) 0; 4 | table-layout: fixed; 5 | width: 100%; 6 | } 7 | 8 | th { 9 | border-bottom: 1px solid darken($base-border-color, 15); 10 | font-weight: bold; 11 | padding: ($base-spacing / 2) 0; 12 | text-align: left; 13 | } 14 | 15 | td { 16 | border-bottom: $base-border; 17 | padding: ($base-spacing / 2) 0; 18 | } 19 | 20 | tr, 21 | td, 22 | th { 23 | vertical-align: middle; 24 | } 25 | -------------------------------------------------------------------------------- /web/static/css/neat/grid/_fill-parent.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Forces the element to fill its parent container. 4 | /// 5 | /// @example scss - Usage 6 | /// .element { 7 | /// @include fill-parent; 8 | /// } 9 | /// 10 | /// @example css - CSS Output 11 | /// .element { 12 | /// width: 100%; 13 | /// box-sizing: border-box; 14 | /// } 15 | 16 | @mixin fill-parent() { 17 | width: 100%; 18 | 19 | @if $border-box-sizing == false { 20 | box-sizing: border-box; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /config/prod.secret.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | # In this file, we keep production configuration that 4 | # you likely want to automate and keep it away from 5 | # your version control system. 6 | config :preview, Preview.Endpoint, 7 | secret_key_base: "Wt6W9dET4zT3PT+cDV5NbF/LabL1g/K2Pk5VGog0oZa3XJ2FiYfPn2wIVm+iYCCU" 8 | 9 | # Configure your database 10 | config :preview, Preview.Repo, 11 | adapter: Ecto.Adapters.Postgres, 12 | username: "postgres", 13 | password: "postgres", 14 | database: "preview_prod" 15 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_grid-width.scss: -------------------------------------------------------------------------------- 1 | @function grid-width($n) { 2 | @return $n * $gw-column + ($n - 1) * $gw-gutter; 3 | } 4 | 5 | // The $gw-column and $gw-gutter variables must be defined in your base stylesheet to properly use the grid-width function. 6 | // 7 | // $gw-column: 100px; // Column Width 8 | // $gw-gutter: 40px; // Gutter Width 9 | // 10 | // div { 11 | // width: grid-width(4); // returns 520px; 12 | // margin-left: $gw-gutter; // returns 40px; 13 | // } 14 | -------------------------------------------------------------------------------- /test/models/signup_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Preview.SignupTest do 2 | use Preview.ModelCase 3 | 4 | alias Preview.Signup 5 | 6 | @valid_attrs %{email: "yoda@example.com"} 7 | @invalid_attrs %{} 8 | 9 | test "changeset with valid attributes" do 10 | changeset = Signup.changeset(%Signup{}, @valid_attrs) 11 | assert changeset.valid? 12 | end 13 | 14 | test "changeset with invalid attributes" do 15 | changeset = Signup.changeset(%Signup{}, @invalid_attrs) 16 | refute changeset.valid? 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/models/user_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Preview.UserTest do 2 | use Preview.ModelCase 3 | 4 | alias Preview.User 5 | 6 | @valid_attrs %{email: "yoda@example.com", password: "hashed"} 7 | @invalid_attrs %{} 8 | 9 | test "changeset with valid attributes" do 10 | changeset = User.changeset(%User{}, @valid_attrs) 11 | assert changeset.valid? 12 | end 13 | 14 | test "changeset with invalid attributes" do 15 | changeset = User.changeset(%User{}, @invalid_attrs) 16 | refute changeset.valid? 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_font-feature-settings.scss: -------------------------------------------------------------------------------- 1 | // Font feature settings mixin and property default. 2 | // Examples: @include font-feature-settings("liga"); 3 | // @include font-feature-settings("lnum" false); 4 | // @include font-feature-settings("pnum" 1, "kern" 0); 5 | // @include font-feature-settings("ss01", "ss02"); 6 | 7 | @mixin font-feature-settings($settings...) { 8 | @if length($settings) == 0 { $settings: none; } 9 | @include prefixer(font-feature-settings, $settings, webkit moz ms spec); 10 | } -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_hidpi-media-query.scss: -------------------------------------------------------------------------------- 1 | // HiDPI mixin. Default value set to 1.3 to target Google Nexus 7 (http://bjango.com/articles/min-device-pixel-ratio/) 2 | @mixin hidpi($ratio: 1.3) { 3 | @media only screen and (-webkit-min-device-pixel-ratio: $ratio), 4 | only screen and (min--moz-device-pixel-ratio: $ratio), 5 | only screen and (-o-min-device-pixel-ratio: #{$ratio}/1), 6 | only screen and (min-resolution: #{round($ratio*96)}dpi), 7 | only screen and (min-resolution: #{$ratio}dppx) { 8 | @content; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /web/static/css/base/_tables.scss: -------------------------------------------------------------------------------- 1 | table { 2 | @include font-feature-settings("kern", "liga", "tnum"); 3 | border-collapse: collapse; 4 | margin: $small-spacing 0; 5 | table-layout: fixed; 6 | width: 100%; 7 | } 8 | 9 | th { 10 | border-bottom: 1px solid darken($base-border-color, 15%); 11 | font-weight: 600; 12 | padding: $small-spacing 0; 13 | text-align: left; 14 | } 15 | 16 | td { 17 | border-bottom: $base-border; 18 | padding: $small-spacing 0; 19 | } 20 | 21 | tr, 22 | td, 23 | th { 24 | vertical-align: middle; 25 | } 26 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_hidpi-media-query.scss: -------------------------------------------------------------------------------- 1 | // HiDPI mixin. Default value set to 1.3 to target Google Nexus 7 (http://bjango.com/articles/min-device-pixel-ratio/) 2 | @mixin hidpi($ratio: 1.3) { 3 | @media only screen and (-webkit-min-device-pixel-ratio: $ratio), 4 | only screen and (min--moz-device-pixel-ratio: $ratio), 5 | only screen and (-o-min-device-pixel-ratio: #{$ratio}/1), 6 | only screen and (min-resolution: round($ratio * 96dpi)), 7 | only screen and (min-resolution: $ratio * 1dppx) { 8 | @content; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /priv/static/sass/base/extends/_button.scss: -------------------------------------------------------------------------------- 1 | %button { 2 | -webkit-font-smoothing: antialiased; 3 | background-color: $base-button-color; 4 | border-radius: $base-border-radius; 5 | color: white; 6 | display: inline-block; 7 | font-size: $base-font-size; 8 | font-weight: bold; 9 | line-height: 1; 10 | padding: 0.75em 1em; 11 | text-decoration: none; 12 | 13 | &:hover { 14 | background-color: $hover-button-color; 15 | color: white; 16 | } 17 | 18 | &:disabled { 19 | cursor: not-allowed; 20 | opacity: 0.5; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_color-lightness.scss: -------------------------------------------------------------------------------- 1 | // Programatically determines whether a color is light or dark 2 | // Returns a boolean 3 | // More details here http://robots.thoughtbot.com/closer-look-color-lightness 4 | 5 | @function is-light($hex-color) { 6 | $-local-red: red(rgba($hex-color, 1.0)); 7 | $-local-green: green(rgba($hex-color, 1.0)); 8 | $-local-blue: blue(rgba($hex-color, 1.0)); 9 | 10 | $-local-lightness: ($-local-red * 0.2126 + $-local-green * 0.7152 + $-local-blue * 0.0722) / 255; 11 | 12 | @return $-local-lightness > .6; 13 | } 14 | -------------------------------------------------------------------------------- /priv/static/sass/neat/grid/_fill-parent.scss: -------------------------------------------------------------------------------- 1 | /// Forces the element to fill its parent container. 2 | /// 3 | /// @example scss - Usage 4 | /// .element { 5 | /// @include fill-parent; 6 | /// } 7 | /// 8 | /// @example css - CSS Output 9 | /// .element { 10 | /// width: 100%; 11 | /// -webkit-box-sizing: border-box; 12 | /// -moz-box-sizing: border-box; 13 | /// box-sizing: border-box; 14 | /// } 15 | 16 | @mixin fill-parent() { 17 | width: 100%; 18 | 19 | @if $border-box-sizing == false { 20 | @include box-sizing(border-box); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web/templates/page/index.html.eex: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

Preview

6 |

Phoenix 1.0 starter app built using the Elixir Language and the Bourbon family

7 |
8 | <%= render Preview.SignupView, "form.html", changeset: @changeset, 9 | action: signup_path(@conn, :create) %> 10 |
11 |
12 | 15 |
16 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_gradient-positions-parser.scss: -------------------------------------------------------------------------------- 1 | @function _gradient-positions-parser($gradient-type, $gradient-positions) { 2 | @if $gradient-positions 3 | and ($gradient-type == linear) 4 | and (type-of($gradient-positions) != color) { 5 | $gradient-positions: _linear-positions-parser($gradient-positions); 6 | } 7 | @else if $gradient-positions 8 | and ($gradient-type == radial) 9 | and (type-of($gradient-positions) != color) { 10 | $gradient-positions: _radial-positions-parser($gradient-positions); 11 | } 12 | @return $gradient-positions; 13 | } 14 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_gradient-positions-parser.scss: -------------------------------------------------------------------------------- 1 | @function _gradient-positions-parser($gradient-type, $gradient-positions) { 2 | @if $gradient-positions 3 | and ($gradient-type == linear) 4 | and (type-of($gradient-positions) != color) { 5 | $gradient-positions: _linear-positions-parser($gradient-positions); 6 | } 7 | @else if $gradient-positions 8 | and ($gradient-type == radial) 9 | and (type-of($gradient-positions) != color) { 10 | $gradient-positions: _radial-positions-parser($gradient-positions); 11 | } 12 | @return $gradient-positions; 13 | } 14 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_transform.scss: -------------------------------------------------------------------------------- 1 | @mixin transform($property: none) { 2 | // none | 3 | @include prefixer(transform, $property, webkit moz ms o spec); 4 | } 5 | 6 | @mixin transform-origin($axes: 50%) { 7 | // x-axis - left | center | right | length | % 8 | // y-axis - top | center | bottom | length | % 9 | // z-axis - length 10 | @include prefixer(transform-origin, $axes, webkit moz ms o spec); 11 | } 12 | 13 | @mixin transform-style ($style: flat) { 14 | @include prefixer(transform-style, $style, webkit moz ms o spec); 15 | } 16 | -------------------------------------------------------------------------------- /web/static/css/base/_lists.scss: -------------------------------------------------------------------------------- 1 | ul, 2 | ol { 3 | list-style-type: none; 4 | margin: 0; 5 | padding: 0; 6 | 7 | &%default-ul { 8 | list-style-type: disc; 9 | margin-bottom: $small-spacing; 10 | padding-left: $base-spacing; 11 | } 12 | 13 | &%default-ol { 14 | list-style-type: decimal; 15 | margin-bottom: $small-spacing; 16 | padding-left: $base-spacing; 17 | } 18 | } 19 | 20 | dl { 21 | margin-bottom: $small-spacing; 22 | 23 | dt { 24 | font-weight: bold; 25 | margin-top: $small-spacing; 26 | } 27 | 28 | dd { 29 | margin: 0; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_transform.scss: -------------------------------------------------------------------------------- 1 | @mixin transform($property: none) { 2 | // none | 3 | @include prefixer(transform, $property, webkit moz ms o spec); 4 | } 5 | 6 | @mixin transform-origin($axes: 50%) { 7 | // x-axis - left | center | right | length | % 8 | // y-axis - top | center | bottom | length | % 9 | // z-axis - length 10 | @include prefixer(transform-origin, $axes, webkit moz ms o spec); 11 | } 12 | 13 | @mixin transform-style($style: flat) { 14 | @include prefixer(transform-style, $style, webkit moz ms o spec); 15 | } 16 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_clearfix.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Provides an easy way to include a clearfix for containing floats. 4 | /// 5 | /// @link http://cssmojo.com/latest_new_clearfix_so_far/ 6 | /// 7 | /// @example scss - Usage 8 | /// .element { 9 | /// @include clearfix; 10 | /// } 11 | /// 12 | /// @example css - CSS Output 13 | /// .element::after { 14 | /// clear: both; 15 | /// content: ""; 16 | /// display: table; 17 | /// } 18 | 19 | @mixin clearfix { 20 | &::after { 21 | clear: both; 22 | content: ""; 23 | display: table; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_tint.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Mixes a color with white. 4 | /// 5 | /// @param {Color} $color 6 | /// 7 | /// @param {Number (Percentage)} $percent 8 | /// The amount of white to be mixed in. 9 | /// 10 | /// @example scss - Usage 11 | /// .element { 12 | /// background-color: tint(#6ecaa6, 40%); 13 | /// } 14 | /// 15 | /// @example css - CSS Output 16 | /// .element { 17 | /// background-color: #a8dfc9; 18 | /// } 19 | /// 20 | /// @return {Color} 21 | 22 | @function tint($color, $percent) { 23 | @return mix(#fff, $color, $percent); 24 | } 25 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_shade.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Mixes a color with black. 4 | /// 5 | /// @param {Color} $color 6 | /// 7 | /// @param {Number (Percentage)} $percent 8 | /// The amount of black to be mixed in. 9 | /// 10 | /// @example scss - Usage 11 | /// .element { 12 | /// background-color: shade(#ffbb52, 60%); 13 | /// } 14 | /// 15 | /// @example css - CSS Output 16 | /// .element { 17 | /// background-color: #664a20; 18 | /// } 19 | /// 20 | /// @return {Color} 21 | 22 | @function shade($color, $percent) { 23 | @return mix(#000, $color, $percent); 24 | } 25 | -------------------------------------------------------------------------------- /priv/static/sass/base/_lists.scss: -------------------------------------------------------------------------------- 1 | ul, 2 | ol { 3 | margin: 0; 4 | padding: 0; 5 | list-style-type: none; 6 | 7 | &%default-ul { 8 | list-style-type: disc; 9 | margin-bottom: $base-spacing / 2; 10 | padding-left: $base-spacing; 11 | } 12 | 13 | &%default-ol { 14 | list-style-type: decimal; 15 | margin-bottom: $base-spacing / 2; 16 | padding-left: $base-spacing; 17 | } 18 | } 19 | 20 | dl { 21 | margin-bottom: $base-spacing / 2; 22 | 23 | dt { 24 | font-weight: bold; 25 | margin-top: $base-spacing / 2; 26 | } 27 | 28 | dd { 29 | margin: 0; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_unpack.scss: -------------------------------------------------------------------------------- 1 | // Convert shorthand to the 4-value syntax 2 | 3 | @function unpack($shorthand) { 4 | @if length($shorthand) == 1 { 5 | @return nth($shorthand, 1) nth($shorthand, 1) nth($shorthand, 1) nth($shorthand, 1); 6 | } 7 | @else if length($shorthand) == 2 { 8 | @return nth($shorthand, 1) nth($shorthand, 2) nth($shorthand, 1) nth($shorthand, 2); 9 | } 10 | @else if length($shorthand) == 3 { 11 | @return nth($shorthand, 1) nth($shorthand, 2) nth($shorthand, 3) nth($shorthand, 2); 12 | } 13 | @else { 14 | @return $shorthand; 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_radial-positions-parser.scss: -------------------------------------------------------------------------------- 1 | @function _radial-positions-parser($gradient-pos) { 2 | $shape-size: nth($gradient-pos, 1); 3 | $pos: nth($gradient-pos, 2); 4 | $shape-size-spec: _shape-size-stripper($shape-size); 5 | 6 | $pre-spec: unquote(if($pos, "#{$pos}, ", null)) 7 | unquote(if($shape-size, "#{$shape-size},", null)); 8 | $pos-spec: if($pos, "at #{$pos}", null); 9 | 10 | $spec: "#{$shape-size-spec} #{$pos-spec}"; 11 | 12 | // Add comma 13 | @if ($spec != ' ') { 14 | $spec: "#{$spec}," 15 | } 16 | 17 | @return $pre-spec $spec; 18 | } 19 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_font-face.scss: -------------------------------------------------------------------------------- 1 | @mixin font-face( 2 | $font-family, 3 | $file-path, 4 | $weight: normal, 5 | $style: normal, 6 | $asset-pipeline: $asset-pipeline, 7 | $file-formats: eot woff2 woff ttf svg) { 8 | 9 | $font-url-prefix: font-url-prefixer($asset-pipeline); 10 | 11 | @font-face { 12 | font-family: $font-family; 13 | font-style: $style; 14 | font-weight: $weight; 15 | 16 | src: font-source-declaration( 17 | $font-family, 18 | $file-path, 19 | $asset-pipeline, 20 | $file-formats, 21 | $font-url-prefix 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_radial-positions-parser.scss: -------------------------------------------------------------------------------- 1 | @function _radial-positions-parser($gradient-pos) { 2 | $shape-size: nth($gradient-pos, 1); 3 | $pos: nth($gradient-pos, 2); 4 | $shape-size-spec: _shape-size-stripper($shape-size); 5 | 6 | $pre-spec: unquote(if($pos, "#{$pos}, ", null)) 7 | unquote(if($shape-size, "#{$shape-size},", null)); 8 | $pos-spec: if($pos, "at #{$pos}", null); 9 | 10 | $spec: "#{$shape-size-spec} #{$pos-spec}"; 11 | 12 | // Add comma 13 | @if ($spec != " ") { 14 | $spec: "#{$spec},"; 15 | } 16 | 17 | @return $pre-spec $spec; 18 | } 19 | -------------------------------------------------------------------------------- /web/static/css/neat/_neat.scss: -------------------------------------------------------------------------------- 1 | // Neat 1.7.2 2 | // http://neat.bourbon.io 3 | // Copyright 2012-2015 thoughtbot, inc. 4 | // MIT License 5 | 6 | // Helpers 7 | @import "neat-helpers"; 8 | 9 | // Grid 10 | @import "grid/private"; 11 | @import "grid/box-sizing"; 12 | @import "grid/omega"; 13 | @import "grid/outer-container"; 14 | @import "grid/span-columns"; 15 | @import "grid/row"; 16 | @import "grid/shift"; 17 | @import "grid/pad"; 18 | @import "grid/fill-parent"; 19 | @import "grid/media"; 20 | @import "grid/to-deprecate"; 21 | @import "grid/visual-grid"; 22 | @import "grid/display-context"; 23 | @import "grid/direction-context"; 24 | -------------------------------------------------------------------------------- /priv/static/sass/neat/_neat.scss: -------------------------------------------------------------------------------- 1 | /* Neat 1.7.0 2 | * http://neat.bourbon.io 3 | * Copyright 2012-2014 thoughtbot, inc. 4 | * MIT License */ 5 | 6 | // Helpers 7 | @import "neat-helpers"; 8 | 9 | // Grid 10 | @import "grid/private"; 11 | @import "grid/box-sizing"; 12 | @import "grid/omega"; 13 | @import "grid/outer-container"; 14 | @import "grid/span-columns"; 15 | @import "grid/row"; 16 | @import "grid/shift"; 17 | @import "grid/pad"; 18 | @import "grid/fill-parent"; 19 | @import "grid/media"; 20 | @import "grid/to-deprecate"; 21 | @import "grid/visual-grid"; 22 | @import "grid/display-context"; 23 | @import "grid/direction-context"; 24 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_contains.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Checks if a list contains a value(s). 4 | /// 5 | /// @access private 6 | /// 7 | /// @param {List} $list 8 | /// The list to check against. 9 | /// 10 | /// @param {List} $values 11 | /// A single value or list of values to check for. 12 | /// 13 | /// @example scss - Usage 14 | /// contains($list, $value) 15 | /// 16 | /// @return {Bool} 17 | 18 | @function contains($list, $values...) { 19 | @each $value in $values { 20 | @if type-of(index($list, $value)) != "number" { 21 | @return false; 22 | } 23 | } 24 | 25 | @return true; 26 | } 27 | -------------------------------------------------------------------------------- /priv/static/sass/base/_base.scss: -------------------------------------------------------------------------------- 1 | /* Bitters 0.10.0 2 | * http://bitters.bourbon.io 3 | * Copyright 2013–2014 thoughtbot, inc. 4 | * MIT License */ 5 | 6 | // Variables 7 | @import "variables"; 8 | 9 | // Neat Settings -- uncomment if using Neat -- must be imported before Neat 10 | // @import "grid-settings"; 11 | 12 | // Extends 13 | @import "extends/button"; 14 | @import "extends/clearfix"; 15 | @import "extends/errors"; 16 | @import "extends/flashes"; 17 | @import "extends/hide-text"; 18 | 19 | // Typography and Elements 20 | @import "typography"; 21 | @import "forms"; 22 | @import "tables"; 23 | @import "lists"; 24 | @import "buttons"; 25 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/addons/_clearfix.scss: -------------------------------------------------------------------------------- 1 | // Modern micro clearfix provides an easy way to contain floats without adding additional markup. 2 | // 3 | // Example usage: 4 | // 5 | // // Contain all floats within .wrapper 6 | // .wrapper { 7 | // @include clearfix; 8 | // .content, 9 | // .sidebar { 10 | // float : left; 11 | // } 12 | // } 13 | 14 | @mixin clearfix { 15 | &:after { 16 | content:""; 17 | display:table; 18 | clear:both; 19 | } 20 | } 21 | 22 | // Acknowledgements 23 | // Beat *that* clearfix: [Thierry Koblentz](http://www.css-101.org/articles/clearfix/latest-new-clearfix-so-far.php) 24 | -------------------------------------------------------------------------------- /web/templates/signup/index.html.eex: -------------------------------------------------------------------------------- 1 |
2 |

Listing signups

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <%= for signup <- @signups do %> 13 | 14 | 15 | 16 | 17 | <% end %> 18 | 19 |
EmailSignup Date
<%= signup.email %><%= signup.inserted_at %>
20 | 21 |

<%= link "Download CSV", to: signup_path(@conn, :csv_export), method: :post %>

22 |

<%= link "Logout", to: logout_path(@conn, :logout), method: :post %>

23 |
24 | -------------------------------------------------------------------------------- /web/models/signup.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.Signup do 2 | use Preview.Web, :model 3 | 4 | alias Preview.Repo 5 | 6 | schema "signups" do 7 | field :email, :string 8 | 9 | timestamps 10 | end 11 | 12 | @required_fields ~w(email) 13 | @optional_fields ~w() 14 | 15 | @doc """ 16 | Creates a changeset based on the `model` and `params`. 17 | 18 | If `params` are nil, an invalid changeset is returned 19 | with no validation performed. 20 | """ 21 | def changeset(model, params \\ :empty) do 22 | model 23 | |> cast(params, @required_fields, @optional_fields) 24 | |> validate_format(:email, ~r/@/) 25 | |> validate_unique(:email, on: Repo) 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_is-light.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Programatically determines whether a color is light or dark. 4 | /// 5 | /// @link http://robots.thoughtbot.com/closer-look-color-lightness 6 | /// 7 | /// @param {Color (Hex)} $color 8 | /// 9 | /// @example scss - Usage 10 | /// is-light($color) 11 | /// 12 | /// @return {Bool} 13 | 14 | @function is-light($hex-color) { 15 | $-local-red: red(rgba($hex-color, 1)); 16 | $-local-green: green(rgba($hex-color, 1)); 17 | $-local-blue: blue(rgba($hex-color, 1)); 18 | $-local-lightness: ($-local-red * 0.2126 + $-local-green * 0.7152 + $-local-blue * 0.0722) / 255; 19 | 20 | @return $-local-lightness > 0.6; 21 | } 22 | -------------------------------------------------------------------------------- /lib/preview/mailer.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.Mailer do 2 | use Mailgun.Client, domain: Application.get_env(:preview, :mailgun_domain), 3 | key: Application.get_env(:preview, :mailgun_key) 4 | 5 | @from "yoda@example.com" 6 | 7 | def send_welcome_email(signup) do 8 | send_email to: signup.email, 9 | from: @from, 10 | subject: "Preview registration", 11 | html: "

Welcome to Preview!

12 |

View or contribute to the source code on GitHub.

13 |

Follow me on Twitter @mutablestate

." 14 | end 15 | end 16 | 17 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_margin.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Provides a quick method for targeting `margin` on specific sides of a box. Use a `null` value to “skip” a side. 4 | /// 5 | /// @param {Arglist} $vals 6 | /// List of arguments 7 | /// 8 | /// @example scss - Usage 9 | /// .element { 10 | /// @include margin(null 10px 3em 20vh); 11 | /// } 12 | /// 13 | /// @example css - CSS Output 14 | /// .element { 15 | /// margin-bottom: 3em; 16 | /// margin-left: 20vh; 17 | /// margin-right: 10px; 18 | /// } 19 | /// 20 | /// @require {mixin} directional-property 21 | /// 22 | /// @output `margin` 23 | 24 | @mixin margin($vals...) { 25 | @include directional-property(margin, false, $vals...); 26 | } 27 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_text-decoration.scss: -------------------------------------------------------------------------------- 1 | @mixin text-decoration($value) { 2 | // || || 3 | @include prefixer(text-decoration, $value, moz); 4 | } 5 | 6 | @mixin text-decoration-line($line: none) { 7 | // none || underline || overline || line-through 8 | @include prefixer(text-decoration-line, $line, moz); 9 | } 10 | 11 | @mixin text-decoration-style($style: solid) { 12 | // solid || double || dotted || dashed || wavy 13 | @include prefixer(text-decoration-style, $style, moz webkit); 14 | } 15 | 16 | @mixin text-decoration-color($color: currentColor) { 17 | // currentColor || 18 | @include prefixer(text-decoration-color, $color, moz); 19 | } 20 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_border-width.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Provides a quick method for targeting `border-width` on specific sides of a box. Use a `null` value to “skip” a side. 4 | /// 5 | /// @param {Arglist} $vals 6 | /// List of arguments 7 | /// 8 | /// @example scss - Usage 9 | /// .element { 10 | /// @include border-width(1em null 20px); 11 | /// } 12 | /// 13 | /// @example css - CSS Output 14 | /// .element { 15 | /// border-bottom-width: 20px; 16 | /// border-top-width: 1em; 17 | /// } 18 | /// 19 | /// @require {mixin} directional-property 20 | /// 21 | /// @output `border-width` 22 | 23 | @mixin border-width($vals...) { 24 | @include directional-property(border, width, $vals...); 25 | } 26 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_padding.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Provides a quick method for targeting `padding` on specific sides of a box. Use a `null` value to “skip” a side. 4 | /// 5 | /// @param {Arglist} $vals 6 | /// List of arguments 7 | /// 8 | /// @example scss - Usage 9 | /// .element { 10 | /// @include padding(12vh null 10px 5%); 11 | /// } 12 | /// 13 | /// @example css - CSS Output 14 | /// .element { 15 | /// padding-bottom: 10px; 16 | /// padding-left: 5%; 17 | /// padding-top: 12vh; 18 | /// } 19 | /// 20 | /// @require {mixin} directional-property 21 | /// 22 | /// @output `padding` 23 | 24 | @mixin padding($vals...) { 25 | @include directional-property(padding, false, $vals...); 26 | } 27 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_border-style.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Provides a quick method for targeting `border-style` on specific sides of a box. Use a `null` value to “skip” a side. 4 | /// 5 | /// @param {Arglist} $vals 6 | /// List of arguments 7 | /// 8 | /// @example scss - Usage 9 | /// .element { 10 | /// @include border-style(dashed null solid); 11 | /// } 12 | /// 13 | /// @example css - CSS Output 14 | /// .element { 15 | /// border-bottom-style: solid; 16 | /// border-top-style: dashed; 17 | /// } 18 | /// 19 | /// @require {mixin} directional-property 20 | /// 21 | /// @output `border-style` 22 | 23 | @mixin border-style($vals...) { 24 | @include directional-property(border, style, $vals...); 25 | } 26 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_word-wrap.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Provides an easy way to change the `word-wrap` property. 4 | /// 5 | /// @param {String} $wrap [break-word] 6 | /// Value for the `word-break` property. 7 | /// 8 | /// @example scss - Usage 9 | /// .wrapper { 10 | /// @include word-wrap(break-word); 11 | /// } 12 | /// 13 | /// @example css - CSS Output 14 | /// .wrapper { 15 | /// overflow-wrap: break-word; 16 | /// word-break: break-all; 17 | /// word-wrap: break-word; 18 | /// } 19 | 20 | @mixin word-wrap($wrap: break-word) { 21 | overflow-wrap: $wrap; 22 | word-wrap: $wrap; 23 | 24 | @if $wrap == break-word { 25 | word-break: break-all; 26 | } @else { 27 | word-break: $wrap; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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 :preview, Preview.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 :preview, Preview.Repo, 14 | adapter: Ecto.Adapters.Postgres, 15 | username: "postgres", 16 | password: "postgres", 17 | database: "preview_test", 18 | pool: Ecto.Adapters.SQL.Sandbox, # Use a sandbox for transactional testing 19 | size: 1 20 | 21 | # Configure mailgun 22 | config :preview, 23 | mailgun_domain: System.get_env("MAILGUN_DOMAIN"), 24 | mailgun_key: System.get_env("MAILGUN_KEY") 25 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{"comeonin": {:hex, :comeonin, "1.1.2"}, 2 | "cowboy": {:hex, :cowboy, "1.0.2"}, 3 | "cowlib": {:hex, :cowlib, "1.0.1"}, 4 | "csvlixir": {:hex, :csvlixir, "2.0.0"}, 5 | "decimal": {:hex, :decimal, "1.1.0"}, 6 | "ecto": {:hex, :ecto, "0.15.0"}, 7 | "fs": {:hex, :fs, "0.9.2"}, 8 | "mailgun": {:hex, :mailgun, "0.1.1"}, 9 | "phoenix": {:hex, :phoenix, "1.0.0"}, 10 | "phoenix_ecto": {:hex, :phoenix_ecto, "0.9.0"}, 11 | "phoenix_html": {:hex, :phoenix_html, "2.1.0"}, 12 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.0"}, 13 | "plug": {:hex, :plug, "1.0.0"}, 14 | "poison": {:hex, :poison, "1.4.0"}, 15 | "poolboy": {:hex, :poolboy, "1.5.1"}, 16 | "postgrex": {:hex, :postgrex, "0.9.1"}, 17 | "ranch": {:hex, :ranch, "1.1.0"}} 18 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_font-stacks.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Georgia font stack. 4 | /// 5 | /// @type List 6 | 7 | $georgia: "Georgia", "Cambria", "Times New Roman", "Times", serif; 8 | 9 | /// Helvetica font stack. 10 | /// 11 | /// @type List 12 | 13 | $helvetica: "Helvetica Neue", "Helvetica", "Roboto", "Arial", sans-serif; 14 | 15 | /// Lucida Grande font stack. 16 | /// 17 | /// @type List 18 | 19 | $lucida-grande: "Lucida Grande", "Tahoma", "Verdana", "Arial", sans-serif; 20 | 21 | /// Monospace font stack. 22 | /// 23 | /// @type List 24 | 25 | $monospace: "Bitstream Vera Sans Mono", "Consolas", "Courier", monospace; 26 | 27 | /// Verdana font stack. 28 | /// 29 | /// @type List 30 | 31 | $verdana: "Verdana", "Geneva", sans-serif; 32 | -------------------------------------------------------------------------------- /priv/static/sass/neat/grid/_pad.scss: -------------------------------------------------------------------------------- 1 | /// Adds padding to the element. 2 | /// 3 | /// @param {List} $padding (flex-gutter()) 4 | /// A list of padding value(s) to use. Passing `default` in the list will result in using the gutter width as a padding value. 5 | /// 6 | /// @example scss - Usage 7 | /// .element { 8 | /// @include pad(30px -20px 10px default); 9 | /// } 10 | /// 11 | /// @example css - CSS Output 12 | /// .element { 13 | /// padding: 30px -20px 10px 2.35765%; 14 | /// } 15 | 16 | @mixin pad($padding: flex-gutter()) { 17 | $padding-list: null; 18 | @each $value in $padding { 19 | $value: if($value == 'default', flex-gutter(), $value); 20 | $padding-list: join($padding-list, $value); 21 | } 22 | padding: $padding-list; 23 | } 24 | -------------------------------------------------------------------------------- /priv/static/sass/base/extends/_flashes.scss: -------------------------------------------------------------------------------- 1 | @mixin flash($color) { 2 | background: $color; 3 | color: darken($color, 60); 4 | 5 | a { 6 | color: darken($color, 70); 7 | 8 | &:hover { 9 | color: darken($color, 90); 10 | } 11 | } 12 | } 13 | 14 | %flash-base { 15 | font-weight: bold; 16 | margin-bottom: $base-spacing / 2; 17 | padding: $base-spacing / 2; 18 | } 19 | 20 | %flash-alert { 21 | @extend %flash-base; 22 | @include flash($alert-color); 23 | } 24 | 25 | %flash-error { 26 | @extend %flash-base; 27 | @include flash($error-color); 28 | } 29 | 30 | %flash-notice { 31 | @extend %flash-base; 32 | @include flash($notice-color); 33 | } 34 | 35 | %flash-success { 36 | @extend %flash-base; 37 | @include flash($success-color); 38 | } 39 | -------------------------------------------------------------------------------- /web/static/css/base/_buttons.scss: -------------------------------------------------------------------------------- 1 | #{$all-button-inputs}, 2 | button { 3 | @include appearance(none); 4 | -webkit-font-smoothing: antialiased; 5 | background-color: $action-color; 6 | border-radius: $base-border-radius; 7 | border: none; 8 | color: #fff; 9 | cursor: pointer; 10 | display: inline-block; 11 | font-family: $base-font-family; 12 | font-size: $base-font-size; 13 | font-weight: 600; 14 | line-height: 1; 15 | padding: 0.75em 1em; 16 | text-decoration: none; 17 | user-select: none; 18 | vertical-align: middle; 19 | white-space: nowrap; 20 | 21 | &:hover, 22 | &:focus { 23 | background-color: darken($action-color, 15%); 24 | color: #fff; 25 | } 26 | 27 | &:disabled { 28 | cursor: not-allowed; 29 | opacity: 0.5; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /priv/static/sass/neat/grid/_display-context.scss: -------------------------------------------------------------------------------- 1 | /// Changes the display property used by other mixins called in the code block argument. 2 | /// 3 | /// @param {String} $display (block) 4 | /// Display value to be used within the block. Can be `table` or `block`. 5 | /// 6 | /// @example scss 7 | /// @include display(table) { 8 | /// .display-table { 9 | /// @include span-columns(6); 10 | /// } 11 | /// } 12 | /// 13 | /// @example css 14 | /// .display-table { 15 | /// display: table-cell; 16 | /// ... 17 | /// } 18 | 19 | @mixin display-context($display: block) { 20 | $scope-display: $container-display-table; 21 | $container-display-table: $display == table !global; 22 | 23 | @content; 24 | 25 | $container-display-table: $scope-display !global; 26 | } 27 | -------------------------------------------------------------------------------- /web/static/css/neat/grid/_pad.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Adds padding to the element. 4 | /// 5 | /// @param {List} $padding [flex-gutter()] 6 | /// A list of padding value(s) to use. Passing `default` in the list will result in using the gutter width as a padding value. 7 | /// 8 | /// @example scss - Usage 9 | /// .element { 10 | /// @include pad(30px -20px 10px default); 11 | /// } 12 | /// 13 | /// @example css - CSS Output 14 | /// .element { 15 | /// padding: 30px -20px 10px 2.35765%; 16 | /// } 17 | 18 | @mixin pad($padding: flex-gutter()) { 19 | $padding-list: null; 20 | @each $value in $padding { 21 | $value: if($value == 'default', flex-gutter(), $value); 22 | $padding-list: join($padding-list, $value); 23 | } 24 | padding: $padding-list; 25 | } 26 | -------------------------------------------------------------------------------- /priv/static/css/flash.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "AAQA,yDAAY;EACV,OAAO,EAAE,KAAK;EACd,WAAW,EAAE,IAAI;EACjB,aAAa,EAAE,MAAiB;EAChC,OAAO,EAAE,MAAiB;;AAM1B,YAAiB;EAEf,UAAU,EAlBC,OAAO;EAmBlB,KAAK,EAAE,OAAkB;EAEzB,cAAE;IACA,KAAK,EAAE,OAAkB;IACzB,aAAa,EAAE,8BAAgD;IAE/D,oBAAQ;MACN,KAAK,EAAE,KAAkB;;AAV/B,YAAiB;EAEf,UAAU,EAnBF,OAAO;EAoBf,KAAK,EAAE,OAAkB;EAEzB,cAAE;IACA,KAAK,EAAE,OAAkB;IACzB,aAAa,EAAE,gCAAgD;IAE/D,oBAAQ;MACN,KAAK,EAAE,OAAkB;;AAV/B,aAAiB;EAEf,UAAU,EAAE,OAAM;EAClB,KAAK,EAAE,OAAkB;EAEzB,eAAE;IACA,KAAK,EAAE,OAAkB;IACzB,aAAa,EAAE,+BAAgD;IAE/D,qBAAQ;MACN,KAAK,EAAE,OAAkB;;AAV/B,cAAiB;EAEf,UAAU,EAjBA,OAAO;EAkBjB,KAAK,EAAE,OAAkB;EAEzB,gBAAE;IACA,KAAK,EAAE,OAAkB;IACzB,aAAa,EAAE,+BAAgD;IAE/D,sBAAQ;MACN,KAAK,EAAE,KAAkB", 4 | "sources": ["../sass/flash.scss"], 5 | "names": [], 6 | "file": "flash.css" 7 | } -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_convert-units.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Helper function for str-to-num fn. 3 | // Source: http://sassmeister.com/gist/9647408 4 | //************************************************************************// 5 | @function _convert-units($number, $unit) { 6 | $strings: 'px' 'cm' 'mm' '%' 'ch' 'pica' 'in' 'em' 'rem' 'pt' 'pc' 'ex' 'vw' 'vh' 'vmin' 'vmax', 'deg', 'rad', 'grad', 'turn'; 7 | $units: 1px 1cm 1mm 1% 1ch 1pica 1in 1em 1rem 1pt 1pc 1ex 1vw 1vh 1vmin 1vmax, 1deg, 1rad, 1grad, 1turn; 8 | $index: index($strings, $unit); 9 | 10 | @if not $index { 11 | @warn "Unknown unit `#{$unit}`."; 12 | @return false; 13 | } 14 | @return $number * nth($units, $index); 15 | } 16 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_border-color.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Provides a quick method for targeting `border-color` on specific sides of a box. Use a `null` value to “skip” a side. 4 | /// 5 | /// @param {Arglist} $vals 6 | /// List of arguments 7 | /// 8 | /// @example scss - Usage 9 | /// .element { 10 | /// @include border-color(#a60b55 #76cd9c null #e8ae1a); 11 | /// } 12 | /// 13 | /// @example css - CSS Output 14 | /// .element { 15 | /// border-left-color: #e8ae1a; 16 | /// border-right-color: #76cd9c; 17 | /// border-top-color: #a60b55; 18 | /// } 19 | /// 20 | /// @require {mixin} directional-property 21 | /// 22 | /// @output `border-color` 23 | 24 | @mixin border-color($vals...) { 25 | @include directional-property(border, color, $vals...); 26 | } 27 | -------------------------------------------------------------------------------- /web/static/css/neat/grid/_display-context.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Changes the display property used by other mixins called in the code block argument. 4 | /// 5 | /// @param {String} $display [block] 6 | /// Display value to be used within the block. Can be `table` or `block`. 7 | /// 8 | /// @example scss 9 | /// @include display-context(table) { 10 | /// .display-table { 11 | /// @include span-columns(6); 12 | /// } 13 | /// } 14 | /// 15 | /// @example css 16 | /// .display-table { 17 | /// display: table-cell; 18 | /// ... 19 | /// } 20 | 21 | @mixin display-context($display: block) { 22 | $scope-display: $container-display-table; 23 | $container-display-table: $display == table !global; 24 | 25 | @content; 26 | 27 | $container-display-table: $scope-display !global; 28 | } 29 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_transition-property-name.scss: -------------------------------------------------------------------------------- 1 | // Return vendor-prefixed property names if appropriate 2 | // Example: transition-property-names((transform, color, background), moz) -> -moz-transform, color, background 3 | //************************************************************************// 4 | @function transition-property-names($props, $vendor: false) { 5 | $new-props: (); 6 | 7 | @each $prop in $props { 8 | $new-props: append($new-props, transition-property-name($prop, $vendor), comma); 9 | } 10 | 11 | @return $new-props; 12 | } 13 | 14 | @function transition-property-name($prop, $vendor: false) { 15 | // put other properties that need to be prefixed here aswell 16 | @if $vendor and $prop == transform { 17 | @return unquote('-'+$vendor+'-'+$prop); 18 | } 19 | @else { 20 | @return $prop; 21 | } 22 | } -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_ellipsis.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Truncates text and adds an ellipsis to represent overflow. 4 | /// 5 | /// @param {Number} $width [100%] 6 | /// Max-width for the string to respect before being truncated 7 | /// 8 | /// @example scss - Usage 9 | /// .element { 10 | /// @include ellipsis; 11 | /// } 12 | /// 13 | /// @example css - CSS Output 14 | /// .element { 15 | /// display: inline-block; 16 | /// max-width: 100%; 17 | /// overflow: hidden; 18 | /// text-overflow: ellipsis; 19 | /// white-space: nowrap; 20 | /// word-wrap: normal; 21 | /// } 22 | 23 | @mixin ellipsis($width: 100%) { 24 | display: inline-block; 25 | max-width: $width; 26 | overflow: hidden; 27 | text-overflow: ellipsis; 28 | white-space: nowrap; 29 | word-wrap: normal; 30 | } 31 | -------------------------------------------------------------------------------- /test/support/model_case.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.ModelCase do 2 | @moduledoc """ 3 | This module defines the test case to be used by 4 | model tests. 5 | 6 | Finally, if the test case interacts with the database, 7 | it cannot be async. For this reason, every test runs 8 | inside a transaction which is reset at the beginning 9 | of the test unless the test case is marked as async. 10 | """ 11 | 12 | use ExUnit.CaseTemplate 13 | 14 | using do 15 | quote do 16 | # Alias the data repository and import query/model functions 17 | alias Preview.Repo 18 | import Ecto.Model 19 | import Ecto.Query, only: [from: 2] 20 | end 21 | end 22 | 23 | setup tags do 24 | unless tags[:async] do 25 | Ecto.Adapters.SQL.restart_test_transaction(Preview.Repo, []) 26 | end 27 | 28 | :ok 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_transition-property-name.scss: -------------------------------------------------------------------------------- 1 | // Return vendor-prefixed property names if appropriate 2 | // Example: transition-property-names((transform, color, background), moz) -> -moz-transform, color, background 3 | //************************************************************************// 4 | @function transition-property-names($props, $vendor: false) { 5 | $new-props: (); 6 | 7 | @each $prop in $props { 8 | $new-props: append($new-props, transition-property-name($prop, $vendor), comma); 9 | } 10 | 11 | @return $new-props; 12 | } 13 | 14 | @function transition-property-name($prop, $vendor: false) { 15 | // put other properties that need to be prefixed here aswell 16 | @if $vendor and $prop == transform { 17 | @return unquote('-'+$vendor+'-'+$prop); 18 | } 19 | @else { 20 | @return $prop; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /priv/static/sass/neat/settings/_visual-grid.scss: -------------------------------------------------------------------------------- 1 | /// Displays the visual grid when set to true. The overlaid grid may be few pixels off depending on the browser's rendering engine and pixel rounding algorithm. Set with the `!global` flag. 2 | /// 3 | /// @type Bool 4 | 5 | $visual-grid: false !default; 6 | 7 | /// Sets the visual grid color. Set with `!global` flag. 8 | /// 9 | /// @type Color 10 | 11 | $visual-grid-color: #EEE !default; 12 | 13 | /// Sets the `z-index` property of the visual grid. Can be `back` (behind content) or `front` (in front of content). Set with `!global` flag. 14 | /// 15 | /// @type String 16 | 17 | $visual-grid-index: back !default; 18 | 19 | /// Sets the opacity property of the visual grid. Set with `!global` flag. 20 | /// 21 | /// @type Number (unitless) 22 | 23 | $visual-grid-opacity: 0.4 !default; 24 | 25 | $visual-grid-breakpoints: () !default; 26 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_unpack.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Converts shorthand to the 4-value syntax. 4 | /// 5 | /// @param {List} $shorthand 6 | /// 7 | /// @example scss - Usage 8 | /// .element { 9 | /// margin: unpack(1em 2em); 10 | /// } 11 | /// 12 | /// @example css - CSS Output 13 | /// .element { 14 | /// margin: 1em 2em 1em 2em; 15 | /// } 16 | 17 | @function unpack($shorthand) { 18 | @if length($shorthand) == 1 { 19 | @return nth($shorthand, 1) nth($shorthand, 1) nth($shorthand, 1) nth($shorthand, 1); 20 | } @else if length($shorthand) == 2 { 21 | @return nth($shorthand, 1) nth($shorthand, 2) nth($shorthand, 1) nth($shorthand, 2); 22 | } @else if length($shorthand) == 3 { 23 | @return nth($shorthand, 1) nth($shorthand, 2) nth($shorthand, 3) nth($shorthand, 2); 24 | } @else { 25 | @return $shorthand; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/addons/_position.scss: -------------------------------------------------------------------------------- 1 | @mixin position ($position: relative, $coordinates: null null null null) { 2 | 3 | @if type-of($position) == list { 4 | $coordinates: $position; 5 | $position: relative; 6 | } 7 | 8 | $coordinates: unpack($coordinates); 9 | 10 | $top: nth($coordinates, 1); 11 | $right: nth($coordinates, 2); 12 | $bottom: nth($coordinates, 3); 13 | $left: nth($coordinates, 4); 14 | 15 | position: $position; 16 | 17 | @if ($top and $top == auto) or (type-of($top) == number) { 18 | top: $top; 19 | } 20 | 21 | @if ($right and $right == auto) or (type-of($right) == number) { 22 | right: $right; 23 | } 24 | 25 | @if ($bottom and $bottom == auto) or (type-of($bottom) == number) { 26 | bottom: $bottom; 27 | } 28 | 29 | @if ($left and $left == auto) or (type-of($left) == number) { 30 | left: $left; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_hide-text.scss: -------------------------------------------------------------------------------- 1 | /// Hides the text in an element, commonly used to show an image. Some elements will need block-level styles applied. 2 | /// 3 | /// @link http://zeldman.com/2012/03/01/replacing-the-9999px-hack-new-image-replacement 4 | /// 5 | /// @example scss - Usage 6 | /// .element { 7 | /// @include hide-text; 8 | /// } 9 | /// 10 | /// @example css - CSS Output 11 | /// .element { 12 | /// overflow: hidden; 13 | /// text-indent: 101%; 14 | /// white-space: nowrap; 15 | /// } 16 | /// 17 | /// @todo Remove height argument in v5.0.0 18 | 19 | @mixin hide-text($height: null) { 20 | overflow: hidden; 21 | text-indent: 101%; 22 | white-space: nowrap; 23 | 24 | @if $height { 25 | @warn "The `hide-text` mixin has changed and no longer requires a height. The height argument will no longer be accepted in v5.0.0"; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_linear-angle-parser.scss: -------------------------------------------------------------------------------- 1 | // Private function for linear-gradient-parser 2 | @function _linear-angle-parser($image, $first-val, $prefix, $suffix) { 3 | $offset: null; 4 | $unit-short: str-slice($first-val, str-length($first-val) - 2, str-length($first-val)); 5 | $unit-long: str-slice($first-val, str-length($first-val) - 3, str-length($first-val)); 6 | 7 | @if ($unit-long == "grad") or 8 | ($unit-long == "turn") { 9 | $offset: if($unit-long == "grad", -100grad * 3, -0.75turn); 10 | } 11 | 12 | @else if ($unit-short == "deg") or 13 | ($unit-short == "rad") { 14 | $offset: if($unit-short == "deg", -90 * 3, 1.6rad); 15 | } 16 | 17 | @if $offset { 18 | $num: _str-to-num($first-val); 19 | 20 | @return ( 21 | webkit-image: -webkit- + $prefix + ($offset - $num) + $suffix, 22 | spec-image: $image 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_linear-angle-parser.scss: -------------------------------------------------------------------------------- 1 | // Private function for linear-gradient-parser 2 | @function _linear-angle-parser($image, $first-val, $prefix, $suffix) { 3 | $offset: null; 4 | $unit-short: str-slice($first-val, str-length($first-val) - 2, str-length($first-val)); 5 | $unit-long: str-slice($first-val, str-length($first-val) - 3, str-length($first-val)); 6 | 7 | @if ($unit-long == "grad") or 8 | ($unit-long == "turn") { 9 | $offset: if($unit-long == "grad", -100grad * 3, -0.75turn); 10 | } 11 | 12 | @else if ($unit-short == "deg") or 13 | ($unit-short == "rad") { 14 | $offset: if($unit-short == "deg", -90 * 3, 1.6rad); 15 | } 16 | 17 | @if $offset { 18 | $num: _str-to-num($first-val); 19 | 20 | @return ( 21 | webkit-image: -webkit- + $prefix + ($offset - $num) + $suffix, 22 | spec-image: $image 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /web/static/css/neat/settings/_visual-grid.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Displays the visual grid when set to true. The overlaid grid may be few pixels off depending on the browser's rendering engine and pixel rounding algorithm. Set with the `!global` flag. 4 | /// 5 | /// @type Bool 6 | 7 | $visual-grid: false !default; 8 | 9 | /// Sets the visual grid color. Set with `!global` flag. 10 | /// 11 | /// @type Color 12 | 13 | $visual-grid-color: #eee !default; 14 | 15 | /// Sets the `z-index` property of the visual grid. Can be `back` (behind content) or `front` (in front of content). Set with `!global` flag. 16 | /// 17 | /// @type String 18 | 19 | $visual-grid-index: back !default; 20 | 21 | /// Sets the opacity property of the visual grid. Set with `!global` flag. 22 | /// 23 | /// @type Number (unitless) 24 | 25 | $visual-grid-opacity: 0.4 !default; 26 | 27 | $visual-grid-breakpoints: () !default; 28 | -------------------------------------------------------------------------------- /web/static/css/base/_flashes.scss: -------------------------------------------------------------------------------- 1 | $base-spacing: 1.5em !default; 2 | $alert-color: #fff6bf !default; 3 | $error-color: #fbe3e4 !default; 4 | $notice-color: #e5edf8 !default; 5 | $success-color: #e6efc2 !default; 6 | 7 | @mixin flash($color) { 8 | background-color: $color; 9 | color: darken($color, 60%); 10 | display: block; 11 | font-weight: 600; 12 | margin-bottom: $base-spacing / 2; 13 | padding: $base-spacing / 2; 14 | text-align: center; 15 | 16 | a { 17 | color: darken($color, 70%); 18 | text-decoration: underline; 19 | 20 | &:focus, 21 | &:hover { 22 | color: darken($color, 90%); 23 | } 24 | } 25 | } 26 | 27 | .flash-alert { 28 | @include flash($alert-color); 29 | } 30 | 31 | .flash-error { 32 | @include flash($error-color); 33 | } 34 | 35 | .flash-notice { 36 | @include flash($notice-color); 37 | } 38 | 39 | .flash-success { 40 | @include flash($success-color); 41 | } 42 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_convert-units.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Helper function for str-to-num fn. 3 | // Source: http://sassmeister.com/gist/9647408 4 | //************************************************************************// 5 | @function _convert-units($number, $unit) { 6 | $strings: "px", "cm", "mm", "%", "ch", "pica", "in", "em", "rem", "pt", "pc", "ex", "vw", "vh", "vmin", "vmax", "deg", "rad", "grad", "turn"; 7 | $units: 1px, 1cm, 1mm, 1%, 1ch, 1pica, 1in, 1em, 1rem, 1pt, 1pc, 1ex, 1vw, 1vh, 1vmin, 1vmax, 1deg, 1rad, 1grad, 1turn; 8 | $index: index($strings, $unit); 9 | 10 | @if not $index { 11 | @warn "Unknown unit `#{$unit}`."; 12 | @return false; 13 | } 14 | 15 | @if type-of($number) != "number" { 16 | @warn "`#{$number} is not a number`"; 17 | @return false; 18 | } 19 | 20 | @return $number * nth($units, $index); 21 | } 22 | -------------------------------------------------------------------------------- /web/static/css/base/_variables.scss: -------------------------------------------------------------------------------- 1 | // Typography 2 | $base-font-family: $helvetica; 3 | $heading-font-family: $base-font-family; 4 | 5 | // Font Sizes 6 | $base-font-size: 1em; 7 | 8 | // Line height 9 | $base-line-height: 1.5; 10 | $heading-line-height: 1.2; 11 | 12 | // Other Sizes 13 | $base-border-radius: 3px; 14 | $base-spacing: $base-line-height * 1em; 15 | $small-spacing: $base-spacing / 2; 16 | $base-z-index: 0; 17 | 18 | // Colors 19 | $blue: #477dca; 20 | $dark-gray: #333; 21 | $medium-gray: #999; 22 | $light-gray: #ddd; 23 | 24 | // Font Colors 25 | $base-background-color: #fff; 26 | $base-font-color: $dark-gray; 27 | $action-color: $blue; 28 | 29 | // Border 30 | $base-border-color: $light-gray; 31 | $base-border: 1px solid $base-border-color; 32 | 33 | // Forms 34 | $form-box-shadow: inset 0 1px 3px rgba(#000, 0.06); 35 | $form-box-shadow-focus: $form-box-shadow, 0 0 5px adjust-color($action-color, $lightness: -5%, $alpha: -0.3); 36 | -------------------------------------------------------------------------------- /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 | # Configures the endpoint 9 | config :preview, Preview.Endpoint, 10 | url: [host: "localhost"], 11 | root: Path.dirname(__DIR__), 12 | secret_key_base: "Wwrg1wMeIZuLP4RuNYItKxk1XHM8HeMPI46Y9Pb/3Zsuvcc+xGagcm9QX6GL662k", 13 | render_errors: [accepts: "html"], 14 | pubsub: [name: Preview.PubSub, 15 | adapter: Phoenix.PubSub.PG2] 16 | 17 | # Configures Elixir's Logger 18 | config :logger, :console, 19 | format: "$time $metadata[$level] $message\n", 20 | metadata: [:request_id] 21 | 22 | # Import environment specific config. This must remain at the bottom 23 | # of this file so it overrides the configuration defined above. 24 | import_config "#{Mix.env}.exs" 25 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_border-radius.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Shorthand Border-radius mixins 3 | //************************************************************************// 4 | @mixin border-top-radius($radii) { 5 | @include prefixer(border-top-left-radius, $radii, spec); 6 | @include prefixer(border-top-right-radius, $radii, spec); 7 | } 8 | 9 | @mixin border-bottom-radius($radii) { 10 | @include prefixer(border-bottom-left-radius, $radii, spec); 11 | @include prefixer(border-bottom-right-radius, $radii, spec); 12 | } 13 | 14 | @mixin border-left-radius($radii) { 15 | @include prefixer(border-top-left-radius, $radii, spec); 16 | @include prefixer(border-bottom-left-radius, $radii, spec); 17 | } 18 | 19 | @mixin border-right-radius($radii) { 20 | @include prefixer(border-top-right-radius, $radii, spec); 21 | @include prefixer(border-bottom-right-radius, $radii, spec); 22 | } 23 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_retina-image.scss: -------------------------------------------------------------------------------- 1 | @mixin retina-image($filename, $background-size, $extension: png, $retina-filename: null, $retina-suffix: _2x, $asset-pipeline: $asset-pipeline) { 2 | @if $asset-pipeline { 3 | background-image: image-url("#{$filename}.#{$extension}"); 4 | } @else { 5 | background-image: url("#{$filename}.#{$extension}"); 6 | } 7 | 8 | @include hidpi { 9 | @if $asset-pipeline { 10 | @if $retina-filename { 11 | background-image: image-url("#{$retina-filename}.#{$extension}"); 12 | } @else { 13 | background-image: image-url("#{$filename}#{$retina-suffix}.#{$extension}"); 14 | } 15 | } @else { 16 | @if $retina-filename { 17 | background-image: url("#{$retina-filename}.#{$extension}"); 18 | } @else { 19 | background-image: url("#{$filename}#{$retina-suffix}.#{$extension}"); 20 | } 21 | } 22 | 23 | background-size: $background-size; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_render-gradients.scss: -------------------------------------------------------------------------------- 1 | // User for linear and radial gradients within background-image or border-image properties 2 | 3 | @function _render-gradients($gradient-positions, $gradients, $gradient-type, $vendor: false) { 4 | $pre-spec: null; 5 | $spec: null; 6 | $vendor-gradients: null; 7 | @if $gradient-type == linear { 8 | @if $gradient-positions { 9 | $pre-spec: nth($gradient-positions, 1); 10 | $spec: nth($gradient-positions, 2); 11 | } 12 | } 13 | @else if $gradient-type == radial { 14 | $pre-spec: nth($gradient-positions, 1); 15 | $spec: nth($gradient-positions, 2); 16 | } 17 | 18 | @if $vendor { 19 | $vendor-gradients: -#{$vendor}-#{$gradient-type}-gradient(#{$pre-spec} $gradients); 20 | } 21 | @else if $vendor == false { 22 | $vendor-gradients: "#{$gradient-type}-gradient(#{$spec} #{$gradients})"; 23 | $vendor-gradients: unquote($vendor-gradients); 24 | } 25 | @return $vendor-gradients; 26 | } 27 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_render-gradients.scss: -------------------------------------------------------------------------------- 1 | // User for linear and radial gradients within background-image or border-image properties 2 | 3 | @function _render-gradients($gradient-positions, $gradients, $gradient-type, $vendor: false) { 4 | $pre-spec: null; 5 | $spec: null; 6 | $vendor-gradients: null; 7 | @if $gradient-type == linear { 8 | @if $gradient-positions { 9 | $pre-spec: nth($gradient-positions, 1); 10 | $spec: nth($gradient-positions, 2); 11 | } 12 | } 13 | @else if $gradient-type == radial { 14 | $pre-spec: nth($gradient-positions, 1); 15 | $spec: nth($gradient-positions, 2); 16 | } 17 | 18 | @if $vendor { 19 | $vendor-gradients: -#{$vendor}-#{$gradient-type}-gradient(#{$pre-spec} $gradients); 20 | } 21 | @else if $vendor == false { 22 | $vendor-gradients: "#{$gradient-type}-gradient(#{$spec} #{$gradients})"; 23 | $vendor-gradients: unquote($vendor-gradients); 24 | } 25 | @return $vendor-gradients; 26 | } 27 | -------------------------------------------------------------------------------- /lib/preview/endpoint.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.Endpoint do 2 | use Phoenix.Endpoint, otp_app: :preview 3 | 4 | socket "/ws", Preview.UserSocket 5 | 6 | # Serve at "/" the given assets from "priv/static" directory 7 | plug Plug.Static, 8 | at: "/", from: :preview, 9 | only: ~w(css images js favicon.ico robots.txt) 10 | 11 | # Code reloading can be explicitly enabled under the 12 | # :code_reloader configuration of your endpoint. 13 | if code_reloading? do 14 | socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket 15 | plug Phoenix.LiveReloader 16 | plug Phoenix.CodeReloader 17 | end 18 | 19 | plug Plug.Logger 20 | 21 | plug Plug.Parsers, 22 | parsers: [:urlencoded, :multipart, :json], 23 | pass: ["*/*"], 24 | json_decoder: Poison 25 | 26 | plug Plug.MethodOverride 27 | plug Plug.Head 28 | 29 | plug Plug.Session, 30 | store: :cookie, 31 | key: "_preview_key", 32 | signing_salt: "W3ASBfEZ" 33 | 34 | plug Preview.Router 35 | end 36 | -------------------------------------------------------------------------------- /priv/static/sass/neat/grid/_direction-context.scss: -------------------------------------------------------------------------------- 1 | /// Changes the direction property used by other mixins called in the code block argument. 2 | /// 3 | /// @param {String} $direction (left-to-right) 4 | /// Layout direction to be used within the block. Can be `left-to-right` or `right-to-left`. 5 | /// 6 | /// @example scss - Usage 7 | /// @include direction(right-to-left) { 8 | /// .right-to-left-block { 9 | /// @include span-columns(6); 10 | /// } 11 | /// } 12 | /// 13 | /// @example css - CSS Output 14 | /// .right-to-left-block { 15 | /// float: right; 16 | /// ... 17 | /// } 18 | 19 | @mixin direction-context($direction: left-to-right) { 20 | $scope-direction: $layout-direction; 21 | 22 | @if to-lower-case($direction) == "left-to-right" { 23 | $layout-direction: LTR !global; 24 | } @else if to-lower-case($direction) == "right-to-left" { 25 | $layout-direction: RTL !global; 26 | } 27 | 28 | @content; 29 | 30 | $layout-direction: $scope-direction !global; 31 | } 32 | -------------------------------------------------------------------------------- /lib/preview/authenticate.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.Authenticate do 2 | alias Comeonin.Bcrypt 3 | 4 | @doc """ 5 | Checks the hashed and non-hashed passwords match 6 | 7 | Returns {:ok, user} or {:error, message} tuple 8 | """ 9 | def password(user, password) do 10 | check_password = 11 | password 12 | |> Bcrypt.checkpw(user.password) 13 | 14 | _password(check_password, user) 15 | end 16 | defp _password(false, _), do: {:error, "Please enter a valid email and password"} 17 | defp _password(true, user), do: {:ok, user} 18 | 19 | @doc """ 20 | Checks an email is in the users table 21 | 22 | Returns {:ok, message} or {:error, message} tuple 23 | """ 24 | def session(users, email) do 25 | email_check = 26 | users 27 | |> Enum.map(fn user -> user.email end) 28 | |> Enum.member?(email) 29 | 30 | case email_check do 31 | false -> {:error, "Unauthorized access attempt!"} 32 | true -> {:ok, "User access granted!"} 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/addons/_retina-image.scss: -------------------------------------------------------------------------------- 1 | @mixin retina-image($filename, $background-size, $extension: png, $retina-filename: null, $retina-suffix: _2x, $asset-pipeline: $asset-pipeline) { 2 | @if $asset-pipeline { 3 | background-image: image-url("#{$filename}.#{$extension}"); 4 | } 5 | @else { 6 | background-image: url("#{$filename}.#{$extension}"); 7 | } 8 | 9 | @include hidpi { 10 | @if $asset-pipeline { 11 | @if $retina-filename { 12 | background-image: image-url("#{$retina-filename}.#{$extension}"); 13 | } 14 | @else { 15 | background-image: image-url("#{$filename}#{$retina-suffix}.#{$extension}"); 16 | } 17 | } 18 | 19 | @else { 20 | @if $retina-filename { 21 | background-image: url("#{$retina-filename}.#{$extension}"); 22 | } 23 | @else { 24 | background-image: url("#{$filename}#{$retina-suffix}.#{$extension}"); 25 | } 26 | } 27 | 28 | background-size: $background-size; 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_selection.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Outputs the spec and prefixed versions of the `::selection` pseudo-element. 4 | /// 5 | /// @param {Bool} $current-selector [false] 6 | /// If set to `true`, it takes the current element into consideration. 7 | /// 8 | /// @example scss - Usage 9 | /// .element { 10 | /// @include selection(true) { 11 | /// background-color: #ffbb52; 12 | /// } 13 | /// } 14 | /// 15 | /// @example css - CSS Output 16 | /// .element::-moz-selection { 17 | /// background-color: #ffbb52; 18 | /// } 19 | /// 20 | /// .element::selection { 21 | /// background-color: #ffbb52; 22 | /// } 23 | 24 | @mixin selection($current-selector: false) { 25 | @if $current-selector { 26 | &::-moz-selection { 27 | @content; 28 | } 29 | 30 | &::selection { 31 | @content; 32 | } 33 | } @else { 34 | ::-moz-selection { 35 | @content; 36 | } 37 | 38 | ::selection { 39 | @content; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /web/static/css/neat/grid/_direction-context.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Changes the direction property used by other mixins called in the code block argument. 4 | /// 5 | /// @param {String} $direction [left-to-right] 6 | /// Layout direction to be used within the block. Can be `left-to-right` or `right-to-left`. 7 | /// 8 | /// @example scss - Usage 9 | /// @include direction-context(right-to-left) { 10 | /// .right-to-left-block { 11 | /// @include span-columns(6); 12 | /// } 13 | /// } 14 | /// 15 | /// @example css - CSS Output 16 | /// .right-to-left-block { 17 | /// float: right; 18 | /// ... 19 | /// } 20 | 21 | @mixin direction-context($direction: left-to-right) { 22 | $scope-direction: $layout-direction; 23 | 24 | @if to-lower-case($direction) == "left-to-right" { 25 | $layout-direction: LTR !global; 26 | } @else if to-lower-case($direction) == "right-to-left" { 27 | $layout-direction: RTL !global; 28 | } 29 | 30 | @content; 31 | 32 | $layout-direction: $scope-direction !global; 33 | } 34 | -------------------------------------------------------------------------------- /lib/preview.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview 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, warn: false 8 | 9 | children = [ 10 | # Start the endpoint when the application starts 11 | supervisor(Preview.Endpoint, []), 12 | # Start the Ecto repository 13 | worker(Preview.Repo, []), 14 | # Here you could define other workers and supervisors as children 15 | # worker(Preview.Worker, [arg1, arg2, arg3]), 16 | ] 17 | 18 | # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html 19 | # for other strategies and supported options 20 | opts = [strategy: :one_for_one, name: Preview.Supervisor] 21 | Supervisor.start_link(children, opts) 22 | end 23 | 24 | # Tell Phoenix to update the endpoint configuration 25 | # whenever the application is updated. 26 | def config_change(changed, _new, removed) do 27 | Preview.Endpoint.config_change(changed, removed) 28 | :ok 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_linear-side-corner-parser.scss: -------------------------------------------------------------------------------- 1 | // Private function for linear-gradient-parser 2 | @function _linear-side-corner-parser($image, $first-val, $prefix, $suffix, $has-multiple-vals) { 3 | $val-1: str-slice($first-val, 0, $has-multiple-vals - 1 ); 4 | $val-2: str-slice($first-val, $has-multiple-vals + 1, str-length($first-val)); 5 | $val-3: null; 6 | $has-val-3: str-index($val-2, " "); 7 | 8 | @if $has-val-3 { 9 | $val-3: str-slice($val-2, $has-val-3 + 1, str-length($val-2)); 10 | $val-2: str-slice($val-2, 0, $has-val-3 - 1); 11 | } 12 | 13 | $pos: _position-flipper($val-1) _position-flipper($val-2) _position-flipper($val-3); 14 | $pos: unquote($pos + ""); 15 | 16 | // Use old spec for webkit 17 | @if $val-1 == "to" { 18 | @return ( 19 | webkit-image: -webkit- + $prefix + $pos + $suffix, 20 | spec-image: $image 21 | ); 22 | } 23 | 24 | // Bring the code up to spec 25 | @else { 26 | @return ( 27 | webkit-image: -webkit- + $image, 28 | spec-image: $prefix + "to " + $pos + $suffix 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /web/static/css/base/_typography.scss: -------------------------------------------------------------------------------- 1 | body { 2 | @include font-feature-settings("kern", "liga", "pnum"); 3 | -webkit-font-smoothing: antialiased; 4 | color: $base-font-color; 5 | font-family: $base-font-family; 6 | font-size: $base-font-size; 7 | line-height: $base-line-height; 8 | } 9 | 10 | h1, 11 | h2, 12 | h3, 13 | h4, 14 | h5, 15 | h6 { 16 | font-family: $heading-font-family; 17 | font-size: $base-font-size; 18 | line-height: $heading-line-height; 19 | margin: 0 0 $small-spacing; 20 | } 21 | 22 | p { 23 | margin: 0 0 $small-spacing; 24 | } 25 | 26 | a { 27 | color: $action-color; 28 | text-decoration: none; 29 | transition: color 0.1s linear; 30 | 31 | &:active, 32 | &:focus, 33 | &:hover { 34 | color: darken($action-color, 15%); 35 | } 36 | 37 | &:active, 38 | &:focus { 39 | outline: none; 40 | } 41 | } 42 | 43 | hr { 44 | border-bottom: $base-border; 45 | border-left: none; 46 | border-right: none; 47 | border-top: none; 48 | margin: $base-spacing 0; 49 | } 50 | 51 | img, 52 | picture { 53 | margin: 0; 54 | max-width: 100%; 55 | } 56 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_linear-side-corner-parser.scss: -------------------------------------------------------------------------------- 1 | // Private function for linear-gradient-parser 2 | @function _linear-side-corner-parser($image, $first-val, $prefix, $suffix, $has-multiple-vals) { 3 | $val-1: str-slice($first-val, 0, $has-multiple-vals - 1 ); 4 | $val-2: str-slice($first-val, $has-multiple-vals + 1, str-length($first-val)); 5 | $val-3: null; 6 | $has-val-3: str-index($val-2, " "); 7 | 8 | @if $has-val-3 { 9 | $val-3: str-slice($val-2, $has-val-3 + 1, str-length($val-2)); 10 | $val-2: str-slice($val-2, 0, $has-val-3 - 1); 11 | } 12 | 13 | $pos: _position-flipper($val-1) _position-flipper($val-2) _position-flipper($val-3); 14 | $pos: unquote($pos + ""); 15 | 16 | // Use old spec for webkit 17 | @if $val-1 == "to" { 18 | @return ( 19 | webkit-image: -webkit- + $prefix + $pos + $suffix, 20 | spec-image: $image 21 | ); 22 | } 23 | 24 | // Bring the code up to spec 25 | @else { 26 | @return ( 27 | webkit-image: -webkit- + $image, 28 | spec-image: $prefix + "to " + $pos + $suffix 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_font-face.scss: -------------------------------------------------------------------------------- 1 | // Order of the includes matters, and it is: normal, bold, italic, bold+italic. 2 | 3 | @mixin font-face($font-family, $file-path, $weight: normal, $style: normal, $asset-pipeline: $asset-pipeline) { 4 | @font-face { 5 | font-family: $font-family; 6 | font-weight: $weight; 7 | font-style: $style; 8 | 9 | @if $asset-pipeline == true { 10 | src: font-url('#{$file-path}.eot'); 11 | src: font-url('#{$file-path}.eot?#iefix') format('embedded-opentype'), 12 | font-url('#{$file-path}.woff') format('woff'), 13 | font-url('#{$file-path}.ttf') format('truetype'), 14 | font-url('#{$file-path}.svg##{$font-family}') format('svg'); 15 | } @else { 16 | src: url('#{$file-path}.eot'); 17 | src: url('#{$file-path}.eot?#iefix') format('embedded-opentype'), 18 | url('#{$file-path}.woff') format('woff'), 19 | url('#{$file-path}.ttf') format('truetype'), 20 | url('#{$file-path}.svg##{$font-family}') format('svg'); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /priv/static/sass/neat/grid/_outer-container.scss: -------------------------------------------------------------------------------- 1 | /// Makes an element a outer container by centring it in the viewport, clearing its floats, and setting its `max-width`. 2 | /// Although optional, using `outer-container` is recommended. The mixin can be called on more than one element per page, as long as they are not nested. 3 | /// 4 | /// @param {Number (unit)} $local-max-width ($max-width) 5 | /// Max width to be applied to the element. Can be a percentage or a measure. 6 | /// 7 | /// @example scss - Usage 8 | /// .element { 9 | /// @include outer-container(100%); 10 | /// } 11 | /// 12 | /// @example css - CSS Output 13 | /// .element { 14 | /// *zoom: 1; 15 | /// max-width: 100%; 16 | /// margin-left: auto; 17 | /// margin-right: auto; 18 | /// } 19 | /// 20 | /// .element:before, .element:after { 21 | /// content: " "; 22 | /// display: table; 23 | /// } 24 | /// 25 | /// .element:after { 26 | /// clear: both; 27 | /// } 28 | 29 | @mixin outer-container($local-max-width: $max-width) { 30 | @include clearfix; 31 | max-width: $local-max-width; 32 | margin: { 33 | left: auto; 34 | right: auto; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/support/channel_case.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.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 | imports other functionality to make it easier 8 | to build and query models. 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 | # Alias the data repository and import query/model functions 24 | alias Preview.Repo 25 | import Ecto.Model 26 | import Ecto.Query, only: [from: 2] 27 | 28 | 29 | # The default endpoint for testing 30 | @endpoint Preview.Endpoint 31 | end 32 | end 33 | 34 | setup tags do 35 | unless tags[:async] do 36 | Ecto.Adapters.SQL.restart_test_transaction(Preview.Repo, []) 37 | end 38 | 39 | :ok 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /web/static/css/neat/grid/_outer-container.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Makes an element a outer container by centring it in the viewport, clearing its floats, and setting its `max-width`. 4 | /// Although optional, using `outer-container` is recommended. The mixin can be called on more than one element per page, as long as they are not nested. 5 | /// 6 | /// @param {Number [unit]} $local-max-width [$max-width] 7 | /// Max width to be applied to the element. Can be a percentage or a measure. 8 | /// 9 | /// @example scss - Usage 10 | /// .element { 11 | /// @include outer-container(100%); 12 | /// } 13 | /// 14 | /// @example css - CSS Output 15 | /// .element { 16 | /// *zoom: 1; 17 | /// max-width: 100%; 18 | /// margin-left: auto; 19 | /// margin-right: auto; 20 | /// } 21 | /// 22 | /// .element:before, .element:after { 23 | /// content: " "; 24 | /// display: table; 25 | /// } 26 | /// 27 | /// .element:after { 28 | /// clear: both; 29 | /// } 30 | 31 | @mixin outer-container($local-max-width: $max-width) { 32 | @include clearfix; 33 | max-width: $local-max-width; 34 | margin: { 35 | left: auto; 36 | right: auto; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /priv/static/css/flash.css: -------------------------------------------------------------------------------- 1 | .flash-alert, .flash-error, .flash-notice, .flash-success { 2 | display: block; 3 | font-weight: bold; 4 | margin-bottom: 0.75em; 5 | padding: 0.75em; } 6 | 7 | .flash-alert { 8 | background: #FFF6BF; 9 | color: #8c7800; } 10 | .flash-alert a { 11 | color: #594c00; 12 | border-bottom: 1px solid rgba(89, 76, 0, 0.3); } 13 | .flash-alert a:hover { 14 | color: black; } 15 | 16 | .flash-error { 17 | background: #FBE3E4; 18 | color: #96151b; } 19 | .flash-error a { 20 | color: #6a0f13; 21 | border-bottom: 1px solid rgba(106, 15, 19, 0.3); } 22 | .flash-error a:hover { 23 | color: #110203; } 24 | 25 | .flash-notice { 26 | background: #e5edf8; 27 | color: #264d85; } 28 | .flash-notice a { 29 | color: #1b365d; 30 | border-bottom: 1px solid rgba(27, 54, 93, 0.3); } 31 | .flash-notice a:hover { 32 | color: #04080e; } 33 | 34 | .flash-success { 35 | background: #E6EFC2; 36 | color: #56651a; } 37 | .flash-success a { 38 | color: #333c10; 39 | border-bottom: 1px solid rgba(51, 60, 16, 0.3); } 40 | .flash-success a:hover { 41 | color: black; } 42 | 43 | /*# sourceMappingURL=flash.css.map */ 44 | -------------------------------------------------------------------------------- /web/router.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.Router do 2 | use Phoenix.Router 3 | 4 | pipeline :browser do 5 | plug :accepts, ["html"] 6 | plug :fetch_session 7 | plug :fetch_flash 8 | plug :protect_from_forgery 9 | plug :put_secure_browser_headers 10 | end 11 | 12 | pipeline :api do 13 | plug :accepts, ["json"] 14 | end 15 | 16 | pipeline :csv do 17 | plug :accepts, ["csv"] 18 | plug :fetch_session 19 | end 20 | 21 | scope "/", Preview do 22 | pipe_through :browser # Use the default browser stack 23 | 24 | get "/", PageController, :index, as: :root 25 | 26 | get "/users/login", UserController, :login, as: :login 27 | post "/users/login", UserController, :authenticate_login, as: :login 28 | post "/users/logout", UserController, :logout, as: :logout 29 | 30 | post "/signups/create", SignupController, :create 31 | get "/signups", SignupController, :index 32 | end 33 | 34 | scope "/signups", Preview do 35 | pipe_through :csv 36 | 37 | post "/export", SignupController, :csv_export 38 | end 39 | 40 | # Other scopes may use custom stacks. 41 | # scope "/api", Preview do 42 | # pipe_through :api 43 | # end 44 | end 45 | -------------------------------------------------------------------------------- /web/channels/user_socket.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.UserSocket do 2 | use Phoenix.Socket 3 | 4 | ## Channels 5 | channel "rooms:*", Preview.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 | def connect(_params, socket) do 20 | {:ok, socket} 21 | end 22 | 23 | # Socket id's are topics that allow you to identify all sockets for a given user: 24 | # 25 | # def id(socket), do: "users_socket:#{socket.assigns.user_id}" 26 | # 27 | # Would allow you to broadcast a "disconnect" event and terminate 28 | # all active sockets and channels for a given user: 29 | # 30 | # MyApp.Endpoint.broadcast("users_socket:" <> user.id, "disconnect", %{}) 31 | # 32 | # Returning `nil` makes this socket anonymous. 33 | def id(_socket), do: nil 34 | end 35 | -------------------------------------------------------------------------------- /priv/static/sass/neat/grid/_visual-grid.scss: -------------------------------------------------------------------------------- 1 | @mixin grid-column-gradient($values...) { 2 | background-image: -webkit-linear-gradient(left, $values); 3 | background-image: -moz-linear-gradient(left, $values); 4 | background-image: -ms-linear-gradient(left, $values); 5 | background-image: -o-linear-gradient(left, $values); 6 | background-image: unquote("linear-gradient(to left, #{$values})"); 7 | } 8 | 9 | @if $visual-grid == true or $visual-grid == yes { 10 | body:before { 11 | content: ''; 12 | display: inline-block; 13 | @include grid-column-gradient(gradient-stops($grid-columns)); 14 | height: 100%; 15 | left: 0; 16 | margin: 0 auto; 17 | max-width: $max-width; 18 | opacity: $visual-grid-opacity; 19 | position: fixed; 20 | right: 0; 21 | width: 100%; 22 | pointer-events: none; 23 | 24 | @if $visual-grid-index == back { 25 | z-index: -1; 26 | } 27 | 28 | @else if $visual-grid-index == front { 29 | z-index: 9999; 30 | } 31 | 32 | @each $breakpoint in $visual-grid-breakpoints { 33 | @if $breakpoint { 34 | @include media($breakpoint) { 35 | @include grid-column-gradient(gradient-stops($grid-columns)); 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_font-source-declaration.scss: -------------------------------------------------------------------------------- 1 | // Used for creating the source string for fonts using @font-face 2 | // Reference: http://goo.gl/Ru1bKP 3 | 4 | @function font-url-prefixer($asset-pipeline) { 5 | @if $asset-pipeline == true { 6 | @return font-url; 7 | } @else { 8 | @return url; 9 | } 10 | } 11 | 12 | @function font-source-declaration( 13 | $font-family, 14 | $file-path, 15 | $asset-pipeline, 16 | $file-formats, 17 | $font-url) { 18 | 19 | $src: null; 20 | 21 | $formats-map: ( 22 | eot: "#{$file-path}.eot?#iefix" format("embedded-opentype"), 23 | woff2: "#{$file-path}.woff2" format("woff2"), 24 | woff: "#{$file-path}.woff" format("woff"), 25 | ttf: "#{$file-path}.ttf" format("truetype"), 26 | svg: "#{$file-path}.svg##{$font-family}" format("svg") 27 | ); 28 | 29 | @each $key, $values in $formats-map { 30 | @if contains($file-formats, $key) { 31 | $file-path: nth($values, 1); 32 | $font-format: nth($values, 2); 33 | 34 | @if $asset-pipeline == true { 35 | $src: append($src, font-url($file-path) $font-format, comma); 36 | } @else { 37 | $src: append($src, url($file-path) $font-format, comma); 38 | } 39 | } 40 | } 41 | 42 | @return $src; 43 | } 44 | -------------------------------------------------------------------------------- /web/static/css/neat/grid/_visual-grid.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | @mixin grid-column-gradient($values...) { 4 | background-image: -webkit-linear-gradient(left, $values); 5 | background-image: -moz-linear-gradient(left, $values); 6 | background-image: -ms-linear-gradient(left, $values); 7 | background-image: -o-linear-gradient(left, $values); 8 | background-image: unquote("linear-gradient(to left, #{$values})"); 9 | } 10 | 11 | @if $visual-grid == true or $visual-grid == yes { 12 | body:before { 13 | @include grid-column-gradient(gradient-stops($grid-columns)); 14 | content: ""; 15 | display: inline-block; 16 | height: 100%; 17 | left: 0; 18 | margin: 0 auto; 19 | max-width: $max-width; 20 | opacity: $visual-grid-opacity; 21 | pointer-events: none; 22 | position: fixed; 23 | right: 0; 24 | width: 100%; 25 | 26 | @if $visual-grid-index == back { 27 | z-index: -1; 28 | } 29 | 30 | @else if $visual-grid-index == front { 31 | z-index: 9999; 32 | } 33 | 34 | @each $breakpoint in $visual-grid-breakpoints { 35 | @if $breakpoint { 36 | @include media($breakpoint) { 37 | @include grid-column-gradient(gradient-stops($grid-columns)); 38 | } 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /brunch-config.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | // See http://brunch.io/#documentation for docs. 3 | files: { 4 | javascripts: { 5 | joinTo: 'js/app.js' 6 | // To change the order of concatenation of files, explictly mention here 7 | // https://github.com/brunch/brunch/tree/stable/docs#concatenation 8 | // order: { 9 | // before: [ 10 | // 'web/static/vendor/js/jquery-2.1.1.js', 11 | // 'web/static/vendor/js/bootstrap.min.js' 12 | // ] 13 | // } 14 | }, 15 | stylesheets: { 16 | joinTo: 'css/app.css' 17 | }, 18 | templates: { 19 | joinTo: 'js/app.js' 20 | } 21 | }, 22 | 23 | modules: { 24 | autoRequire: { 25 | 'js/app.js': ['web/static/js/app'] 26 | } 27 | }, 28 | 29 | // Phoenix paths configuration 30 | paths: { 31 | // Which directories to watch 32 | watched: ["deps/phoenix/web/static", 33 | "deps/phoenix_html/web/static", 34 | "web/static", "test/static"], 35 | 36 | // Where to compile files to 37 | public: "priv/static" 38 | }, 39 | 40 | // Configure your plugins 41 | plugins: { 42 | ES6to5: { 43 | // Do not use ES6 compiler in vendor code 44 | ignore: [/^(web\/static\/vendor)/] 45 | } 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /priv/static/sass/neat/grid/_private.scss: -------------------------------------------------------------------------------- 1 | $parent-columns: $grid-columns !default; 2 | $fg-column: $column; 3 | $fg-gutter: $gutter; 4 | $fg-max-columns: $grid-columns; 5 | $container-display-table: false !default; 6 | $layout-direction: LTR !default; 7 | 8 | @function flex-grid($columns, $container-columns: $fg-max-columns) { 9 | $width: $columns * $fg-column + ($columns - 1) * $fg-gutter; 10 | $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter; 11 | @return percentage($width / $container-width); 12 | } 13 | 14 | @function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) { 15 | $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter; 16 | @return percentage($gutter / $container-width); 17 | } 18 | 19 | @function grid-width($n) { 20 | @return $n * $gw-column + ($n - 1) * $gw-gutter; 21 | } 22 | 23 | @function get-parent-columns($columns) { 24 | @if $columns != $grid-columns { 25 | $parent-columns: $columns !global; 26 | } @else { 27 | $parent-columns: $grid-columns !global; 28 | } 29 | 30 | @return $parent-columns; 31 | } 32 | 33 | @function is-display-table($container-is-display-table, $display) { 34 | @return $container-is-display-table == true or $display == table; 35 | } 36 | -------------------------------------------------------------------------------- /web/static/css/neat/grid/_private.scss: -------------------------------------------------------------------------------- 1 | $parent-columns: $grid-columns !default; 2 | $fg-column: $column; 3 | $fg-gutter: $gutter; 4 | $fg-max-columns: $grid-columns; 5 | $container-display-table: false !default; 6 | $layout-direction: LTR !default; 7 | 8 | @function flex-grid($columns, $container-columns: $fg-max-columns) { 9 | $width: $columns * $fg-column + ($columns - 1) * $fg-gutter; 10 | $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter; 11 | @return percentage($width / $container-width); 12 | } 13 | 14 | @function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) { 15 | $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter; 16 | @return percentage($gutter / $container-width); 17 | } 18 | 19 | @function grid-width($n) { 20 | @return $n * $gw-column + ($n - 1) * $gw-gutter; 21 | } 22 | 23 | @function get-parent-columns($columns) { 24 | @if $columns != $grid-columns { 25 | $parent-columns: $columns !global; 26 | } @else { 27 | $parent-columns: $grid-columns !global; 28 | } 29 | 30 | @return $parent-columns; 31 | } 32 | 33 | @function is-display-table($container-is-display-table, $display) { 34 | @return $container-is-display-table == true or $display == table; 35 | } 36 | -------------------------------------------------------------------------------- /test/support/conn_case.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.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 | imports other functionalities to make it easier 8 | to build and query models. 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 | 23 | # Alias the data repository and import query/model functions 24 | alias Preview.Repo 25 | import Ecto.Model 26 | import Ecto.Query, only: [from: 2] 27 | 28 | # Import URL helpers from the router 29 | import Preview.Router.Helpers 30 | 31 | # The default endpoint for testing 32 | @endpoint Preview.Endpoint 33 | end 34 | end 35 | 36 | setup tags do 37 | unless tags[:async] do 38 | Ecto.Adapters.SQL.restart_test_transaction(Preview.Repo, []) 39 | end 40 | 41 | :ok 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /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 :preview, Preview.Endpoint, 10 | http: [port: 4000], 11 | debug_errors: true, 12 | code_reloader: true, 13 | cache_static_lookup: false, 14 | watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin"]] 15 | 16 | # Watch static and templates for browser reloading. 17 | config :preview, Preview.Endpoint, 18 | live_reload: [ 19 | patterns: [ 20 | ~r{priv/static/.*(js|css|png|jpeg|jpg|gif)$}, 21 | ~r{web/views/.*(ex)$}, 22 | ~r{web/templates/.*(eex)$} 23 | ] 24 | ] 25 | 26 | # Do not include metadata nor timestamps in development logs 27 | config :logger, :console, format: "[$level] $message\n" 28 | 29 | # Configure your database 30 | config :preview, Preview.Repo, 31 | adapter: Ecto.Adapters.Postgres, 32 | username: "postgres", 33 | password: "postgres", 34 | database: "preview_dev" 35 | 36 | # Configure mailgun 37 | config :preview, 38 | mailgun_domain: System.get_env("MAILGUN_DOMAIN"), 39 | mailgun_key: System.get_env("MAILGUN_KEY") 40 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_linear-gradient-parser.scss: -------------------------------------------------------------------------------- 1 | @function _linear-gradient-parser($image) { 2 | $image: unquote($image); 3 | $gradients: (); 4 | $start: str-index($image, "("); 5 | $end: str-index($image, ","); 6 | $first-val: str-slice($image, $start + 1, $end - 1); 7 | 8 | $prefix: str-slice($image, 0, $start); 9 | $suffix: str-slice($image, $end, str-length($image)); 10 | 11 | $has-multiple-vals: str-index($first-val, " "); 12 | $has-single-position: unquote(_position-flipper($first-val) + ""); 13 | $has-angle: _is-num(str-slice($first-val, 0, 0)); 14 | 15 | @if $has-multiple-vals { 16 | $gradients: _linear-side-corner-parser($image, $first-val, $prefix, $suffix, $has-multiple-vals); 17 | } 18 | 19 | @else if $has-single-position != "" { 20 | $pos: unquote($has-single-position + ""); 21 | 22 | $gradients: ( 23 | webkit-image: -webkit- + $image, 24 | spec-image: $prefix + "to " + $pos + $suffix 25 | ); 26 | } 27 | 28 | @else if $has-angle { 29 | // Rotate degree for webkit 30 | $gradients: _linear-angle-parser($image, $first-val, $prefix, $suffix); 31 | } 32 | 33 | @else { 34 | $gradients: ( 35 | webkit-image: -webkit- + $image, 36 | spec-image: $image 37 | ); 38 | } 39 | 40 | @return $gradients; 41 | } 42 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_linear-gradient-parser.scss: -------------------------------------------------------------------------------- 1 | @function _linear-gradient-parser($image) { 2 | $image: unquote($image); 3 | $gradients: (); 4 | $start: str-index($image, "("); 5 | $end: str-index($image, ","); 6 | $first-val: str-slice($image, $start + 1, $end - 1); 7 | 8 | $prefix: str-slice($image, 0, $start); 9 | $suffix: str-slice($image, $end, str-length($image)); 10 | 11 | $has-multiple-vals: str-index($first-val, " "); 12 | $has-single-position: unquote(_position-flipper($first-val) + ""); 13 | $has-angle: is-number(str-slice($first-val, 0, 0)); 14 | 15 | @if $has-multiple-vals { 16 | $gradients: _linear-side-corner-parser($image, $first-val, $prefix, $suffix, $has-multiple-vals); 17 | } 18 | 19 | @else if $has-single-position != "" { 20 | $pos: unquote($has-single-position + ""); 21 | 22 | $gradients: ( 23 | webkit-image: -webkit- + $image, 24 | spec-image: $prefix + "to " + $pos + $suffix 25 | ); 26 | } 27 | 28 | @else if $has-angle { 29 | // Rotate degree for webkit 30 | $gradients: _linear-angle-parser($image, $first-val, $prefix, $suffix); 31 | } 32 | 33 | @else { 34 | $gradients: ( 35 | webkit-image: -webkit- + $image, 36 | spec-image: $image 37 | ); 38 | } 39 | 40 | @return $gradients; 41 | } 42 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_border-radius.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Provides a quick method for targeting `border-radius` on both corners on the side of a box. 4 | /// 5 | /// @param {Number} $radii 6 | /// List of arguments 7 | /// 8 | /// @example scss - Usage 9 | /// .element-one { 10 | /// @include border-top-radius(5px); 11 | /// } 12 | /// 13 | /// .element-two { 14 | /// @include border-left-radius(3px); 15 | /// } 16 | /// 17 | /// @example css - CSS Output 18 | /// .element-one { 19 | /// border-top-left-radius: 5px; 20 | /// border-top-right-radius: 5px; 21 | /// } 22 | /// 23 | /// .element-two { 24 | /// border-bottom-left-radius: 3px; 25 | /// border-top-left-radius: 3px; 26 | /// } 27 | /// 28 | /// @output `border-radius` 29 | 30 | @mixin border-top-radius($radii) { 31 | border-top-left-radius: $radii; 32 | border-top-right-radius: $radii; 33 | } 34 | 35 | @mixin border-right-radius($radii) { 36 | border-bottom-right-radius: $radii; 37 | border-top-right-radius: $radii; 38 | } 39 | 40 | @mixin border-bottom-radius($radii) { 41 | border-bottom-left-radius: $radii; 42 | border-bottom-right-radius: $radii; 43 | } 44 | 45 | @mixin border-left-radius($radii) { 46 | border-bottom-left-radius: $radii; 47 | border-top-left-radius: $radii; 48 | } 49 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Preview.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [app: :preview, 6 | version: "0.1.0", 7 | elixir: "~> 1.0", 8 | elixirc_paths: elixirc_paths(Mix.env), 9 | compilers: [:phoenix] ++ Mix.compilers, 10 | build_embedded: Mix.env == :prod, 11 | start_permanent: Mix.env == :prod, 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: {Preview, []}, 20 | applications: [:phoenix, :cowboy, :logger, :ecto, 21 | :postgrex, :comeonin, :mailgun, :csvlixir]] 22 | end 23 | 24 | # Specifies which paths to compile per environment 25 | defp elixirc_paths(:test), do: ["lib", "web", "test/support"] 26 | defp elixirc_paths(_), do: ["lib", "web"] 27 | 28 | # Specifies your project dependencies 29 | # 30 | # Type `mix help deps` for examples and options 31 | defp deps do 32 | [{:phoenix, "~> 1.0"}, 33 | {:phoenix_ecto, "~> 0.8"}, 34 | {:phoenix_html, "~> 2.0"}, 35 | {:phoenix_live_reload, "~> 1.0", only: :dev}, 36 | {:postgrex, ">= 0.0.0"}, 37 | {:cowboy, "~> 1.0"}, 38 | {:comeonin, "~> 1.1"}, 39 | {:mailgun, "~> 0.1"}, 40 | {:csvlixir, "~> 2.0.0"}] 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_keyframes.scss: -------------------------------------------------------------------------------- 1 | // Adds keyframes blocks for supported prefixes, removing redundant prefixes in the block's content 2 | @mixin keyframes($name) { 3 | $original-prefix-for-webkit: $prefix-for-webkit; 4 | $original-prefix-for-mozilla: $prefix-for-mozilla; 5 | $original-prefix-for-microsoft: $prefix-for-microsoft; 6 | $original-prefix-for-opera: $prefix-for-opera; 7 | $original-prefix-for-spec: $prefix-for-spec; 8 | 9 | @if $original-prefix-for-webkit { 10 | @include disable-prefix-for-all(); 11 | $prefix-for-webkit: true !global; 12 | @-webkit-keyframes #{$name} { 13 | @content; 14 | } 15 | } 16 | @if $original-prefix-for-mozilla { 17 | @include disable-prefix-for-all(); 18 | $prefix-for-mozilla: true !global; 19 | @-moz-keyframes #{$name} { 20 | @content; 21 | } 22 | } 23 | 24 | $prefix-for-webkit: $original-prefix-for-webkit !global; 25 | $prefix-for-mozilla: $original-prefix-for-mozilla !global; 26 | $prefix-for-microsoft: $original-prefix-for-microsoft !global; 27 | $prefix-for-opera: $original-prefix-for-opera !global; 28 | $prefix-for-spec: $original-prefix-for-spec !global; 29 | 30 | @if $original-prefix-for-spec { 31 | @keyframes #{$name} { 32 | @content; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_keyframes.scss: -------------------------------------------------------------------------------- 1 | // Adds keyframes blocks for supported prefixes, removing redundant prefixes in the block's content 2 | @mixin keyframes($name) { 3 | $original-prefix-for-webkit: $prefix-for-webkit; 4 | $original-prefix-for-mozilla: $prefix-for-mozilla; 5 | $original-prefix-for-microsoft: $prefix-for-microsoft; 6 | $original-prefix-for-opera: $prefix-for-opera; 7 | $original-prefix-for-spec: $prefix-for-spec; 8 | 9 | @if $original-prefix-for-webkit { 10 | @include disable-prefix-for-all(); 11 | $prefix-for-webkit: true !global; 12 | @-webkit-keyframes #{$name} { 13 | @content; 14 | } 15 | } 16 | 17 | @if $original-prefix-for-mozilla { 18 | @include disable-prefix-for-all(); 19 | $prefix-for-mozilla: true !global; 20 | @-moz-keyframes #{$name} { 21 | @content; 22 | } 23 | } 24 | 25 | $prefix-for-webkit: $original-prefix-for-webkit !global; 26 | $prefix-for-mozilla: $original-prefix-for-mozilla !global; 27 | $prefix-for-microsoft: $original-prefix-for-microsoft !global; 28 | $prefix-for-opera: $original-prefix-for-opera !global; 29 | $prefix-for-spec: $original-prefix-for-spec !global; 30 | 31 | @if $original-prefix-for-spec { 32 | @keyframes #{$name} { 33 | @content; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_size.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Sets the `width` and `height` of the element. 4 | /// 5 | /// @param {List} $size 6 | /// A list of at most 2 size values. 7 | /// 8 | /// If there is only a single value in `$size` it is used for both width and height. All units are supported. 9 | /// 10 | /// @example scss - Usage 11 | /// .first-element { 12 | /// @include size(2em); 13 | /// } 14 | /// 15 | /// .second-element { 16 | /// @include size(auto 10em); 17 | /// } 18 | /// 19 | /// @example css - CSS Output 20 | /// .first-element { 21 | /// width: 2em; 22 | /// height: 2em; 23 | /// } 24 | /// 25 | /// .second-element { 26 | /// width: auto; 27 | /// height: 10em; 28 | /// } 29 | /// 30 | /// @todo Refactor in 5.0.0 to use a comma-separated argument 31 | 32 | @mixin size($value) { 33 | $width: nth($value, 1); 34 | $height: $width; 35 | 36 | @if length($value) > 1 { 37 | $height: nth($value, 2); 38 | } 39 | 40 | @if is-size($height) { 41 | height: $height; 42 | } @else { 43 | @warn "`#{$height}` is not a valid length for the `$height` parameter in the `size` mixin."; 44 | } 45 | 46 | @if is-size($width) { 47 | width: $width; 48 | } @else { 49 | @warn "`#{$width}` is not a valid length for the `$width` parameter in the `size` mixin."; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_position.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Provides a quick method for setting an element’s position. Use a `null` value to “skip” a side. 4 | /// 5 | /// @param {Position} $position [relative] 6 | /// A CSS position value 7 | /// 8 | /// @param {Arglist} $coordinates [null null null null] 9 | /// List of values that correspond to the 4-value syntax for the edges of a box 10 | /// 11 | /// @example scss - Usage 12 | /// .element { 13 | /// @include position(absolute, 0 null null 10em); 14 | /// } 15 | /// 16 | /// @example css - CSS Output 17 | /// .element { 18 | /// left: 10em; 19 | /// position: absolute; 20 | /// top: 0; 21 | /// } 22 | /// 23 | /// @require {function} is-length 24 | /// @require {function} unpack 25 | 26 | @mixin position($position: relative, $coordinates: null null null null) { 27 | @if type-of($position) == list { 28 | $coordinates: $position; 29 | $position: relative; 30 | } 31 | 32 | $coordinates: unpack($coordinates); 33 | 34 | $offsets: ( 35 | top: nth($coordinates, 1), 36 | right: nth($coordinates, 2), 37 | bottom: nth($coordinates, 3), 38 | left: nth($coordinates, 4) 39 | ); 40 | 41 | position: $position; 42 | 43 | @each $offset, $value in $offsets { 44 | @if is-length($value) { 45 | #{$offset}: $value; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /web/models/queries.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.Queries do 2 | import Ecto.Query 3 | alias Preview.Repo 4 | alias Preview.Authenticate 5 | alias Preview.Signup 6 | alias Preview.User 7 | 8 | @doc """ 9 | Query for all users 10 | Returns list of structs 11 | """ 12 | def all_users do 13 | query = from user in User, 14 | select: user 15 | 16 | Repo.all(query) 17 | end 18 | 19 | @doc """ 20 | Query finds user by email 21 | Returns user struct or nil 22 | """ 23 | def find_by_email(email) do 24 | query = from user in User, 25 | where: user.email == ^email, 26 | select: user 27 | 28 | Repo.one(query) 29 | end 30 | 31 | @doc """ 32 | Query for all signups 33 | Returns list of structs 34 | """ 35 | def all_signups do 36 | query = from signup in Signup, 37 | select: signup 38 | 39 | Repo.all(query) 40 | end 41 | 42 | @doc """ 43 | Query authenticates user 44 | Returns user struct or nil 45 | """ 46 | def login(email, password) do 47 | query = from user in User, 48 | where: user.email == ^email, 49 | select: user 50 | 51 | user = Repo.one(query) 52 | 53 | _login(user, password) 54 | end 55 | 56 | defp _login(nil, _) do 57 | Comeonin.Bcrypt.dummy_checkpw 58 | nil 59 | end 60 | defp _login(user, password), do: Authenticate.password(user, password) 61 | end 62 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/addons/_prefixer.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Example: @include prefixer(border-radius, $radii, webkit ms spec); 3 | //************************************************************************// 4 | // Variables located in /settings/_prefixer.scss 5 | 6 | @mixin prefixer ($property, $value, $prefixes) { 7 | @each $prefix in $prefixes { 8 | @if $prefix == webkit { 9 | @if $prefix-for-webkit { 10 | -webkit-#{$property}: $value; 11 | } 12 | } 13 | @else if $prefix == moz { 14 | @if $prefix-for-mozilla { 15 | -moz-#{$property}: $value; 16 | } 17 | } 18 | @else if $prefix == ms { 19 | @if $prefix-for-microsoft { 20 | -ms-#{$property}: $value; 21 | } 22 | } 23 | @else if $prefix == o { 24 | @if $prefix-for-opera { 25 | -o-#{$property}: $value; 26 | } 27 | } 28 | @else if $prefix == spec { 29 | @if $prefix-for-spec { 30 | #{$property}: $value; 31 | } 32 | } 33 | @else { 34 | @warn "Unrecognized prefix: #{$prefix}"; 35 | } 36 | } 37 | } 38 | 39 | @mixin disable-prefix-for-all() { 40 | $prefix-for-webkit: false !global; 41 | $prefix-for-mozilla: false !global; 42 | $prefix-for-microsoft: false !global; 43 | $prefix-for-opera: false !global; 44 | $prefix-for-spec: false !global; 45 | } 46 | -------------------------------------------------------------------------------- /web/controllers/user_controller.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.UserController do 2 | use Preview.Web, :controller 3 | 4 | alias Preview.User 5 | 6 | def login(conn, _params) do 7 | changeset = User.changeset(%User{}) 8 | render conn, "login.html", changeset: changeset 9 | end 10 | 11 | def authenticate_login(conn, %{"user" => user_params}) do 12 | email = user_params["email"] 13 | password = user_params["password"] 14 | 15 | if required_params?(email, password) do 16 | case User.letmein(email, password) do 17 | {:error, message} -> 18 | conn 19 | |> put_flash(:error, message) 20 | |> redirect to: login_path(conn, :login) 21 | {:ok, _} -> 22 | conn 23 | |> put_flash(:success, "Welcome #{email}!") 24 | |> put_session(:email, email) 25 | |> redirect to: signup_path(conn, :index) 26 | end 27 | else 28 | conn 29 | |> put_flash(:error, "Please enter a valid email and password") 30 | |> redirect to: login_path(conn, :login) 31 | end 32 | end 33 | 34 | defp required_params?(_, ""), do: false 35 | defp required_params?("", _), do: false 36 | defp required_params?(_, _), do: true 37 | 38 | def logout(conn, _params) do 39 | conn 40 | |> put_session(:email, "") 41 | |> put_flash(:notice, "You're logged out.") 42 | |> redirect to: root_path(conn, :index) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /web/models/user.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.User do 2 | use Preview.Web, :model 3 | 4 | import Ecto.Query 5 | alias Preview.User 6 | alias Preview.Repo 7 | alias Preview.Authenticate 8 | 9 | schema "users" do 10 | field :email, :string 11 | field :password, :string 12 | 13 | timestamps 14 | end 15 | 16 | @required_fields ~w(email password) 17 | @optional_fields ~w() 18 | 19 | @doc """ 20 | Creates a changeset based on the `model` and `params`. 21 | 22 | If `params` are nil, an invalid changeset is returned 23 | with no validation performed. 24 | """ 25 | def changeset(model, params \\ :empty) do 26 | model 27 | |> cast(params, @required_fields, @optional_fields) 28 | |> validate_format(:email, ~r/@/) 29 | |> validate_unique(:email, on: Repo) 30 | end 31 | 32 | @doc """ 33 | Checks an email is stored in the db before 34 | authenticating the user with the password 35 | 36 | Returns {:ok, message} or {:error, message} tuple 37 | """ 38 | def letmein(email, password) do 39 | user = 40 | User 41 | |> select([user], user) 42 | |> where([user], user.email == ^email) 43 | |> Repo.one 44 | 45 | _letmein(user, password) 46 | end 47 | 48 | defp _letmein(nil, _) do 49 | Comeonin.Bcrypt.dummy_checkpw 50 | {:error, "Please enter a valid email and password"} 51 | end 52 | defp _letmein(user, password), do: Authenticate.password(user, password) 53 | end 54 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_columns.scss: -------------------------------------------------------------------------------- 1 | @mixin columns($arg: auto) { 2 | // || 3 | @include prefixer(columns, $arg, webkit moz spec); 4 | } 5 | 6 | @mixin column-count($int: auto) { 7 | // auto || integer 8 | @include prefixer(column-count, $int, webkit moz spec); 9 | } 10 | 11 | @mixin column-gap($length: normal) { 12 | // normal || length 13 | @include prefixer(column-gap, $length, webkit moz spec); 14 | } 15 | 16 | @mixin column-fill($arg: auto) { 17 | // auto || length 18 | @include prefixer(column-fill, $arg, webkit moz spec); 19 | } 20 | 21 | @mixin column-rule($arg) { 22 | // || || 23 | @include prefixer(column-rule, $arg, webkit moz spec); 24 | } 25 | 26 | @mixin column-rule-color($color) { 27 | @include prefixer(column-rule-color, $color, webkit moz spec); 28 | } 29 | 30 | @mixin column-rule-style($style: none) { 31 | // none | hidden | dashed | dotted | double | groove | inset | inset | outset | ridge | solid 32 | @include prefixer(column-rule-style, $style, webkit moz spec); 33 | } 34 | 35 | @mixin column-rule-width ($width: none) { 36 | @include prefixer(column-rule-width, $width, webkit moz spec); 37 | } 38 | 39 | @mixin column-span($arg: none) { 40 | // none || all 41 | @include prefixer(column-span, $arg, webkit moz spec); 42 | } 43 | 44 | @mixin column-width($length: auto) { 45 | // auto || length 46 | @include prefixer(column-width, $length, webkit moz spec); 47 | } 48 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_columns.scss: -------------------------------------------------------------------------------- 1 | @mixin columns($arg: auto) { 2 | // || 3 | @include prefixer(columns, $arg, webkit moz spec); 4 | } 5 | 6 | @mixin column-count($int: auto) { 7 | // auto || integer 8 | @include prefixer(column-count, $int, webkit moz spec); 9 | } 10 | 11 | @mixin column-gap($length: normal) { 12 | // normal || length 13 | @include prefixer(column-gap, $length, webkit moz spec); 14 | } 15 | 16 | @mixin column-fill($arg: auto) { 17 | // auto || length 18 | @include prefixer(column-fill, $arg, webkit moz spec); 19 | } 20 | 21 | @mixin column-rule($arg) { 22 | // || || 23 | @include prefixer(column-rule, $arg, webkit moz spec); 24 | } 25 | 26 | @mixin column-rule-color($color) { 27 | @include prefixer(column-rule-color, $color, webkit moz spec); 28 | } 29 | 30 | @mixin column-rule-style($style: none) { 31 | // none | hidden | dashed | dotted | double | groove | inset | inset | outset | ridge | solid 32 | @include prefixer(column-rule-style, $style, webkit moz spec); 33 | } 34 | 35 | @mixin column-rule-width ($width: none) { 36 | @include prefixer(column-rule-width, $width, webkit moz spec); 37 | } 38 | 39 | @mixin column-span($arg: none) { 40 | // none || all 41 | @include prefixer(column-span, $arg, webkit moz spec); 42 | } 43 | 44 | @mixin column-width($length: auto) { 45 | // auto || length 46 | @include prefixer(column-width, $length, webkit moz spec); 47 | } 48 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_str-to-num.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Helper function for linear/radial-gradient-parsers. 3 | // Source: http://sassmeister.com/gist/9647408 4 | //************************************************************************// 5 | @function _str-to-num($string) { 6 | // Matrices 7 | $strings: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"; 8 | $numbers: 0 1 2 3 4 5 6 7 8 9; 9 | 10 | // Result 11 | $result: 0; 12 | $divider: 0; 13 | $minus: false; 14 | 15 | // Looping through all characters 16 | @for $i from 1 through str-length($string) { 17 | $character: str-slice($string, $i, $i); 18 | $index: index($strings, $character); 19 | 20 | @if $character == "-" { 21 | $minus: true; 22 | } 23 | 24 | @else if $character == "." { 25 | $divider: 1; 26 | } 27 | 28 | @else { 29 | @if not $index { 30 | $result: if($minus, $result * -1, $result); 31 | @return _convert-units($result, str-slice($string, $i)); 32 | } 33 | 34 | $number: nth($numbers, $index); 35 | 36 | @if $divider == 0 { 37 | $result: $result * 10; 38 | } 39 | 40 | @else { 41 | // Move the decimal dot to the left 42 | $divider: $divider * 10; 43 | $number: $number / $divider; 44 | } 45 | 46 | $result: $result + $number; 47 | } 48 | } 49 | @return if($minus, $result * -1, $result); 50 | } 51 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_str-to-num.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Helper function for linear/radial-gradient-parsers. 3 | // Source: http://sassmeister.com/gist/9647408 4 | //************************************************************************// 5 | @function _str-to-num($string) { 6 | // Matrices 7 | $strings: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9'; 8 | $numbers: 0 1 2 3 4 5 6 7 8 9; 9 | 10 | // Result 11 | $result: 0; 12 | $divider: 0; 13 | $minus: false; 14 | 15 | // Looping through all characters 16 | @for $i from 1 through str-length($string) { 17 | $character: str-slice($string, $i, $i); 18 | $index: index($strings, $character); 19 | 20 | @if $character == '-' { 21 | $minus: true; 22 | } 23 | 24 | @else if $character == '.' { 25 | $divider: 1; 26 | } 27 | 28 | @else { 29 | @if not $index { 30 | $result: if($minus, $result * -1, $result); 31 | @return _convert-units($result, str-slice($string, $i)); 32 | } 33 | 34 | $number: nth($numbers, $index); 35 | 36 | @if $divider == 0 { 37 | $result: $result * 10; 38 | } 39 | 40 | @else { 41 | // Move the decimal dot to the left 42 | $divider: $divider * 10; 43 | $number: $number / $divider; 44 | } 45 | 46 | $result: $result + $number; 47 | } 48 | } 49 | @return if($minus, $result * -1, $result); 50 | } 51 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_linear-gradient.scss: -------------------------------------------------------------------------------- 1 | @mixin linear-gradient($pos, $g1, $g2: null, 2 | $g3: null, $g4: null, 3 | $g5: null, $g6: null, 4 | $g7: null, $g8: null, 5 | $g9: null, $g10: null, 6 | $fallback: null) { 7 | // Detect what type of value exists in $pos 8 | $pos-type: type-of(nth($pos, 1)); 9 | $pos-spec: null; 10 | $pos-degree: null; 11 | 12 | // If $pos is missing from mixin, reassign vars and add default position 13 | @if ($pos-type == color) or (nth($pos, 1) == "transparent") { 14 | $g10: $g9; $g9: $g8; $g8: $g7; $g7: $g6; $g6: $g5; 15 | $g5: $g4; $g4: $g3; $g3: $g2; $g2: $g1; $g1: $pos; 16 | $pos: null; 17 | } 18 | 19 | @if $pos { 20 | $positions: _linear-positions-parser($pos); 21 | $pos-degree: nth($positions, 1); 22 | $pos-spec: nth($positions, 2); 23 | } 24 | 25 | $full: $g1, $g2, $g3, $g4, $g5, $g6, $g7, $g8, $g9, $g10; 26 | 27 | // Set $g1 as the default fallback color 28 | $fallback-color: nth($g1, 1); 29 | 30 | // If $fallback is a color use that color as the fallback color 31 | @if (type-of($fallback) == color) or ($fallback == "transparent") { 32 | $fallback-color: $fallback; 33 | } 34 | 35 | background-color: $fallback-color; 36 | background-image: -webkit-linear-gradient($pos-degree $full); // Safari 5.1+, Chrome 37 | background-image: unquote("linear-gradient(#{$pos-spec}#{$full})"); 38 | } 39 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_linear-gradient.scss: -------------------------------------------------------------------------------- 1 | @mixin linear-gradient($pos, $G1, $G2: null, 2 | $G3: null, $G4: null, 3 | $G5: null, $G6: null, 4 | $G7: null, $G8: null, 5 | $G9: null, $G10: null, 6 | $fallback: null) { 7 | // Detect what type of value exists in $pos 8 | $pos-type: type-of(nth($pos, 1)); 9 | $pos-spec: null; 10 | $pos-degree: null; 11 | 12 | // If $pos is missing from mixin, reassign vars and add default position 13 | @if ($pos-type == color) or (nth($pos, 1) == "transparent") { 14 | $G10: $G9; $G9: $G8; $G8: $G7; $G7: $G6; $G6: $G5; 15 | $G5: $G4; $G4: $G3; $G3: $G2; $G2: $G1; $G1: $pos; 16 | $pos: null; 17 | } 18 | 19 | @if $pos { 20 | $positions: _linear-positions-parser($pos); 21 | $pos-degree: nth($positions, 1); 22 | $pos-spec: nth($positions, 2); 23 | } 24 | 25 | $full: $G1, $G2, $G3, $G4, $G5, $G6, $G7, $G8, $G9, $G10; 26 | 27 | // Set $G1 as the default fallback color 28 | $fallback-color: nth($G1, 1); 29 | 30 | // If $fallback is a color use that color as the fallback color 31 | @if (type-of($fallback) == color) or ($fallback == "transparent") { 32 | $fallback-color: $fallback; 33 | } 34 | 35 | background-color: $fallback-color; 36 | background-image: -webkit-linear-gradient($pos-degree $full); // Safari 5.1+, Chrome 37 | background-image: unquote("linear-gradient(#{$pos-spec}#{$full})"); 38 | } 39 | -------------------------------------------------------------------------------- /web/views/layout_view.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.LayoutView do 2 | use Preview.Web, :view 3 | 4 | alias Phoenix.Controller 5 | 6 | @doc """ 7 | Maps the first flash message level to a css class 8 | 9 | ## Examples 10 | 11 | iex> conn = %Plug.Conn{private: %{plug_session: %{}}} 12 | ...> conn |> 13 | ...> Phoenix.Controller.fetch_flash |> 14 | ...> Phoenix.Controller.put_flash(:notice, "Message!") |> 15 | ...> Preview.LayoutView.flash_class 16 | "flash-notice" 17 | """ 18 | @spec flash_class(map) :: String.t 19 | def flash_class(conn), do: _flash_class(Controller.get_flash(conn)) 20 | def _flash_class(%{"info" => _}), do: "flash-notice" 21 | def _flash_class(%{"error" => _}), do: "flash-error" 22 | def _flash_class(%{"alert" => _}), do: "flash-alert" 23 | def _flash_class(%{"notice" => _}), do: "flash-notice" 24 | def _flash_class(%{"success" => _}), do: "flash-success" 25 | def _flash_class(%{}), do: "" 26 | 27 | @doc """ 28 | Returns the flash message string 29 | 30 | ## Examples 31 | 32 | iex> conn = %Plug.Conn{private: %{plug_session: %{}}} 33 | ...> conn |> 34 | ...> Phoenix.Controller.fetch_flash |> 35 | ...> Phoenix.Controller.put_flash(:notice, "Message!") |> 36 | ...> Preview.LayoutView.flash_message 37 | "Message!" 38 | """ 39 | @spec flash_message(map) :: String.t 40 | def flash_message(conn) do 41 | flash = Controller.get_flash(conn) 42 | flash |> Map.values |> List.first 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_radial-gradient-parser.scss: -------------------------------------------------------------------------------- 1 | @function _radial-gradient-parser($image) { 2 | $image: unquote($image); 3 | $gradients: (); 4 | $start: str-index($image, "("); 5 | $end: str-index($image, ","); 6 | $first-val: str-slice($image, $start + 1, $end - 1); 7 | 8 | $prefix: str-slice($image, 0, $start); 9 | $suffix: str-slice($image, $end, str-length($image)); 10 | 11 | $is-spec-syntax: str-index($first-val, "at"); 12 | 13 | @if $is-spec-syntax and $is-spec-syntax > 1 { 14 | $keyword: str-slice($first-val, 1, $is-spec-syntax - 2); 15 | $pos: str-slice($first-val, $is-spec-syntax + 3, str-length($first-val)); 16 | $pos: append($pos, $keyword, comma); 17 | 18 | $gradients: ( 19 | webkit-image: -webkit- + $prefix + $pos + $suffix, 20 | spec-image: $image 21 | ) 22 | } 23 | 24 | @else if $is-spec-syntax == 1 { 25 | $pos: str-slice($first-val, $is-spec-syntax + 3, str-length($first-val)); 26 | 27 | $gradients: ( 28 | webkit-image: -webkit- + $prefix + $pos + $suffix, 29 | spec-image: $image 30 | ) 31 | } 32 | 33 | @else if str-index($image, "cover") or str-index($image, "contain") { 34 | @warn "Radial-gradient needs to be updated to conform to latest spec."; 35 | 36 | $gradients: ( 37 | webkit-image: null, 38 | spec-image: $image 39 | ) 40 | } 41 | 42 | @else { 43 | $gradients: ( 44 | webkit-image: -webkit- + $image, 45 | spec-image: $image 46 | ) 47 | } 48 | 49 | @return $gradients; 50 | } 51 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_background-image.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Background-image property for adding multiple background images with 3 | // gradients, or for stringing multiple gradients together. 4 | //************************************************************************// 5 | 6 | @mixin background-image($images...) { 7 | $webkit-images: (); 8 | $spec-images: (); 9 | 10 | @each $image in $images { 11 | $webkit-image: (); 12 | $spec-image: (); 13 | 14 | @if (type-of($image) == string) { 15 | $url-str: str-slice($image, 0, 3); 16 | $gradient-type: str-slice($image, 0, 6); 17 | 18 | @if $url-str == "url" { 19 | $webkit-image: $image; 20 | $spec-image: $image; 21 | } 22 | 23 | @else if $gradient-type == "linear" { 24 | $gradients: _linear-gradient-parser($image); 25 | $webkit-image: map-get($gradients, webkit-image); 26 | $spec-image: map-get($gradients, spec-image); 27 | } 28 | 29 | @else if $gradient-type == "radial" { 30 | $gradients: _radial-gradient-parser($image); 31 | $webkit-image: map-get($gradients, webkit-image); 32 | $spec-image: map-get($gradients, spec-image); 33 | } 34 | } 35 | 36 | $webkit-images: append($webkit-images, $webkit-image, comma); 37 | $spec-images: append($spec-images, $spec-image, comma); 38 | } 39 | 40 | background-image: $webkit-images; 41 | background-image: $spec-images; 42 | } 43 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_radial-gradient-parser.scss: -------------------------------------------------------------------------------- 1 | @function _radial-gradient-parser($image) { 2 | $image: unquote($image); 3 | $gradients: (); 4 | $start: str-index($image, "("); 5 | $end: str-index($image, ","); 6 | $first-val: str-slice($image, $start + 1, $end - 1); 7 | 8 | $prefix: str-slice($image, 0, $start); 9 | $suffix: str-slice($image, $end, str-length($image)); 10 | 11 | $is-spec-syntax: str-index($first-val, "at"); 12 | 13 | @if $is-spec-syntax and $is-spec-syntax > 1 { 14 | $keyword: str-slice($first-val, 1, $is-spec-syntax - 2); 15 | $pos: str-slice($first-val, $is-spec-syntax + 3, str-length($first-val)); 16 | $pos: append($pos, $keyword, comma); 17 | 18 | $gradients: ( 19 | webkit-image: -webkit- + $prefix + $pos + $suffix, 20 | spec-image: $image 21 | ); 22 | } 23 | 24 | @else if $is-spec-syntax == 1 { 25 | $pos: str-slice($first-val, $is-spec-syntax + 3, str-length($first-val)); 26 | 27 | $gradients: ( 28 | webkit-image: -webkit- + $prefix + $pos + $suffix, 29 | spec-image: $image 30 | ); 31 | } 32 | 33 | @else if str-index($image, "cover") or str-index($image, "contain") { 34 | @warn "Radial-gradient needs to be updated to conform to latest spec."; 35 | 36 | $gradients: ( 37 | webkit-image: null, 38 | spec-image: $image 39 | ); 40 | } 41 | 42 | @else { 43 | $gradients: ( 44 | webkit-image: -webkit- + $image, 45 | spec-image: $image 46 | ); 47 | } 48 | 49 | @return $gradients; 50 | } 51 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_background-image.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Background-image property for adding multiple background images with 3 | // gradients, or for stringing multiple gradients together. 4 | //************************************************************************// 5 | 6 | @mixin background-image($images...) { 7 | $webkit-images: (); 8 | $spec-images: (); 9 | 10 | @each $image in $images { 11 | $webkit-image: (); 12 | $spec-image: (); 13 | 14 | @if (type-of($image) == string) { 15 | $url-str: str-slice($image, 0, 3); 16 | $gradient-type: str-slice($image, 0, 6); 17 | 18 | @if $url-str == "url" { 19 | $webkit-image: $image; 20 | $spec-image: $image; 21 | } 22 | 23 | @else if $gradient-type == "linear" { 24 | $gradients: _linear-gradient-parser($image); 25 | $webkit-image: map-get($gradients, webkit-image); 26 | $spec-image: map-get($gradients, spec-image); 27 | } 28 | 29 | @else if $gradient-type == "radial" { 30 | $gradients: _radial-gradient-parser($image); 31 | $webkit-image: map-get($gradients, webkit-image); 32 | $spec-image: map-get($gradients, spec-image); 33 | } 34 | } 35 | 36 | $webkit-images: append($webkit-images, $webkit-image, comma); 37 | $spec-images: append($spec-images, $spec-image, comma); 38 | } 39 | 40 | background-image: $webkit-images; 41 | background-image: $spec-images; 42 | } 43 | -------------------------------------------------------------------------------- /test/lib/authenticate_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Preview.AuthenticateTest do 2 | use ExUnit.Case, async: true 3 | alias Preview.Authenticate 4 | 5 | def letmein_user do 6 | %Preview.User{ 7 | id: 1, 8 | password: "$2a$10$2EIByNPyYvI55PyzoQVeHe.XFYktolJRDse0m3Gl8v28cxdK8wusu", 9 | email: "obiwan@example.com"} 10 | end 11 | 12 | def users do 13 | [%Preview.User{ 14 | id: 1, 15 | password: "$2a$10$2EIByNPyYvI55PyzoQVeHe.XFYktolJRDse0m3Gl8v28cxdK8wusu", 16 | email: "luke@example.com"}, 17 | %Preview.User{ 18 | id: 1, 19 | password: "$2a$10$2EIByNPyYvI55PyzoQVeHe.XFYktolJRDse0m3Gl8v28cxdK8wusu", 20 | email: "chewie@example.com"}] 21 | end 22 | 23 | # password/2 24 | test "matched password returns the :ok user struct tuple" do 25 | password = "letmein" 26 | 27 | assert Authenticate.password(letmein_user, password) == {:ok, letmein_user} 28 | end 29 | 30 | test "unmatched password returns the :error message tuple" do 31 | password = "nochance" 32 | 33 | assert Authenticate.password(letmein_user, password) == {:error, "Please enter a valid email and password"} 34 | end 35 | 36 | # session/2 37 | test "user email in the session is a registered user" do 38 | user = "luke@example.com" 39 | 40 | assert Authenticate.session(users, user) == {:ok, "User access granted!"} 41 | end 42 | 43 | test "user email in the session is not a registered user" do 44 | user = "boba@example.com" 45 | 46 | assert Authenticate.session(users, user) == {:error, "Unauthorized access attempt!"} 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_radial-gradient.scss: -------------------------------------------------------------------------------- 1 | // Requires Sass 3.1+ 2 | @mixin radial-gradient($g1, $g2, 3 | $g3: null, $g4: null, 4 | $g5: null, $g6: null, 5 | $g7: null, $g8: null, 6 | $g9: null, $g10: null, 7 | $pos: null, 8 | $shape-size: null, 9 | $fallback: null) { 10 | 11 | $data: _radial-arg-parser($g1, $g2, $pos, $shape-size); 12 | $g1: nth($data, 1); 13 | $g2: nth($data, 2); 14 | $pos: nth($data, 3); 15 | $shape-size: nth($data, 4); 16 | 17 | $full: $g1, $g2, $g3, $g4, $g5, $g6, $g7, $g8, $g9, $g10; 18 | 19 | // Strip deprecated cover/contain for spec 20 | $shape-size-spec: _shape-size-stripper($shape-size); 21 | 22 | // Set $g1 as the default fallback color 23 | $first-color: nth($full, 1); 24 | $fallback-color: nth($first-color, 1); 25 | 26 | @if (type-of($fallback) == color) or ($fallback == "transparent") { 27 | $fallback-color: $fallback; 28 | } 29 | 30 | // Add Commas and spaces 31 | $shape-size: if($shape-size, "#{$shape-size}, ", null); 32 | $pos: if($pos, "#{$pos}, ", null); 33 | $pos-spec: if($pos, "at #{$pos}", null); 34 | $shape-size-spec: if(($shape-size-spec != " ") and ($pos == null), "#{$shape-size-spec}, ", "#{$shape-size-spec} "); 35 | 36 | background-color: $fallback-color; 37 | background-image: -webkit-radial-gradient(unquote(#{$pos}#{$shape-size}#{$full})); 38 | background-image: unquote("radial-gradient(#{$shape-size-spec}#{$pos-spec}#{$full})"); 39 | } 40 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_radial-gradient.scss: -------------------------------------------------------------------------------- 1 | // Requires Sass 3.1+ 2 | @mixin radial-gradient($G1, $G2, 3 | $G3: null, $G4: null, 4 | $G5: null, $G6: null, 5 | $G7: null, $G8: null, 6 | $G9: null, $G10: null, 7 | $pos: null, 8 | $shape-size: null, 9 | $fallback: null) { 10 | 11 | $data: _radial-arg-parser($G1, $G2, $pos, $shape-size); 12 | $G1: nth($data, 1); 13 | $G2: nth($data, 2); 14 | $pos: nth($data, 3); 15 | $shape-size: nth($data, 4); 16 | 17 | $full: $G1, $G2, $G3, $G4, $G5, $G6, $G7, $G8, $G9, $G10; 18 | 19 | // Strip deprecated cover/contain for spec 20 | $shape-size-spec: _shape-size-stripper($shape-size); 21 | 22 | // Set $G1 as the default fallback color 23 | $first-color: nth($full, 1); 24 | $fallback-color: nth($first-color, 1); 25 | 26 | @if (type-of($fallback) == color) or ($fallback == "transparent") { 27 | $fallback-color: $fallback; 28 | } 29 | 30 | // Add Commas and spaces 31 | $shape-size: if($shape-size, '#{$shape-size}, ', null); 32 | $pos: if($pos, '#{$pos}, ', null); 33 | $pos-spec: if($pos, 'at #{$pos}', null); 34 | $shape-size-spec: if(($shape-size-spec != ' ') and ($pos == null), '#{$shape-size-spec}, ', '#{$shape-size-spec} '); 35 | 36 | background-color: $fallback-color; 37 | background-image: -webkit-radial-gradient(unquote(#{$pos}#{$shape-size}#{$full})); 38 | background-image: unquote("radial-gradient(#{$shape-size-spec}#{$pos-spec}#{$full})"); 39 | } 40 | -------------------------------------------------------------------------------- /web/static/css/base/_forms.scss: -------------------------------------------------------------------------------- 1 | fieldset { 2 | background-color: lighten($base-border-color, 10%); 3 | border: $base-border; 4 | margin: 0 0 $small-spacing; 5 | padding: $base-spacing; 6 | } 7 | 8 | input, 9 | label, 10 | select { 11 | display: block; 12 | font-family: $base-font-family; 13 | font-size: $base-font-size; 14 | } 15 | 16 | label { 17 | font-weight: 600; 18 | margin-bottom: $small-spacing / 2; 19 | 20 | &.required::after { 21 | content: "*"; 22 | } 23 | 24 | abbr { 25 | display: none; 26 | } 27 | } 28 | 29 | #{$all-text-inputs}, 30 | select[multiple=multiple], 31 | textarea { 32 | background-color: $base-background-color; 33 | border: $base-border; 34 | border-radius: $base-border-radius; 35 | box-shadow: $form-box-shadow; 36 | box-sizing: border-box; 37 | font-family: $base-font-family; 38 | font-size: $base-font-size; 39 | margin-bottom: $base-spacing / 2; 40 | padding: $base-spacing / 3; 41 | transition: border-color; 42 | width: 100%; 43 | 44 | &:hover { 45 | border-color: darken($base-border-color, 10%); 46 | } 47 | 48 | &:focus { 49 | border-color: $action-color; 50 | box-shadow: $form-box-shadow-focus; 51 | outline: none; 52 | } 53 | } 54 | 55 | textarea { 56 | resize: vertical; 57 | } 58 | 59 | input[type="search"] { 60 | @include appearance(none); 61 | } 62 | 63 | input[type="checkbox"], 64 | input[type="radio"] { 65 | display: inline; 66 | margin-right: $small-spacing / 2; 67 | } 68 | 69 | input[type="file"] { 70 | padding-bottom: $small-spacing; 71 | width: 100%; 72 | } 73 | 74 | select { 75 | margin-bottom: $base-spacing; 76 | max-width: 100%; 77 | width: auto; 78 | } 79 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_animation.scss: -------------------------------------------------------------------------------- 1 | // http://www.w3.org/TR/css3-animations/#the-animation-name-property- 2 | // Each of these mixins support comma separated lists of values, which allows different transitions for individual properties to be described in a single style rule. Each value in the list corresponds to the value at that same position in the other properties. 3 | 4 | @mixin animation($animations...) { 5 | @include prefixer(animation, $animations, webkit moz spec); 6 | } 7 | 8 | @mixin animation-name($names...) { 9 | @include prefixer(animation-name, $names, webkit moz spec); 10 | } 11 | 12 | @mixin animation-duration($times...) { 13 | @include prefixer(animation-duration, $times, webkit moz spec); 14 | } 15 | 16 | @mixin animation-timing-function($motions...) { 17 | // ease | linear | ease-in | ease-out | ease-in-out 18 | @include prefixer(animation-timing-function, $motions, webkit moz spec); 19 | } 20 | 21 | @mixin animation-iteration-count($values...) { 22 | // infinite | 23 | @include prefixer(animation-iteration-count, $values, webkit moz spec); 24 | } 25 | 26 | @mixin animation-direction($directions...) { 27 | // normal | alternate 28 | @include prefixer(animation-direction, $directions, webkit moz spec); 29 | } 30 | 31 | @mixin animation-play-state($states...) { 32 | // running | paused 33 | @include prefixer(animation-play-state, $states, webkit moz spec); 34 | } 35 | 36 | @mixin animation-delay($times...) { 37 | @include prefixer(animation-delay, $times, webkit moz spec); 38 | } 39 | 40 | @mixin animation-fill-mode($modes...) { 41 | // none | forwards | backwards | both 42 | @include prefixer(animation-fill-mode, $modes, webkit moz spec); 43 | } 44 | -------------------------------------------------------------------------------- /priv/static/sass/base/_forms.scss: -------------------------------------------------------------------------------- 1 | fieldset { 2 | background: lighten($base-border-color, 10); 3 | border: $base-border; 4 | margin: 0 0 ($base-spacing / 2) 0; 5 | padding: $base-spacing; 6 | } 7 | 8 | input, 9 | label, 10 | select { 11 | display: block; 12 | font-family: $form-font-family; 13 | font-size: $form-font-size; 14 | } 15 | 16 | label { 17 | font-weight: bold; 18 | margin-bottom: $base-spacing / 4; 19 | 20 | &.required:after { 21 | content: "*"; 22 | } 23 | 24 | abbr { 25 | display: none; 26 | } 27 | } 28 | 29 | textarea, 30 | #{$all-text-inputs}, 31 | select[multiple=multiple] { 32 | @include box-sizing(border-box); 33 | @include transition(border-color); 34 | background-color: white; 35 | border-radius: $form-border-radius; 36 | border: 1px solid $form-border-color; 37 | box-shadow: $form-box-shadow; 38 | font-family: $form-font-family; 39 | font-size: $form-font-size; 40 | margin-bottom: $base-spacing / 2; 41 | padding: ($base-spacing / 3) ($base-spacing / 3); 42 | width: 100%; 43 | 44 | &:hover { 45 | border-color: $form-border-color-hover; 46 | } 47 | 48 | &:focus { 49 | border-color: $form-border-color-focus; 50 | box-shadow: $form-box-shadow-focus; 51 | outline: none; 52 | } 53 | } 54 | 55 | textarea { 56 | resize: vertical; 57 | } 58 | 59 | input[type="search"] { 60 | @include appearance(none); 61 | } 62 | 63 | input[type="checkbox"], 64 | input[type="radio"] { 65 | display: inline; 66 | margin-right: $base-spacing / 4; 67 | } 68 | 69 | input[type="file"] { 70 | padding-bottom: $base-spacing / 2; 71 | width: 100%; 72 | } 73 | 74 | select { 75 | margin-bottom: $base-spacing; 76 | max-width: 100%; 77 | width: auto; 78 | } 79 | -------------------------------------------------------------------------------- /priv/static/sass/neat/grid/_shift.scss: -------------------------------------------------------------------------------- 1 | /// Translates an element horizontally by a number of columns. Positive arguments shift the element to the active layout direction, while negative ones shift it to the opposite direction. 2 | /// 3 | /// @param {Number (unitless)} $n-columns (1) 4 | /// Number of columns by which the element shifts. 5 | /// 6 | /// @example scss - Usage 7 | /// .element { 8 | /// @include shift(-3); 9 | /// } 10 | /// 11 | /// @example css - CSS output 12 | /// .element { 13 | /// margin-left: -25.58941%; 14 | /// } 15 | 16 | @mixin shift($n-columns: 1) { 17 | @include shift-in-context($n-columns); 18 | } 19 | 20 | /// Translates an element horizontally by a number of columns, in a specific nesting context. 21 | /// 22 | /// @param {List} $shift 23 | /// A list containing the number of columns to shift (`$columns`) and the number of columns of the parent element (`$container-columns`). 24 | /// 25 | /// The two values can be separated with any string such as `of`, `/`, etc. 26 | /// 27 | /// @example scss - Usage 28 | /// .element { 29 | /// @include shift(-3 of 6); 30 | /// } 31 | /// 32 | /// @example css - CSS output 33 | /// .element { 34 | /// margin-left: -52.41458%; 35 | /// } 36 | 37 | @mixin shift-in-context($shift: $columns of $container-columns) { 38 | $n-columns: nth($shift, 1); 39 | $parent-columns: container-shift($shift) !global; 40 | 41 | $direction: get-direction($layout-direction, $default-layout-direction); 42 | $opposite-direction: get-opposite-direction($direction); 43 | 44 | margin-#{$opposite-direction}: $n-columns * flex-grid(1, $parent-columns) + $n-columns * flex-gutter($parent-columns); 45 | 46 | // Reset nesting context 47 | $parent-columns: $grid-columns !global; 48 | } 49 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_flex-grid.scss: -------------------------------------------------------------------------------- 1 | // Flexible grid 2 | @function flex-grid($columns, $container-columns: $fg-max-columns) { 3 | $width: $columns * $fg-column + ($columns - 1) * $fg-gutter; 4 | $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter; 5 | @return percentage($width / $container-width); 6 | } 7 | 8 | // Flexible gutter 9 | @function flex-gutter($container-columns: $fg-max-columns, $gutter: $fg-gutter) { 10 | $container-width: $container-columns * $fg-column + ($container-columns - 1) * $fg-gutter; 11 | @return percentage($gutter / $container-width); 12 | } 13 | 14 | // The $fg-column, $fg-gutter and $fg-max-columns variables must be defined in your base stylesheet to properly use the flex-grid function. 15 | // This function takes the fluid grid equation (target / context = result) and uses columns to help define each. 16 | // 17 | // The calculation presumes that your column structure will be missing the last gutter: 18 | // 19 | // -- column -- gutter -- column -- gutter -- column 20 | // 21 | // $fg-column: 60px; // Column Width 22 | // $fg-gutter: 25px; // Gutter Width 23 | // $fg-max-columns: 12; // Total Columns For Main Container 24 | // 25 | // div { 26 | // width: flex-grid(4); // returns (315px / 995px) = 31.65829%; 27 | // margin-left: flex-gutter(); // returns (25px / 995px) = 2.51256%; 28 | // 29 | // p { 30 | // width: flex-grid(2, 4); // returns (145px / 315px) = 46.031746%; 31 | // float: left; 32 | // margin: flex-gutter(4); // returns (25px / 315px) = 7.936508%; 33 | // } 34 | // 35 | // blockquote { 36 | // float: left; 37 | // width: flex-grid(2, 4); // returns (145px / 315px) = 46.031746%; 38 | // } 39 | // } -------------------------------------------------------------------------------- /web/templates/layout/app.html.eex: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Preview Phoenix! 14 | "> 15 | 16 | 17 | 18 |
19 | <%= tag :img, src: static_path(@conn, "/images/phoenix.png") %> 20 |
21 | 22 |
23 |
24 | <%= flash_message(@conn) %> 25 |
26 | 27 | <%= @inner %> 28 |
29 | 30 |
31 | 32 | 33 | 34 | Fork me on GitHub 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /web/static/css/neat/grid/_shift.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Translates an element horizontally by a number of columns. Positive arguments shift the element to the active layout direction, while negative ones shift it to the opposite direction. 4 | /// 5 | /// @param {Number (unitless)} $n-columns [1] 6 | /// Number of columns by which the element shifts. 7 | /// 8 | /// @example scss - Usage 9 | /// .element { 10 | /// @include shift(-3); 11 | /// } 12 | /// 13 | /// @example css - CSS output 14 | /// .element { 15 | /// margin-left: -25.58941%; 16 | /// } 17 | 18 | @mixin shift($n-columns: 1) { 19 | @include shift-in-context($n-columns); 20 | } 21 | 22 | /// Translates an element horizontally by a number of columns, in a specific nesting context. 23 | /// 24 | /// @param {List} $shift 25 | /// A list containing the number of columns to shift (`$columns`) and the number of columns of the parent element (`$container-columns`). 26 | /// 27 | /// The two values can be separated with any string such as `of`, `/`, etc. 28 | /// 29 | /// @example scss - Usage 30 | /// .element { 31 | /// @include shift(-3 of 6); 32 | /// } 33 | /// 34 | /// @example css - CSS output 35 | /// .element { 36 | /// margin-left: -52.41458%; 37 | /// } 38 | 39 | @mixin shift-in-context($shift: $columns of $container-columns) { 40 | $n-columns: nth($shift, 1); 41 | $parent-columns: container-shift($shift) !global; 42 | 43 | $direction: get-direction($layout-direction, $default-layout-direction); 44 | $opposite-direction: get-opposite-direction($direction); 45 | 46 | margin-#{$opposite-direction}: $n-columns * flex-grid(1, $parent-columns) + $n-columns * flex-gutter($parent-columns); 47 | 48 | // Reset nesting context 49 | $parent-columns: $grid-columns !global; 50 | } 51 | -------------------------------------------------------------------------------- /priv/static/sass/base/_typography.scss: -------------------------------------------------------------------------------- 1 | body { 2 | -webkit-font-smoothing: antialiased; 3 | background-color: $base-background-color; 4 | color: $base-font-color; 5 | font-family: $base-font-family; 6 | font-size: $base-font-size; 7 | line-height: $base-line-height; 8 | } 9 | 10 | h1, 11 | h2, 12 | h3, 13 | h4, 14 | h5, 15 | h6 { 16 | font-family: $header-font-family; 17 | line-height: $header-line-height; 18 | margin: 0; 19 | text-rendering: optimizeLegibility; // Fix the character spacing for headings 20 | } 21 | 22 | h1 { 23 | font-size: $h1-font-size; 24 | } 25 | 26 | h2 { 27 | font-size: $h2-font-size; 28 | } 29 | 30 | h3 { 31 | font-size: $h3-font-size; 32 | } 33 | 34 | h4 { 35 | font-size: $h4-font-size; 36 | } 37 | 38 | h5 { 39 | font-size: $h5-font-size; 40 | } 41 | 42 | h6 { 43 | font-size: $h6-font-size; 44 | } 45 | 46 | p { 47 | margin: 0 0 ($base-spacing / 2); 48 | } 49 | 50 | a { 51 | @include transition(color 0.1s linear); 52 | color: $base-link-color; 53 | text-decoration: none; 54 | 55 | &:hover { 56 | color: $hover-link-color; 57 | } 58 | 59 | &:active, &:focus { 60 | color: $hover-link-color; 61 | outline: none; 62 | } 63 | } 64 | 65 | hr { 66 | border-bottom: $base-border; 67 | border-left: none; 68 | border-right: none; 69 | border-top: none; 70 | margin: $base-spacing 0; 71 | } 72 | 73 | img, 74 | picture { 75 | margin: 0; 76 | max-width: 100%; 77 | } 78 | 79 | blockquote { 80 | border-left: 2px solid $base-border-color; 81 | color: lighten($base-font-color, 15); 82 | margin: $base-spacing 0; 83 | padding-left: $base-spacing / 2; 84 | } 85 | 86 | cite { 87 | color: lighten($base-font-color, 25); 88 | font-style: italic; 89 | 90 | &:before { 91 | content: "\2014 \00A0"; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/functions/_modular-scale.scss: -------------------------------------------------------------------------------- 1 | // Scaling Variables 2 | $golden: 1.618; 3 | $minor-second: 1.067; 4 | $major-second: 1.125; 5 | $minor-third: 1.2; 6 | $major-third: 1.25; 7 | $perfect-fourth: 1.333; 8 | $augmented-fourth: 1.414; 9 | $perfect-fifth: 1.5; 10 | $minor-sixth: 1.6; 11 | $major-sixth: 1.667; 12 | $minor-seventh: 1.778; 13 | $major-seventh: 1.875; 14 | $octave: 2; 15 | $major-tenth: 2.5; 16 | $major-eleventh: 2.667; 17 | $major-twelfth: 3; 18 | $double-octave: 4; 19 | 20 | @function modular-scale($value, $increment, $ratio) { 21 | $v1: nth($value, 1); 22 | $v2: nth($value, length($value)); 23 | $value: $v1; 24 | 25 | // scale $v2 to just above $v1 26 | @while $v2 > $v1 { 27 | $v2: ($v2 / $ratio); // will be off-by-1 28 | } 29 | @while $v2 < $v1 { 30 | $v2: ($v2 * $ratio); // will fix off-by-1 31 | } 32 | 33 | // check AFTER scaling $v2 to prevent double-counting corner-case 34 | $double-stranded: $v2 > $v1; 35 | 36 | @if $increment > 0 { 37 | @for $i from 1 through $increment { 38 | @if $double-stranded and ($v1 * $ratio) > $v2 { 39 | $value: $v2; 40 | $v2: ($v2 * $ratio); 41 | } @else { 42 | $v1: ($v1 * $ratio); 43 | $value: $v1; 44 | } 45 | } 46 | } 47 | 48 | @if $increment < 0 { 49 | // adjust $v2 to just below $v1 50 | @if $double-stranded { 51 | $v2: ($v2 / $ratio); 52 | } 53 | 54 | @for $i from $increment through -1 { 55 | @if $double-stranded and ($v1 / $ratio) < $v2 { 56 | $value: $v2; 57 | $v2: ($v2 / $ratio); 58 | } @else { 59 | $v1: ($v1 / $ratio); 60 | $value: $v1; 61 | } 62 | } 63 | } 64 | 65 | @return $value; 66 | } 67 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_animation.scss: -------------------------------------------------------------------------------- 1 | // http://www.w3.org/TR/css3-animations/#the-animation-name-property- 2 | // Each of these mixins support comma separated lists of values, which allows different transitions for individual properties to be described in a single style rule. Each value in the list corresponds to the value at that same position in the other properties. 3 | 4 | // Official animation shorthand property. 5 | @mixin animation ($animations...) { 6 | @include prefixer(animation, $animations, webkit moz spec); 7 | } 8 | 9 | // Individual Animation Properties 10 | @mixin animation-name ($names...) { 11 | @include prefixer(animation-name, $names, webkit moz spec); 12 | } 13 | 14 | 15 | @mixin animation-duration ($times...) { 16 | @include prefixer(animation-duration, $times, webkit moz spec); 17 | } 18 | 19 | 20 | @mixin animation-timing-function ($motions...) { 21 | // ease | linear | ease-in | ease-out | ease-in-out 22 | @include prefixer(animation-timing-function, $motions, webkit moz spec); 23 | } 24 | 25 | 26 | @mixin animation-iteration-count ($values...) { 27 | // infinite | 28 | @include prefixer(animation-iteration-count, $values, webkit moz spec); 29 | } 30 | 31 | 32 | @mixin animation-direction ($directions...) { 33 | // normal | alternate 34 | @include prefixer(animation-direction, $directions, webkit moz spec); 35 | } 36 | 37 | 38 | @mixin animation-play-state ($states...) { 39 | // running | paused 40 | @include prefixer(animation-play-state, $states, webkit moz spec); 41 | } 42 | 43 | 44 | @mixin animation-delay ($times...) { 45 | @include prefixer(animation-delay, $times, webkit moz spec); 46 | } 47 | 48 | 49 | @mixin animation-fill-mode ($modes...) { 50 | // none | forwards | backwards | both 51 | @include prefixer(animation-fill-mode, $modes, webkit moz spec); 52 | } 53 | -------------------------------------------------------------------------------- /priv/static/sass/neat/grid/_row.scss: -------------------------------------------------------------------------------- 1 | /// Designates the element as a row of columns in the grid layout. It clears the floats on the element and sets its display property. Rows can't be nested, but there can be more than one row element—with different display properties—per layout. 2 | /// 3 | /// @param {String} $display (default) 4 | /// Sets the display property of the element and the display context that will be used by its children. Can be `block` or `table`. 5 | /// 6 | /// @param {String} $direction ($default-layout-direction) 7 | /// Sets the layout direction. Can be `LTR` (left-to-right) or `RTL` (right-to-left). 8 | /// 9 | /// @example scss - Usage 10 | /// .element { 11 | /// @include row(); 12 | /// } 13 | /// 14 | /// @example css - CSS Output 15 | /// .element { 16 | /// *zoom: 1; 17 | /// display: block; 18 | /// } 19 | /// 20 | /// .element:before, .element:after { 21 | /// content: " "; 22 | /// display: table; 23 | /// } 24 | /// 25 | /// .element:after { 26 | /// clear: both; 27 | /// } 28 | 29 | @mixin row($display: default, $direction: $default-layout-direction) { 30 | @if $direction != $default-layout-direction { 31 | @include -neat-warn("The $direction argument will be deprecated in future versions in favor of the direction(){...} mixin."); 32 | } 33 | 34 | $layout-direction: $direction !global; 35 | 36 | @if $display != default { 37 | @include -neat-warn("The $display argument will be deprecated in future versions in favor of the display(){...} mixin."); 38 | } 39 | 40 | @if $display == table { 41 | display: table; 42 | @include fill-parent; 43 | table-layout: fixed; 44 | $container-display-table: true !global; 45 | } 46 | 47 | @else { 48 | @include clearfix; 49 | display: block; 50 | $container-display-table: false !global; 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /web/static/css/neat/grid/_row.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Designates the element as a row of columns in the grid layout. It clears the floats on the element and sets its display property. Rows can't be nested, but there can be more than one row element—with different display properties—per layout. 4 | /// 5 | /// @param {String} $display [default] 6 | /// Sets the display property of the element and the display context that will be used by its children. Can be `block` or `table`. 7 | /// 8 | /// @param {String} $direction [$default-layout-direction] 9 | /// Sets the layout direction. Can be `LTR` (left-to-right) or `RTL` (right-to-left). 10 | /// 11 | /// @example scss - Usage 12 | /// .element { 13 | /// @include row(); 14 | /// } 15 | /// 16 | /// @example css - CSS Output 17 | /// .element { 18 | /// *zoom: 1; 19 | /// display: block; 20 | /// } 21 | /// 22 | /// .element:before, .element:after { 23 | /// content: " "; 24 | /// display: table; 25 | /// } 26 | /// 27 | /// .element:after { 28 | /// clear: both; 29 | /// } 30 | 31 | @mixin row($display: default, $direction: $default-layout-direction) { 32 | @if $direction != $default-layout-direction { 33 | @include -neat-warn("The $direction argument will be deprecated in future versions in favor of the direction(){...} mixin."); 34 | } 35 | 36 | $layout-direction: $direction !global; 37 | 38 | @if $display != default { 39 | @include -neat-warn("The $display argument will be deprecated in future versions in favor of the display(){...} mixin."); 40 | } 41 | 42 | @if $display == table { 43 | display: table; 44 | @include fill-parent; 45 | table-layout: fixed; 46 | $container-display-table: true !global; 47 | } @else { 48 | @include clearfix; 49 | display: block; 50 | $container-display-table: false !global; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /web/web.ex: -------------------------------------------------------------------------------- 1 | defmodule Preview.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 Preview.Web, :controller 9 | use Preview.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.Model 22 | end 23 | end 24 | 25 | def controller do 26 | quote do 27 | use Phoenix.Controller 28 | 29 | # Alias the data repository and import query/model functions 30 | alias Preview.Repo 31 | import Ecto.Model 32 | import Ecto.Query, only: [from: 2] 33 | 34 | # Import URL helpers from the router 35 | import Preview.Router.Helpers 36 | end 37 | end 38 | 39 | def view do 40 | quote do 41 | use Phoenix.View, root: "web/templates" 42 | 43 | # Import convenience functions from controllers 44 | import Phoenix.Controller, only: [get_flash: 2] 45 | 46 | # Import URL helpers from the router 47 | import Preview.Router.Helpers 48 | 49 | # Use all HTML functionality (forms, tags, etc) 50 | use Phoenix.HTML 51 | end 52 | end 53 | 54 | def channel do 55 | quote do 56 | use Phoenix.Channel 57 | 58 | # Alias the data repository and import query/model functions 59 | alias Preview.Repo 60 | import Ecto.Model 61 | import Ecto.Query, only: [from: 2] 62 | 63 | end 64 | end 65 | 66 | @doc """ 67 | When used, dispatch to the appropriate controller/view/etc. 68 | """ 69 | defmacro __using__(which) when is_atom(which) do 70 | apply(__MODULE__, which, []) 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/addons/_timing-functions.scss: -------------------------------------------------------------------------------- 1 | // CSS cubic-bezier timing functions. Timing functions courtesy of jquery.easie (github.com/jaukia/easie) 2 | // Timing functions are the same as demo'ed here: http://jqueryui.com/resources/demos/effect/easing.html 3 | 4 | // EASE IN 5 | $ease-in-quad: cubic-bezier(0.550, 0.085, 0.680, 0.530); 6 | $ease-in-cubic: cubic-bezier(0.550, 0.055, 0.675, 0.190); 7 | $ease-in-quart: cubic-bezier(0.895, 0.030, 0.685, 0.220); 8 | $ease-in-quint: cubic-bezier(0.755, 0.050, 0.855, 0.060); 9 | $ease-in-sine: cubic-bezier(0.470, 0.000, 0.745, 0.715); 10 | $ease-in-expo: cubic-bezier(0.950, 0.050, 0.795, 0.035); 11 | $ease-in-circ: cubic-bezier(0.600, 0.040, 0.980, 0.335); 12 | $ease-in-back: cubic-bezier(0.600, -0.280, 0.735, 0.045); 13 | 14 | // EASE OUT 15 | $ease-out-quad: cubic-bezier(0.250, 0.460, 0.450, 0.940); 16 | $ease-out-cubic: cubic-bezier(0.215, 0.610, 0.355, 1.000); 17 | $ease-out-quart: cubic-bezier(0.165, 0.840, 0.440, 1.000); 18 | $ease-out-quint: cubic-bezier(0.230, 1.000, 0.320, 1.000); 19 | $ease-out-sine: cubic-bezier(0.390, 0.575, 0.565, 1.000); 20 | $ease-out-expo: cubic-bezier(0.190, 1.000, 0.220, 1.000); 21 | $ease-out-circ: cubic-bezier(0.075, 0.820, 0.165, 1.000); 22 | $ease-out-back: cubic-bezier(0.175, 0.885, 0.320, 1.275); 23 | 24 | // EASE IN OUT 25 | $ease-in-out-quad: cubic-bezier(0.455, 0.030, 0.515, 0.955); 26 | $ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1.000); 27 | $ease-in-out-quart: cubic-bezier(0.770, 0.000, 0.175, 1.000); 28 | $ease-in-out-quint: cubic-bezier(0.860, 0.000, 0.070, 1.000); 29 | $ease-in-out-sine: cubic-bezier(0.445, 0.050, 0.550, 0.950); 30 | $ease-in-out-expo: cubic-bezier(1.000, 0.000, 0.000, 1.000); 31 | $ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.150, 0.860); 32 | $ease-in-out-back: cubic-bezier(0.680, -0.550, 0.265, 1.550); 33 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_timing-functions.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// CSS cubic-bezier timing functions. Timing functions courtesy of jquery.easie (github.com/jaukia/easie) 4 | /// 5 | /// Timing functions are the same as demoed here: http://jqueryui.com/resources/demos/effect/easing.html 6 | /// 7 | /// @type cubic-bezier 8 | 9 | $ease-in-quad: cubic-bezier(0.550, 0.085, 0.680, 0.530); 10 | $ease-in-cubic: cubic-bezier(0.550, 0.055, 0.675, 0.190); 11 | $ease-in-quart: cubic-bezier(0.895, 0.030, 0.685, 0.220); 12 | $ease-in-quint: cubic-bezier(0.755, 0.050, 0.855, 0.060); 13 | $ease-in-sine: cubic-bezier(0.470, 0.000, 0.745, 0.715); 14 | $ease-in-expo: cubic-bezier(0.950, 0.050, 0.795, 0.035); 15 | $ease-in-circ: cubic-bezier(0.600, 0.040, 0.980, 0.335); 16 | $ease-in-back: cubic-bezier(0.600, -0.280, 0.735, 0.045); 17 | 18 | $ease-out-quad: cubic-bezier(0.250, 0.460, 0.450, 0.940); 19 | $ease-out-cubic: cubic-bezier(0.215, 0.610, 0.355, 1.000); 20 | $ease-out-quart: cubic-bezier(0.165, 0.840, 0.440, 1.000); 21 | $ease-out-quint: cubic-bezier(0.230, 1.000, 0.320, 1.000); 22 | $ease-out-sine: cubic-bezier(0.390, 0.575, 0.565, 1.000); 23 | $ease-out-expo: cubic-bezier(0.190, 1.000, 0.220, 1.000); 24 | $ease-out-circ: cubic-bezier(0.075, 0.820, 0.165, 1.000); 25 | $ease-out-back: cubic-bezier(0.175, 0.885, 0.320, 1.275); 26 | 27 | $ease-in-out-quad: cubic-bezier(0.455, 0.030, 0.515, 0.955); 28 | $ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1.000); 29 | $ease-in-out-quart: cubic-bezier(0.770, 0.000, 0.175, 1.000); 30 | $ease-in-out-quint: cubic-bezier(0.860, 0.000, 0.070, 1.000); 31 | $ease-in-out-sine: cubic-bezier(0.445, 0.050, 0.550, 0.950); 32 | $ease-in-out-expo: cubic-bezier(1.000, 0.000, 0.000, 1.000); 33 | $ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.150, 0.860); 34 | $ease-in-out-back: cubic-bezier(0.680, -0.550, 0.265, 1.550); 35 | -------------------------------------------------------------------------------- /web/static/css/bourbon/functions/_modular-scale.scss: -------------------------------------------------------------------------------- 1 | // Scaling Variables 2 | $golden: 1.618; 3 | $minor-second: 1.067; 4 | $major-second: 1.125; 5 | $minor-third: 1.2; 6 | $major-third: 1.25; 7 | $perfect-fourth: 1.333; 8 | $augmented-fourth: 1.414; 9 | $perfect-fifth: 1.5; 10 | $minor-sixth: 1.6; 11 | $major-sixth: 1.667; 12 | $minor-seventh: 1.778; 13 | $major-seventh: 1.875; 14 | $octave: 2; 15 | $major-tenth: 2.5; 16 | $major-eleventh: 2.667; 17 | $major-twelfth: 3; 18 | $double-octave: 4; 19 | 20 | $modular-scale-ratio: $perfect-fourth !default; 21 | $modular-scale-base: em($em-base) !default; 22 | 23 | @function modular-scale($increment, $value: $modular-scale-base, $ratio: $modular-scale-ratio) { 24 | $v1: nth($value, 1); 25 | $v2: nth($value, length($value)); 26 | $value: $v1; 27 | 28 | // scale $v2 to just above $v1 29 | @while $v2 > $v1 { 30 | $v2: ($v2 / $ratio); // will be off-by-1 31 | } 32 | @while $v2 < $v1 { 33 | $v2: ($v2 * $ratio); // will fix off-by-1 34 | } 35 | 36 | // check AFTER scaling $v2 to prevent double-counting corner-case 37 | $double-stranded: $v2 > $v1; 38 | 39 | @if $increment > 0 { 40 | @for $i from 1 through $increment { 41 | @if $double-stranded and ($v1 * $ratio) > $v2 { 42 | $value: $v2; 43 | $v2: ($v2 * $ratio); 44 | } @else { 45 | $v1: ($v1 * $ratio); 46 | $value: $v1; 47 | } 48 | } 49 | } 50 | 51 | @if $increment < 0 { 52 | // adjust $v2 to just below $v1 53 | @if $double-stranded { 54 | $v2: ($v2 / $ratio); 55 | } 56 | 57 | @for $i from $increment through -1 { 58 | @if $double-stranded and ($v1 / $ratio) < $v2 { 59 | $value: $v2; 60 | $v2: ($v2 / $ratio); 61 | } @else { 62 | $v1: ($v1 / $ratio); 63 | $value: $v1; 64 | } 65 | } 66 | } 67 | 68 | @return $value; 69 | } 70 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_prefixer.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// A mixin for generating vendor prefixes on non-standardized properties. 4 | /// 5 | /// @param {String} $property 6 | /// Property to prefix 7 | /// 8 | /// @param {*} $value 9 | /// Value to use 10 | /// 11 | /// @param {List} $prefixes 12 | /// Prefixes to define 13 | /// 14 | /// @example scss - Usage 15 | /// .element { 16 | /// @include prefixer(border-radius, 10px, webkit ms spec); 17 | /// } 18 | /// 19 | /// @example css - CSS Output 20 | /// .element { 21 | /// -webkit-border-radius: 10px; 22 | /// -moz-border-radius: 10px; 23 | /// border-radius: 10px; 24 | /// } 25 | /// 26 | /// @require {variable} $prefix-for-webkit 27 | /// @require {variable} $prefix-for-mozilla 28 | /// @require {variable} $prefix-for-microsoft 29 | /// @require {variable} $prefix-for-opera 30 | /// @require {variable} $prefix-for-spec 31 | 32 | @mixin prefixer($property, $value, $prefixes) { 33 | @each $prefix in $prefixes { 34 | @if $prefix == webkit { 35 | @if $prefix-for-webkit { 36 | -webkit-#{$property}: $value; 37 | } 38 | } @else if $prefix == moz { 39 | @if $prefix-for-mozilla { 40 | -moz-#{$property}: $value; 41 | } 42 | } @else if $prefix == ms { 43 | @if $prefix-for-microsoft { 44 | -ms-#{$property}: $value; 45 | } 46 | } @else if $prefix == o { 47 | @if $prefix-for-opera { 48 | -o-#{$property}: $value; 49 | } 50 | } @else if $prefix == spec { 51 | @if $prefix-for-spec { 52 | #{$property}: $value; 53 | } 54 | } @else { 55 | @warn "Unrecognized prefix: #{$prefix}"; 56 | } 57 | } 58 | } 59 | 60 | @mixin disable-prefix-for-all() { 61 | $prefix-for-webkit: false !global; 62 | $prefix-for-mozilla: false !global; 63 | $prefix-for-microsoft: false !global; 64 | $prefix-for-opera: false !global; 65 | $prefix-for-spec: false !global; 66 | } 67 | -------------------------------------------------------------------------------- /priv/static/sass/base/_variables.scss: -------------------------------------------------------------------------------- 1 | // Typography 2 | $sans-serif: 'Oxygen', $helvetica; 3 | $serif: $georgia; 4 | $base-font-family: $sans-serif; 5 | $header-font-family: $base-font-family; 6 | 7 | // Font Sizes 8 | $base-font-size: 1em; 9 | $h1-font-size: $base-font-size * 2.25; 10 | $h2-font-size: $base-font-size * 2; 11 | $h3-font-size: $base-font-size * 1.75; 12 | $h4-font-size: $base-font-size * 1.5; 13 | $h5-font-size: $base-font-size * 1.25; 14 | $h6-font-size: $base-font-size; 15 | 16 | // Line height 17 | $base-line-height: 1.5; 18 | $header-line-height: 1.25; 19 | 20 | // Other Sizes 21 | $base-border-radius: 3px; 22 | $base-spacing: $base-line-height * 1em; 23 | $base-z-index: 0; 24 | 25 | // Colors 26 | $blue: #477DCA; 27 | $dark-gray: #333; 28 | $medium-gray: #999; 29 | $light-gray: #DDD; 30 | $light-red: #FBE3E4; 31 | $light-yellow: #FFF6BF; 32 | $light-green: #E6EFC2; 33 | 34 | // Background Color 35 | $base-background-color: white; 36 | 37 | // Font Colors 38 | $base-font-color: $dark-gray; 39 | $base-accent-color: $blue; 40 | 41 | // Link Colors 42 | $base-link-color: $base-accent-color; 43 | $hover-link-color: darken($base-accent-color, 15); 44 | $base-button-color: $base-link-color; 45 | $hover-button-color: $hover-link-color; 46 | 47 | // Flash Colors 48 | $alert-color: $light-yellow; 49 | $error-color: $light-red; 50 | $notice-color: lighten($base-accent-color, 40); 51 | $success-color: $light-green; 52 | 53 | // Border color 54 | $base-border-color: $light-gray; 55 | $base-border: 1px solid $base-border-color; 56 | 57 | // Forms 58 | $form-border-color: $base-border-color; 59 | $form-border-color-hover: darken($base-border-color, 10); 60 | $form-border-color-focus: $base-accent-color; 61 | $form-border-radius: $base-border-radius; 62 | $form-box-shadow: inset 0 1px 3px rgba(black,0.06); 63 | $form-box-shadow-focus: $form-box-shadow, 0 0 5px rgba(darken($form-border-color-focus, 5), 0.7); 64 | $form-font-size: $base-font-size; 65 | $form-font-family: $base-font-family; 66 | -------------------------------------------------------------------------------- /config/prod.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | # For production, we configure the host to read the PORT 4 | # from the system environment. Therefore, you will need 5 | # to set PORT=80 before running your server. 6 | # 7 | # You should also configure the url host to something 8 | # meaningful, we use this information when generating URLs. 9 | # 10 | # Finally, we also include the path to a manifest 11 | # containing the digested version of static files. This 12 | # manifest is generated by the mix phoenix.digest task 13 | # which you typically run after static files are built. 14 | config :preview, Preview.Endpoint, 15 | http: [port: {:system, "PORT"}], 16 | url: [host: "example.com"], 17 | cache_static_manifest: "priv/static/manifest.json" 18 | 19 | # ## SSL Support 20 | # 21 | # To get SSL working, you will need to add the `https` key 22 | # to the previous section, and set your `:url` port to 443 23 | # 24 | # config :preview, Preview.Endpoint, 25 | # ... 26 | # url: [host: "example.com", port: 443], 27 | # https: [port: 443, 28 | # keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), 29 | # certfile: System.get_env("SOME_APP_SSL_CERT_PATH")] 30 | # 31 | # Where those two env variables point to a file on 32 | # disk for the key and cert. 33 | 34 | # Do not print debug messages in production 35 | config :logger, level: :info 36 | 37 | # Configure mailgun 38 | config :preview, 39 | mailgun_domain: System.get_env("MAILGUN_DOMAIN"), 40 | mailgun_key: System.get_env("MAILGUN_KEY") 41 | 42 | # ## Using releases 43 | # 44 | # If you are doing OTP releases, you need to instruct Phoenix 45 | # to start the server for all endpoints: 46 | # 47 | # config :phoenix, :serve_endpoints, true 48 | # 49 | # Alternatively, you can configure exactly which server to 50 | # start per endpoint: 51 | # 52 | # config :preview, Preview.Endpoint, server: true 53 | # 54 | 55 | # Finally import the config/prod.secret.exs 56 | # which should be versioned separately. 57 | import_config "prod.secret.exs" 58 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_background.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Background property for adding multiple backgrounds using shorthand 3 | // notation. 4 | //************************************************************************// 5 | 6 | @mixin background($backgrounds...) { 7 | $webkit-backgrounds: (); 8 | $spec-backgrounds: (); 9 | 10 | @each $background in $backgrounds { 11 | $webkit-background: (); 12 | $spec-background: (); 13 | $background-type: type-of($background); 14 | 15 | @if $background-type == string or list { 16 | $background-str: if($background-type == list, nth($background, 1), $background); 17 | 18 | $url-str: str-slice($background-str, 0, 3); 19 | $gradient-type: str-slice($background-str, 0, 6); 20 | 21 | @if $url-str == "url" { 22 | $webkit-background: $background; 23 | $spec-background: $background; 24 | } 25 | 26 | @else if $gradient-type == "linear" { 27 | $gradients: _linear-gradient-parser("#{$background}"); 28 | $webkit-background: map-get($gradients, webkit-image); 29 | $spec-background: map-get($gradients, spec-image); 30 | } 31 | 32 | @else if $gradient-type == "radial" { 33 | $gradients: _radial-gradient-parser("#{$background}"); 34 | $webkit-background: map-get($gradients, webkit-image); 35 | $spec-background: map-get($gradients, spec-image); 36 | } 37 | 38 | @else { 39 | $webkit-background: $background; 40 | $spec-background: $background; 41 | } 42 | } 43 | 44 | @else { 45 | $webkit-background: $background; 46 | $spec-background: $background; 47 | } 48 | 49 | $webkit-backgrounds: append($webkit-backgrounds, $webkit-background, comma); 50 | $spec-backgrounds: append($spec-backgrounds, $spec-background, comma); 51 | } 52 | 53 | background: $webkit-backgrounds; 54 | background: $spec-backgrounds; 55 | } 56 | -------------------------------------------------------------------------------- /web/static/css/bourbon/addons/_buttons.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Generates variables for all buttons. Please note that you must use interpolation on the variable: `#{$all-buttons}`. 4 | /// 5 | /// @example scss - Usage 6 | /// #{$all-buttons} { 7 | /// background-color: #f00; 8 | /// } 9 | /// 10 | /// #{$all-buttons-focus}, 11 | /// #{$all-buttons-hover} { 12 | /// background-color: #0f0; 13 | /// } 14 | /// 15 | /// #{$all-buttons-active} { 16 | /// background-color: #00f; 17 | /// } 18 | /// 19 | /// @example css - CSS Output 20 | /// button, 21 | /// input[type="button"], 22 | /// input[type="reset"], 23 | /// input[type="submit"] { 24 | /// background-color: #f00; 25 | /// } 26 | /// 27 | /// button:focus, 28 | /// input[type="button"]:focus, 29 | /// input[type="reset"]:focus, 30 | /// input[type="submit"]:focus, 31 | /// button:hover, 32 | /// input[type="button"]:hover, 33 | /// input[type="reset"]:hover, 34 | /// input[type="submit"]:hover { 35 | /// background-color: #0f0; 36 | /// } 37 | /// 38 | /// button:active, 39 | /// input[type="button"]:active, 40 | /// input[type="reset"]:active, 41 | /// input[type="submit"]:active { 42 | /// background-color: #00f; 43 | /// } 44 | /// 45 | /// @require assign-inputs 46 | /// 47 | /// @type List 48 | /// 49 | /// @todo Remove double assigned variables (Lines 59–62) in v5.0.0 50 | 51 | $buttons-list: 'button', 52 | 'input[type="button"]', 53 | 'input[type="reset"]', 54 | 'input[type="submit"]'; 55 | 56 | $all-buttons: assign-inputs($buttons-list); 57 | $all-buttons-active: assign-inputs($buttons-list, active); 58 | $all-buttons-focus: assign-inputs($buttons-list, focus); 59 | $all-buttons-hover: assign-inputs($buttons-list, hover); 60 | 61 | $all-button-inputs: $all-buttons; 62 | $all-button-inputs-active: $all-buttons-active; 63 | $all-button-inputs-focus: $all-buttons-focus; 64 | $all-button-inputs-hover: $all-buttons-hover; 65 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_background.scss: -------------------------------------------------------------------------------- 1 | //************************************************************************// 2 | // Background property for adding multiple backgrounds using shorthand 3 | // notation. 4 | //************************************************************************// 5 | 6 | @mixin background($backgrounds...) { 7 | $webkit-backgrounds: (); 8 | $spec-backgrounds: (); 9 | 10 | @each $background in $backgrounds { 11 | $webkit-background: (); 12 | $spec-background: (); 13 | $background-type: type-of($background); 14 | 15 | @if $background-type == string or $background-type == list { 16 | $background-str: if($background-type == list, nth($background, 1), $background); 17 | 18 | $url-str: str-slice($background-str, 0, 3); 19 | $gradient-type: str-slice($background-str, 0, 6); 20 | 21 | @if $url-str == "url" { 22 | $webkit-background: $background; 23 | $spec-background: $background; 24 | } 25 | 26 | @else if $gradient-type == "linear" { 27 | $gradients: _linear-gradient-parser("#{$background}"); 28 | $webkit-background: map-get($gradients, webkit-image); 29 | $spec-background: map-get($gradients, spec-image); 30 | } 31 | 32 | @else if $gradient-type == "radial" { 33 | $gradients: _radial-gradient-parser("#{$background}"); 34 | $webkit-background: map-get($gradients, webkit-image); 35 | $spec-background: map-get($gradients, spec-image); 36 | } 37 | 38 | @else { 39 | $webkit-background: $background; 40 | $spec-background: $background; 41 | } 42 | } 43 | 44 | @else { 45 | $webkit-background: $background; 46 | $spec-background: $background; 47 | } 48 | 49 | $webkit-backgrounds: append($webkit-backgrounds, $webkit-background, comma); 50 | $spec-backgrounds: append($spec-backgrounds, $spec-background, comma); 51 | } 52 | 53 | background: $webkit-backgrounds; 54 | background: $spec-backgrounds; 55 | } 56 | -------------------------------------------------------------------------------- /test/lib/csv_helper_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Preview.CsvHelperTest do 2 | use ExUnit.Case, async: true 3 | alias Preview.CsvHelper, as: CSV 4 | 5 | defmodule Sith do 6 | defstruct id: "", email: "", updated_at: "" 7 | end 8 | 9 | setup context do 10 | context = Dict.put(context, :headers, [:id, :email]) 11 | context = Dict.put(context, :records, 12 | [ 13 | %Sith{email: "darthmaul@example.com", id: 1, updated_at: "time"}, 14 | %Sith{email: "emperor@example.com", id: 2, updated_at: "time"} 15 | ] 16 | ) 17 | 18 | {:ok, context} 19 | end 20 | 21 | ## valid_header?/2 22 | test "a list of atoms defined in the module struct is valid", context do 23 | assert CSV.valid_headers?(context.headers, Sith) == true 24 | end 25 | 26 | test "a list containing an undefined atom in the module struct is invalid" do 27 | undefined_atom = [:id, :email, :username] 28 | 29 | assert CSV.valid_headers?(undefined_atom, Sith) == false 30 | end 31 | 32 | test "a list containing a string causes an error" do 33 | invalid_type = [:id, "email"] 34 | 35 | assert_raise ArgumentError, "Header must contain atoms pre-defined in the passed module struct", fn -> 36 | CSV.valid_headers?(invalid_type, Sith) 37 | end 38 | end 39 | 40 | ## generate_csv/3 41 | test "generates csv content with headers by default", context do 42 | assert CSV.generate_csv(context.records, context.headers) == "id,email\n1,darthmaul@example.com\n2,emperor@example.com\n" 43 | end 44 | 45 | test "generates csv content without headers", context do 46 | assert CSV.generate_csv(context.records, context.headers, false) == "1,darthmaul@example.com\n2,emperor@example.com\n" 47 | end 48 | 49 | ## header_row/1 50 | test "header row", context do 51 | assert CSV.header_row(context.headers) == "id,email\n" 52 | end 53 | 54 | ## data_rows/2 55 | test "data rows", context do 56 | assert CSV.data_rows(context.records, context.headers) == "1,darthmaul@example.com\n2,emperor@example.com\n" 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /priv/static/sass/neat/functions/_new-breakpoint.scss: -------------------------------------------------------------------------------- 1 | /// Returns a media context (media query / grid context) that can be stored in a variable and passed to `media()` as a single-keyword argument. Media contexts defined using `new-breakpoint` are used by the visual grid, as long as they are defined before importing Neat. 2 | /// 3 | /// @param {List} $query 4 | /// A list of media query features and values. Each `$feature` should have a corresponding `$value`. 5 | /// 6 | /// If there is only a single `$value` in `$query`, `$default-feature` is going to be used. 7 | /// 8 | /// The number of total columns in the grid can be set by passing `$columns` at the end of the list (overrides `$total-columns`). For a list of valid values for `$feature`, click [here](http://www.w3.org/TR/css3-mediaqueries/#media1). 9 | /// 10 | /// @param {Number (unitless)} $total-columns ($grid-columns) 11 | /// - Number of columns to use in the new grid context. Can be set as a shorthand in the first parameter. 12 | /// 13 | /// @example scss - Usage 14 | /// $mobile: new-breakpoint(max-width 480px 4); 15 | /// 16 | /// .element { 17 | /// @include media($mobile) { 18 | /// @include span-columns(4); 19 | /// } 20 | /// } 21 | /// 22 | /// @example css - CSS Output 23 | /// @media screen and (max-width: 480px) { 24 | /// .element { 25 | /// display: block; 26 | /// float: left; 27 | /// margin-right: 7.42297%; 28 | /// width: 100%; 29 | /// } 30 | /// .element:last-child { 31 | /// margin-right: 0; 32 | /// } 33 | /// } 34 | 35 | @function new-breakpoint($query: $feature $value $columns, $total-columns: $grid-columns) { 36 | @if length($query) == 1 { 37 | $query: $default-feature nth($query, 1) $total-columns; 38 | } 39 | 40 | @else if is-even(length($query)) { 41 | $query: append($query, $total-columns); 42 | } 43 | 44 | @if not belongs-to($query, $visual-grid-breakpoints) { 45 | $visual-grid-breakpoints: append($visual-grid-breakpoints, $query, comma) !global; 46 | } 47 | 48 | @return $query; 49 | } 50 | -------------------------------------------------------------------------------- /web/static/css/neat/functions/_new-breakpoint.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Returns a media context (media query / grid context) that can be stored in a variable and passed to `media()` as a single-keyword argument. Media contexts defined using `new-breakpoint` are used by the visual grid, as long as they are defined before importing Neat. 4 | /// 5 | /// @param {List} $query 6 | /// A list of media query features and values. Each `$feature` should have a corresponding `$value`. 7 | /// 8 | /// If there is only a single `$value` in `$query`, `$default-feature` is going to be used. 9 | /// 10 | /// The number of total columns in the grid can be set by passing `$columns` at the end of the list (overrides `$total-columns`). For a list of valid values for `$feature`, click [here](http://www.w3.org/TR/css3-mediaqueries/#media1). 11 | /// 12 | /// @param {Number (unitless)} $total-columns [$grid-columns] 13 | /// - Number of columns to use in the new grid context. Can be set as a shorthand in the first parameter. 14 | /// 15 | /// @example scss - Usage 16 | /// $mobile: new-breakpoint(max-width 480px 4); 17 | /// 18 | /// .element { 19 | /// @include media($mobile) { 20 | /// @include span-columns(4); 21 | /// } 22 | /// } 23 | /// 24 | /// @example css - CSS Output 25 | /// @media screen and (max-width: 480px) { 26 | /// .element { 27 | /// display: block; 28 | /// float: left; 29 | /// margin-right: 7.42297%; 30 | /// width: 100%; 31 | /// } 32 | /// .element:last-child { 33 | /// margin-right: 0; 34 | /// } 35 | /// } 36 | 37 | @function new-breakpoint($query: $feature $value $columns, $total-columns: $grid-columns) { 38 | @if length($query) == 1 { 39 | $query: $default-feature nth($query, 1) $total-columns; 40 | } @else if is-even(length($query)) { 41 | $query: append($query, $total-columns); 42 | } 43 | 44 | @if is-not(belongs-to($query, $visual-grid-breakpoints)) { 45 | $visual-grid-breakpoints: append($visual-grid-breakpoints, $query, comma) !global; 46 | } 47 | 48 | @return $query; 49 | } 50 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/css3/_border-image.scss: -------------------------------------------------------------------------------- 1 | @mixin border-image($borders...) { 2 | $webkit-borders: (); 3 | $spec-borders: (); 4 | 5 | @each $border in $borders { 6 | $webkit-border: (); 7 | $spec-border: (); 8 | $border-type: type-of($border); 9 | 10 | @if $border-type == string or list { 11 | $border-str: if($border-type == list, nth($border, 1), $border); 12 | 13 | $url-str: str-slice($border-str, 0, 3); 14 | $gradient-type: str-slice($border-str, 0, 6); 15 | 16 | @if $url-str == "url" { 17 | $webkit-border: $border; 18 | $spec-border: $border; 19 | } 20 | 21 | @else if $gradient-type == "linear" { 22 | $gradients: _linear-gradient-parser("#{$border}"); 23 | $webkit-border: map-get($gradients, webkit-image); 24 | $spec-border: map-get($gradients, spec-image); 25 | } 26 | 27 | @else if $gradient-type == "radial" { 28 | $gradients: _radial-gradient-parser("#{$border}"); 29 | $webkit-border: map-get($gradients, webkit-image); 30 | $spec-border: map-get($gradients, spec-image); 31 | } 32 | 33 | @else { 34 | $webkit-border: $border; 35 | $spec-border: $border; 36 | } 37 | } 38 | 39 | @else { 40 | $webkit-border: $border; 41 | $spec-border: $border; 42 | } 43 | 44 | $webkit-borders: append($webkit-borders, $webkit-border, comma); 45 | $spec-borders: append($spec-borders, $spec-border, comma); 46 | } 47 | 48 | -webkit-border-image: $webkit-borders; 49 | border-image: $spec-borders; 50 | border-style: solid; 51 | } 52 | 53 | //Examples: 54 | // @include border-image(url("image.png")); 55 | // @include border-image(url("image.png") 20 stretch); 56 | // @include border-image(linear-gradient(45deg, orange, yellow)); 57 | // @include border-image(linear-gradient(45deg, orange, yellow) stretch); 58 | // @include border-image(linear-gradient(45deg, orange, yellow) 20 30 40 50 stretch round); 59 | // @include border-image(radial-gradient(top, cover, orange, yellow, orange)); 60 | -------------------------------------------------------------------------------- /web/static/css/bourbon/css3/_border-image.scss: -------------------------------------------------------------------------------- 1 | @mixin border-image($borders...) { 2 | $webkit-borders: (); 3 | $spec-borders: (); 4 | 5 | @each $border in $borders { 6 | $webkit-border: (); 7 | $spec-border: (); 8 | $border-type: type-of($border); 9 | 10 | @if $border-type == string or list { 11 | $border-str: if($border-type == list, nth($border, 1), $border); 12 | 13 | $url-str: str-slice($border-str, 0, 3); 14 | $gradient-type: str-slice($border-str, 0, 6); 15 | 16 | @if $url-str == "url" { 17 | $webkit-border: $border; 18 | $spec-border: $border; 19 | } 20 | 21 | @else if $gradient-type == "linear" { 22 | $gradients: _linear-gradient-parser("#{$border}"); 23 | $webkit-border: map-get($gradients, webkit-image); 24 | $spec-border: map-get($gradients, spec-image); 25 | } 26 | 27 | @else if $gradient-type == "radial" { 28 | $gradients: _radial-gradient-parser("#{$border}"); 29 | $webkit-border: map-get($gradients, webkit-image); 30 | $spec-border: map-get($gradients, spec-image); 31 | } 32 | 33 | @else { 34 | $webkit-border: $border; 35 | $spec-border: $border; 36 | } 37 | } 38 | 39 | @else { 40 | $webkit-border: $border; 41 | $spec-border: $border; 42 | } 43 | 44 | $webkit-borders: append($webkit-borders, $webkit-border, comma); 45 | $spec-borders: append($spec-borders, $spec-border, comma); 46 | } 47 | 48 | -webkit-border-image: $webkit-borders; 49 | border-image: $spec-borders; 50 | border-style: solid; 51 | } 52 | 53 | //Examples: 54 | // @include border-image(url("image.png")); 55 | // @include border-image(url("image.png") 20 stretch); 56 | // @include border-image(linear-gradient(45deg, orange, yellow)); 57 | // @include border-image(linear-gradient(45deg, orange, yellow) stretch); 58 | // @include border-image(linear-gradient(45deg, orange, yellow) 20 30 40 50 stretch round); 59 | // @include border-image(radial-gradient(top, cover, orange, yellow, orange)); 60 | -------------------------------------------------------------------------------- /web/static/css/neat/settings/_grid.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /// Sets the relative width of a single grid column. The unit used should be the same one used to define `$gutter`. To learn more about modular-scale() see [Bourbon docs](http://bourbon.io/docs/#modular-scale). Set with a `!global` flag. 4 | /// 5 | /// @type Number (Unit) 6 | 7 | $column: modular-scale(3, 1em, $golden) !default; 8 | 9 | /// Sets the relative width of a single grid gutter. The unit used should be the same one used to define `$column`. To learn more about modular-scale() see [Bourbon docs](http://bourbon.io/docs/#modular-scale). Set with the `!global` flag. 10 | /// 11 | /// @type Number (Unit) 12 | 13 | $gutter: modular-scale(1, 1em, $golden) !default; 14 | 15 | /// Sets the total number of columns in the grid. Its value can be overridden inside a media query using the `media()` mixin. Set with the `!global` flag. 16 | /// 17 | /// @type Number (Unitless) 18 | 19 | $grid-columns: 12 !default; 20 | 21 | /// Sets the max-width property of the element that includes `outer-container()`. To learn more about `em()` see [Bourbon docs](http://bourbon.io/docs/#px-to-em). Set with the `!global` flag. 22 | /// 23 | /// @type Number (Unit) 24 | /// 25 | $max-width: em(1088) !default; 26 | 27 | /// When set to true, it sets the box-sizing property of all elements to `border-box`. Set with a `!global` flag. 28 | /// 29 | /// @type Bool 30 | /// 31 | /// @example css - CSS Output 32 | /// html { 33 | /// box-sizing: border-box; } 34 | /// 35 | /// *, *::after, *::before { 36 | /// box-sizing: inherit; 37 | /// } 38 | 39 | $border-box-sizing: true !default; 40 | 41 | /// Sets the default [media feature](http://www.w3.org/TR/css3-mediaqueries/#media) that `media()` and `new-breakpoint()` revert to when only a breakpoint value is passed. Set with a `!global` flag. 42 | /// 43 | /// @type String 44 | 45 | $default-feature: min-width; // Default @media feature for the breakpoint() mixin 46 | 47 | ///Sets the default layout direction of the grid. Can be `LTR` or `RTL`. Set with a `!global` flag. 48 | /// 49 | ///@type String 50 | 51 | $default-layout-direction: LTR !default; 52 | -------------------------------------------------------------------------------- /web/static/css/bourbon/helpers/_radial-arg-parser.scss: -------------------------------------------------------------------------------- 1 | @function _radial-arg-parser($g1, $g2, $pos, $shape-size) { 2 | @each $value in $g1, $g2 { 3 | $first-val: nth($value, 1); 4 | $pos-type: type-of($first-val); 5 | $spec-at-index: null; 6 | 7 | // Determine if spec was passed to mixin 8 | @if type-of($value) == list { 9 | $spec-at-index: if(index($value, at), index($value, at), false); 10 | } 11 | @if $spec-at-index { 12 | @if $spec-at-index > 1 { 13 | @for $i from 1 through ($spec-at-index - 1) { 14 | $shape-size: $shape-size nth($value, $i); 15 | } 16 | @for $i from ($spec-at-index + 1) through length($value) { 17 | $pos: $pos nth($value, $i); 18 | } 19 | } 20 | @else if $spec-at-index == 1 { 21 | @for $i from ($spec-at-index + 1) through length($value) { 22 | $pos: $pos nth($value, $i); 23 | } 24 | } 25 | $g1: null; 26 | } 27 | 28 | // If not spec calculate correct values 29 | @else { 30 | @if ($pos-type != color) or ($first-val != "transparent") { 31 | @if ($pos-type == number) 32 | or ($first-val == "center") 33 | or ($first-val == "top") 34 | or ($first-val == "right") 35 | or ($first-val == "bottom") 36 | or ($first-val == "left") { 37 | 38 | $pos: $value; 39 | 40 | @if $pos == $g1 { 41 | $g1: null; 42 | } 43 | } 44 | 45 | @else if 46 | ($first-val == "ellipse") 47 | or ($first-val == "circle") 48 | or ($first-val == "closest-side") 49 | or ($first-val == "closest-corner") 50 | or ($first-val == "farthest-side") 51 | or ($first-val == "farthest-corner") 52 | or ($first-val == "contain") 53 | or ($first-val == "cover") { 54 | 55 | $shape-size: $value; 56 | 57 | @if $value == $g1 { 58 | $g1: null; 59 | } 60 | 61 | @else if $value == $g2 { 62 | $g2: null; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | @return $g1, $g2, $pos, $shape-size; 69 | } 70 | -------------------------------------------------------------------------------- /priv/static/sass/bourbon/helpers/_radial-arg-parser.scss: -------------------------------------------------------------------------------- 1 | @function _radial-arg-parser($G1, $G2, $pos, $shape-size) { 2 | @each $value in $G1, $G2 { 3 | $first-val: nth($value, 1); 4 | $pos-type: type-of($first-val); 5 | $spec-at-index: null; 6 | 7 | // Determine if spec was passed to mixin 8 | @if type-of($value) == list { 9 | $spec-at-index: if(index($value, at), index($value, at), false); 10 | } 11 | @if $spec-at-index { 12 | @if $spec-at-index > 1 { 13 | @for $i from 1 through ($spec-at-index - 1) { 14 | $shape-size: $shape-size nth($value, $i); 15 | } 16 | @for $i from ($spec-at-index + 1) through length($value) { 17 | $pos: $pos nth($value, $i); 18 | } 19 | } 20 | @else if $spec-at-index == 1 { 21 | @for $i from ($spec-at-index + 1) through length($value) { 22 | $pos: $pos nth($value, $i); 23 | } 24 | } 25 | $G1: null; 26 | } 27 | 28 | // If not spec calculate correct values 29 | @else { 30 | @if ($pos-type != color) or ($first-val != "transparent") { 31 | @if ($pos-type == number) 32 | or ($first-val == "center") 33 | or ($first-val == "top") 34 | or ($first-val == "right") 35 | or ($first-val == "bottom") 36 | or ($first-val == "left") { 37 | 38 | $pos: $value; 39 | 40 | @if $pos == $G1 { 41 | $G1: null; 42 | } 43 | } 44 | 45 | @else if 46 | ($first-val == "ellipse") 47 | or ($first-val == "circle") 48 | or ($first-val == "closest-side") 49 | or ($first-val == "closest-corner") 50 | or ($first-val == "farthest-side") 51 | or ($first-val == "farthest-corner") 52 | or ($first-val == "contain") 53 | or ($first-val == "cover") { 54 | 55 | $shape-size: $value; 56 | 57 | @if $value == $G1 { 58 | $G1: null; 59 | } 60 | 61 | @else if $value == $G2 { 62 | $G2: null; 63 | } 64 | } 65 | } 66 | } 67 | } 68 | @return $G1, $G2, $pos, $shape-size; 69 | } 70 | --------------------------------------------------------------------------------