`, and ``.
+@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
+@font-family-base: @font-family-sans-serif;
+
+@font-size-base: 13px;
+@font-size-large: ceil((@font-size-base * 1.25)); // ~18px
+@font-size-small: ceil((@font-size-base * 0.85)); // ~12px
+
+@font-size-h1: floor((@font-size-base * 2.6)); // ~36px
+@font-size-h2: floor((@font-size-base * 2.15)); // ~30px
+@font-size-h3: ceil((@font-size-base * 1.7)); // ~24px
+@font-size-h4: ceil((@font-size-base * 1.25)); // ~18px
+@font-size-h5: @font-size-base;
+@font-size-h6: ceil((@font-size-base * 0.85)); // ~12px
+
+//** Unit-less `line-height` for use in components like buttons.
+@line-height-base: 1.828571429; // 20/14
+//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
+@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px
+
+//** By default, this inherits from the ``.
+@headings-font-family: inherit;
+@headings-font-weight: 400;
+@headings-line-height: 1.1;
+@headings-color: #444;
+
+
+//-- Iconography
+//
+//## Specify custom locations of the include Glyphicons icon font. Useful for those including Bootstrap via Bower.
+
+@icon-font-path: "../assets/fonts/";
+@icon-font-name: "glyphicons-halflings-regular";
+@icon-font-svg-id: "glyphicons_halflingsregular";
+
+//== Components
+//
+//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
+
+@padding-base-vertical: 4px;
+@padding-base-horizontal: 12px;
+
+@padding-large-vertical: 16px;
+@padding-large-horizontal: 20px;
+
+@padding-small-vertical: 3px;
+@padding-small-horizontal: 8px;
+
+@padding-xs-vertical: 1px;
+@padding-xs-horizontal: 5px;
+
+@line-height-large: 1.33;
+@line-height-small: 1.5;
+
+@border-radius-base: 0px;
+@border-radius-large: 0px;
+@border-radius-small: 3px;
+
+//** Global color for active items (e.g., navs or dropdowns).
+@component-active-color: #fff;
+//** Global background color for active items (e.g., navs or dropdowns).
+@component-active-bg: @brand-primary;
+
+//** Width of the `border` for generating carets that indicator dropdowns.
+@caret-width-base: 4px;
+//** Carets increase slightly in size for larger components.
+@caret-width-large: 5px;
+
+
+//== Tables
+//
+//## Customizes the `.table` component with basic values, each used across all table variations.
+
+//** Padding for ``s and ` | `s.
+@table-cell-padding: 8px;
+//** Padding for cells in `.table-condensed`.
+@table-condensed-cell-padding: 9px;
+
+//** Default background color used for all tables.
+@table-bg: #fff;
+//** Background color used for `.table-striped`.
+@table-bg-accent: #f9f9f9;
+//** Background color used for `.table-hover`.
+@table-bg-hover: #f5f5f5;
+@table-bg-active: @table-bg-hover;
+
+//** Border color for table and cell borders.
+@table-border-color: #ddd;
+
+
+//== Buttons
+//
+//## For each of Bootstrap's buttons, define text, background and border color.
+
+@btn-font-weight: normal;
+
+@btn-default-color: #333;
+@btn-default-bg: #fff;
+@btn-default-border: #ccc;
+
+@btn-primary-color: #fff;
+@btn-primary-bg: @brand-primary;
+@btn-primary-border: darken(@btn-primary-bg, 5%);
+
+@btn-success-color: #fff;
+@btn-success-bg: @brand-success;
+@btn-success-border: darken(@btn-success-bg, 5%);
+
+@btn-info-color: #fff;
+@btn-info-bg: @brand-info;
+@btn-info-border: darken(@btn-info-bg, 5%);
+
+@btn-warning-color: #fff;
+@btn-warning-bg: @brand-warning;
+@btn-warning-border: darken(@btn-warning-bg, 5%);
+
+@btn-danger-color: #fff;
+@btn-danger-bg: @brand-danger;
+@btn-danger-border: darken(@btn-danger-bg, 5%);
+
+@btn-link-disabled-color: @gray-light;
+
+
+//== Forms
+//
+//##
+
+//** `` background color
+@input-bg: #fff;
+//** `` background color
+@input-bg-disabled: @gray-lighter;
+
+//** Text color for ``s
+@input-color: @gray;
+//** `` border color
+@input-border: #ccc;
+//** `` border radius
+@input-border-radius: @border-radius-base;
+//** Border color for inputs on focus
+@input-border-focus: #66afe9;
+
+//** Placeholder text color
+@input-color-placeholder: @gray-light;
+
+//** Default `.form-control` height
+@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);
+//** Large `.form-control` height
+@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 4);
+//** Small `.form-control` height
+@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
+
+@legend-color: @gray-dark;
+@legend-border-color: #e5e5e5;
+
+//** Background color for textual input addons
+@input-group-addon-bg: @gray-lighter;
+//** Border color for textual input addons
+@input-group-addon-border-color: @input-border;
+
+
+//== Dropdowns
+//
+//## Dropdown menu container and contents.
+
+//** Background for the dropdown menu.
+@dropdown-bg: #fff;
+//** Dropdown menu `border-color`.
+@dropdown-border: rgba(0,0,0,.15);
+//** Dropdown menu `border-color` **for IE8**.
+@dropdown-fallback-border: #ccc;
+//** Divider color for between dropdown items.
+@dropdown-divider-bg: #e5e5e5;
+
+//** Dropdown link text color.
+@dropdown-link-color: @gray-dark;
+//** Hover color for dropdown links.
+@dropdown-link-hover-color: darken(@gray-dark, 5%);
+//** Hover background for dropdown links.
+@dropdown-link-hover-bg: #f5f5f5;
+
+//** Active dropdown menu item text color.
+@dropdown-link-active-color: @component-active-color;
+//** Active dropdown menu item background color.
+@dropdown-link-active-bg: @component-active-bg;
+
+//** Disabled dropdown menu item background color.
+@dropdown-link-disabled-color: @gray-light;
+
+//** Text color for headers within dropdown menus.
+@dropdown-header-color: @gray-light;
+
+// Note: Deprecated @dropdown-caret-color as of v3.1.0
+@dropdown-caret-color: #000;
+
+
+//-- Z-index master list
+//
+// Warning: Avoid customizing these values. They're used for a bird's eye view
+// of components dependent on the z-axis and are designed to all work together.
+//
+// Note: These variables are not generated into the Customizer.
+
+@zindex-navbar: 1000;
+@zindex-dropdown: 1000;
+@zindex-popover: 1010;
+@zindex-tooltip: 1030;
+@zindex-navbar-fixed: 1030;
+@zindex-modal-background: 1040;
+@zindex-modal: 1050;
+
+
+//== Media queries breakpoints
+//
+//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
+
+// Extra small screen / phone
+// Note: Deprecated @screen-xs and @screen-phone as of v3.0.1
+@screen-xs: 480px;
+@screen-xs-min: @screen-xs;
+@screen-phone: @screen-xs-min;
+
+// Small screen / tablet
+// Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1
+@screen-sm: 768px;
+@screen-sm-min: @screen-sm;
+@screen-tablet: @screen-sm-min;
+
+// Medium screen / desktop
+// Note: Deprecated @screen-md and @screen-desktop as of v3.0.1
+@screen-md: 992px;
+@screen-md-min: @screen-md;
+@screen-desktop: @screen-md-min;
+
+// Large screen / wide desktop
+// Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1
+@screen-lg: 1200px;
+@screen-lg-min: @screen-lg;
+@screen-lg-desktop: @screen-lg-min;
+
+// So media queries don't overlap when required, provide a maximum
+@screen-xs-max: (@screen-sm-min - 1);
+@screen-sm-max: (@screen-md-min - 1);
+@screen-md-max: (@screen-lg-min - 1);
+
+
+//== Grid system
+//
+//## Define your custom responsive grid.
+
+//** Number of columns in the grid.
+@grid-columns: 12;
+//** Padding between columns. Gets divided in half for the left and right.
+@grid-gutter-width: 30px;
+// Navbar collapse
+//** Point at which the navbar becomes uncollapsed.
+@grid-float-breakpoint: @screen-sm-min;
+//** Point at which the navbar begins collapsing.
+@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
+
+
+//== Container sizes
+//
+//## Define the maximum width of `.container` for different screen sizes.
+
+// Small screen / tablet
+@container-tablet: ((720px + @grid-gutter-width));
+//** For `@screen-sm-min` and up.
+@container-sm: @container-tablet;
+
+// Medium screen / desktop
+@container-desktop: ((940px + @grid-gutter-width));
+//** For `@screen-md-min` and up.
+@container-md: @container-desktop;
+
+// Large screen / wide desktop
+@container-large-desktop: ((1140px + @grid-gutter-width));
+//** For `@screen-lg-min` and up.
+@container-lg: @container-large-desktop;
+
+
+//== Navbar
+//
+//##
+
+// Basics of a navbar
+@navbar-height: 50px;
+@navbar-margin-bottom: @line-height-computed;
+@navbar-border-radius: @border-radius-base;
+@navbar-padding-horizontal: floor((@grid-gutter-width / 2));
+@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
+@navbar-collapse-max-height: 340px;
+
+@navbar-default-color: #5d5d5e;
+@navbar-default-bg: #eee;
+@navbar-default-border: darken(@navbar-default-bg, 6.5%);
+
+// Navbar links
+@navbar-default-link-color: #5d5d5e;
+@navbar-default-link-hover-color: #5d5d5e;
+@navbar-default-link-hover-bg: #2dc3e8;
+@navbar-default-link-active-color: #5d5d5e;
+@navbar-default-link-active-bg: #2bbee2;
+@navbar-default-link-disabled-color: #ccc;
+@navbar-default-link-disabled-bg: transparent;
+
+// Navbar brand label
+@navbar-default-brand-color: @navbar-default-link-color;
+@navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%);
+@navbar-default-brand-hover-bg: transparent;
+
+// Navbar toggle
+@navbar-default-toggle-hover-bg: #ddd;
+@navbar-default-toggle-icon-bar-bg: #888;
+@navbar-default-toggle-border-color: #ddd;
+
+
+// Inverted navbar
+// Reset inverted navbar basics
+@navbar-inverse-color: @gray-light;
+@navbar-inverse-bg: #222;
+@navbar-inverse-border: darken(@navbar-inverse-bg, 10%);
+
+// Inverted navbar links
+@navbar-inverse-link-color: @gray-light;
+@navbar-inverse-link-hover-color: #fff;
+@navbar-inverse-link-hover-bg: transparent;
+@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color;
+@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%);
+@navbar-inverse-link-disabled-color: #444;
+@navbar-inverse-link-disabled-bg: transparent;
+
+// Inverted navbar brand label
+@navbar-inverse-brand-color: @navbar-inverse-link-color;
+@navbar-inverse-brand-hover-color: #fff;
+@navbar-inverse-brand-hover-bg: transparent;
+
+// Inverted navbar toggle
+@navbar-inverse-toggle-hover-bg: #333;
+@navbar-inverse-toggle-icon-bar-bg: #fff;
+@navbar-inverse-toggle-border-color: #333;
+
+
+//== Navs
+//
+//##
+
+//=== Shared nav styles
+@nav-link-padding: 10px 15px;
+@nav-link-hover-bg: #fbfbfb;
+
+@nav-disabled-link-color: @gray-light;
+@nav-disabled-link-hover-color: @gray-light;
+
+@nav-open-link-hover-color: #fff;
+
+//== Tabs
+@nav-tabs-border-color: transparent;
+
+@nav-tabs-link-hover-border-color: transparent;
+
+@nav-tabs-active-link-hover-bg: #f5f5f5;
+@nav-tabs-active-link-hover-color: @gray;
+@nav-tabs-active-link-hover-border-color: transparent;
+
+@nav-tabs-justified-link-border-color: #ddd;
+@nav-tabs-justified-active-link-border-color: @body-bg;
+
+//== Pills
+@nav-pills-border-radius: @border-radius-base;
+@nav-pills-active-link-hover-bg: @body-bg;
+@nav-pills-active-link-hover-color: @component-active-color;
+
+
+//== Pagination
+//
+//##
+
+@pagination-color: @brand-primary;
+@pagination-bg: #fff;
+@pagination-border: #ddd;
+
+@pagination-hover-color: #fff;
+@pagination-hover-bg: @brand-primary;
+@pagination-hover-border: #ddd;
+
+@pagination-active-color: #fff;
+@pagination-active-bg: @brand-primary;
+@pagination-active-border: #ddd;
+
+@pagination-disabled-color: @gray-light;
+@pagination-disabled-bg: #fff;
+@pagination-disabled-border: #ddd;
+
+
+//== Pager
+//
+//##
+
+@pager-bg: @pagination-bg;
+@pager-border: @pagination-border;
+@pager-border-radius: 15px;
+
+@pager-hover-bg: @pagination-hover-bg;
+
+@pager-active-bg: @pagination-active-bg;
+@pager-active-color: @pagination-active-color;
+
+@pager-disabled-color: @pagination-disabled-color;
+
+
+//== Jumbotron
+//
+//##
+
+@jumbotron-padding: 30px;
+@jumbotron-color: inherit;
+@jumbotron-bg: #eee;
+@jumbotron-heading-color: inherit;
+@jumbotron-font-size: ceil((@font-size-base * 1.5));
+
+
+//== Form states and alerts
+//
+//## Define colors for form feedback states and, by default, alerts.
+
+@state-success-text: @brand-success;
+@state-success-bg: @brand-success;
+@state-success-border: transparent;
+
+@state-info-text: @brand-info;
+@state-info-bg: @brand-info;
+@state-info-border: transparent;
+
+@state-warning-text: @brand-warning;
+@state-warning-bg: @brand-warning;
+@state-warning-border: transparent;
+
+@state-danger-text: @brand-danger;
+@state-danger-bg: @brand-danger;
+@state-danger-border: transparent;
+
+
+//== Tooltips
+//
+//##
+
+//** Tooltip max width
+@tooltip-max-width: 200px;
+//** Tooltip text color
+@tooltip-color: #fff;
+//** Tooltip background color
+@tooltip-bg: #000;
+@tooltip-opacity: .9;
+
+//** Tooltip arrow width
+@tooltip-arrow-width: 5px;
+//** Tooltip arrow color
+@tooltip-arrow-color: @tooltip-bg;
+
+
+//== Popovers
+//
+//##
+
+//** Popover body background color
+@popover-bg: #fff;
+//** Popover maximum width
+@popover-max-width: 276px;
+//** Popover border color
+@popover-border-color: rgba(0,0,0,.2);
+//** Popover fallback border color
+@popover-fallback-border-color: #ccc;
+
+//** Popover title background color
+@popover-title-bg: darken(@popover-bg, 3%);
+
+//** Popover arrow width
+@popover-arrow-width: 10px;
+//** Popover arrow color
+@popover-arrow-color: #fff;
+
+//** Popover outer arrow width
+@popover-arrow-outer-width: (@popover-arrow-width + 1);
+//** Popover outer arrow color
+@popover-arrow-outer-color: fadein(@popover-border-color, 5%);
+//** Popover outer arrow fallback color
+@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%);
+
+
+//== Labels
+//
+//##
+
+//** Default label background color
+@label-default-bg: @gray-light;
+//** Primary label background color
+@label-primary-bg: @brand-primary;
+//** Success label background color
+@label-success-bg: @brand-success;
+//** Info label background color
+@label-info-bg: @brand-info;
+//** Warning label background color
+@label-warning-bg: @brand-warning;
+//** Danger label background color
+@label-danger-bg: @brand-danger;
+
+//** Default label text color
+@label-color: #fff;
+//** Default text color of a linked label
+@label-link-hover-color: #fff;
+
+
+//== Modals
+//
+//##
+
+//** Padding applied to the modal body
+@modal-inner-padding: 20px;
+
+//** Padding applied to the modal title
+@modal-title-padding: 15px;
+//** Modal title line-height
+@modal-title-line-height: @line-height-base;
+
+//** Background color of modal content area
+@modal-content-bg: #fff;
+//** Modal content border color
+@modal-content-border-color: rgba(0,0,0,.2);
+//** Modal content border color **for IE8**
+@modal-content-fallback-border-color: #999;
+
+//** Modal backdrop background color
+@modal-backdrop-bg: #000;
+//** Modal backdrop opacity
+@modal-backdrop-opacity: .5;
+//** Modal header border color
+@modal-header-border-color: #e5e5e5;
+//** Modal footer border color
+@modal-footer-border-color: @modal-header-border-color;
+
+@modal-lg: 900px;
+@modal-md: 600px;
+@modal-sm: 300px;
+
+
+//== Alerts
+//
+//## Define alert colors, border radius, and padding.
+
+@alert-padding: 15px;
+@alert-border-radius: @border-radius-base;
+@alert-link-font-weight: bold;
+
+@alert-success-bg: #dff0d8;
+@alert-success-text: #3c763d;
+@alert-success-border: @state-success-border;
+
+@alert-info-bg: #d9edf7;
+@alert-info-text: #31708f;
+@alert-info-border: @state-info-border;
+
+@alert-warning-bg: #fcf8e3;
+@alert-warning-text: #8a6d3b;
+@alert-warning-border: @state-warning-border;
+
+@alert-danger-bg: #fad5ce;
+@alert-danger-text: #e82c0c;
+@alert-danger-border: @state-danger-border;
+
+
+//== Progress bars
+//
+//##
+
+//** Background color of the whole progress component
+@progress-bg: #f5f5f5;
+//** Progress bar text color
+@progress-bar-color: #fff;
+
+//** Default progress bar color
+@progress-bar-bg: @brand-primary;
+//** Success progress bar color
+@progress-bar-success-bg: @brand-success;
+//** Warning progress bar color
+@progress-bar-warning-bg: @brand-warning;
+//** Danger progress bar color
+@progress-bar-danger-bg: @brand-danger;
+//** Info progress bar color
+@progress-bar-info-bg: @brand-info;
+
+
+//== List group
+//
+//##
+
+//** Background color on `.list-group-item`
+@list-group-bg: #fff;
+//** `.list-group-item` border color
+@list-group-border: #ddd;
+//** List group border radius
+@list-group-border-radius: @border-radius-base;
+
+//** Background color of single list elements on hover
+@list-group-hover-bg: #f5f5f5;
+//** Text color of active list elements
+@list-group-active-color: @component-active-color;
+//** Background color of active list elements
+@list-group-active-bg: @component-active-bg;
+//** Border color of active list elements
+@list-group-active-border: @list-group-active-bg;
+@list-group-active-text-color: lighten(@list-group-active-bg, 40%);
+
+@list-group-link-color: #555;
+@list-group-link-heading-color: #333;
+
+
+//== Panels
+//
+//##
+
+@panel-bg: #fff;
+@panel-body-padding: 15px;
+@panel-border-radius: @border-radius-base;
+
+//** Border color for elements within panels
+@panel-inner-border: #ddd;
+@panel-footer-bg: #f5f5f5;
+
+@panel-default-text: @gray-dark;
+@panel-default-border: #ddd;
+@panel-default-heading-bg: #f5f5f5;
+
+@panel-primary-text: #fff;
+@panel-primary-border: @brand-primary;
+@panel-primary-heading-bg: @brand-primary;
+
+@panel-success-text: @state-success-text;
+@panel-success-border: @state-success-border;
+@panel-success-heading-bg: @state-success-bg;
+
+@panel-info-text: @state-info-text;
+@panel-info-border: @state-info-border;
+@panel-info-heading-bg: @state-info-bg;
+
+@panel-warning-text: @state-warning-text;
+@panel-warning-border: @state-warning-border;
+@panel-warning-heading-bg: @state-warning-bg;
+
+@panel-danger-text: @state-danger-text;
+@panel-danger-border: @state-danger-border;
+@panel-danger-heading-bg: @state-danger-bg;
+
+
+//== Thumbnails
+//
+//##
+
+//** Padding around the thumbnail image
+@thumbnail-padding: 4px;
+//** Thumbnail background color
+@thumbnail-bg: #ffffff;
+//** Thumbnail border color
+@thumbnail-border: #ddd;
+//** Thumbnail border radius
+@thumbnail-border-radius: @border-radius-base;
+
+//** Custom text color for thumbnail captions
+@thumbnail-caption-color: @text-color;
+//** Padding around the thumbnail caption
+@thumbnail-caption-padding: 9px;
+
+
+//== Wells
+//
+//##
+
+@well-bg: #f5f5f5;
+@well-border: darken(@well-bg, 7%);
+
+
+//== Badges
+//
+//##
+
+@badge-color: #fff;
+//** Linked badge text color on hover
+@badge-link-hover-color: #fff;
+@badge-bg: @gray-light;
+
+//** Badge text color in active nav link
+@badge-active-color: @link-color;
+//** Badge background color in active nav link
+@badge-active-bg: #fff;
+
+@badge-font-weight: bold;
+@badge-line-height: 1;
+@badge-border-radius: 10px;
+
+
+//== Breadcrumbs
+//
+//##
+
+@breadcrumb-padding-vertical: 8px;
+@breadcrumb-padding-horizontal: 15px;
+//** Breadcrumb background color
+@breadcrumb-bg: #f5f5f5;
+//** Breadcrumb text color
+@breadcrumb-color: #ccc;
+//** Text color of current page in the breadcrumb
+@breadcrumb-active-color: #777779;
+//** Textual separator for between breadcrumb elements
+@breadcrumb-separator: "/";
+
+
+//== Carousel
+//
+//##
+
+@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6);
+
+@carousel-control-color: #fff;
+@carousel-control-width: 15%;
+@carousel-control-opacity: .5;
+@carousel-control-font-size: 20px;
+
+@carousel-indicator-active-bg: #fff;
+@carousel-indicator-border-color: #fff;
+
+@carousel-caption-color: #fff;
+
+
+//== Close
+//
+//##
+
+@close-font-weight: bold;
+@close-color: #000;
+@close-text-shadow: 0 1px 0 #fff;
+
+
+//== Code
+//
+//##
+
+@code-color: #c7254e;
+@code-bg: #f9f2f4;
+
+@kbd-color: #fff;
+@kbd-bg: #333;
+
+@pre-bg: #f5f5f5;
+@pre-color: @gray-dark;
+@pre-border-color: #ccc;
+@pre-scrollable-max-height: 340px;
+
+
+//== Type
+//
+//##
+
+//** Text muted color
+@text-muted: #999;
+//** Abbreviations and acronyms border color
+@abbr-border-color: @gray-light;
+//** Headings small color
+@headings-small-color: @gray-light;
+//** Blockquote small color
+@blockquote-small-color: @gray-light;
+//** Blockquote font size
+@blockquote-font-size: (@font-size-base * 1.25);
+//** Blockquote border color
+@blockquote-border-color: @gray-lighter;
+//** Page header border color
+@page-header-border-color: @gray-lighter;
+
+
+//== Miscellaneous
+//
+//##
+
+//** Horizontal line color.
+@hr-border: #f3f3f3;
+
+//** Horizontal offset for forms and lists.
+@component-offset-horizontal: 180px;
\ No newline at end of file
diff --git a/client/ag-admin/css/wells.less b/client/ag-admin/css/wells.less
new file mode 100644
index 0000000..add4c89
--- /dev/null
+++ b/client/ag-admin/css/wells.less
@@ -0,0 +1,29 @@
+//
+// Wells
+// --------------------------------------------------
+
+
+// Base class
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: @well-bg;
+ border: 1px solid @well-border;
+ border-radius: @border-radius-base;
+ .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));
+ blockquote {
+ border-color: #ddd;
+ border-color: rgba(0,0,0,.15);
+ }
+}
+
+// Sizes
+.well-lg {
+ padding: 24px;
+ border-radius: @border-radius-large;
+}
+.well-sm {
+ padding: 9px;
+ border-radius: @border-radius-small;
+}
diff --git a/client/ag-admin/fonts/FontAwesome.otf b/client/ag-admin/fonts/FontAwesome.otf
new file mode 100644
index 0000000..f7936cc
Binary files /dev/null and b/client/ag-admin/fonts/FontAwesome.otf differ
diff --git a/client/ag-admin/fonts/fontawesome-webfont.eot b/client/ag-admin/fonts/fontawesome-webfont.eot
new file mode 100644
index 0000000..33b2bb8
Binary files /dev/null and b/client/ag-admin/fonts/fontawesome-webfont.eot differ
diff --git a/client/ag-admin/fonts/fontawesome-webfont.svg b/client/ag-admin/fonts/fontawesome-webfont.svg
new file mode 100644
index 0000000..0d2afc7
--- /dev/null
+++ b/client/ag-admin/fonts/fontawesome-webfont.svg
@@ -0,0 +1,565 @@
+
+
+
\ No newline at end of file
diff --git a/client/ag-admin/fonts/fontawesome-webfont.ttf b/client/ag-admin/fonts/fontawesome-webfont.ttf
new file mode 100644
index 0000000..ed9372f
Binary files /dev/null and b/client/ag-admin/fonts/fontawesome-webfont.ttf differ
diff --git a/client/ag-admin/fonts/fontawesome-webfont.woff b/client/ag-admin/fonts/fontawesome-webfont.woff
new file mode 100644
index 0000000..8b280b9
Binary files /dev/null and b/client/ag-admin/fonts/fontawesome-webfont.woff differ
diff --git a/client/ag-admin/fonts/fontawesome-webfont.woff2 b/client/ag-admin/fonts/fontawesome-webfont.woff2
new file mode 100644
index 0000000..3311d58
Binary files /dev/null and b/client/ag-admin/fonts/fontawesome-webfont.woff2 differ
diff --git a/client/ag-admin/fonts/glyphicons-halflings-regular.eot b/client/ag-admin/fonts/glyphicons-halflings-regular.eot
new file mode 100644
index 0000000..4a4ca86
Binary files /dev/null and b/client/ag-admin/fonts/glyphicons-halflings-regular.eot differ
diff --git a/client/ag-admin/fonts/glyphicons-halflings-regular.svg b/client/ag-admin/fonts/glyphicons-halflings-regular.svg
new file mode 100644
index 0000000..3e09a1c
--- /dev/null
+++ b/client/ag-admin/fonts/glyphicons-halflings-regular.svg
@@ -0,0 +1,229 @@
+
+
+
\ No newline at end of file
diff --git a/client/ag-admin/fonts/glyphicons-halflings-regular.ttf b/client/ag-admin/fonts/glyphicons-halflings-regular.ttf
new file mode 100644
index 0000000..67fa00b
Binary files /dev/null and b/client/ag-admin/fonts/glyphicons-halflings-regular.ttf differ
diff --git a/client/ag-admin/fonts/glyphicons-halflings-regular.woff b/client/ag-admin/fonts/glyphicons-halflings-regular.woff
new file mode 100644
index 0000000..8c54182
Binary files /dev/null and b/client/ag-admin/fonts/glyphicons-halflings-regular.woff differ
diff --git a/client/ag-admin/images/sudopay-icon.png b/client/ag-admin/images/sudopay-icon.png
new file mode 100644
index 0000000..6214f88
Binary files /dev/null and b/client/ag-admin/images/sudopay-icon.png differ
diff --git a/client/ag-admin/index.html b/client/ag-admin/index.html
new file mode 100644
index 0000000..b1f314b
--- /dev/null
+++ b/client/ag-admin/index.html
@@ -0,0 +1,31 @@
+
+
+
+ Admin Panel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/ag-admin/js/ng-admin.app.js b/client/ag-admin/js/ng-admin.app.js
new file mode 100644
index 0000000..94bf008
--- /dev/null
+++ b/client/ag-admin/js/ng-admin.app.js
@@ -0,0 +1,2733 @@
+
+var ngapp = angular.module('aceadmin', ['ng-admin', 'http-auth-interceptor']);
+var admin_api_url = '/acev3/';
+var limit_per_page = 20;
+var enabledPlugins;
+//$httpProvider, it adds token to every request because our API is token based API.
+
+var token = '';
+ngapp.config(['$httpProvider',
+ function($httpProvider) {
+ if (!$.cookie('refresh_token')) {
+ var _params = {};
+ $.get(admin_api_url + 'api/v1/token.json', _params, function(data) {
+ data = angular.fromJson(data);
+ if (angular.isDefined(data.access_token)) {
+ token = data.access_token;
+ }
+ });
+ } else {
+ token = $.cookie('token');
+ }
+ $httpProvider.interceptors.push('interceptor');
+ $httpProvider.interceptors.push('oauthTokenInjector');
+ }
+]);
+
+//oauthTokenInjector, it adds .json to every request
+
+ngapp.factory('oauthTokenInjector', ['$rootScope', '$timeout',
+ function($rootScope, $timeout) {
+ var oauthTokenInjector = {
+ request: function(config) {
+ if (config.url.indexOf('.html') === -1) {
+ if (token) {
+ var sep = config.url.indexOf('?') === -1 ? '?' : '&';
+ config.url = config.url + '.json' + sep + 'token=' + token;
+ }
+ }
+ return config;
+ }
+ };
+ return oauthTokenInjector;
+ }
+]);
+//Interceptor, this capture the response and process it before completing call
+ngapp.factory('interceptor', ['$q', '$location', '$injector', '$rootScope', 'SessionService','$timeout', function($q, $location, $injector, $rootScope, SessionService, $timeout) {
+ return {
+ // On response success
+ response: function(response) {
+ if (angular.isDefined(response.data)) {
+ if (angular.isDefined(response.data.error_message) && parseInt(response.data.error) === 1 && response.data.error_message === 'Authentication failed') {
+ SessionService.setUserAuthenticated(false);
+ Auth = Array();
+ token = '';
+ $.removeCookie('auth');
+ $.removeCookie('token');
+ $.removeCookie('refresh_token');
+ $.removeCookie('enabled_plugins');
+ window.location = "/#!/users/login";
+ }
+ }
+
+ // Return the response or promise.
+ return response || $q.when(response);
+ },
+
+ // On response failture
+ responseError: function(response) {
+ // Return the promise rejection.
+ return $q.reject(response);
+ }
+ };
+ }]);
+//Service to set and get authentication status
+ngapp.service('SessionService', ['$rootScope', function($rootScope) {
+ var userIsAuthenticated = false;
+ this.setUserAuthenticated = function(value) {
+ userIsAuthenticated = value;
+ };
+
+ this.getUserAuthenticated = function() {
+ return userIsAuthenticated;
+ };
+}]);
+//Customize API Mapping
+//Referenced Document Link: http://ng-admin-book.marmelab.com/doc/API-mapping.html
+
+ngapp.config(['RestangularProvider', function(RestangularProvider) {
+ // use the custom query parameters function to format the API request correctly
+ RestangularProvider.addFullRequestInterceptor(function(element, operation, what, url, headers, params) {
+ // Added this filter param changes for admin and user end listing for same api call - for admin - we send filter=all param for display all active and inactive records
+ if(operation === "getList" && (what == 'courses' || what == 'categories' || what == 'users' || what == 'cities' || what == 'states' || what == 'languages' || what == 'providers' || what == 'subscriptions' || what == 'transactions')) {
+ if(!params._filters){
+ params.filter = {'filter':'all'};
+ if(what == 'users') {
+ params.filter = {'filter' : 'all', 'providertype' : 'all' };
+ }
+ if(what == 'users') {
+ params.filter = {'filter' : 'all', 'providertype' : 'all' };
+ }
+ delete params._filter;
+ }
+ }
+ // custom pagination params
+ if (params._sortField) {
+ params.sort = params._sortField;
+ }
+ delete params._sortField;
+ if (params._sortDir) {
+ params.sort_by = params._sortDir;
+ }
+ delete params._sortDir;
+ if (params._perPage !== null && params._perPage !== 'all' && params._page) {
+ params._start = (params._page - 1) * params._perPage;
+ params._end = params._page * params._perPage;
+ //In REST file, we added page and limit query parameters for pagination
+ //Get Reference from http://ng-admin-book.marmelab.com/doc/API-mapping.html
+ //Keyword - pagination
+ params.page = params._page;
+ params.limit = params._perPage;
+ }
+ delete params._start;
+ delete params._end;
+
+ if (params._perPage === null) {
+ params.limit = limit_per_page;
+ }
+ if(angular.isUndefined(params._perPage)){
+ params.limit = 'all';
+ }
+ //limit('all') is used for dropdown values, our api default limit value is '10', to show all the value we should pass string 'all' in limit parameter.
+ if (params._perPage == 'all') {
+ params.limit = 'all';
+ }
+ delete params._page;
+ delete params._perPage;
+ // custom sort params
+ if (params._sortField) {
+ delete params._sortField;
+ delete params._sortDir;
+ }
+ // custom filters
+ if (params._filters) {
+ params.filter = params._filters;
+ delete params._filters;
+ }
+ return {
+ params: params
+ };
+ });
+ //Total Number of Results
+ //Our API doesn't return a X-Total-Count header, so we added a totalCount property to the response object using a Restangular response interceptor.
+ //Removed metadata info from response
+ RestangularProvider.addResponseInterceptor(function(data, operation, what, url, response) {
+ if (operation === "getList") {
+ var headers = response.headers();
+ if (response.data._metadata.total_records !== null) {
+ response.totalCount = response.data._metadata.total_records;
+ }
+ }
+ return data;
+ });
+ //To cutomize single view results, we added setResponseExtractor.
+ //Our API Edit view results single array with following data format data[{}], Its not working with ng-admin format
+ //so we returned data like data[0];
+ RestangularProvider.setResponseExtractor(function(response, operation, what, url) {
+ if (response.data) {
+ if (operation === "getList") {
+ // Use results as the return type, and save the result metadata
+ var newResponse = response.data;
+ newResponse._metadata = response._metadata;
+ return newResponse;
+ }
+ return response.data[0];
+ } else {
+ return response;
+ }
+ });
+
+
+}]);
+//Checking Admin Authentication by checking auth credentials, Redirected to site page if not admin logged in.
+ngapp.run(function($rootScope, $location, SessionService) {
+
+ if (!$.cookie('refresh_token')) {
+ window.location = "/#!/users/login";
+ } else {
+ var auth = angular.fromJson($.cookie("auth"));
+ if (auth.providertype !== 'admin') {
+ window.location = "/";
+ } else {
+ SessionService.setUserAuthenticated(true);
+ $rootScope.$on('$viewContentLoaded', function () {
+ $('body').removeClass('site-loading');
+ $('div.js-loader').hide();
+ });
+ }
+ }
+ //For enabling or disabling plugin based menu items
+ $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
+
+ });
+
+});
+//time ago filter using jquery timeago plugin
+ngapp.filter("timeago",function() {
+ //passed extra argument to get time zome
+ return function(date, timeZone) {
+ jQuery.timeago.settings.strings = {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: "ago",
+ suffixFromNow: "from now",
+ seconds: "less than a minute",
+ minute: "a minute",
+ minutes: "%d minutes",
+ hour: "an hour",
+ hours: "%d hours",
+ day: "a day",
+ days: "%d days",
+ month: "a month",
+ months: "%d months",
+ year: "a year",
+ years: "%d years",
+ wordSeparator: " ",
+ numbers: []
+ };
+ return jQuery.timeago(date + timeZone);
+ };
+});
+
+//Custom Header
+//Referenced Link: http://ng-admin-book.marmelab.com/doc/Dashboard.html
+//Above link has details about dashboard customization, we follwed the same steps for header customization.
+//Created custom directive for header, reference http://ng-admin-book.marmelab.com/doc/Custom-pages.html keyword - directive.
+//Template files created under 'tpl' directory.
+ngapp.directive('customHeader', ['$location', '$state', '$http', function($location, $state, $http, $scope) {
+ return {
+ restrict: 'E',
+ scope: {},
+ templateUrl: '../ag-admin/tpl/customHeader.tpl.html',
+ link: function(scope) {}
+
+ };
+}]);
+//Custom Dashboard
+//Referenced Link: http://ng-admin-book.marmelab.com/doc/Dashboard.html
+//Created custom directive for header, reference http://ng-admin-book.marmelab.com/doc/Custom-pages.html keyword - directive.
+//Template files created under 'tpl' directory.
+ngapp.directive('dashboardSummary', ['$location', '$state', '$http', function($location, $state, $http) {
+ return {
+ restrict: 'E',
+ scope: {
+ entity: "&",
+ entityName: "@",
+ entry: "&",
+ size: "@",
+ label: "@",
+ revenueDetails: "&"
+ },
+ templateUrl: '../ag-admin/tpl/dashboardSummary.tpl.html',
+ link: function(scope) {
+
+ scope.rangeVal = [{
+ "key": "lastDays",
+ "value": "Last 7 Days"
+ }, {
+ "key": "lastWeeks",
+ "value": "Last 4 Weeks"
+ }, {
+ "key": "lastMonths",
+ "value": "Last 3 Months"
+ }, {
+ "key": "lastYears",
+ "value": "Last 3 Years"
+ }];
+ if (scope.rangeText === undefined) {
+ scope.rangeText = "Last 7 Days";
+ }
+ scope.selectedRangeItem = function(rangeVal, rangeText) {
+ $http.get(admin_api_url + 'api/v1/admin/stats', {
+ params: {
+ filter: rangeVal
+ }
+ }).success(function(response) {
+ scope.adminstats = response.data;
+ scope.rangeText = rangeText;
+ });
+ };
+
+ $http.get(admin_api_url + 'api/v1/admin/stats').success(function(response) {
+ scope.adminstats = response.data;
+ });
+ $http.get(admin_api_url + 'api/v1/admin/activities').success(function(response) {
+ scope.adminactivities = response.data;
+ });
+ $http.get(admin_api_url + 'api/v1/admin/overview').success(function(response) {
+ scope.adminoverview = response.data;
+ });
+ //getting time zone from settings
+ var timeZone;
+ $http.get(admin_api_url + 'api/v1/settings.json', { params:{
+ limit: 'all'
+ }}).success(function(settingsData) {
+ settingsResponse = angular.fromJson(settingsData);
+ $.each(settingsResponse.data, function(i, settingData) {
+ if (settingData.name === 'site.timezone') {
+ timeZone = settingData.value;
+ }
+ });
+ scope.timeZone = timeZone;
+ });
+ scope.enabledPlugins = $.cookie("enabled_plugins");
+
+ }
+
+
+ };
+}]);
+//We need custom pages for online lessons add forms, because ng-admin doesn't support single entity with multiple create view.
+//So we created custom pages for online lessons like add articele, add video, add document, add downloadble file and add file.
+//Custom directives created for custom pages.
+//Template files created under 'tpl' directory.
+//Referenced Link: http://ng-admin-book.marmelab.com/doc/Custom-pages.html
+ngapp.directive('manageLesson', ['$location', '$state', function($location, $state) {
+ return {
+ restrict: 'E',
+ scope: {
+ entity: "&",
+ entityName: "@",
+ entry: "&",
+ size: "@",
+ label: "@"
+ },
+ template: '\n {{label}}\n',
+ link: function(scope) {
+ scope.id =scope.entry().values.id;
+ }
+ };
+}]);
+ngapp.directive('createManageLesson', ['$location', '$state', function($location, $state) {
+ return {
+ restrict: 'E',
+ scope: {
+ entity: "&",
+ entityName: "@",
+ entry: "&",
+ size: "@",
+ label: "@"
+ },
+ template: '\n \n',
+ link: function(scope) {
+ }
+ };
+}]);
+ngapp.directive('previewThisCourse', ['$location', '$state', function($location, $state) {
+ return {
+ restrict: 'E',
+ scope: {
+ entity: "&",
+ entityName: "@",
+ entry: "&",
+ size: "@",
+ label: "@"
+ },
+ template: '\n {{label}}\n',
+ link: function(scope) {
+ scope.id =scope.entry().values.id;
+ scope.slug =scope.entry().values.slug;
+ }
+ };
+}]);
+ngapp.directive('previewLearnPage', ['$location', '$state', function($location, $state) {
+ return {
+ restrict: 'E',
+ scope: {
+ entity: "&",
+ entityName: "@",
+ entry: "&",
+ size: "@",
+ label: "@"
+ },
+ template: '\n {{label}}\n',
+ link: function(scope) {
+ scope.id =scope.entry().values.id;
+ scope.slug =scope.entry().values.slug;
+ }
+ };
+}]);
+ngapp.directive('createPage', ['$location', '$state', function($location, $state) {
+ return {
+ restrict: 'E',
+ scope: {
+ entity: "&",
+ entityName: "@",
+ entry: "&",
+ size: "@",
+ label: "@"
+ },
+ template: '\n \n',
+ link: function(scope) {
+
+ }
+ };
+}]);
+ngapp.directive('batchDraft', ['$location', '$state', 'notification', '$q','Restangular', function($location, $state, notification, $q , Restangular) {
+ return{
+ restrict: 'E',
+ scope: {
+ selection: '=',
+ type: '@'
+ },
+ link: function(scope, element, attrs) {
+ const status = attrs.type == 'draft' ? '1' : '1';
+ const status_name = attrs.type == 'draft' ? 'Draft' : 'Draft';
+ scope.icon = attrs.type == 'draft' ? 'glyphicon-envelope' : 'glyphicon-envelope';
+ scope.label = attrs.type == 'draft' ? 'Draft' : 'Draft';
+ scope.updateStatus = function() {
+ $q.all(scope.selection.map(function(e){Restangular.one('api/v1/courses', e.values.id).get()
+ .then(function(courseReponses){return courseReponses.data;})
+ .then(function(course){ // your API my support batch updates, this one doesn't, so we iterate
+ course.course_status_id = status;
+ course.course_status_name = status_name;
+ return course.put(); // course.put() is a promise
+ }) // this executes all put() promises in parallel and returns
+ .then(function(){$state.reload()})
+ .then(function(){notification.log(scope.selection.length + ' Courses status changed to ' + status_name, { addnCls: 'humane-flatty-success' });})
+ .catch(function(){notification.log('A problem occurred, please try again', { addnCls: 'humane-flatty-error' });} )
+ }))
+ }
+ },
+ template: ' {{ label }}'
+ };
+}]);
+ngapp.directive('batchActive', ['$location', '$state', 'notification', '$q','Restangular', function($location, $state, notification, $q , Restangular) {
+ return{
+ restrict: 'E',
+ scope: {
+ selection: '=',
+ type: '@'
+ },
+ link: function(scope, element, attrs) {
+ const status = attrs.type == 'active' ? '3' : '3';
+ const status_name = attrs.type == 'active' ? 'Active' : 'Active';
+ scope.icon = attrs.type == 'active' ? 'glyphicon-thumbs-up' : 'glyphicon-thumbs-up';
+ scope.label = attrs.type == 'active' ? 'Active' : 'Active';
+ scope.updateStatus = function() {
+ $q.all(scope.selection.map(function(e){Restangular.one('api/v1/courses', e.values.id).get()
+ .then(function(courseReponses){return courseReponses.data;})
+ .then(function(course){ // your API my support batch updates, this one doesn't, so we iterate
+ course.course_status_id = status;
+ course.course_status_name = status_name;
+ return course.put(); // course.put() is a promise
+ }) // this executes all put() promises in parallel and returns
+ .then(function(){$state.reload()})
+ .then(function(){notification.log(scope.selection.length + ' Courses status changed to ' + status_name, { addnCls: 'humane-flatty-success' });})
+ .catch(function(){notification.log('A problem occurred, please try again', { addnCls: 'humane-flatty-error' });} )
+ }))
+ }
+ },
+ template: ' {{ label }}'
+ };
+}]);
+ngapp.directive('batchWaitingForApproval', ['$location', '$state', 'notification', '$$q','Restangular', function($location, $state, notification, $q , Restangular) {
+ return{
+ restrict: 'E',
+ scope: {
+ selection: '=',
+ type: '@'
+ },
+ link: function(scope, element, attrs) {
+ const status = attrs.type == 'waiting' ? '2' : '2';
+ const status_name = attrs.type == 'waiting' ? 'Waiting For Approval' : 'Waiting For Approval';
+ scope.icon = attrs.type == 'waiting' ? 'glyphicon-thumbs-down' : 'glyphicon-thumbs-down';
+ scope.label = attrs.type == 'waiting' ? 'Waiting For Approval' : 'Waiting For Approval';
+ scope.updateStatus = function() {
+ $q.all(scope.selection.map(function(e){Restangular.one('api/v1/courses', e.values.id).get()
+ .then(function(courseReponses){return courseReponses.data;})
+ .then(function(course){ // your API my support batch updates, this one doesn't, so we iterate
+ course.course_status_id = status;
+ course.course_status_name = status_name;
+ return course.put(); // course.put() is a promise
+ }) // this executes all put() promises in parallel and returns
+ .then(function(){$state.reload()})
+ .then(function(){notification.log(scope.selection.length + ' Courses status changed to ' + status_name, { addnCls: 'humane-flatty-success' });})
+ .catch(function(){notification.log('A problem occurred, please try again', { addnCls: 'humane-flatty-error' });} )
+ }))
+ }
+ },
+ template: ' {{ label }}'
+ };
+}]);
+ngapp.directive('batchFeatured', ['$location', '$state', 'notification', '$q','Restangular', function($location, $state, notification, $q , Restangular) {
+ return{
+ restrict: 'E',
+ scope: {
+ selection: '=',
+ type: '@'
+ },
+ link: function(scope, element, attrs) {
+ const status = attrs.type == 'is_featured' ? true : true;
+ const status_name = attrs.type == 'is_featured' ? 'Featured' : 'Featured';
+ scope.icon = attrs.type == 'is_featured' ? 'glyphicon-leaf' : 'glyphicon-leaf';
+ scope.label = attrs.type == 'is_featured' ? 'Featured' : 'Featured';
+ scope.updateStatus = function() {
+ $q.all(scope.selection.map(function(e){Restangular.one('api/v1/courses', e.values.id).get()
+ .then(function(courseReponses){return courseReponses.data;})
+ .then(function(course){ // your API my support batch updates, this one doesn't, so we iterate
+ course.is_featured = status;
+ return course.put(); // course.put() is a promise
+ }) // this executes all put() promises in parallel and returns
+ .then(function(){$state.reload()})
+ .then(function(){notification.log(scope.selection.length + ' Courses status changed to ' + status_name, { addnCls: 'humane-flatty-success' });})
+ .catch(function(){notification.log('A problem occurred, please try again', { addnCls: 'humane-flatty-error' });} )
+ }))
+ }
+ },
+ template: ' {{ label }}'
+ };
+}]);
+ngapp.directive('batchUnfeatured', ['$location', '$state', 'notification', '$q','Restangular', function($location, $state, notification, $q , Restangular) {
+ return{
+ restrict: 'E',
+ scope: {
+ selection: '=',
+ type: '@'
+ },
+ link: function(scope, element, attrs) {
+ const status = attrs.type == 'is_featured' ? false : false;
+ const status_name = attrs.type == 'is_featured' ? 'Unfeatured' : 'Unfeatured';
+ scope.icon = attrs.type == 'is_featured' ? 'glyphicon-ban-circle' : 'glyphicon-ban-circle';
+ scope.label = attrs.type == 'is_featured' ? 'Unfeatured' : 'Unfeatured';
+ scope.updateStatus = function() {
+ $q.all(scope.selection.map(function(e){Restangular.one('api/v1/courses', e.values.id).get()
+ .then(function(courseReponses){return courseReponses.data;})
+ .then(function(course){ // your API my support batch updates, this one doesn't, so we iterate
+ course.is_featured = status;
+ return course.put(); // course.put() is a promise
+ }) // this executes all put() promises in parallel and returns
+ .then(function(){$state.reload()})
+ .then(function(){notification.log(scope.selection.length + ' Courses status changed to ' + status_name, { addnCls: 'humane-flatty-success' });})
+ .catch(function(){notification.log('A problem occurred, please try again', { addnCls: 'humane-flatty-error' });} )
+ }))
+ }
+ },
+ template: ' {{ label }}'
+ };
+}]);
+//Custom button Creation
+//Synchronize button to synchronize payment gateways
+//Referenced Link: http://ng-admin-book.marmelab.com/doc/reference/View.html
+ngapp.directive('addSync', ['$location', '$state', '$http', 'notification', function($location, $state, $http, notification) {
+ return {
+ restrict: 'E',
+ scope: {
+ entity: "&",
+ entityName: "@",
+ entry: "&",
+ size: "@",
+ label: "@"
+ },
+ template: "\n {{label}} \n",
+
+ link: function(scope, element) {
+ scope.syncSudopay = false;
+ if(scope.entry().values.name === 'SudoPay') {
+ scope.syncSudopay = true;
+ }
+ scope.sync = function() {
+ scope.disableButton = true;
+ $http.get(admin_api_url + 'api/v1/sudopay_synchronize').success(function(response) {
+ if(response.error.code === 1){
+ notification.log(response.message,{ addnCls: 'humane-flatty-error'});
+ }else{
+ notification.log('Synchronized Successfully',{ addnCls: 'humane-flatty-success'});
+ }
+ scope.disableButton = false;
+ });
+ };
+
+ }
+ };
+}]);
+//Custom button Creation
+//Mooc affliate button to synchronize payment gateways
+//Referenced Link: http://ng-admin-book.marmelab.com/doc/reference/View.html
+ngapp.directive('moocSync', ['$location', '$state', '$http', 'notification', function($location, $state, $http, notification) {
+ return {
+ restrict: 'E',
+ scope: {
+ entity: "&",
+ entityName: "@",
+ entry: "&",
+ size: "@",
+ label: "@"
+ },
+ template: "\n {{label}} \n",
+ link: function(scope) {
+ scope.syncMoocAffliate = false;
+ if(scope.entry().values.name === 'MOOC Affiliate'){
+ scope.syncMoocAffliate = true;
+ }
+ scope.moocsync = function() {
+ scope.disableButton = true;
+ $http.get(admin_api_url + 'api/v1/mooc_affiliate_synchronize.json').success(function(response) {
+ if(response.error.code === 1){
+ notification.log(response.message,{ addnCls: 'humane-flatty-error'});
+ }else{
+ notification.log('Synchronized Successfully',{ addnCls: 'humane-flatty-success'});
+ }
+ scope.disableButton = false;
+ });
+ };
+
+ }
+ };
+}]);
+//Custom directives controlller function called here
+function logoutController($scope, $http, $location) {
+ $http.get(admin_api_url + 'api/v1/users/logout.json').success(function(response) {
+ $scope.response = response;
+ $scope.title = 'Logout';
+ if (!$scope.response.error) {
+ $.get(admin_api_url + 'api/v1/token.json', function(data) {
+ data = angular.fromJson(data);
+ if (angular.isDefined(data.access_token)) {
+ token = data.access_token;
+ Auth = Array();
+ $.removeCookie('auth');
+ $.removeCookie('token');
+ $.removeCookie('refresh_token');
+ $.removeCookie('enabled_plugins');
+ var redirectto = $location.absUrl().split('/#/');
+ redirectto = redirectto[0].split('ag-admin');
+ redirectto = redirectto[0];
+ window.location.href = redirectto;
+ //location.reload(true);
+ }
+ });
+ }
+ });
+}
+//Custom pages controller function
+function pagesController($scope, $http, $location, notification) {
+ $scope.languageArr = [];
+ $http.get(admin_api_url + 'api/v1/settings/site_languages.json', {}).success(function(response) {
+ $scope.languageList = response;
+ }, function() {});
+ $scope.pageAdd = function(){
+ $scope.languageArr.pages['type'] = 'bulk';
+ $http.post(admin_api_url + 'api/v1/pages.json', $scope.languageArr.pages).success(function(response) {
+ if(response.error.code === 1){
+ notification.log(response.message,{ addnCls: 'humane-flatty-error'});
+ }else{
+ notification.log('Create Element Successfully',{ addnCls: 'humane-flatty-success'});
+ $location.path('/pages/list');
+ }
+ });
+ }
+}
+//plugins controller function
+function pluginsController($scope, $http, notification, $state, $window) {
+ $scope.languageArr = [];
+ getPluginDetails();
+ function getPluginDetails(){
+ $http.get(admin_api_url + 'api/v1/plugins.json', {}).success(function(response) {
+ $scope.course_plugin = response.course_plugin;
+ $scope.payment_and_cart_plugin = response.payment_and_cart_plugin;
+ $scope.payment_gateway_plugin = response.payment_gateway_plugin;
+ $scope.other_plugin = response.other_plugin;
+ $scope.enabled_plugin = response.enabled_plugin;
+ enabledPlugin = response.enabled_plugin;
+ $.cookie('enabled_plugins', JSON.stringify(enabledPlugin), {
+ path: '/'
+ });
+ }, function(error){});
+ };
+ $scope.checkStatus = function(plugin, enabled_plugins){
+ if ($.inArray(plugin, enabled_plugins) > -1) {
+ return true;
+ }else{
+ return false;
+ }
+ }
+ $scope.updatePluginStatus = function(e, plugin_name, status, hash){
+ e.preventDefault();
+ var target = angular.element(e.target);
+ checkDisabled = target.parent().hasClass('disabled');
+ if(checkDisabled === true){
+ return false;
+ }
+ var params = {};
+ var confirm_msg = '';
+ params.plugin_name = plugin_name;
+ params.is_enabled = status;
+ confirm_msg = (status === 0)?"Are you sure want to disable?":"Are you sure want to enable?";
+ notification_msg = (status === 0)?"disabled":"enabled";
+ if (confirm(confirm_msg)) {
+ $http.put(admin_api_url + 'api/v1/settings/plugins.json', params).success(function(response) {
+ if(response.error.code === 0){
+ notification.log(plugin_name+' Plugin '+notification_msg+' successfully.',{ addnCls: 'humane-flatty-success'});
+ getPluginDetails();
+ }
+ }, function(error){});
+ }
+ }
+ $scope.fullRefresh = function(){
+ $window.location.reload();
+ }
+}
+//Custom directives controlller function called here
+//State Provider defined for custom pages.
+//Templates created under 'tp' directory and controller functions defined above.
+ngapp.config(function($stateProvider) {
+ $stateProvider.state('logout', {
+ parent: 'main',
+ url: '/logout',
+ params: {
+ id: null
+ },
+ controller: logoutController,
+ controllerAs: 'controller',
+ })
+ .state('pages', {
+ parent: 'main',
+ url: '/pages/add',
+ templateUrl: '../ag-admin/tpl/pages.tpl.html',
+ params: {
+ id: null
+ },
+ controller: pagesController,
+ controllerAs: 'controller',
+ })
+ .state('plugins', {
+ parent: 'main',
+ url: '/plugins',
+ templateUrl: '../ag-admin/tpl/plugins.tpl.html',
+ controller: pluginsController,
+ controllerAs: 'controller',
+ });
+});
+//Configuration of the administration
+//All the views for admin entity created here
+ngapp.config(['NgAdminConfigurationProvider', function(nga) {
+ //enabled plugins details from cookies
+ var enabledPlugins = $.cookie("enabled_plugins");
+ //trunctate function to shorten text length.
+ function truncate(value) {
+ if (!value) {
+ return '';
+ }
+
+ return value.length > 50 ? value.substr(0, 50) + '...' : value;
+ }
+ // create an admin application
+ var admin = nga.application('Admin Panel')
+ .baseApiUrl(admin_api_url + 'api/v1/'); // main API endpoint
+ // Creating all entities
+ // the API endpoint for this entity will be '/acev3/api/v1/courses.json'
+ var courses = nga.entity('courses');
+ var categories = nga.entity('categories');
+ var users = nga.entity('users');
+ var course_favourites = nga.entity('course_favourites');
+ var course_users = nga.entity('course_users');
+ var course_user_feedbacks = nga.entity('course_user_feedbacks');
+ var user_logins = nga.entity('user_logins');
+ var cities = nga.entity('cities');
+ var states = nga.entity('states');
+ var countries = nga.entity('countries');
+ var instructional_levels = nga.entity('instructional_levels');
+ var pages = nga.entity('pages');
+ var email_templates = nga.entity('email_templates');
+ var languages = nga.entity('languages');
+ var transactions = nga.entity('transactions');
+ var ipn_logs = nga.entity('ipn_logs');
+ var settings = nga.entity('settings');
+ var sudopay_payment_gateways = nga.entity('sudopay_payment_gateways');
+ var setting_categories = nga.entity('setting_categories');
+ var ips = nga.entity('ips');
+ var online_course_lessons = nga.entity('online_course_lessons');
+ var adminstats = nga.entity('/admin/stats');
+ var course_statuses = nga.entity('course_statuses');
+ var contacts = nga.entity('contacts');
+ var providers = nga.entity('providers');
+ var subscriptions = nga.entity('subscriptions');
+ var subscription_statuses = nga.entity('subscription_statuses');
+ var user_subscription_logs = nga.entity('user_subscription_logs');
+ var withdrawal_statuses = nga.entity('withdrawal_statuses');
+ var user_cash_withdrawals = nga.entity('user_cash_withdrawals');
+ var money_transfer_accounts = nga.entity('money_transfer_accounts');
+ var coupons = nga.entity('coupons');
+ var subscription_statuses = nga.entity('subscription_statuses');
+ //Adding all the entities to the admin application.
+ admin.addEntity(courses);
+ admin.addEntity(categories);
+ admin.addEntity(users);
+ admin.addEntity(course_favourites);
+ admin.addEntity(course_users);
+ admin.addEntity(course_user_feedbacks);
+ admin.addEntity(user_logins);
+ admin.addEntity(cities);
+ admin.addEntity(states);
+ admin.addEntity(countries);
+ admin.addEntity(instructional_levels);
+ admin.addEntity(pages);
+ admin.addEntity(email_templates);
+ admin.addEntity(languages);
+ admin.addEntity(transactions);
+ admin.addEntity(ipn_logs);
+ admin.addEntity(settings);
+ admin.addEntity(sudopay_payment_gateways);
+ admin.addEntity(setting_categories);
+ admin.addEntity(ips);
+ admin.addEntity(online_course_lessons);
+ admin.addEntity(adminstats);
+ admin.addEntity(contacts);
+ admin.addEntity(providers);
+ admin.addEntity(subscriptions);
+ admin.addEntity(subscription_statuses);
+ admin.addEntity(user_subscription_logs);
+ admin.addEntity(user_cash_withdrawals);
+ admin.addEntity(withdrawal_statuses);
+ admin.addEntity(money_transfer_accounts);
+ admin.addEntity(coupons);
+ admin.addEntity(subscription_statuses);
+
+ //custom template for course list view actions
+
+ var custom_tmp_course_lst = ''+
+ ''+
+ '';
+
+
+ //Set the fields of the course entity list view
+ courses.listView().title('Courses') // default title is "[Entity_name] list"
+ .filters([
+ nga.field('q', 'template').label('Search')
+ .pinned(true)
+ .template(' '), //custom template for search filter
+ nga.field('filter', 'choice').label('Status').attributes({
+ placeholder: 'Apply Filter'
+ })
+ .choices([{
+ label: 'Draft',
+ value: 'Draft'
+ }, {
+ label: 'Active',
+ value: 'Active'
+ }, {
+ label: 'Waiting for Approval',
+ value: 'Waiting for Approval'
+ },{
+ label: 'Featured',
+ value: 'is_featured'
+ } ])
+
+ ])
+ .infinitePagination(false) // load pages as the user scrolls
+ .perPage(limit_per_page)
+ .fields([
+ nga.field('id'), // The default displayed name is the camelCase field name. label() overrides id
+ nga.field('title').label('Course').map(truncate),
+ nga.field('displayname').label('Instructor'),
+ nga.field('active_online_course_lesson_count').label('Lessons'),
+ nga.field('price').cssClasses(function(){
+ if(enabledPlugins.indexOf("CourseCheckout") === -1) {
+ return "ng-hide";
+ }
+ }),
+ nga.field('course_user_count').label('Students'),
+ nga.field('course_user_feedback_count').label('Total Feedback').cssClasses(function(){
+ if(enabledPlugins.indexOf("RatingAndReview") === -1) {
+ return "ng-hide";
+ }
+ }),
+ nga.field('average_rating').label('Average Rating').cssClasses(function(){
+ if(enabledPlugins.indexOf("RatingAndReview") === -1) {
+ return "ng-hide";
+ }
+ }),
+ nga.field('total_revenue_amount').label('Revenue').cssClasses(function(){
+ if(enabledPlugins.indexOf("CourseCheckout") === -1) {
+ return "ng-hide";
+ }
+ }),
+ nga.field('site_revenue_amount').label('Site Revenue').cssClasses(function(){
+ if(enabledPlugins.indexOf("CourseCheckout") === -1) {
+ return "ng-hide";
+ }
+ }),
+ nga.field('course_status_name').label('Status')
+ ])
+ .actions(['batch',custom_tmp_course_lst])
+ .batchActions([
+ '',
+ '',
+ '',
+ '',
+ '',
+ 'delete'
+ ])
+ .listActions(['','','','delete']);
+
+
+ categories.listView().fields([
+ nga.field('id').label('ID'),
+ nga.field('sub_category_name').label('Category').map(truncate),
+ nga.field('parent_category_name').label('Parent Category').map(truncate),
+ nga.field('is_active', 'boolean').label('Active?'),
+ ]);
+
+ categories.listView().title('Categories') // default title is "[Entity_name] list"
+ .description('List of categories and subcategories list') // description appears under the title
+ .infinitePagination(false) // load pages as the user scrolls
+ .perPage(limit_per_page)
+ .filters([
+ nga.field('q').label('Search')
+ .pinned(true)
+ .template(' '),
+ nga.field('filter', 'choice').label('Status').attributes({
+ placeholder: 'Apply Filter'
+ })
+ .choices([{
+ label: 'Active',
+ value: 'active'
+ }, {
+ label: 'Inactive',
+ value: 'inactive'
+ }, ])
+ ])
+ .listActions(['edit', 'delete']);
+ categories.creationView()
+ .fields([
+ nga.field('parent_id', 'reference').label('Parent Category')
+ .targetEntity(categories)
+ .perPage('all') // For getting all list
+ .targetField(nga.field('sub_category_name'))
+ .remoteComplete(true)
+ .permanentFilters({
+ parent_id: null
+ })
+ .attributes({
+ placeholder: 'Parent Category'
+ }),
+ nga.field('name').label('Name')
+ .attributes({
+ placeholder: 'Name'
+ })
+ .validation({
+ required: true
+ }),
+ nga.field('is_active', 'choice').label('Active?')
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ])
+ .attributes({
+ placeholder: 'Active?'
+ })
+ .validation({
+ required: true
+ }),
+ nga.field('description', 'text').label('Description')
+ .attributes({
+ placeholder: 'Description'
+ })
+ .validation({
+ required: true
+ }),
+ ]);
+ categories.editionView()
+ .title('Edit #{{ entry.values.id }}')
+ .fields([
+ nga.field('parent_id', 'reference').label('Parent Category')
+ .targetEntity(categories)
+ .perPage('all') // For getting all list
+ .targetField(nga.field('sub_category_name'))
+ .remoteComplete(true)
+ .permanentFilters({
+ parent_id: null
+ })
+ .attributes({
+ placeholder: 'Parent Category'
+ }),
+ nga.field('sub_category_name')
+ .label('Name')
+ .validation({
+ required: true
+ }),
+ nga.field('is_active', 'choice')
+ .label('Active?')
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ])
+ .validation({
+ required: true
+ }),
+ nga.field('description', 'text').label('Description')
+ .attributes({
+ placeholder: 'Description'
+ })
+ .validation({
+ required: true
+ }),
+
+ ]);
+
+ /** user configuration starts**/
+
+
+ users.listView()
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('displayname').label('Name'),
+ nga.field('email').label('Email'),
+ nga.field('active_course_count').label('Active Courses'),
+ nga.field('total_earned').label('Earned Amount').cssClasses(function(){
+ if(enabledPlugins.indexOf("CourseCheckout") === -1) {
+ return "ng-hide";
+ }
+ }),
+ nga.field('total_site_revenue_amount').label('Site Revenue').cssClasses(function(){
+ if(enabledPlugins.indexOf("CourseCheckout") === -1) {
+ return "ng-hide";
+ }
+ }),
+ nga.field('course_user_count').label('Taken Courses'),
+ nga.field('total_spend').label('Spend Amount').cssClasses(function(){
+ if(enabledPlugins.indexOf("CourseCheckout") === -1 && enabledPlugins.indexOf("Subscriptions") === -1 ) {
+ return "ng-hide";
+ }
+ }),
+ nga.field('user_login_count').label('Logins'),
+ nga.field('last_login_ip').label('Last Login IP'),
+ nga.field('created').label('Registered On'),
+ ])
+ .filters([
+ nga.field('q').label('Search')
+ .pinned(true)
+ .template(' '),
+ nga.field('filter', 'choice').label('Status').attributes({
+ placeholder: 'Select Status'
+ })
+ .choices([{
+ label: 'Active',
+ value: 'active'
+ }, {
+ label: 'Inactive',
+ value: 'inactive'
+ }, ]),
+ nga.field('providertype', 'choice').label('User Type').attributes({
+ placeholder: 'Select User Type'
+ })
+ .choices([{
+ label: 'Admin',
+ value: 'admin'
+ }, {
+ label: 'User',
+ value: 'userpass'
+ }, {
+ label: 'Instructor',
+ value: 'instructor'
+ }, ]),
+ nga.field('isemailverified', 'choice').label('Email Status').attributes({
+ placeholder: 'Email Verified?'
+ })
+ .choices([{
+ label: 'Yes',
+ value: 'yes'
+ }, {
+ label: 'No',
+ value: 'no'
+ }, ])
+ ]);
+ // set the fields of the user entity list view
+ users.listView().title('Users') // default title is "[Entity_name] list"
+ .infinitePagination(false) // load pages as the user scrolls
+ .perPage(limit_per_page)
+ .listActions(['show', 'edit', 'delete']);
+ users.creationView()
+ .fields([
+ nga.field('providertype', 'choice').label('User Type')
+ .choices([{
+ label: 'Admin',
+ value: 'admin'
+ }, {
+ label: 'User',
+ value: 'userpass'
+ }, ])
+ .attributes({
+ placeholder: 'User Type'
+ })
+ .validation({
+ required: true
+ }),
+ nga.field('displayname').label('Name').map(truncate)
+ .attributes({
+ placeholder: 'Name'
+ })
+ .validation({
+ required: true
+ }),
+ nga.field('email', 'email').label('Email')
+ .attributes({
+ placeholder: 'Email'
+ })
+ .validation({
+ required: true
+ }),
+ nga.field('password', 'password').label('Password')
+ .attributes({
+ placeholder: 'Password'
+ })
+ .validation({
+ required: true
+ }),
+ nga.field('is_active', 'choice').label('Active?')
+ .attributes({
+ placeholder: 'Active?'
+ })
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ]),
+ nga.field('isemailverified', 'choice').label('Email Confirmed?')
+ .attributes({
+ placeholder: 'Email Confirmed?'
+ })
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: 1
+ }, {
+ label: 'No',
+ value: 0
+ }, ]),
+ nga.field('is_teacher', 'choice').label('Instructor?')
+ .attributes({
+ placeholder: 'Instructor?'
+ })
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: 1
+ }, {
+ label: 'No',
+ value: 0
+ }, ]),
+ ]);
+ users.editionView()
+ .fields([
+ nga.field('providertype', 'choice').label('User Type')
+ .choices([{
+ label: 'Admin',
+ value: 'admin'
+ }, {
+ label: 'User',
+ value: 'userpass'
+ }, ]).validation({
+ required: true
+ }),
+ nga.field('displayname').label('Name').map(truncate)
+ .validation({
+ required: true
+ }),
+ nga.field('available_balance').label('Available Balance')
+ .validation({
+ required: true
+ }),
+ nga.field('email', 'email').label('Email')
+ .validation({
+ required: true
+ }),
+ nga.field('password', 'password').label('Password'),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ]),
+ nga.field('isemailverified', 'choice').label('Email Confirmed?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: 1
+ }, {
+ label: 'No',
+ value: 0
+ }, ]),
+ nga.field('is_teacher', 'choice').label('Instructor?')
+ .attributes({
+ placeholder: 'Instructor?'
+ })
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: 1
+ }, {
+ label: 'No',
+ value: 0
+ }, ]),
+ nga.field('Course Bookings', 'referenced_list') // display list of related course users
+ .targetEntity(nga.entity('course_users'))
+ .targetReferenceField('user_id')
+ .perPage(10)
+ .targetFields([
+ nga.field('id').label('ID'),
+ nga.field('learner_name').label('Student'),
+ nga.field('course_user_status').label('Status'),
+ nga.field('booked_date').label('Booked On'),
+ ]),
+ nga.field('', 'template').label('')
+ .template(''),
+ nga.field('Courses', 'referenced_list') // display list of related courses
+ .targetEntity(nga.entity('courses'))
+ .targetReferenceField('user_id')
+ .perPage(10)
+ .targetFields([
+ nga.field('id').label('ID'), // The default displayed name is the camelCase field name. label() overrides id
+ nga.field('title').label('Course').map(truncate),
+ nga.field('displayname').label('Instructor'),
+ nga.field('total_sub_course_count').label('Lessons'),
+ nga.field('price'),
+ nga.field('course_user_count').label('Students'),
+ nga.field('total_revenue_amount').label('Revenue'),
+ nga.field('site_revenue_amount').label('Site Revenue')
+ ]),
+ nga.field('', 'template').label('')
+ .template(''),
+ nga.field('User Logins', 'referenced_list') // display list of related user logins
+ .targetEntity(nga.entity('user_logins'))
+ .targetReferenceField('user_id')
+ .perPage(10)
+ .targetFields([
+ nga.field('id').label('ID'),
+ nga.field('displayname').label('User Name'),
+ nga.field('ip').label('IP Address'),
+ nga.field('user_agent').label('User Agent')
+ ]),
+ nga.field('', 'template').label('')
+ .template(''),
+ ]);
+ users.showView()
+ .title('Show #{{ entry.values.displayname }}')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('displayname').label('Name'),
+ nga.field('email').label('Email'),
+ nga.field('course_count').label('Courses'),
+ nga.field('course_user_count').label('Taken Courses'),
+ nga.field('created').label('Registered On'),
+ nga.field('user_login_count').label('Logins'),
+ nga.field('last_login_ip').label('Last Login IP'),
+ nga.field('Course Bookings', 'referenced_list') // display list of related course users
+ .targetEntity(nga.entity('course_users'))
+ .targetReferenceField('user_id')
+ .perPage(10)
+ .targetFields([
+ nga.field('id').label('ID'),
+ nga.field('learner_name').label('Student'),
+ nga.field('course_user_status').label('Status'),
+ nga.field('booked_date').label('Booked On'),
+ ]),
+ nga.field('', 'template').label('')
+ .template(''),
+ nga.field('Courses', 'referenced_list') // display list of related courses
+ .targetEntity(nga.entity('courses'))
+ .targetReferenceField('user_id')
+ .perPage(10)
+ .targetFields([
+ nga.field('id').label('ID'), // The default displayed name is the camelCase field name. label() overrides id
+ nga.field('title').label('Course').map(truncate),
+ nga.field('displayname').label('Instructor'),
+ nga.field('total_sub_course_count').label('Lessons'),
+ nga.field('price'),
+ nga.field('course_user_count').label('Students'),
+ nga.field('total_revenue_amount').label('Revenue'),
+ nga.field('site_revenue_amount').label('Site Revenue')
+ ]),
+ nga.field('', 'template').label('')
+ .template(''),
+ nga.field('User Logins', 'referenced_list') // display list of related user logins
+ .targetEntity(nga.entity('user_logins'))
+ .targetReferenceField('user_id')
+ .perPage(10)
+ .targetFields([
+ nga.field('id').label('ID'),
+ nga.field('displayname').label('User Name'),
+ nga.field('ip').label('IP Address'),
+ nga.field('user_agent').label('User Agent')
+ ]),
+ nga.field('', 'template').label('')
+ .template(''),
+ ]);
+ course_favourites.listView().fields([
+ //nga.field('id', 'reference_many'),
+ nga.field('id').label('ID'),
+ nga.field('displayname').label('Student').map(truncate),
+ nga.field('course_title').label('Course').map(truncate),
+ nga.field('created').label('Created'),
+ ])
+ .infinitePagination(false) // load pages as the user scrolls
+ .perPage(limit_per_page)
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+
+ course_favourites.listView().title('Course Wishlist') // default title is "[Entity_name] list"
+ .actions(['batch'])
+ .infinitePagination(false) // load pages as the user scrolls
+ .perPage('all') // For getting all list
+ .listActions(['delete']);
+
+ course_favourites.showView()
+ .title('Show course favourites details')
+ .fields([
+ nga.field('displayname').label('User Name'),
+ nga.field('course_title').label('Course Name'),
+ nga.field('created').label('Created Date'),
+ nga.field('usr_id').label('User ID')
+ ]);
+
+ course_users.listView().title('Course Booking')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('course_title').label('Course'),
+ nga.field('teacher_name').label('Lecture'),
+ nga.field('learner_name').label('Student'),
+ nga.field('price').label('Amount').cssClasses(function(){
+ if(enabledPlugins.indexOf("CourseCheckout") === -1) {
+ return "ng-hide";
+ }
+ }),
+ nga.field('site_commission_amount').label('Site Commission').cssClasses(function(){
+ if(enabledPlugins.indexOf("CourseCheckout") === -1) {
+ return "ng-hide";
+ }
+ }),
+ nga.field('booked_date').label('Booked On'),
+ nga.field('course_user_status').label('Status'),
+ ])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' '),
+ nga.field('filter', 'choice').label('Status').attributes({
+ placeholder: 'Active?'
+ })
+ .choices([{
+ label: 'Not Started',
+ value: 'not_started'
+ }, {
+ label: 'In Progress',
+ value: 'in_progress'
+ }, {
+ label: 'Completed',
+ value: 'completed'
+ }, {
+ label: 'Archived',
+ value: 'archived'
+ }, {
+ label: 'Payment Pending',
+ value: 'payment_pending'
+ }, {
+ label: 'All Booked List',
+ value: 'paid'
+ }, ])
+
+ ])
+ .listActions(['delete']);
+ course_user_feedbacks.listView().title('Course Feedback')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('course_id', 'reference').isDetailLink(false)
+ .label('Course')
+ .targetEntity(nga.entity('courses'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('title'))
+ .validation({
+ required: true
+ }),
+ nga.field('user_id', 'reference')
+ .label('Student')
+ .targetEntity(nga.entity('users'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('displayname'))
+ .validation({
+ required: true
+ }),
+ nga.field('review_title').label('Title'),
+ nga.field('feedback').label('Feedback'),
+ nga.field('rating').label('Rating'),
+ ])
+ .perPage(limit_per_page)
+ .listActions(['edit', 'delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+ //custom template for edit view actions, added back button in course user feedback edit view
+ var backbtn_template = '' +
+ '' +
+ '';
+
+ course_user_feedbacks.editionView().title('Edit Course Feedback')
+ .fields([
+ nga.field('course_user_id').editable(false).label('Book ID'),
+ nga.field('course_id', 'reference').editable(false)
+ .label('Title')
+ .targetEntity(nga.entity('courses'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('title').map(truncate)),
+ nga.field('user_id', 'reference').editable(false)
+ .label('Student')
+ .targetEntity(nga.entity('users'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('displayname')),
+ nga.field('review_title').label('Title')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Title'
+ }),
+ nga.field('feedback', 'text').label('Feedback')
+ .validation({
+ required: true
+ }),
+ nga.field('rating', 'choice').label('Rating')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: '0',
+ value: 0
+ }, {
+ label: '1',
+ value: 1
+ }, {
+ label: '2',
+ value: 2
+ }, {
+ label: '3',
+ value: 3
+ }, {
+ label: '4',
+ value: 4
+ }, {
+ label: '5',
+ value: 5
+ }, ]),
+ ])
+ .actions(backbtn_template); //custom template for course user feedback added to edit view.
+
+ course_user_feedbacks.creationView().title('Create Course Feedback')
+ .fields([
+ nga.field('course_id', 'reference')
+ .label('Course')
+ .attributes({
+ placeholder: 'Course'
+ })
+ .targetEntity(nga.entity('courses'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('title').map(truncate))
+ .validation({
+ required: true
+ }),
+ nga.field('course_user_id', 'reference').attributes({
+ placehoder: 'Order Id'
+ })
+ .label('Order ID')
+ .attributes({
+ placeholder: 'Order ID'
+ })
+ .perPage('all') // For getting all list
+ .targetEntity(nga.entity('course_users'))
+ .targetField(nga.field('id').map(truncate))
+ .validation({
+ required: true
+ }),
+ nga.field('user_id', 'reference')
+ .label('Student')
+ .attributes({
+ placeholder: 'Student'
+ })
+ .targetEntity(nga.entity('users'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('displayname'))
+ .validation({
+ required: true
+ }),
+ nga.field('review_title').label('Title')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Title'
+ }),
+ nga.field('feedback', 'text').label('Feedback')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Feedback'
+ }),
+ nga.field('rating', 'choice').label('Rating')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: '0',
+ value: 0
+ }, {
+ label: '1',
+ value: 1
+ }, {
+ label: '2',
+ value: 2
+ }, {
+ label: '3',
+ value: 4
+ }, {
+ label: '4',
+ value: 4
+ }, {
+ label: '5',
+ value: 5
+ }, ])
+ .attributes({
+ placeholder: 'Rating'
+ })
+ ]);
+
+ user_logins.listView().title('User Logins')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('created').label('Login Time'),
+ nga.field('displayname').label('User'),
+ nga.field('ip').label('IP Address'),
+ nga.field('user_agent').label('User Agent'),
+ ])
+ .actions(['batch'])
+ .perPage(limit_per_page)
+ .listActions(['delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+
+ cities.listView().title('Cities')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('name').label('Name'),
+ nga.field('state_name').label('State'),
+ nga.field('country_name').label('Country'),
+ nga.field('is_active', 'boolean').label('Active?'),
+
+ ])
+ .perPage(limit_per_page)
+ .listActions(['edit', 'delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' '),
+ nga.field('filter', 'choice').label('Status').attributes({
+ placeholder: 'Active?'
+ })
+ .choices([{
+ label: 'Active',
+ value: 'active'
+ }, {
+ label: 'Inactive',
+ value: 'inactive'
+ }, ])
+ ])
+ .actions(['batch','create','']);
+
+ cities.editionView()
+ .fields([
+ nga.field('name')
+ .label('Name')
+ .validation({
+ required: true
+ }),
+ nga.field('state_id', 'reference')
+ .label('State')
+ .validation({
+ required: true
+ })
+ .targetEntity(nga.entity('states'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('name').map(truncate)),
+ nga.field('country_id', 'reference')
+ .label('Country')
+ .validation({
+ required: true
+ })
+ .targetEntity(nga.entity('countries'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('name').map(truncate)),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ])
+ ]);
+
+ cities.creationView().fields([
+ nga.field('name').label('Name')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Name'
+ }),
+ nga.field('state_id', 'reference')
+ .label('State')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'State'
+ })
+ .targetEntity(nga.entity('states'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('name').map(truncate)),
+ nga.field('country_id', 'reference')
+ .label('Country')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Country'
+ })
+ .perPage('all') // For getting all list
+ .targetEntity(nga.entity('countries'))
+ .targetField(nga.field('name').map(truncate)),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Active?'
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ])
+ ]);
+
+ states.listView().title('States')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('name').label('Name'),
+ nga.field('country_name').label('Country'),
+ nga.field('is_active', 'boolean').label('Active?'),
+ ])
+ .perPage(limit_per_page)
+ .listActions(['edit', 'delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' '),
+ nga.field('filter', 'choice').label('Status').attributes({
+ placeholder: 'Active?'
+ })
+ .choices([{
+ label: 'Active',
+ value: 'active'
+ }, {
+ label: 'Inactive',
+ value: 'inactive'
+ }, ])
+ ])
+ .actions(['batch','create','']);
+
+ states.editionView().fields([
+ nga.field('name')
+ .validation({
+ required: true
+ })
+ .label('State'),
+ //.targetEntity(nga.entity('states'))
+ //.perPage('all') // For getting all list
+ //.targetField(nga.field('name').map(truncate)),
+ nga.field('country_id', 'reference')
+ .label('Country')
+ .validation({
+ required: true
+ })
+ .perPage('all') // For getting all list
+ .targetEntity(nga.entity('countries'))
+ .targetField(nga.field('name').map(truncate)),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ])
+ ]);
+
+ states.creationView().fields([
+ nga.field('name')
+ .label('State')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'State'
+ }),
+ //.targetEntity(nga.entity('states'))
+ //.perPage('all') // For getting all list
+ //.targetField(nga.field('name').map(truncate)),
+ nga.field('country_id', 'reference')
+ .label('Country')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Country'
+ })
+ .perPage('all') // For getting all list
+ .targetEntity(nga.entity('countries'))
+ .targetField(nga.field('name').map(truncate)),
+ nga.field('is_active', 'choice').label('Active?')
+ .attributes({
+ placeholder: 'Active?'
+ })
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ])
+ ]);
+
+ countries.listView().title('Countries')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('name').label('Name'),
+ nga.field('iso2').label('ISO2'),
+ ])
+ .actions(['batch','create'])
+ .perPage(limit_per_page)
+ .listActions(['edit', 'delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+
+
+ countries.editionView().title('Countries')
+ .fields([
+ nga.field('name')
+ .label('Name')
+ .validation({ required: true }),
+ nga.field('iso2')
+ .label('ISO2')
+ .attributes({ placeholder: '2 character allowed' })
+ .validation({ validator: function(value) {
+ if (value.length !== 2) throw new Error ('ISO2 should be 2 characters');
+ } })
+ ]);
+
+ countries.creationView().title('Add Country')
+ .fields([
+ nga.field('name').label('Country')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Country'
+ }),
+ nga.field('iso2')
+ .label('ISO2')
+ .attributes({ placeholder: '2 character allowed' })
+ .validation({ validator: function(value) {
+ if (value.length !== 2) throw new Error ('ISO2 should be 2 characters');
+ } })
+ ]);
+
+ instructional_levels.listView().title('Instructional Levels')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('name').label('Name'),
+ nga.field('active_course_count').label('Courses'),
+ ])
+ .actions(['batch','create'])
+ .perPage(limit_per_page)
+ .listActions(['edit', 'delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+
+ instructional_levels.creationView().title('Add Instructional Level')
+ .fields([
+ nga.field('name').label('Name')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Name'
+ }),
+ ]);
+
+ instructional_levels.editionView().title('Edit Instructional Level')
+ .fields([
+ nga.field('name').label('Name')
+ .validation({
+ required: true
+ }),
+ ]);
+ var pages_custom_tmp = ''+
+ '';
+
+ pages.listView().title('Pages')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('language_id', 'reference')
+ .label('Language')
+ .targetEntity(nga.entity('languages'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('name'))
+ .validation({
+ required: true
+ }),
+ nga.field('title').label('Title').map(truncate),
+ nga.field('content').label('Content').map(truncate),
+ nga.field('slug').label('Page Slug'),
+ ])
+ .perPage(limit_per_page)
+ .listActions(['show','edit', 'delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ])
+ .actions(['batch',pages_custom_tmp]);
+
+ pages.showView().title('Pages - {{ entry.values.id }}')
+ .fields([
+ nga.field('language_id', 'reference')
+ .label('Language')
+ .targetEntity(nga.entity('languages'))
+ .perPage('all') // For getting all list
+ .targetField(nga.field('name')),
+ nga.field('title').label('Title'),
+ nga.field('content', 'wysiwyg').label('Content'),
+ nga.field('slug').label('Page Slug')
+ ]);
+
+ pages.editionView().title('Pages - {{ entry.values.title }}')
+ .fields([
+ nga.field('title')
+ .validation({
+ required: true
+ })
+ .label('Title'),
+ nga.field('content', 'wysiwyg')
+ .stripTags(true)
+ .validation({
+ required: true
+ }),
+ nga.field('slug')
+ .label('Page Slug')
+ .validation({
+ required: true
+ })
+ ]);
+
+ email_templates.listView().title('Email Templates')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('name').label('Name'),
+ nga.field('from_name').label('From Name'),
+ nga.field('subject').label('Subject'),
+ nga.field('content').label('Content'),
+ ])
+ .actions(['batch'])
+ .perPage(limit_per_page)
+ .listActions(['edit'])
+ .batchActions([])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+
+ email_templates.editionView().title('Edit Email Template')
+ .fields([
+ nga.field('name').editable(false).label('Name'),
+ nga.field('from_name')
+ .label('From Name')
+ .validation({
+ required: true
+ }),
+ nga.field('subject')
+ .label('Subject')
+ .validation({
+ required: true
+ }),
+ nga.field('content', 'text').label('Content')
+ .validation({
+ required: true
+ }),
+ nga.field('info').editable(false).label('Constant for Subject and Content'),
+ ]);
+
+ languages.listView().title('Languages')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('name').label('Name'),
+ nga.field('iso2').label('ISO2'),
+ nga.field('is_active', 'boolean').label('Active?'),
+ ])
+ .perPage(limit_per_page)
+ .listActions(['edit', 'delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' '),
+ nga.field('filter', 'choice').label('Status').attributes({
+ placeholder: 'Active?'
+ })
+ .choices([{
+ label: 'Active',
+ value: 'active'
+ }, {
+ label: 'Inactive',
+ value: 'inactive'
+ }, ])
+ ])
+ .actions(['batch','create','']);
+
+
+ languages.creationView().title('Create Language')
+ .fields([
+ nga.field('name').label('Name')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Name'
+ }),
+ nga.field('iso2')
+ .label('ISO2')
+ .attributes({ placeholder: '2 character allowed' })
+ .validation({ validator: function(value) {
+ if (value.length !== 2) throw new Error ('ISO2 should be 2 characters');
+ }}),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ])
+ ]);
+
+ languages.editionView().title('Edit Language')
+ .fields([
+ nga.field('name')
+ .label('Name')
+ .validation({
+ required: true
+ }),
+ nga.field('iso2')
+ .label('ISO2')
+ .attributes({ placeholder: '2 character allowed' })
+ .validation({ validator: function(value) {
+ if (value.length !== 2) throw new Error ('ISO2 should be 2 characters');
+ }}),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ])
+ ]);
+
+ transactions.listView().title('Transactions')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('course_title').label('Course'),
+ nga.field('displayname').label('User'),
+ nga.field('amount').label('Amount'),
+ nga.field('site_commission_amount').label('Site Commission'),
+ ])
+ .listActions(['delete'])
+ .perPage(limit_per_page)
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+
+ ipn_logs.listView().title('IPN Logs')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('post_variable').label('Post Variables').map(truncate),
+ //nga.field('ip').label('IP'),
+ nga.field('ip_id', 'reference')
+ .label('Ip Address')
+ .targetEntity(nga.entity('ips'))
+ .targetField(nga.field('ip'))
+ .validation({
+ required: true
+ }),
+ ])
+ .actions(['batch'])
+ .listActions(['show','delete'])
+ .perPage(limit_per_page)
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+
+ ipn_logs.showView().title('IPN Logs - {{ entry.values.id }}')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('post_variable').cssClasses(function() {
+ return 'line-break col-sm-10 col-md-8 col-lg-7 ';
+ })
+ .label('Post Variables'),
+ nga.field('ip_id', 'reference')
+ .label('Ip Address')
+ .targetEntity(nga.entity('ips'))
+ .targetField(nga.field('ip'))
+ .validation({
+ required: true
+ }),
+ ]);
+
+
+
+ var setting_edit_template = '';
+
+ settings.editionView().title('Edit - {{entry.values.label}}')
+ .fields([
+ nga.field('label').editable(false).label('Name'),
+ nga.field('description').editable(false).label('Description'),
+ nga.field('value', 'text').label('Value')
+ .validation({
+ validator: function(value, entry) {
+ if (entry.name === "payment.is_live_mode" || entry.name === "paypal.is_live_mode" || entry.name === "course.is_auto_approval_enabled" || entry.name === "facebook.is_enabled_facebook_comment" || entry.name === "disqus.is_enabled_disqus_comment" || entry.name === "analytics.is_enabled_facebook_pixel" || entry.name === "analytics.is_enabled_google_analytics" || entry.name === "paypal.is_paypal_enabled_for_payments" || entry.name === "payment.is_sudopay_enabled_for_payments" || entry.name === "video.is_enabled_promo_video" || entry.name === "video.is_keep_original_video_file_in_server") {
+ if (value !== "0" && value !== "1") {
+ throw new Error('Value must be either 0 or 1');
+ }
+ }
+ else if(entry.name === "course.max_course_fee"){
+ if (isNaN(value)) {
+ throw new Error('Value must be numeric');
+ }
+ }else if(entry.name === "video.max_size_to_allow_video_file"){
+ if (isNaN(value)) {
+ throw new Error('Value must be numeric');
+ }
+ }
+ }
+ })
+ ])
+ .actions(setting_edit_template);
+ //custom button template for settings actions
+ sudopay_payment_gateways.listView().title('SudoPay Payment Gateways')
+ .fields([
+ nga.field('sudopay_gateway_id').label('Gateway ID'),
+ nga.field('sudopay_gateway_name').label('Gateway Name'),
+ nga.field('supported_features_actions').label('Supported Actions'),
+ nga.field('supported_features_currencies').label('Supported Currencies'),
+ nga.field('supported_features_countries ').label('Supported Countries '),
+ ])
+ .batchActions([])
+ .perPage(limit_per_page)
+ .actions('') //custom button added to settings actions
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+ var setting_category_list_tpl = '';
+ var setting_category_action_tpl = '';
+ setting_categories.listView().title('Site Settings')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('name').label('Name'),
+ nga.field('description').label('Description'),
+ ])
+ .batchActions([])
+ .perPage(limit_per_page)
+ .actions(setting_category_action_tpl)
+ .listActions(setting_category_list_tpl)
+
+ settings_category_edit_template ='';
+ setting_categories.editionView().title('Edit Settings')
+ .fields([
+ nga.field('name').editable(false).label('Name'),
+ nga.field('description').editable(false).label('Description'),
+ nga.field('Related Settings', 'referenced_list') // display list of related settings
+ .targetEntity(nga.entity('settings'))
+ .targetReferenceField('setting_category_id')
+ .targetFields([
+ nga.field('label').label('Name'),
+ nga.field('value').label('Value')
+ ])
+ .listActions(['edit']),
+ nga.field('', 'template').label('').template(''),
+ nga.field('', 'template').label('').template(''),
+ ])
+ .actions(settings_category_edit_template);
+
+ ips.listView().title('IPs')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('ip').label('IP'),
+ nga.field('city_name')
+ .label('City')
+ .map(truncate),
+ nga.field('state_name')
+ .label('State')
+ .map(truncate),
+ nga.field('country_name')
+ .label('Country')
+ .map(truncate),
+ nga.field('latitude').label('Latitude'),
+ nga.field('longitude').label('Longitude'),
+ ])
+ .actions(['batch'])
+ .listActions(['delete'])
+ .perPage(limit_per_page)
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+ contacts.listView().title('Contacts')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('first_name').label('First Name'),
+ nga.field('last_name').label('Last Name'),
+ nga.field('email').label('Email'),
+ nga.field('subject').label('Subject'),
+ nga.field('message').label('Message'),
+ nga.field('ip').label('IP')
+ ])
+ .actions(['batch'])
+ .listActions(['show', 'delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+
+ contacts.showView().title('Contact - {{ entry.values.id }}')
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('first_name').label('First Name'),
+ nga.field('last_name').label('Last Name'),
+ nga.field('email').label('Email'),
+ nga.field('subject').label('Subject'),
+ nga.field('message').label('Message'),
+ nga.field('ip').label('IP')
+
+ ]);
+ online_course_lessons.editionView().disable();
+ // attach the admin application to the DOM and execute it
+ providers.listView().title('Providers') // @todo @boopathi - need to move "Social Login" plugin
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('name').label('Name'),
+ nga.field('api_key').label('Client ID'),
+ nga.field('secret_key').label('Secret Key'),
+ nga.field('display_order').label('Display Order'),
+ nga.field('is_active', 'boolean').label('Active?'),
+ ])
+ .actions(['batch'])
+ .listActions(['edit'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+ providers.editionView().title('Providers') // @todo @boopathi - need to move "Social Login" plugin
+ .fields([
+ nga.field('name')
+ .label('Name')
+ .validation({
+ required: true
+ }),
+ nga.field('api_key').label('Client ID')
+ .validation({
+ required: true
+ }),
+ nga.field('secret_key').label('Secret Key')
+ .validation({
+ required: true
+ }),
+ nga.field('display_order')
+ .label('Display Order')
+ .validation({
+ required: true
+ }),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }, ])
+ ]);
+
+ subscriptions.listView().title('Subscription Plans') // @todo @boopathi - need to move "Subscriptions" plugin
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('name').label('Name'),
+ nga.field('price').label('Price'),
+ nga.field('interval_period').label('Interval Period'),
+ nga.field('interval_unit').label('Interval Unit'),
+ nga.field('is_active', 'boolean').label('Active?'),
+ ])
+ .actions(['batch','create'])
+ .listActions(['edit','delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+ subscriptions.editionView().title('Edit - {{ entry.values.name }}')// @todo @boopathi - need to move "Subscriptions" plugin
+ .fields([
+ nga.field('name').label('Name')
+ .validation({
+ required: true
+ }),
+ nga.field('price', 'number').label('Price')
+ .validation({
+ required: true
+ })
+ .template(''),
+ nga.field('interval_period', 'number').label('Interval Period')
+ .validation({
+ required: true
+ })
+ .template(''),
+ nga.field('interval_unit', 'choice').label('Interval Unit')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Interval Unit by Days / Month'
+ })
+ .choices([{
+ label: 'Days',
+ value: 'Days'
+ }, {
+ label: 'Month',
+ value: 'Month'
+ }]),
+ nga.field('instruction_levels', 'reference_many').label('Instruction Levels to Access')
+ .targetEntity(instructional_levels)
+ .targetField(nga.field('name'))
+ .remoteComplete(true)
+ .singleApiCall(function (tagIds) {
+ return { 'instruction_level_id': tagIds };
+ })
+ .validation({
+ required: true
+ }),
+ nga.field('description', 'wysiwyg').label('Description')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Description'
+ }),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }])
+ ]);
+
+ subscriptions.creationView().title('Create Subscription Plan') // @todo @boopathi - need to move "Subscriptions" plugin
+ .fields([
+ nga.field('name').label('Name')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Name'
+ }),
+ nga.field('price', 'number').label('Price')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Price'
+ })
+ .template(''),
+ nga.field('interval_period', 'number').label('Interval Period')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Interval Period'
+ })
+ .template(''),
+ nga.field('interval_unit', 'choice').label('Interval Unit')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Interval Unit by Days / Month'
+ })
+ .choices([{
+ label: 'Days',
+ value: 'Days'
+ }, {
+ label: 'Month',
+ value: 'Month'
+ }]),
+ nga.field('instruction_levels', 'reference_many').label('Instruction Levels to Access')
+ .targetEntity(instructional_levels)
+ .targetField(nga.field('name'))
+ .remoteComplete(true)
+ .singleApiCall(function (tagIds) {
+ return { 'instruction_level_id': tagIds };
+ })
+ .validation({
+ required: true
+ }),
+ nga.field('description', 'wysiwyg').label('Description')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Description'
+ }),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }])
+ ]);
+
+ user_subscription_logs.listView().title('Subscriptions') // @todo @boopathi - need to move "Subscriptions" plugin
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('user_id', 'reference')
+ .label('User')
+ .targetEntity(nga.entity('users'))
+ .targetField(nga.field('displayname')),
+ nga.field('subscription_id', 'reference')
+ .label('Subscription')
+ .targetEntity(nga.entity('subscriptions'))
+ .targetField(nga.field('name')),
+ nga.field('amount').label('Amount'),
+ nga.field('subscription_start_date', 'datetime').label('Subscription start date'),
+ nga.field('subscription_end_date', 'datetime').label('Subscription end date'),
+ nga.field('subscription_status_id', 'reference')
+ .label('Status')
+ .targetEntity(nga.entity('subscription_statuses'))
+ .targetField(nga.field('name')),
+ ]).listActions(['edit','delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+ user_subscription_logs.editionView().title('Subscription') // @todo @boopathi - need to move "Subscriptions" plugin
+ .fields([
+ nga.field('user_id', 'reference')
+ .label('User')
+ .targetEntity(nga.entity('users'))
+ .targetField(nga.field('displayname'))
+ .validation({
+ required: true
+ }),
+ nga.field('subscription_id', 'reference')
+ .label('Subscription')
+ .targetEntity(nga.entity('subscriptions'))
+ .targetField(nga.field('name'))
+ .validation({
+ required: true
+ }),
+ nga.field('amount').label('Amount'),
+ nga.field('subscription_start_date', 'datetime').label('Subscription start date'),
+ nga.field('subscription_end_date', 'datetime').label('Subscription end date'),
+ nga.field('subscription_status_id', 'reference')
+ .label('Subscription Status')
+ .attributes({
+ placeholder: 'Status'
+ })
+ .perPage('all') // For getting all list
+ .targetEntity(nga.entity('subscription_statuses'))
+ .targetField(nga.field('name'))
+ .validation({
+ required: true
+ }),
+ ]);
+
+ user_cash_withdrawals.listView().title('Withdraw Requests')
+ .infinitePagination(false) // load pages as the user scrolls
+ .perPage(limit_per_page)
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('created').label('Created'),
+ nga.field('user_id', 'reference')
+ .label('User')
+ .targetEntity(nga.entity('users'))
+ .targetField(nga.field('displayname')),
+ nga.field('amount').label('Amount'),
+ nga.field('withdrawal_status_name').label('Status'),
+ nga.field('money_transfer_account_name').label('Money Transfer Account'),
+ ])
+ .listActions(['edit', 'delete'])
+ .filters([
+ nga.field('q').label('Search')
+ .pinned(true)
+ .template(' '),
+ nga.field('filter', 'choice').label('Status').attributes({
+ placeholder: 'Select Status'
+ })
+ .choices([{
+ label: 'Pending',
+ value: 'Pending'
+ }, {
+ label: 'Under Process',
+ value: 'Under Process'
+ }, {
+ label: 'Rejected',
+ value: 'Rejected'
+ }, {
+ label: 'Amount Transferred',
+ value: 'Amount Transferred'
+ }, ])
+ ]);
+
+ user_cash_withdrawals.editionView().title('Edit Withdraw Requests')
+ .fields([
+ nga.field('amount')
+ .label('Amount')
+ .validation({
+ required: true
+ })
+ .template(''),
+ nga.field('withdrawal_status_id', 'choice').label('Status')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Pending',
+ value: 1
+ }, {
+ label: 'Under Process',
+ value: 2
+ }, {
+ label: 'Rejected',
+ value: 3
+ }, {
+ label: 'Amount Transferred',
+ value: 4
+ }, ])
+ ]);
+
+ coupons.listView().title('Coupons') //
+ .fields([
+ nga.field('id').label('ID'),
+ nga.field('teacher_user_id', 'reference')
+ .label('Teacher Name')
+ .targetEntity(nga.entity('users'))
+ .targetField(nga.field('displayname'))
+ .validation({
+ required: true
+ }),
+ nga.field('course_id', 'reference')
+ .label('Course')
+ .targetEntity(nga.entity('courses'))
+ .targetField(nga.field('title'))
+ .validation({
+ required: true
+ }),
+ nga.field('coupon_code').label('Coupon Code'),
+ nga.field('max_number_of_time_can_use').label('Max Number of Time Can Use This Coupon'),
+ nga.field('coupon_user_count').label('Coupon Used Count'),
+ nga.field('is_active', 'boolean').label('Active?'),
+ ])
+ .actions(['batch', 'create'])
+ .listActions(['edit','delete'])
+ .filters([
+ nga.field('q').label('Search', 'template')
+ .pinned(true)
+ .template(' ')
+ ]);
+
+ coupons.creationView().title('Add Coupon')
+ .fields([
+ nga.field('course_id', 'reference')
+ .label('Course')
+ .validation({
+ required: true
+ })
+ .attributes({
+ placeholder: 'Course'
+ })
+ .perPage('all') // For getting all list
+ .targetEntity(nga.entity('courses'))
+ .targetField(nga.field('title').map(truncate)),
+ nga.field('max_number_of_time_can_use')
+ .label('Max Number of Time Can Use This Coupon')
+ .validation({
+ required: true
+ })
+ .template(''),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }])
+ ]);
+
+ coupons.editionView().title('Edit Coupon')
+ .fields([
+ nga.field('course_id', 'reference').editable(false)
+ .label('Course')
+ .perPage('all') // For getting all list
+ .targetEntity(nga.entity('courses'))
+ .targetField(nga.field('title').map(truncate)),
+ nga.field('teacher_user_id', 'reference').editable(false)
+ .label('Teacher')
+ .perPage('all') // For getting all list
+ .targetEntity(nga.entity('users'))
+ .targetField(nga.field('displayname').map(truncate)),
+ nga.field('max_number_of_time_can_use')
+ .label('Max Number of Time Can Use This Coupon')
+ .validation({
+ required: true
+ })
+ .template(''),
+ nga.field('is_active', 'choice').label('Active?')
+ .validation({
+ required: true
+ })
+ .choices([{
+ label: 'Yes',
+ value: true
+ }, {
+ label: 'No',
+ value: false
+ }])
+ ]);
+
+
+ //Menu configuration
+
+ admin.menu(nga.menu()
+ .addChild(nga.menu().title('Courses').icon('')
+ .addChild(nga.menu(courses).title('Courses').icon(''))
+ .addChild(nga.menu(categories).title('Categories').icon(''))
+ .addChild(nga.menu(course_users).title('Course Bookings').icon(''))
+ )
+ .addChild(nga.menu().title('Users').icon('')
+ .addChild(nga.menu(users).title('Users').icon(''))
+ .addChild(nga.menu(contacts).title('Contacts').icon(''))
+ .addChild(nga.menu(user_logins).title('User Logins').icon(''))
+ )
+ .addChild(nga.menu().title('Payments & Logs').icon('')
+
+ )
+ .addChild(nga.menu().title('Settings').icon('')
+ .addChild(nga.menu(setting_categories).title('Site Settings').icon(''))
+ )
+ .addChild(nga.menu().title('Master').icon('')
+ .addChild(nga.menu(pages).title('Pages').icon(''))
+ .addChild(nga.menu(cities).title('Cities').icon(''))
+ .addChild(nga.menu(states).title('States').icon(''))
+ .addChild(nga.menu(countries).title('Countries').icon(''))
+ .addChild(nga.menu(instructional_levels).title('Instructional Levels').icon(''))
+ .addChild(nga.menu(email_templates).title('Email Templates').icon(''))
+ .addChild(nga.menu(languages).title('Languages').icon(''))
+ .addChild(nga.menu(ips).title('IPs').icon(''))
+ )
+ .addChild(nga.menu(subscriptions).title('Plugins').icon('').link("/plugins"))
+ );
+ if(angular.isDefined(enabledPlugins)){
+ if(enabledPlugins.indexOf("Subscriptions") > -1) {
+ admin.menu().getChildByTitle('Master')
+ .addChild(nga.menu(subscriptions).title('Subscription Plans').template(' Subscription Plans'));
+ admin.menu().getChildByTitle('Users')
+ .addChild(nga.menu(user_subscription_logs).title('User Subscriptions').template(' User Subscriptions'))
+ }
+ if(enabledPlugins.indexOf("Withdrawal") > -1) {
+ admin.menu().getChildByTitle('Users')
+ .addChild(nga.menu(user_cash_withdrawals).title('Withdraw Requests').template(' Withdraw Requests'))
+ }
+
+ if(enabledPlugins.indexOf("SocialLogins") > -1) {
+ admin.menu().getChildByTitle('Master')
+ .addChild(nga.menu(providers).title('Providers').template(' Providers'))
+ }
+ if(enabledPlugins.indexOf("Coupons") > -1) {
+ admin.menu().getChildByTitle('Courses')
+ .addChild(nga.menu(coupons).title('Coupons').template(' Coupons'))
+ }
+ if(enabledPlugins.indexOf("CourseWishlist") > -1) {
+ admin.menu().getChildByTitle('Courses')
+ .addChild(nga.menu(course_favourites).title('Course Wishlists').template(' Course Wishlists'))
+ }
+ if(enabledPlugins.indexOf("RatingAndReview") > -1) {
+ admin.menu().getChildByTitle('Courses')
+ .addChild(nga.menu(course_user_feedbacks).title('Course Feedback').template(' Course Feedback'))
+ }
+ if(enabledPlugins.indexOf("SudoPay") > -1) {
+ admin.menu().getChildByTitle('Payments & Logs')
+ .addChild(nga.menu(sudopay_payment_gateways).title('SudoPay Gateways').template(' SudoPay Gateways'))
+ }
+ if(enabledPlugins.indexOf("CourseCheckout") > -1) {
+ admin.menu().getChildByTitle('Payments & Logs')
+ .addChild(nga.menu(transactions).title('Transactions').icon(''))
+ }
+ if(enabledPlugins.indexOf("CourseCheckout") > -1 || enabledPlugins.indexOf("Subscriptions") > -1) {
+ admin.menu().getChildByTitle('Payments & Logs')
+ .addChild(nga.menu(ipn_logs).title('IPN Logs').icon(''))
+ }
+ //disable Payments & Logs parent menu if both subscriptions and CourseCheckout plugin disabled
+ if(enabledPlugins.indexOf("CourseCheckout") === -1 && enabledPlugins.indexOf("Subscriptions") === -1) {
+ admin.menu().getChildByTitle('Payments & Logs')
+ .title('')
+ .template('')
+ .icon('');
+ }
+ }
+ // customize header
+ var customHeaderTemplate = '' + ''; // this is custom directive
+ admin.header(customHeaderTemplate);
+
+ // customize dashboard
+ var dashboardTpl = '' +
+ '';
+ admin.dashboard(nga.dashboard()
+ .template(dashboardTpl)
+ );
+ nga.configure(admin);
+}]);
\ No newline at end of file
diff --git a/client/ag-admin/tpl/VimeoSettings.tpl.html b/client/ag-admin/tpl/VimeoSettings.tpl.html
new file mode 100644
index 0000000..43b1100
--- /dev/null
+++ b/client/ag-admin/tpl/VimeoSettings.tpl.html
@@ -0,0 +1,81 @@
+
+
+
\ No newline at end of file
diff --git a/client/ag-admin/tpl/YoutubeSettings.tpl.html b/client/ag-admin/tpl/YoutubeSettings.tpl.html
new file mode 100644
index 0000000..c7ce7ad
--- /dev/null
+++ b/client/ag-admin/tpl/YoutubeSettings.tpl.html
@@ -0,0 +1,81 @@
+
+
+
diff --git a/client/ag-admin/tpl/customHeader.tpl.html b/client/ag-admin/tpl/customHeader.tpl.html
new file mode 100644
index 0000000..ca42e04
--- /dev/null
+++ b/client/ag-admin/tpl/customHeader.tpl.html
@@ -0,0 +1,10 @@
+
diff --git a/client/ag-admin/tpl/dashboardSummary.tpl.html b/client/ag-admin/tpl/dashboardSummary.tpl.html
new file mode 100644
index 0000000..c796152
--- /dev/null
+++ b/client/ag-admin/tpl/dashboardSummary.tpl.html
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+ {{adminoverview.users}}
+ Total Customers
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{adminoverview.total_active_courses}}
+ Active Courses
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{adminoverview.total_order}}
+ Total Orders
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{adminoverview.revenue}}
+ Total Revenue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Overview - {{rangeText}}
+
+
+
+
+ -
+
+
{{adminstats.user_register}}
+ User Register
+
+ -
+
+
{{adminstats.user_login}}
+ User Login
+
+ -
+
+
{{adminstats.course_posted}}
+ Course Created
+
+ -
+
+
{{adminstats.course_bookings}}
+ Course Booking
+
+ -
+
+
{{adminstats.course_favorites}}
+ Course Favourites
+
+
+ -
+
+
{{adminstats.course_feedbacks}}
+ Course Feedbacks
+
+ -
+
+
{{adminstats.transactions}}
+ Transactions
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/client/ag-admin/tpl/pages.tpl.html b/client/ag-admin/tpl/pages.tpl.html
new file mode 100644
index 0000000..cf3c63b
--- /dev/null
+++ b/client/ag-admin/tpl/pages.tpl.html
@@ -0,0 +1,46 @@
+
+
diff --git a/client/ag-admin/tpl/plugins.tpl.html b/client/ag-admin/tpl/plugins.tpl.html
new file mode 100644
index 0000000..8f96f45
--- /dev/null
+++ b/client/ag-admin/tpl/plugins.tpl.html
@@ -0,0 +1,178 @@
+
+
+
+ To reflect plugin changes, you need to refresh.
+
+
+
+
+
+
+
+
+ {{payment_and_cart_plugin.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/ag-admin/tpl/scribdSettings.tpl.html b/client/ag-admin/tpl/scribdSettings.tpl.html
new file mode 100644
index 0000000..25a269b
--- /dev/null
+++ b/client/ag-admin/tpl/scribdSettings.tpl.html
@@ -0,0 +1,74 @@
+
+
+
\ No newline at end of file
diff --git a/client/assets/README.md b/client/assets/README.md
new file mode 100644
index 0000000..70f2979
--- /dev/null
+++ b/client/assets/README.md
@@ -0,0 +1,4 @@
+# The `src/assets` Directory
+
+There's really not much to say here. Every file in this directory is recursively transferred to `dist/assets/`.
+
diff --git a/client/assets/apple-touch-icon-114x114.png b/client/assets/apple-touch-icon-114x114.png
new file mode 100644
index 0000000..7e88a60
Binary files /dev/null and b/client/assets/apple-touch-icon-114x114.png differ
diff --git a/client/assets/apple-touch-icon-72x72.png b/client/assets/apple-touch-icon-72x72.png
new file mode 100644
index 0000000..d7ac60d
Binary files /dev/null and b/client/assets/apple-touch-icon-72x72.png differ
diff --git a/client/assets/apple-touch-icon.png b/client/assets/apple-touch-icon.png
new file mode 100644
index 0000000..02ae6c2
Binary files /dev/null and b/client/assets/apple-touch-icon.png differ
diff --git a/client/assets/css/alerts.less b/client/assets/css/alerts.less
new file mode 100644
index 0000000..3b1f3e4
--- /dev/null
+++ b/client/assets/css/alerts.less
@@ -0,0 +1,67 @@
+//
+// Alerts
+// --------------------------------------------------
+
+
+// Base styles
+// -------------------------
+
+.alert {
+ padding: @alert-padding;
+ margin-bottom: @line-height-computed;
+ border: 1px solid transparent;
+ border-radius: @alert-border-radius;
+
+ // Headings for larger alerts
+ h4 {
+ margin-top: 0;
+ // Specified for the h4 to prevent conflicts of changing @headings-color
+ color: inherit;
+ }
+ // Provide class for links that match alerts
+ .alert-link {
+ font-weight: @alert-link-font-weight;
+ }
+
+ // Improve alignment and spacing of inner content
+ > p,
+ > ul {
+ margin-bottom: 0;
+ }
+ > p + p {
+ margin-top: 5px;
+ }
+}
+
+// Dismissable alerts
+//
+// Expand the right padding and account for the close button's positioning.
+
+.alert-dismissable {
+ padding-right: (@alert-padding + 20);
+
+ // Adjust close link position
+ .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ color: inherit;
+ }
+}
+
+// Alternate styles
+//
+// Generate contextual modifier classes for colorizing the alert.
+
+.alert-success {
+ .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);
+}
+.alert-info {
+ .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);
+}
+.alert-warning {
+ .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);
+}
+.alert-danger {
+ .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);
+}
diff --git a/client/assets/css/badges.less b/client/assets/css/badges.less
new file mode 100644
index 0000000..e528347
--- /dev/null
+++ b/client/assets/css/badges.less
@@ -0,0 +1,55 @@
+//
+// Badges
+// --------------------------------------------------
+
+
+// Base classes
+.badge {
+ display: inline-block;
+ min-width: 10px;
+ padding: 3px 7px;
+ font-size: @font-size-small;
+ font-weight: @badge-font-weight;
+ color: @badge-color;
+ line-height: @badge-line-height;
+ vertical-align: baseline;
+ white-space: nowrap;
+ text-align: center;
+ background-color: @badge-bg;
+ border-radius: @badge-border-radius;
+
+ // Empty badges collapse automatically (not available in IE8)
+ &:empty {
+ display: none;
+ }
+
+ // Quick fix for badges in buttons
+ .btn & {
+ position: relative;
+ top: -1px;
+ }
+ .btn-xs & {
+ top: 0;
+ padding: 1px 5px;
+ }
+}
+
+// Hover state, but only for links
+a.badge {
+ &:hover,
+ &:focus {
+ color: @badge-link-hover-color;
+ text-decoration: none;
+ cursor: pointer;
+ }
+}
+
+// Account for counters in navs
+a.list-group-item.active > .badge,
+.nav-pills > .active > a > .badge {
+ color: @badge-active-color;
+ background-color: @badge-active-bg;
+}
+.nav-pills > li > a > .badge {
+ margin-left: 3px;
+}
diff --git a/client/assets/css/bootstrap-social.less b/client/assets/css/bootstrap-social.less
new file mode 100644
index 0000000..1f2b505
--- /dev/null
+++ b/client/assets/css/bootstrap-social.less
@@ -0,0 +1,114 @@
+/*
+ * Social Buttons for Bootstrap
+ *
+ * Copyright 2013-2015 Panayiotis Lipiridis
+ * Licensed under the MIT License
+ *
+ * https://github.com/lipis/bootstrap-social
+ */
+
+@bs-height-base: (@line-height-computed + @padding-base-vertical * 2);
+@bs-height-lg: (floor(@font-size-large * @line-height-base) + @padding-large-vertical * 2);
+@bs-height-sm: (floor(@font-size-small * 1.5) + @padding-small-vertical * 2);
+@bs-height-xs: (floor(@font-size-small * 1.2) + @padding-small-vertical + 1);
+
+.btn-social {
+ position: relative;
+ padding-left: (@bs-height-base + @padding-base-horizontal);
+ text-align: left;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ > :first-child {
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: @bs-height-base;
+ line-height: (@bs-height-base + 2);
+ font-size: 1.6em;
+ text-align: center;
+ border-right: 1px solid rgba(0, 0, 0, 0.2);
+ }
+ &.btn-lg {
+ > :first-child {
+ line-height: @bs-height-lg;
+ width: @bs-height-lg;
+ font-size: 1.8em;
+ }
+ }
+ &.btn-sm {
+ padding-left: (@bs-height-sm + @padding-small-horizontal);
+ > :first-child {
+ line-height: @bs-height-sm;
+ width: @bs-height-sm;
+ font-size: 1.4em;
+ }
+ }
+ &.btn-xs {
+ padding-left: (@bs-height-xs + @padding-small-horizontal);
+ > :first-child {
+ line-height: @bs-height-xs;
+ width: @bs-height-xs;
+ font-size: 1.2em;
+ }
+ }
+}
+
+.btn-social-icon {
+ .btn-social;
+ height: (@bs-height-base + 2);
+ width: (@bs-height-base + 2);
+ padding: 0;
+ > :first-child {
+ border: none;
+ text-align: center;
+ width: 100%!important;
+ }
+ &.btn-lg {
+ height: @bs-height-lg;
+ width: @bs-height-lg;
+ padding-left: 0;
+ padding-right: 0;
+ }
+ &.btn-sm {
+ height: (@bs-height-sm + 2);
+ width: (@bs-height-sm + 2);
+ padding-left: 0;
+ padding-right: 0;
+ }
+ &.btn-xs {
+ height: (@bs-height-xs + 2);
+ width: (@bs-height-xs + 2);
+ padding-left: 0;
+ padding-right: 0;
+ }
+}
+
+.btn-social(@color-bg, @color: #fff) {
+ background-color: @color-bg;
+ .button-variant(@color, @color-bg, rgba(0,0,0,.2));
+}
+
+
+.btn-adn { .btn-social(#d87a68); }
+.btn-bitbucket { .btn-social(#205081); }
+.btn-dropbox { .btn-social(#1087dd); }
+.btn-facebook { .btn-social(#3b5998); }
+.btn-flickr { .btn-social(#ff0084); }
+.btn-foursquare { .btn-social(#f94877); }
+.btn-github { .btn-social(#444444); }
+.btn-google { .btn-social(#dd4b39); }
+.btn-instagram { .btn-social(#3f729b); }
+.btn-linkedin { .btn-social(#007bb6); }
+.btn-microsoft { .btn-social(#2672ec); }
+.btn-odnoklassniki { .btn-social(#f4731c); }
+.btn-openid { .btn-social(#f7931e); }
+.btn-pinterest { .btn-social(#cb2027); }
+.btn-reddit { .btn-social(#eff7ff, #000); }
+.btn-soundcloud { .btn-social(#ff5500); }
+.btn-tumblr { .btn-social(#2c4762); }
+.btn-twitter { .btn-social(#55acee); }
+.btn-vimeo { .btn-social(#1ab7ea); }
+.btn-vk { .btn-social(#587ea3); }
+.btn-yahoo { .btn-social(#720e9e); }
diff --git a/client/assets/css/bootstrap.less b/client/assets/css/bootstrap.less
new file mode 100644
index 0000000..76f9c50
--- /dev/null
+++ b/client/assets/css/bootstrap.less
@@ -0,0 +1,62 @@
+// Core variables and mixins
+@import "variables.less";
+@import "mixins.less";
+
+// Reset
+@import "normalize.less";
+@import "print.less";
+
+// Core CSS
+@import "scaffolding.less";
+@import "type.less";
+@import "code.less";
+@import "grid.less";
+@import "tables.less";
+@import "forms.less";
+@import "buttons.less";
+
+// Components
+@import "component-animations.less";
+@import "glyphicons.less";
+@import "dropdowns.less";
+@import "button-groups.less";
+@import "input-groups.less";
+@import "navs.less";
+@import "navbar.less";
+@import "breadcrumbs.less";
+@import "pagination.less";
+@import "pager.less";
+@import "labels.less";
+@import "badges.less";
+@import "jumbotron.less";
+@import "thumbnails.less";
+@import "alerts.less";
+@import "progress-bars.less";
+@import "media.less";
+@import "list-group.less";
+@import "panels.less";
+@import "wells.less";
+@import "close.less";
+
+// Components w/ JavaScript
+@import "modals.less";
+@import "tooltip.less";
+@import "popovers.less";
+@import "carousel.less";
+
+// Utility classes
+@import "utilities.less";
+@import "responsive-utilities.less";
+
+// Fontawesome classes
+@import "font-awesome.less";
+
+// Custom classes
+@import "custom.less";
+@import "custom-responsive.less";
+
+//Components: footer
+@import "sticky-footer.less";
+
+//Components: social Buttons
+@import "bootstrap-social.less";
diff --git a/client/assets/css/breadcrumbs.less b/client/assets/css/breadcrumbs.less
new file mode 100644
index 0000000..3008810
--- /dev/null
+++ b/client/assets/css/breadcrumbs.less
@@ -0,0 +1,26 @@
+//
+// Breadcrumbs
+// --------------------------------------------------
+
+
+.breadcrumb {
+ padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;
+ margin-bottom: @line-height-computed;
+ list-style: none;
+ background-color: @breadcrumb-bg;
+ border-radius: @border-radius-base;
+
+ > li {
+ display: inline-block;
+
+ + li:before {
+ content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space
+ padding: 0 5px;
+ color: @breadcrumb-color;
+ }
+ }
+
+ > .active {
+ color: @breadcrumb-active-color;
+ }
+}
diff --git a/client/assets/css/button-groups.less b/client/assets/css/button-groups.less
new file mode 100644
index 0000000..1e6976a
--- /dev/null
+++ b/client/assets/css/button-groups.less
@@ -0,0 +1,226 @@
+//
+// Button groups
+// --------------------------------------------------
+
+// Make the div behave like a button
+.btn-group,
+.btn-group-vertical {
+ position: relative;
+ display: inline-block;
+ vertical-align: middle; // match .btn alignment given font-size hack above
+ > .btn {
+ position: relative;
+ float: left;
+ // Bring the "active" button to the front
+ &:hover,
+ &:focus,
+ &:active,
+ &.active {
+ z-index: 2;
+ }
+ &:focus {
+ // Remove focus outline when dropdown JS adds it after closing the menu
+ outline: none;
+ }
+ }
+}
+
+// Prevent double borders when buttons are next to each other
+.btn-group {
+ .btn + .btn,
+ .btn + .btn-group,
+ .btn-group + .btn,
+ .btn-group + .btn-group {
+ margin-left: -1px;
+ }
+}
+
+// Optional: Group multiple button groups together for a toolbar
+.btn-toolbar {
+ margin-left: -5px; // Offset the first child's margin
+ &:extend(.clearfix all);
+
+ .btn-group,
+ .input-group {
+ float: left;
+ }
+ > .btn,
+ > .btn-group,
+ > .input-group {
+ margin-left: 5px;
+ }
+}
+
+.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
+ border-radius: 0;
+}
+
+// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match
+.btn-group > .btn:first-child {
+ margin-left: 0;
+ &:not(:last-child):not(.dropdown-toggle) {
+ .border-right-radius(0);
+ }
+}
+// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it
+.btn-group > .btn:last-child:not(:first-child),
+.btn-group > .dropdown-toggle:not(:first-child) {
+ .border-left-radius(0);
+}
+
+// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)
+.btn-group > .btn-group {
+ float: left;
+}
+.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-radius: 0;
+}
+.btn-group > .btn-group:first-child {
+ > .btn:last-child,
+ > .dropdown-toggle {
+ .border-right-radius(0);
+ }
+}
+.btn-group > .btn-group:last-child > .btn:first-child {
+ .border-left-radius(0);
+}
+
+// On active and open, don't show outline
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+ outline: 0;
+}
+
+
+// Sizing
+//
+// Remix the default button sizing classes into new ones for easier manipulation.
+
+.btn-group-xs > .btn { &:extend(.btn-xs); }
+.btn-group-sm > .btn { &:extend(.btn-sm); }
+.btn-group-lg > .btn { &:extend(.btn-lg); }
+
+
+// Split button dropdowns
+// ----------------------
+
+// Give the line between buttons some depth
+.btn-group > .btn + .dropdown-toggle {
+ padding-left: 8px;
+ padding-right: 8px;
+}
+.btn-group > .btn-lg + .dropdown-toggle {
+ padding-left: 12px;
+ padding-right: 12px;
+}
+
+// The clickable button for toggling the menu
+// Remove the gradient and set the same inset shadow as the :active state
+.btn-group.open .dropdown-toggle {
+ .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));
+
+ // Show no shadow for `.btn-link` since it has no other button styles.
+ &.btn-link {
+ .box-shadow(none);
+ }
+}
+
+
+// Reposition the caret
+.btn .caret {
+ margin-left: 0;
+}
+// Carets in other button sizes
+.btn-lg .caret {
+ border-width: @caret-width-large @caret-width-large 0;
+ border-bottom-width: 0;
+}
+// Upside down carets for .dropup
+.dropup .btn-lg .caret {
+ border-width: 0 @caret-width-large @caret-width-large;
+}
+
+
+// Vertical button groups
+// ----------------------
+
+.btn-group-vertical {
+ > .btn,
+ > .btn-group,
+ > .btn-group > .btn {
+ display: block;
+ float: none;
+ width: 100%;
+ max-width: 100%;
+ }
+
+ // Clear floats so dropdown menus can be properly placed
+ > .btn-group {
+ &:extend(.clearfix all);
+ > .btn {
+ float: none;
+ }
+ }
+
+ > .btn + .btn,
+ > .btn + .btn-group,
+ > .btn-group + .btn,
+ > .btn-group + .btn-group {
+ margin-top: -1px;
+ margin-left: 0;
+ }
+}
+
+.btn-group-vertical > .btn {
+ &:not(:first-child):not(:last-child) {
+ border-radius: 0;
+ }
+ &:first-child:not(:last-child) {
+ border-top-right-radius: @border-radius-base;
+ .border-bottom-radius(0);
+ }
+ &:last-child:not(:first-child) {
+ border-bottom-left-radius: @border-radius-base;
+ .border-top-radius(0);
+ }
+}
+.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
+ border-radius: 0;
+}
+.btn-group-vertical > .btn-group:first-child:not(:last-child) {
+ > .btn:last-child,
+ > .dropdown-toggle {
+ .border-bottom-radius(0);
+ }
+}
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
+ .border-top-radius(0);
+}
+
+
+
+// Justified button groups
+// ----------------------
+
+.btn-group-justified {
+ display: table;
+ width: 100%;
+ table-layout: fixed;
+ border-collapse: separate;
+ > .btn,
+ > .btn-group {
+ float: none;
+ display: table-cell;
+ width: 1%;
+ }
+ > .btn-group .btn {
+ width: 100%;
+ }
+}
+
+
+// Checkbox and radio options
+[data-toggle="buttons"] > .btn > input[type="radio"],
+[data-toggle="buttons"] > .btn > input[type="checkbox"] {
+ display: none;
+}
diff --git a/client/assets/css/buttons.less b/client/assets/css/buttons.less
new file mode 100644
index 0000000..5241e7a
--- /dev/null
+++ b/client/assets/css/buttons.less
@@ -0,0 +1,159 @@
+//
+// Buttons
+// --------------------------------------------------
+
+
+// Base styles
+// --------------------------------------------------
+
+.btn {
+ display: inline-block;
+ margin-bottom: 0; // For input.btn
+ font-weight: @btn-font-weight;
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+ background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214
+ border: 1px solid transparent;
+ white-space: nowrap;
+ .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base);
+ .user-select(none);
+
+ &,
+ &:active,
+ &.active {
+ &:focus {
+ .tab-focus();
+ }
+ }
+
+ &:hover,
+ &:focus {
+ color: @btn-default-color;
+ text-decoration: none;
+ }
+
+ &:active,
+ &.active {
+ outline: 0;
+ background-image: none;
+ .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));
+ }
+
+ &.disabled,
+ &[disabled],
+ fieldset[disabled] & {
+ cursor: not-allowed;
+ pointer-events: none; // Future-proof disabling of clicks
+ .opacity(.65);
+ .box-shadow(none);
+ }
+}
+
+
+// Alternate buttons
+// --------------------------------------------------
+
+.btn-default {
+ .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);
+}
+.btn-primary {
+ .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);
+}
+// Success appears as green
+.btn-success {
+ .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);
+}
+// Info appears as blue-green
+.btn-info {
+ .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);
+}
+// Warning appears as orange
+.btn-warning {
+ .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);
+}
+// Danger and error appear as red
+.btn-danger {
+ .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);
+}
+
+
+// Link buttons
+// -------------------------
+
+// Make a button look and behave like a link
+.btn-link {
+ color: @link-color;
+ font-weight: normal;
+ cursor: pointer;
+ border-radius: 0;
+
+ &,
+ &:active,
+ &[disabled],
+ fieldset[disabled] & {
+ background-color: transparent;
+ .box-shadow(none);
+ }
+ &,
+ &:hover,
+ &:focus,
+ &:active {
+ border-color: transparent;
+ }
+ &:hover,
+ &:focus {
+ color: @link-hover-color;
+ text-decoration: underline;
+ background-color: transparent;
+ }
+ &[disabled],
+ fieldset[disabled] & {
+ &:hover,
+ &:focus {
+ color: @btn-link-disabled-color;
+ text-decoration: none;
+ }
+ }
+}
+
+
+// Button Sizes
+// --------------------------------------------------
+
+.btn-lg {
+ // line-height: ensure even-numbered height of button next to large input
+ .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);
+}
+.btn-sm {
+ // line-height: ensure proper height of button next to small input
+ .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);
+}
+.btn-xs {
+ .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small);
+}
+
+
+// Block button
+// --------------------------------------------------
+
+.btn-block {
+ display: block;
+ width: 100%;
+ padding-left: 0;
+ padding-right: 0;
+}
+
+// Vertically space out multiple block buttons
+.btn-block + .btn-block {
+ margin-top: 5px;
+}
+
+// Specificity overrides
+input[type="submit"],
+input[type="reset"],
+input[type="button"] {
+ &.btn-block {
+ width: 100%;
+ }
+}
diff --git a/client/assets/css/carousel.less b/client/assets/css/carousel.less
new file mode 100644
index 0000000..2cdc1ee
--- /dev/null
+++ b/client/assets/css/carousel.less
@@ -0,0 +1,232 @@
+//
+// Carousel
+// --------------------------------------------------
+
+
+// Wrapper for the slide container and indicators
+.carousel {
+ position: relative;
+}
+
+.carousel-inner {
+ position: relative;
+ overflow: hidden;
+ width: 100%;
+
+ > .item {
+ display: none;
+ position: relative;
+ .transition(.6s ease-in-out left);
+
+ // Account for jankitude on images
+ > img,
+ > a > img {
+ &:extend(.img-responsive);
+ line-height: 1;
+ }
+ }
+
+ > .active,
+ > .next,
+ > .prev { display: block; }
+
+ > .active {
+ left: 0;
+ }
+
+ > .next,
+ > .prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
+ }
+
+ > .next {
+ left: 100%;
+ }
+ > .prev {
+ left: -100%;
+ }
+ > .next.left,
+ > .prev.right {
+ left: 0;
+ }
+
+ > .active.left {
+ left: -100%;
+ }
+ > .active.right {
+ left: 100%;
+ }
+
+}
+
+// Left/right controls for nav
+// ---------------------------
+
+.carousel-control {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: @carousel-control-width;
+ .opacity(@carousel-control-opacity);
+ font-size: @carousel-control-font-size;
+ color: @carousel-control-color;
+ text-align: center;
+ text-shadow: @carousel-text-shadow;
+ // We can't have this transition here because WebKit cancels the carousel
+ // animation if you trip this while in the middle of another animation.
+
+ // Set gradients for backgrounds
+ &.left {
+ #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001));
+ }
+ &.right {
+ left: auto;
+ right: 0;
+ #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5));
+ }
+
+ // Hover/focus state
+ &:hover,
+ &:focus {
+ outline: none;
+ color: @carousel-control-color;
+ text-decoration: none;
+ .opacity(.9);
+ }
+
+ // Toggles
+ .icon-prev,
+ .icon-next,
+ .glyphicon-chevron-left,
+ .glyphicon-chevron-right {
+ position: absolute;
+ top: 50%;
+ z-index: 5;
+ display: inline-block;
+ }
+ .icon-prev,
+ .glyphicon-chevron-left {
+ left: 50%;
+ }
+ .icon-next,
+ .glyphicon-chevron-right {
+ right: 50%;
+ }
+ .icon-prev,
+ .icon-next {
+ width: 20px;
+ height: 20px;
+ margin-top: -10px;
+ margin-left: -10px;
+ font-family: serif;
+ }
+
+ .icon-prev {
+ &:before {
+ content: '\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)
+ }
+ }
+ .icon-next {
+ &:before {
+ content: '\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)
+ }
+ }
+}
+
+// Optional indicator pips
+//
+// Add an unordered list with the following class and add a list item for each
+// slide your carousel holds.
+
+.carousel-indicators {
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ z-index: 15;
+ width: 60%;
+ margin-left: -30%;
+ padding-left: 0;
+ list-style: none;
+ text-align: center;
+
+ li {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ margin: 1px;
+ text-indent: -999px;
+ border: 1px solid @carousel-indicator-border-color;
+ border-radius: 10px;
+ cursor: pointer;
+
+ // IE8-9 hack for event handling
+ //
+ // Internet Explorer 8-9 does not support clicks on elements without a set
+ // `background-color`. We cannot use `filter` since that's not viewed as a
+ // background color by the browser. Thus, a hack is needed.
+ //
+ // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we
+ // set alpha transparency for the best results possible.
+ background-color: #000 \9; // IE8
+ background-color: rgba(0,0,0,0); // IE9
+ }
+ .active {
+ margin: 0;
+ width: 12px;
+ height: 12px;
+ background-color: @carousel-indicator-active-bg;
+ }
+}
+
+// Optional captions
+// -----------------------------
+// Hidden by default for smaller viewports
+.carousel-caption {
+ position: absolute;
+ left: 15%;
+ right: 15%;
+ bottom: 20px;
+ z-index: 10;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ color: @carousel-caption-color;
+ text-align: center;
+ text-shadow: @carousel-text-shadow;
+ & .btn {
+ text-shadow: none; // No shadow for button elements in carousel-caption
+ }
+}
+
+
+// Scale up controls for tablets and up
+@media screen and (min-width: @screen-sm-min) {
+
+ // Scale up the controls a smidge
+ .carousel-control {
+ .glyphicon-chevron-left,
+ .glyphicon-chevron-right,
+ .icon-prev,
+ .icon-next {
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ margin-left: -15px;
+ font-size: 30px;
+ }
+ }
+
+ // Show and left align the captions
+ .carousel-caption {
+ left: 20%;
+ right: 20%;
+ padding-bottom: 30px;
+ }
+
+ // Move up the indicators
+ .carousel-indicators {
+ bottom: 20px;
+ }
+}
diff --git a/client/assets/css/close.less b/client/assets/css/close.less
new file mode 100644
index 0000000..18c62f9
--- /dev/null
+++ b/client/assets/css/close.less
@@ -0,0 +1,33 @@
+//
+// Close icons
+// --------------------------------------------------
+
+
+.close {
+ float: right;
+ font-size: (@font-size-base * 1.5);
+ font-weight: @close-font-weight;
+ line-height: 1;
+ color: @close-color;
+ text-shadow: @close-text-shadow;
+ .opacity(.2);
+
+ &:hover,
+ &:focus {
+ color: @close-color;
+ text-decoration: none;
+ cursor: pointer;
+ .opacity(.5);
+ }
+
+ // Additional properties for button version
+ // iOS requires the button element instead of an anchor tag.
+ // If you want the anchor version, it requires `href="#"`.
+ button& {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
+ }
+}
diff --git a/client/assets/css/code.less b/client/assets/css/code.less
new file mode 100644
index 0000000..ec8f935
--- /dev/null
+++ b/client/assets/css/code.less
@@ -0,0 +1,63 @@
+//
+// Code (inline and block)
+// --------------------------------------------------
+
+
+// Inline and block code styles
+code,
+kbd,
+pre,
+samp {
+ font-family: @font-family-monospace;
+}
+
+// Inline code
+code {
+ padding: 2px 4px;
+ font-size: 90%;
+ color: @code-color;
+ background-color: @code-bg;
+ white-space: nowrap;
+ border-radius: @border-radius-base;
+}
+
+// User input typically entered via keyboard
+kbd {
+ padding: 2px 4px;
+ font-size: 90%;
+ color: @kbd-color;
+ background-color: @kbd-bg;
+ border-radius: @border-radius-small;
+ box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);
+}
+
+// Blocks of code
+pre {
+ display: block;
+ padding: ((@line-height-computed - 1) / 2);
+ margin: 0 0 (@line-height-computed / 2);
+ font-size: (@font-size-base - 1); // 14px to 13px
+ line-height: @line-height-base;
+ word-break: break-all;
+ word-wrap: break-word;
+ color: @pre-color;
+ background-color: @pre-bg;
+ border: 1px solid @pre-border-color;
+ border-radius: @border-radius-base;
+
+ // Account for some code outputs that place code tags in pre tags
+ code {
+ padding: 0;
+ font-size: inherit;
+ color: inherit;
+ white-space: pre-wrap;
+ background-color: transparent;
+ border-radius: 0;
+ }
+}
+
+// Enable scrollable blocks of code
+.pre-scrollable {
+ max-height: @pre-scrollable-max-height;
+ overflow-y: scroll;
+}
diff --git a/client/assets/css/component-animations.less b/client/assets/css/component-animations.less
new file mode 100644
index 0000000..724bbed
--- /dev/null
+++ b/client/assets/css/component-animations.less
@@ -0,0 +1,29 @@
+//
+// Component animations
+// --------------------------------------------------
+
+// Heads up!
+//
+// We don't use the `.opacity()` mixin here since it causes a bug with text
+// fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552.
+
+.fade {
+ opacity: 0;
+ .transition(opacity .15s linear);
+ &.in {
+ opacity: 1;
+ }
+}
+
+.collapse {
+ display: none;
+ &.in {
+ display: block;
+ }
+}
+.collapsing {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ .transition(height .35s ease);
+}
diff --git a/client/assets/css/custom-responsive.less b/client/assets/css/custom-responsive.less
new file mode 100644
index 0000000..15bb579
--- /dev/null
+++ b/client/assets/css/custom-responsive.less
@@ -0,0 +1,159 @@
+/*Media query styles*/
+@media(min-width: 1441px){
+ .carousel-caption .col-md-12{
+ left:35%;
+ }
+}
+@media (max-width: 1200px) {
+ .slider .carousel-inner .item img,
+ .slide .carousel-inner .item img {
+ min-height: 500px;
+ }
+}
+@media (max-width: 991px) {
+ .slider .carousel-caption h2,
+ .slide .carousel-caption h2 {
+ font-size: 37px;
+ }
+ .slider .carousel-caption .col-xs-12 .col-sm-2.col-xs-12
+ .slide .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 {
+ padding: 34px 0;
+ }
+ .slider .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 h3,
+ .slider .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 i,
+ .slide .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 h3,
+ .slide .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 i {
+ font-size: 12px;
+ }
+ .slider .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 a,
+ .slide .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 a {
+ font-size: 12px;
+ padding: 3px 5px;
+ margin-top: -4px;
+ }
+}
+@media(min-width:768px) {
+ .navbar-default ul.list-unstyled li.learn-page-lesson-list {
+ padding:0px;
+ }
+}
+@media (max-width: 767px) {
+ .slider .carousel-caption,
+ .slide .carousel-caption {
+ left: 0;
+ width: 100%;
+ }
+ .slider .carousel-inner .item img,
+ .slide .carousel-inner .item img {
+ min-height: 300px;
+ }
+ .slider .carousel-caption h2,
+ .slider .carousel-caption h3,
+ .slide .carousel-caption h2,
+ .slide .carousel-caption h3 {
+ font-size: 17px;
+ }
+ .slider .carousel-caption p,
+ .slider .carousel-caption h4,
+ .slide .carousel-caption p,
+ .slide .carousel-caption h4 {
+ font-size: 14px;
+ }
+ .slider .carousel-caption .col-xs-12 .col-sm-2.col-xs-12,
+ .slide .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 {
+ padding: 10px 0;
+ }
+ .slider .carousel-caption .col-xs-12 .col-sm-2.col-xs-12,
+ .slide .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 {
+ width: 30%;
+ }
+ .h1 {
+ font-size: 25px;
+ }
+ .h2 {
+ font-size: 20px;
+ }
+ #course_goals .panel.clearfix form .form-group .col-md-12.col-sm-12 .radio.radio-inline {
+ display: block;
+ margin: 0;
+ }
+ .navbar-collapse ul.navbar-nav {
+ text-align:center;
+ }
+}
+@media (max-width: 480px) {
+ .slider .carousel-caption .col-xs-12 .col-sm-2.col-xs-12,
+ .slide .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 {
+ width: 50%;
+ }
+ .slider .carousel-caption .col-xs-12 .col-sm-2.col-xs-12,
+ .slide .carousel-caption .col-xs-12 .col-sm-2.col-xs-12 {
+ padding: 0;
+ }
+ .btn {
+ margin-bottom: 10px;
+ }
+}
+@media (max-width: 320px) {
+ .teachinglist .media.row.teachingoverlay a > img {
+ height: 30px;
+ }
+ .payment-form.cc-section h3 span img {
+ max-width: 100%;
+ }
+}
+
+@media(max-width:767px) {
+ .course-listing li .mooc_thumb_img img {
+ width: 100%;
+ }
+ ul.course-listing li .panel span.mooc_thumb_img {
+ height:auto !important;
+ }
+}
+@media(min-width:768px) {
+ .dashboardPanelHeight{
+ height:310px;
+ }
+ .searchPanelHeight{
+ height:238px;
+ }
+ .homePanelHeight{
+ height:222px;
+ }
+ .userProfilePanel{
+ height:296px;
+ }
+}
+@media(min-width:768px) and (max-width:1198px) {
+ ul.course-listing li .panel span.mooc_dashboard_img {
+ height:138px !important;
+ }
+ ul.course-listing li .panel span.mooc_search_img {
+ height:130px !important;
+ }
+ ul.course-listing li .panel span.mooc_home_img {
+ height:118px !important;
+ }
+ ul.course-listing li .panel span.mooc_user_img {
+ height:140px !important;
+ }
+
+}
+
+@media(min-width:991px) and (max-width:1199px){
+ .online-lessons-list {
+ .list-group {
+ height: 320px;
+ overflow-y: auto;
+ }
+ }
+}
+@media(min-width:1200px){
+ .online-lessons-list {
+ .list-group {
+ height: 392px;
+ overflow-y: auto;
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/assets/css/custom.less b/client/assets/css/custom.less
new file mode 100644
index 0000000..e8b8874
--- /dev/null
+++ b/client/assets/css/custom.less
@@ -0,0 +1,397 @@
+/* Custom style */
+.pr {
+ position:relative;
+}
+.pa {
+ position:absolute;
+}
+.play-block {
+ right:45%;
+ bottom:30%;
+ i{
+ color:#333;
+ }
+}
+.offer-block {
+ right:0;
+ bottom:0;
+ cursor:pointer;
+ padding:3px;
+}
+#course-list {
+ .affix {
+ width:11.6%;
+ }
+}
+#course {
+ .affix {
+ width: 100%;
+ top: 0;
+ z-index: 1000;
+ background: #fff;
+ padding-top: 20px;
+ }
+}
+
+.htruncate-m2 {
+ vertical-align: bottom;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display:block;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ height: 40px;
+}
+.htruncate-m3 {
+ vertical-align: bottom;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display:block;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ height: 32px;
+}
+.htruncate {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display:block;
+ height:20px;
+ width:170px;
+}
+.htruncate-m1 {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display:block;
+ height:20px;
+ width:120px;
+}
+/* Fade in/out */
+.content .page.fade {
+ left: 0;
+ right: 0;
+ height: 100%;
+ opacity: 1;
+}
+
+.content .page.fade.ng-enter,
+.content .page.fade.ng-leave {
+ -webkit-animation: my_fade_animation 0.5s linear;
+}
+
+.content .page.fade.ng-enter {
+ transition:0.5s linear all;
+ opacity: 0;
+}
+
+.content .page.fade.ng-enter-active {
+ opacity: 1;
+}
+
+.content .page.fade.ng-leave {
+ transition:0.5s linear all;
+ opacity: 1;
+}
+
+.content .page.fade.ng-leave-active {
+ opacity: 0;
+}
+
+// PRELOADER
+#preloader {
+ position:fixed;
+ top:0;
+ left:0;
+ right:0;
+ bottom:0;
+ background-color:#fff;
+ z-index:999;
+}
+
+#status {
+ width:200px;
+ height:200px;
+ position:absolute;
+ left:50%;
+ top:50%;
+ background-image:url(../assets/img/loader.gif);
+ background-repeat:no-repeat;
+ background-position:center;
+ margin:-100px 0 0 -100px;
+}
+.payment-form {
+ input,select,textarea {
+ background: #fff;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset;
+ webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset;
+ color: #555;
+ display: block;
+ float: left;
+ font-size: 14px;
+ height: 30px;
+ line-height: 1.42;
+ margin-bottom: 15px;
+ margin-left: 12px;
+ padding: 4px 12px;
+ width: 48%;
+ &:focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
+ }
+ }
+}
+/* cc fields animation */
+.cc-section {
+ position:relative;
+}
+fieldset.identified {
+ .cc-defalt {
+ transform:scale(2);
+ -webkit-transform:scale(2);
+ -moz-transform:scale(2);
+ opacity:0;
+ }
+ .cc-type {
+ transform:scale(1);
+ -webkit-transform:scale(1);
+ -moz-transform:scale(1);
+ opacity:1;
+ }
+}
+.cc-type,.cc-defalt {
+ background:url("../assets/img/cc-sprite.png") no-repeat 97% 2px;
+ bottom:-27px;
+ height:22px;
+ pointer-events:none;
+ position:absolute;
+ left:45%;
+ width:32px;
+ transition:all 0.4s cubic-bezier(0.455,0.03,0.515,0.955) 0s;
+ -webkit-transition:all 0.4s cubic-bezier(0.455,0.03,0.515,0.955) 0s;
+ -moz-transition:all 0.4s cubic-bezier(0.455,0.03,0.515,0.955) 0s;
+ -ms-transition:all 0.4s cubic-bezier(0.455,0.03,0.515,0.955) 0s;
+}
+.visa .cc-type {
+ background-position:97% -30px;
+}
+.mastercard .cc-type {
+ background-position:97% -61px;
+}
+.amex .cc-type {
+ background-position:97% -91px;
+}
+.dinersclub .cc-type {
+ background-position:97% -121px;
+}
+.maestro .cc-type {
+ background-position:97% -152px;
+}
+.laser .cc-type {
+ background-position:97% -182px;
+}
+.unionpay .card-type {
+ background-position:97% -211px;
+}
+.discover .cc-type {
+ background-position:97% -241px;
+}
+.jcb .cc-type {
+ background-position: 97% -273px;
+}
+//textangular
+.ta-editor{
+ max-height:310px;
+}
+.growl {
+ z-index: 1050;
+}
+.vdivide [class*='teachinglist']:not(:last-child):after {
+ background: #e0e0e0;
+ width: 1px;
+ content: "";
+ display:block;
+ position: absolute;
+ top:0;
+ bottom: 0;
+ right: 0;
+ min-height: 40px;
+}
+//lesson page height
+.lesson_container{
+ .navbar-inverse{
+ min-height: 603px;
+ }
+}
+//course highlighting list
+.course-list {
+ li:hover {
+ background: #eeeeee;
+ }
+}
+//course highlighting grid
+.course-listing {
+ li {
+ &:hover{
+ .panel {
+ box-shadow: 0 2px 2px rgba(0,0,0,0.25);
+ .course_thumb_img, .mooc_thumb_img {
+ display: block;
+ background: #333;
+ img {
+ opacity:0.4 !important;
+ }
+ }
+ }
+ }
+ .course_thumb_img {
+ align-items:center;
+ display: block;
+ img {
+ max-width:100%;
+ margin: 0 auto;
+ width:100%;
+ }
+ }
+ .mooc_thumb_img{
+ text-align: center;
+ display: block;
+ img {
+ max-width:100%;
+ margin: 0px auto !important;
+ }
+ }
+ }
+}
+
+.action-btn{
+ position: relative;
+ .btn {
+ position: absolute;
+ right: 10px;
+ bottom: 10px;
+ background-color: #fff;
+ }
+ i {
+ color:@brand-primary;
+ }
+}
+.max-img {
+ img {
+ max-width:100%;
+ }
+}
+#search_results div.text-center h2.text-success,
+#search_results div.text-center h2.text-warning,
+#search_results div.text-center h2.text-danger,
+#search_results div.text-center h2.text-info,
+.search-results-sidebar a,
+.subscription_plans, .lesson_details {
+ word-wrap: break-word;
+
+}
+
+//to disable caurosel indicators and arrows
+.right.carousel-control, .left.carousel-control, .carousel-indicators {
+ display: none;
+}
+
+ma-filter-button .dropdown-menu>li>a {
+ padding: 0px 7px;
+}
+
+.navbar-default .form-horizontal input[type=radio], input[type=checkbox] {
+ margin-top:1px \9 !important;
+}
+.form-horizontal .control-label{
+ padding-top: 10px;
+}
+.page .panel{
+ box-shadow: 0 1px 20px rgba(0, 0, 0, 0.1) !important;
+}
+ul {
+ &.course-listing {
+ li {
+ .panel {
+ span {
+ &.mooc_dashboard_img {
+ display:flex !important;
+ align-items:center;
+ height:168px;
+ overflow: hidden;
+ img{
+ margin: 0px auto !important;
+ }
+ }
+ &.mooc_search_img{
+ display:flex !important;
+ align-items:center;
+ height:165px;
+ overflow: hidden;
+ img{
+ margin: 0px auto !important;
+ }
+ }
+ &.mooc_home_img{
+ display:flex !important;
+ align-items:center;
+ height:140px;
+ overflow: hidden;
+ img{
+ margin: 0px auto !important;
+ }
+
+ }
+ &.mooc_user_img{
+ display:flex !important;
+ align-items:center;
+ height:174px;
+ overflow: hidden;
+ img{
+ margin: 0px auto !important;
+ }
+
+ }
+
+ }
+ }
+ }
+ }
+ }
+#curriculum .add-lession-cancel {
+ margin-top:15px;
+}
+.videoWrapper {
+ position: relative;
+ padding-bottom: 48%;
+ padding-top: 25px;
+ height: 0;
+}
+.videoWrapper iframe {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+video{
+ width:100%;
+ height:auto;
+}
+li.dropdown-img .user-img {
+ padding:9px;
+}
+.curriculum .btn-default{
+ background-color:#f5f5f5;
+}
+.categories_lst li {
+ display: inline;
+ word-wrap: break-word;
+}
+.ml-4 {
+ left: 4%;
+}
\ No newline at end of file
diff --git a/client/assets/css/dropdowns.less b/client/assets/css/dropdowns.less
new file mode 100644
index 0000000..db853cb
--- /dev/null
+++ b/client/assets/css/dropdowns.less
@@ -0,0 +1,213 @@
+//
+// Dropdown menus
+// --------------------------------------------------
+
+
+// Dropdown arrow/caret
+.caret {
+ display: inline-block;
+ width: 0;
+ height: 0;
+ margin-left: 2px;
+ vertical-align: middle;
+ border-top: @caret-width-base solid;
+ border-right: @caret-width-base solid transparent;
+ border-left: @caret-width-base solid transparent;
+}
+
+// The dropdown wrapper (div)
+.dropdown {
+ position: relative;
+}
+
+// Prevent the focus on the dropdown toggle when closing dropdowns
+.dropdown-toggle:focus {
+ outline: 0;
+}
+
+// The dropdown menu (ul)
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: @zindex-dropdown;
+ display: none; // none by default, but block on "open" of the menu
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0; // override default ul
+ list-style: none;
+ font-size: @font-size-base;
+ background-color: @dropdown-bg;
+ border: 1px solid @dropdown-fallback-border; // IE8 fallback
+ border: 1px solid @dropdown-border;
+ border-radius: @border-radius-base;
+ .box-shadow(0 6px 12px rgba(0,0,0,.175));
+ background-clip: padding-box;
+
+ // Aligns the dropdown menu to right
+ //
+ // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`
+ &.pull-right {
+ right: 0;
+ left: auto;
+ }
+
+ // Dividers (basically an hr) within the dropdown
+ .divider {
+ .nav-divider(@dropdown-divider-bg);
+ }
+
+ // Links within the dropdown menu
+ > li > a {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: @line-height-base;
+ color: @dropdown-link-color;
+ white-space: nowrap; // prevent links from randomly breaking onto new lines
+ }
+}
+
+// Hover/Focus state
+.dropdown-menu > li > a {
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ color: @dropdown-link-hover-color;
+ background-color: @dropdown-link-hover-bg;
+ }
+}
+
+// Active state
+.dropdown-menu > .active > a {
+ &,
+ &:hover,
+ &:focus {
+ color: @dropdown-link-active-color;
+ text-decoration: none;
+ outline: 0;
+ background-color: @dropdown-link-active-bg;
+ }
+}
+
+// Disabled state
+//
+// Gray out text and ensure the hover/focus state remains gray
+
+.dropdown-menu > .disabled > a {
+ &,
+ &:hover,
+ &:focus {
+ color: @dropdown-link-disabled-color;
+ }
+}
+// Nuke hover/focus effects
+.dropdown-menu > .disabled > a {
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ background-color: transparent;
+ background-image: none; // Remove CSS gradient
+ .reset-filter();
+ cursor: not-allowed;
+ }
+}
+
+// Open state for the dropdown
+.open {
+ // Show the menu
+ > .dropdown-menu {
+ display: block;
+ }
+
+ // Remove the outline when :focus is triggered
+ > a {
+ outline: 0;
+ }
+}
+
+// Menu positioning
+//
+// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown
+// menu with the parent.
+.dropdown-menu-right {
+ left: auto; // Reset the default from `.dropdown-menu`
+ right: 0;
+}
+// With v3, we enabled auto-flipping if you have a dropdown within a right
+// aligned nav component. To enable the undoing of that, we provide an override
+// to restore the default dropdown menu alignment.
+//
+// This is only for left-aligning a dropdown menu within a `.navbar-right` or
+// `.pull-right` nav component.
+.dropdown-menu-left {
+ left: 0;
+ right: auto;
+}
+
+// Dropdown section headers
+.dropdown-header {
+ display: block;
+ padding: 3px 20px;
+ font-size: @font-size-small;
+ line-height: @line-height-base;
+ color: @dropdown-header-color;
+}
+
+// Backdrop to catch body clicks on mobile, etc.
+.dropdown-backdrop {
+ position: fixed;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ top: 0;
+ z-index: (@zindex-dropdown - 10);
+}
+
+// Right aligned dropdowns
+.pull-right > .dropdown-menu {
+ right: 0;
+ left: auto;
+}
+
+// Allow for dropdowns to go bottom up (aka, dropup-menu)
+//
+// Just add .dropup after the standard .dropdown class and you're set, bro.
+// TODO: abstract this so that the navbar fixed styles are not placed here?
+
+.dropup,
+.navbar-fixed-bottom .dropdown {
+ // Reverse the caret
+ .caret {
+ border-top: 0;
+ border-bottom: @caret-width-base solid;
+ content: "";
+ }
+ // Different positioning for bottom up menu
+ .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-bottom: 1px;
+ }
+}
+
+
+// Component alignment
+//
+// Reiterate per navbar.less and the modified component alignment there.
+
+@media (min-width: @grid-float-breakpoint) {
+ .navbar-right {
+ .dropdown-menu {
+ .dropdown-menu-right();
+ }
+ // Necessary for overrides of the default right aligned menu.
+ // Will remove come v4 in all likelihood.
+ .dropdown-menu-left {
+ .dropdown-menu-left();
+ }
+ }
+}
+
diff --git a/client/assets/css/font-awesome.less b/client/assets/css/font-awesome.less
new file mode 100644
index 0000000..47f7ee2
--- /dev/null
+++ b/client/assets/css/font-awesome.less
@@ -0,0 +1,1415 @@
+/*!
+ * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */
+
+
+// Variables
+// --------------------------
+
+@fa-font-path: "../assets/fonts";
+@fa-font-size-base: 14px;
+//@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.3.0/fonts"; // for referencing Bootstrap CDN font files directly
+@fa-css-prefix: fa;
+@fa-version: "4.3.0";
+@fa-border-color: #eee;
+@fa-inverse: #fff;
+@fa-li-width: (30em / 14);
+
+@fa-var-adjust: "\f042";
+@fa-var-adn: "\f170";
+@fa-var-align-center: "\f037";
+@fa-var-align-justify: "\f039";
+@fa-var-align-left: "\f036";
+@fa-var-align-right: "\f038";
+@fa-var-ambulance: "\f0f9";
+@fa-var-anchor: "\f13d";
+@fa-var-android: "\f17b";
+@fa-var-angellist: "\f209";
+@fa-var-angle-double-down: "\f103";
+@fa-var-angle-double-left: "\f100";
+@fa-var-angle-double-right: "\f101";
+@fa-var-angle-double-up: "\f102";
+@fa-var-angle-down: "\f107";
+@fa-var-angle-left: "\f104";
+@fa-var-angle-right: "\f105";
+@fa-var-angle-up: "\f106";
+@fa-var-apple: "\f179";
+@fa-var-archive: "\f187";
+@fa-var-area-chart: "\f1fe";
+@fa-var-arrow-circle-down: "\f0ab";
+@fa-var-arrow-circle-left: "\f0a8";
+@fa-var-arrow-circle-o-down: "\f01a";
+@fa-var-arrow-circle-o-left: "\f190";
+@fa-var-arrow-circle-o-right: "\f18e";
+@fa-var-arrow-circle-o-up: "\f01b";
+@fa-var-arrow-circle-right: "\f0a9";
+@fa-var-arrow-circle-up: "\f0aa";
+@fa-var-arrow-down: "\f063";
+@fa-var-arrow-left: "\f060";
+@fa-var-arrow-right: "\f061";
+@fa-var-arrow-up: "\f062";
+@fa-var-arrows: "\f047";
+@fa-var-arrows-alt: "\f0b2";
+@fa-var-arrows-h: "\f07e";
+@fa-var-arrows-v: "\f07d";
+@fa-var-asterisk: "\f069";
+@fa-var-at: "\f1fa";
+@fa-var-automobile: "\f1b9";
+@fa-var-backward: "\f04a";
+@fa-var-ban: "\f05e";
+@fa-var-bank: "\f19c";
+@fa-var-bar-chart: "\f080";
+@fa-var-bar-chart-o: "\f080";
+@fa-var-barcode: "\f02a";
+@fa-var-bars: "\f0c9";
+@fa-var-bed: "\f236";
+@fa-var-beer: "\f0fc";
+@fa-var-behance: "\f1b4";
+@fa-var-behance-square: "\f1b5";
+@fa-var-bell: "\f0f3";
+@fa-var-bell-o: "\f0a2";
+@fa-var-bell-slash: "\f1f6";
+@fa-var-bell-slash-o: "\f1f7";
+@fa-var-bicycle: "\f206";
+@fa-var-binoculars: "\f1e5";
+@fa-var-birthday-cake: "\f1fd";
+@fa-var-bitbucket: "\f171";
+@fa-var-bitbucket-square: "\f172";
+@fa-var-bitcoin: "\f15a";
+@fa-var-bold: "\f032";
+@fa-var-bolt: "\f0e7";
+@fa-var-bomb: "\f1e2";
+@fa-var-book: "\f02d";
+@fa-var-bookmark: "\f02e";
+@fa-var-bookmark-o: "\f097";
+@fa-var-briefcase: "\f0b1";
+@fa-var-btc: "\f15a";
+@fa-var-bug: "\f188";
+@fa-var-building: "\f1ad";
+@fa-var-building-o: "\f0f7";
+@fa-var-bullhorn: "\f0a1";
+@fa-var-bullseye: "\f140";
+@fa-var-bus: "\f207";
+@fa-var-buysellads: "\f20d";
+@fa-var-cab: "\f1ba";
+@fa-var-calculator: "\f1ec";
+@fa-var-calendar: "\f073";
+@fa-var-calendar-o: "\f133";
+@fa-var-camera: "\f030";
+@fa-var-camera-retro: "\f083";
+@fa-var-car: "\f1b9";
+@fa-var-caret-down: "\f0d7";
+@fa-var-caret-left: "\f0d9";
+@fa-var-caret-right: "\f0da";
+@fa-var-caret-square-o-down: "\f150";
+@fa-var-caret-square-o-left: "\f191";
+@fa-var-caret-square-o-right: "\f152";
+@fa-var-caret-square-o-up: "\f151";
+@fa-var-caret-up: "\f0d8";
+@fa-var-cart-arrow-down: "\f218";
+@fa-var-cart-plus: "\f217";
+@fa-var-cc: "\f20a";
+@fa-var-cc-amex: "\f1f3";
+@fa-var-cc-discover: "\f1f2";
+@fa-var-cc-mastercard: "\f1f1";
+@fa-var-cc-paypal: "\f1f4";
+@fa-var-cc-stripe: "\f1f5";
+@fa-var-cc-visa: "\f1f0";
+@fa-var-certificate: "\f0a3";
+@fa-var-chain: "\f0c1";
+@fa-var-chain-broken: "\f127";
+@fa-var-check: "\f00c";
+@fa-var-check-circle: "\f058";
+@fa-var-check-circle-o: "\f05d";
+@fa-var-check-square: "\f14a";
+@fa-var-check-square-o: "\f046";
+@fa-var-chevron-circle-down: "\f13a";
+@fa-var-chevron-circle-left: "\f137";
+@fa-var-chevron-circle-right: "\f138";
+@fa-var-chevron-circle-up: "\f139";
+@fa-var-chevron-down: "\f078";
+@fa-var-chevron-left: "\f053";
+@fa-var-chevron-right: "\f054";
+@fa-var-chevron-up: "\f077";
+@fa-var-child: "\f1ae";
+@fa-var-circle: "\f111";
+@fa-var-circle-o: "\f10c";
+@fa-var-circle-o-notch: "\f1ce";
+@fa-var-circle-thin: "\f1db";
+@fa-var-clipboard: "\f0ea";
+@fa-var-clock-o: "\f017";
+@fa-var-close: "\f00d";
+@fa-var-cloud: "\f0c2";
+@fa-var-cloud-download: "\f0ed";
+@fa-var-cloud-upload: "\f0ee";
+@fa-var-cny: "\f157";
+@fa-var-code: "\f121";
+@fa-var-code-fork: "\f126";
+@fa-var-codepen: "\f1cb";
+@fa-var-coffee: "\f0f4";
+@fa-var-cog: "\f013";
+@fa-var-cogs: "\f085";
+@fa-var-columns: "\f0db";
+@fa-var-comment: "\f075";
+@fa-var-comment-o: "\f0e5";
+@fa-var-comments: "\f086";
+@fa-var-comments-o: "\f0e6";
+@fa-var-compass: "\f14e";
+@fa-var-compress: "\f066";
+@fa-var-connectdevelop: "\f20e";
+@fa-var-copy: "\f0c5";
+@fa-var-copyright: "\f1f9";
+@fa-var-credit-card: "\f09d";
+@fa-var-crop: "\f125";
+@fa-var-crosshairs: "\f05b";
+@fa-var-css3: "\f13c";
+@fa-var-cube: "\f1b2";
+@fa-var-cubes: "\f1b3";
+@fa-var-cut: "\f0c4";
+@fa-var-cutlery: "\f0f5";
+@fa-var-dashboard: "\f0e4";
+@fa-var-dashcube: "\f210";
+@fa-var-database: "\f1c0";
+@fa-var-dedent: "\f03b";
+@fa-var-delicious: "\f1a5";
+@fa-var-desktop: "\f108";
+@fa-var-deviantart: "\f1bd";
+@fa-var-diamond: "\f219";
+@fa-var-digg: "\f1a6";
+@fa-var-dollar: "\f155";
+@fa-var-dot-circle-o: "\f192";
+@fa-var-download: "\f019";
+@fa-var-dribbble: "\f17d";
+@fa-var-dropbox: "\f16b";
+@fa-var-drupal: "\f1a9";
+@fa-var-edit: "\f044";
+@fa-var-eject: "\f052";
+@fa-var-ellipsis-h: "\f141";
+@fa-var-ellipsis-v: "\f142";
+@fa-var-empire: "\f1d1";
+@fa-var-envelope: "\f0e0";
+@fa-var-envelope-o: "\f003";
+@fa-var-envelope-square: "\f199";
+@fa-var-eraser: "\f12d";
+@fa-var-eur: "\f153";
+@fa-var-euro: "\f153";
+@fa-var-exchange: "\f0ec";
+@fa-var-exclamation: "\f12a";
+@fa-var-exclamation-circle: "\f06a";
+@fa-var-exclamation-triangle: "\f071";
+@fa-var-expand: "\f065";
+@fa-var-external-link: "\f08e";
+@fa-var-external-link-square: "\f14c";
+@fa-var-eye: "\f06e";
+@fa-var-eye-slash: "\f070";
+@fa-var-eyedropper: "\f1fb";
+@fa-var-facebook: "\f09a";
+@fa-var-facebook-f: "\f09a";
+@fa-var-facebook-official: "\f230";
+@fa-var-facebook-square: "\f082";
+@fa-var-fast-backward: "\f049";
+@fa-var-fast-forward: "\f050";
+@fa-var-fax: "\f1ac";
+@fa-var-female: "\f182";
+@fa-var-fighter-jet: "\f0fb";
+@fa-var-file: "\f15b";
+@fa-var-file-archive-o: "\f1c6";
+@fa-var-file-audio-o: "\f1c7";
+@fa-var-file-code-o: "\f1c9";
+@fa-var-file-excel-o: "\f1c3";
+@fa-var-file-image-o: "\f1c5";
+@fa-var-file-movie-o: "\f1c8";
+@fa-var-file-o: "\f016";
+@fa-var-file-pdf-o: "\f1c1";
+@fa-var-file-photo-o: "\f1c5";
+@fa-var-file-picture-o: "\f1c5";
+@fa-var-file-powerpoint-o: "\f1c4";
+@fa-var-file-sound-o: "\f1c7";
+@fa-var-file-text: "\f15c";
+@fa-var-file-text-o: "\f0f6";
+@fa-var-file-video-o: "\f1c8";
+@fa-var-file-word-o: "\f1c2";
+@fa-var-file-zip-o: "\f1c6";
+@fa-var-files-o: "\f0c5";
+@fa-var-film: "\f008";
+@fa-var-filter: "\f0b0";
+@fa-var-fire: "\f06d";
+@fa-var-fire-extinguisher: "\f134";
+@fa-var-flag: "\f024";
+@fa-var-flag-checkered: "\f11e";
+@fa-var-flag-o: "\f11d";
+@fa-var-flash: "\f0e7";
+@fa-var-flask: "\f0c3";
+@fa-var-flickr: "\f16e";
+@fa-var-floppy-o: "\f0c7";
+@fa-var-folder: "\f07b";
+@fa-var-folder-o: "\f114";
+@fa-var-folder-open: "\f07c";
+@fa-var-folder-open-o: "\f115";
+@fa-var-font: "\f031";
+@fa-var-forumbee: "\f211";
+@fa-var-forward: "\f04e";
+@fa-var-foursquare: "\f180";
+@fa-var-frown-o: "\f119";
+@fa-var-futbol-o: "\f1e3";
+@fa-var-gamepad: "\f11b";
+@fa-var-gavel: "\f0e3";
+@fa-var-gbp: "\f154";
+@fa-var-ge: "\f1d1";
+@fa-var-gear: "\f013";
+@fa-var-gears: "\f085";
+@fa-var-genderless: "\f1db";
+@fa-var-gift: "\f06b";
+@fa-var-git: "\f1d3";
+@fa-var-git-square: "\f1d2";
+@fa-var-github: "\f09b";
+@fa-var-github-alt: "\f113";
+@fa-var-github-square: "\f092";
+@fa-var-gittip: "\f184";
+@fa-var-glass: "\f000";
+@fa-var-globe: "\f0ac";
+@fa-var-google: "\f1a0";
+@fa-var-google-plus: "\f0d5";
+@fa-var-google-plus-square: "\f0d4";
+@fa-var-google-wallet: "\f1ee";
+@fa-var-graduation-cap: "\f19d";
+@fa-var-gratipay: "\f184";
+@fa-var-group: "\f0c0";
+@fa-var-h-square: "\f0fd";
+@fa-var-hacker-news: "\f1d4";
+@fa-var-hand-o-down: "\f0a7";
+@fa-var-hand-o-left: "\f0a5";
+@fa-var-hand-o-right: "\f0a4";
+@fa-var-hand-o-up: "\f0a6";
+@fa-var-hdd-o: "\f0a0";
+@fa-var-header: "\f1dc";
+@fa-var-headphones: "\f025";
+@fa-var-heart: "\f004";
+@fa-var-heart-o: "\f08a";
+@fa-var-heartbeat: "\f21e";
+@fa-var-history: "\f1da";
+@fa-var-home: "\f015";
+@fa-var-hospital-o: "\f0f8";
+@fa-var-hotel: "\f236";
+@fa-var-html5: "\f13b";
+@fa-var-ils: "\f20b";
+@fa-var-image: "\f03e";
+@fa-var-inbox: "\f01c";
+@fa-var-indent: "\f03c";
+@fa-var-info: "\f129";
+@fa-var-info-circle: "\f05a";
+@fa-var-inr: "\f156";
+@fa-var-instagram: "\f16d";
+@fa-var-institution: "\f19c";
+@fa-var-ioxhost: "\f208";
+@fa-var-italic: "\f033";
+@fa-var-joomla: "\f1aa";
+@fa-var-jpy: "\f157";
+@fa-var-jsfiddle: "\f1cc";
+@fa-var-key: "\f084";
+@fa-var-keyboard-o: "\f11c";
+@fa-var-krw: "\f159";
+@fa-var-language: "\f1ab";
+@fa-var-laptop: "\f109";
+@fa-var-lastfm: "\f202";
+@fa-var-lastfm-square: "\f203";
+@fa-var-leaf: "\f06c";
+@fa-var-leanpub: "\f212";
+@fa-var-legal: "\f0e3";
+@fa-var-lemon-o: "\f094";
+@fa-var-level-down: "\f149";
+@fa-var-level-up: "\f148";
+@fa-var-life-bouy: "\f1cd";
+@fa-var-life-buoy: "\f1cd";
+@fa-var-life-ring: "\f1cd";
+@fa-var-life-saver: "\f1cd";
+@fa-var-lightbulb-o: "\f0eb";
+@fa-var-line-chart: "\f201";
+@fa-var-link: "\f0c1";
+@fa-var-linkedin: "\f0e1";
+@fa-var-linkedin-square: "\f08c";
+@fa-var-linux: "\f17c";
+@fa-var-list: "\f03a";
+@fa-var-list-alt: "\f022";
+@fa-var-list-ol: "\f0cb";
+@fa-var-list-ul: "\f0ca";
+@fa-var-location-arrow: "\f124";
+@fa-var-lock: "\f023";
+@fa-var-long-arrow-down: "\f175";
+@fa-var-long-arrow-left: "\f177";
+@fa-var-long-arrow-right: "\f178";
+@fa-var-long-arrow-up: "\f176";
+@fa-var-magic: "\f0d0";
+@fa-var-magnet: "\f076";
+@fa-var-mail-forward: "\f064";
+@fa-var-mail-reply: "\f112";
+@fa-var-mail-reply-all: "\f122";
+@fa-var-male: "\f183";
+@fa-var-map-marker: "\f041";
+@fa-var-mars: "\f222";
+@fa-var-mars-double: "\f227";
+@fa-var-mars-stroke: "\f229";
+@fa-var-mars-stroke-h: "\f22b";
+@fa-var-mars-stroke-v: "\f22a";
+@fa-var-maxcdn: "\f136";
+@fa-var-meanpath: "\f20c";
+@fa-var-medium: "\f23a";
+@fa-var-medkit: "\f0fa";
+@fa-var-meh-o: "\f11a";
+@fa-var-mercury: "\f223";
+@fa-var-microphone: "\f130";
+@fa-var-microphone-slash: "\f131";
+@fa-var-minus: "\f068";
+@fa-var-minus-circle: "\f056";
+@fa-var-minus-square: "\f146";
+@fa-var-minus-square-o: "\f147";
+@fa-var-mobile: "\f10b";
+@fa-var-mobile-phone: "\f10b";
+@fa-var-money: "\f0d6";
+@fa-var-moon-o: "\f186";
+@fa-var-mortar-board: "\f19d";
+@fa-var-motorcycle: "\f21c";
+@fa-var-music: "\f001";
+@fa-var-navicon: "\f0c9";
+@fa-var-neuter: "\f22c";
+@fa-var-newspaper-o: "\f1ea";
+@fa-var-openid: "\f19b";
+@fa-var-outdent: "\f03b";
+@fa-var-pagelines: "\f18c";
+@fa-var-paint-brush: "\f1fc";
+@fa-var-paper-plane: "\f1d8";
+@fa-var-paper-plane-o: "\f1d9";
+@fa-var-paperclip: "\f0c6";
+@fa-var-paragraph: "\f1dd";
+@fa-var-paste: "\f0ea";
+@fa-var-pause: "\f04c";
+@fa-var-paw: "\f1b0";
+@fa-var-paypal: "\f1ed";
+@fa-var-pencil: "\f040";
+@fa-var-pencil-square: "\f14b";
+@fa-var-pencil-square-o: "\f044";
+@fa-var-phone: "\f095";
+@fa-var-phone-square: "\f098";
+@fa-var-photo: "\f03e";
+@fa-var-picture-o: "\f03e";
+@fa-var-pie-chart: "\f200";
+@fa-var-pied-piper: "\f1a7";
+@fa-var-pied-piper-alt: "\f1a8";
+@fa-var-pinterest: "\f0d2";
+@fa-var-pinterest-p: "\f231";
+@fa-var-pinterest-square: "\f0d3";
+@fa-var-plane: "\f072";
+@fa-var-play: "\f04b";
+@fa-var-play-circle: "\f144";
+@fa-var-play-circle-o: "\f01d";
+@fa-var-plug: "\f1e6";
+@fa-var-plus: "\f067";
+@fa-var-plus-circle: "\f055";
+@fa-var-plus-square: "\f0fe";
+@fa-var-plus-square-o: "\f196";
+@fa-var-power-off: "\f011";
+@fa-var-print: "\f02f";
+@fa-var-puzzle-piece: "\f12e";
+@fa-var-qq: "\f1d6";
+@fa-var-qrcode: "\f029";
+@fa-var-question: "\f128";
+@fa-var-question-circle: "\f059";
+@fa-var-quote-left: "\f10d";
+@fa-var-quote-right: "\f10e";
+@fa-var-ra: "\f1d0";
+@fa-var-random: "\f074";
+@fa-var-rebel: "\f1d0";
+@fa-var-recycle: "\f1b8";
+@fa-var-reddit: "\f1a1";
+@fa-var-reddit-square: "\f1a2";
+@fa-var-refresh: "\f021";
+@fa-var-remove: "\f00d";
+@fa-var-renren: "\f18b";
+@fa-var-reorder: "\f0c9";
+@fa-var-repeat: "\f01e";
+@fa-var-reply: "\f112";
+@fa-var-reply-all: "\f122";
+@fa-var-retweet: "\f079";
+@fa-var-rmb: "\f157";
+@fa-var-road: "\f018";
+@fa-var-rocket: "\f135";
+@fa-var-rotate-left: "\f0e2";
+@fa-var-rotate-right: "\f01e";
+@fa-var-rouble: "\f158";
+@fa-var-rss: "\f09e";
+@fa-var-rss-square: "\f143";
+@fa-var-rub: "\f158";
+@fa-var-ruble: "\f158";
+@fa-var-rupee: "\f156";
+@fa-var-save: "\f0c7";
+@fa-var-scissors: "\f0c4";
+@fa-var-search: "\f002";
+@fa-var-search-minus: "\f010";
+@fa-var-search-plus: "\f00e";
+@fa-var-sellsy: "\f213";
+@fa-var-send: "\f1d8";
+@fa-var-send-o: "\f1d9";
+@fa-var-server: "\f233";
+@fa-var-share: "\f064";
+@fa-var-share-alt: "\f1e0";
+@fa-var-share-alt-square: "\f1e1";
+@fa-var-share-square: "\f14d";
+@fa-var-share-square-o: "\f045";
+@fa-var-shekel: "\f20b";
+@fa-var-sheqel: "\f20b";
+@fa-var-shield: "\f132";
+@fa-var-ship: "\f21a";
+@fa-var-shirtsinbulk: "\f214";
+@fa-var-shopping-cart: "\f07a";
+@fa-var-sign-in: "\f090";
+@fa-var-sign-out: "\f08b";
+@fa-var-signal: "\f012";
+@fa-var-simplybuilt: "\f215";
+@fa-var-sitemap: "\f0e8";
+@fa-var-skyatlas: "\f216";
+@fa-var-skype: "\f17e";
+@fa-var-slack: "\f198";
+@fa-var-sliders: "\f1de";
+@fa-var-slideshare: "\f1e7";
+@fa-var-smile-o: "\f118";
+@fa-var-soccer-ball-o: "\f1e3";
+@fa-var-sort: "\f0dc";
+@fa-var-sort-alpha-asc: "\f15d";
+@fa-var-sort-alpha-desc: "\f15e";
+@fa-var-sort-amount-asc: "\f160";
+@fa-var-sort-amount-desc: "\f161";
+@fa-var-sort-asc: "\f0de";
+@fa-var-sort-desc: "\f0dd";
+@fa-var-sort-down: "\f0dd";
+@fa-var-sort-numeric-asc: "\f162";
+@fa-var-sort-numeric-desc: "\f163";
+@fa-var-sort-up: "\f0de";
+@fa-var-soundcloud: "\f1be";
+@fa-var-space-shuttle: "\f197";
+@fa-var-spinner: "\f110";
+@fa-var-spoon: "\f1b1";
+@fa-var-spotify: "\f1bc";
+@fa-var-square: "\f0c8";
+@fa-var-square-o: "\f096";
+@fa-var-stack-exchange: "\f18d";
+@fa-var-stack-overflow: "\f16c";
+@fa-var-star: "\f005";
+@fa-var-star-half: "\f089";
+@fa-var-star-half-empty: "\f123";
+@fa-var-star-half-full: "\f123";
+@fa-var-star-half-o: "\f123";
+@fa-var-star-o: "\f006";
+@fa-var-steam: "\f1b6";
+@fa-var-steam-square: "\f1b7";
+@fa-var-step-backward: "\f048";
+@fa-var-step-forward: "\f051";
+@fa-var-stethoscope: "\f0f1";
+@fa-var-stop: "\f04d";
+@fa-var-street-view: "\f21d";
+@fa-var-strikethrough: "\f0cc";
+@fa-var-stumbleupon: "\f1a4";
+@fa-var-stumbleupon-circle: "\f1a3";
+@fa-var-subscript: "\f12c";
+@fa-var-subway: "\f239";
+@fa-var-suitcase: "\f0f2";
+@fa-var-sun-o: "\f185";
+@fa-var-superscript: "\f12b";
+@fa-var-support: "\f1cd";
+@fa-var-table: "\f0ce";
+@fa-var-tablet: "\f10a";
+@fa-var-tachometer: "\f0e4";
+@fa-var-tag: "\f02b";
+@fa-var-tags: "\f02c";
+@fa-var-tasks: "\f0ae";
+@fa-var-taxi: "\f1ba";
+@fa-var-tencent-weibo: "\f1d5";
+@fa-var-terminal: "\f120";
+@fa-var-text-height: "\f034";
+@fa-var-text-width: "\f035";
+@fa-var-th: "\f00a";
+@fa-var-th-large: "\f009";
+@fa-var-th-list: "\f00b";
+@fa-var-thumb-tack: "\f08d";
+@fa-var-thumbs-down: "\f165";
+@fa-var-thumbs-o-down: "\f088";
+@fa-var-thumbs-o-up: "\f087";
+@fa-var-thumbs-up: "\f164";
+@fa-var-ticket: "\f145";
+@fa-var-times: "\f00d";
+@fa-var-times-circle: "\f057";
+@fa-var-times-circle-o: "\f05c";
+@fa-var-tint: "\f043";
+@fa-var-toggle-down: "\f150";
+@fa-var-toggle-left: "\f191";
+@fa-var-toggle-off: "\f204";
+@fa-var-toggle-on: "\f205";
+@fa-var-toggle-right: "\f152";
+@fa-var-toggle-up: "\f151";
+@fa-var-train: "\f238";
+@fa-var-transgender: "\f224";
+@fa-var-transgender-alt: "\f225";
+@fa-var-trash: "\f1f8";
+@fa-var-trash-o: "\f014";
+@fa-var-tree: "\f1bb";
+@fa-var-trello: "\f181";
+@fa-var-trophy: "\f091";
+@fa-var-truck: "\f0d1";
+@fa-var-try: "\f195";
+@fa-var-tty: "\f1e4";
+@fa-var-tumblr: "\f173";
+@fa-var-tumblr-square: "\f174";
+@fa-var-turkish-lira: "\f195";
+@fa-var-twitch: "\f1e8";
+@fa-var-twitter: "\f099";
+@fa-var-twitter-square: "\f081";
+@fa-var-umbrella: "\f0e9";
+@fa-var-underline: "\f0cd";
+@fa-var-undo: "\f0e2";
+@fa-var-university: "\f19c";
+@fa-var-unlink: "\f127";
+@fa-var-unlock: "\f09c";
+@fa-var-unlock-alt: "\f13e";
+@fa-var-unsorted: "\f0dc";
+@fa-var-upload: "\f093";
+@fa-var-usd: "\f155";
+@fa-var-user: "\f007";
+@fa-var-user-md: "\f0f0";
+@fa-var-user-plus: "\f234";
+@fa-var-user-secret: "\f21b";
+@fa-var-user-times: "\f235";
+@fa-var-users: "\f0c0";
+@fa-var-venus: "\f221";
+@fa-var-venus-double: "\f226";
+@fa-var-venus-mars: "\f228";
+@fa-var-viacoin: "\f237";
+@fa-var-video-camera: "\f03d";
+@fa-var-vimeo-square: "\f194";
+@fa-var-vine: "\f1ca";
+@fa-var-vk: "\f189";
+@fa-var-volume-down: "\f027";
+@fa-var-volume-off: "\f026";
+@fa-var-volume-up: "\f028";
+@fa-var-warning: "\f071";
+@fa-var-wechat: "\f1d7";
+@fa-var-weibo: "\f18a";
+@fa-var-weixin: "\f1d7";
+@fa-var-whatsapp: "\f232";
+@fa-var-wheelchair: "\f193";
+@fa-var-wifi: "\f1eb";
+@fa-var-windows: "\f17a";
+@fa-var-won: "\f159";
+@fa-var-wordpress: "\f19a";
+@fa-var-wrench: "\f0ad";
+@fa-var-xing: "\f168";
+@fa-var-xing-square: "\f169";
+@fa-var-yahoo: "\f19e";
+@fa-var-yelp: "\f1e9";
+@fa-var-yen: "\f157";
+@fa-var-youtube: "\f167";
+@fa-var-youtube-play: "\f16a";
+@fa-var-youtube-square: "\f166";
+
+
+
+// Mixins
+// --------------------------
+
+.fa-icon() {
+ display: inline-block;
+ font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration
+ font-size: inherit; // can't have font-size inherit on line above, so need to override
+ text-rendering: auto; // optimizelegibility throws things off #1094
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ transform: translate(0, 0); // ensures no half-pixel rendering in firefox
+
+}
+
+.fa-icon-rotate(@degrees, @rotation) {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
+ -webkit-transform: rotate(@degrees);
+ -ms-transform: rotate(@degrees);
+ transform: rotate(@degrees);
+}
+
+.fa-icon-flip(@horiz, @vert, @rotation) {
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1);
+ -webkit-transform: scale(@horiz, @vert);
+ -ms-transform: scale(@horiz, @vert);
+ transform: scale(@horiz, @vert);
+}
+
+
+/* FONT PATH
+ * -------------------------- */
+
+@font-face {
+ font-family: 'FontAwesome';
+ src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}');
+ src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'),
+ url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'),
+ url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'),
+ url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'),
+ url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg');
+// src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
+ font-weight: normal;
+ font-style: normal;
+}
+
+
+// Base Class Definition
+// -------------------------
+
+.@{fa-css-prefix} {
+ display: inline-block;
+ font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration
+ font-size: inherit; // can't have font-size inherit on line above, so need to override
+ text-rendering: auto; // optimizelegibility throws things off #1094
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ transform: translate(0, 0); // ensures no half-pixel rendering in firefox
+
+}
+
+
+// Icon Sizes
+// -------------------------
+
+/* makes the font 33% larger relative to the icon container */
+.@{fa-css-prefix}-lg {
+ font-size: (4em / 3);
+ line-height: (3em / 4);
+ vertical-align: -15%;
+}
+.@{fa-css-prefix}-2x { font-size: 2em; }
+.@{fa-css-prefix}-3x { font-size: 3em; }
+.@{fa-css-prefix}-4x { font-size: 4em; }
+.@{fa-css-prefix}-5x { font-size: 5em; }
+
+
+
+// Fixed Width Icons
+// -------------------------
+.@{fa-css-prefix}-fw {
+ width: (18em / 14);
+ text-align: center;
+}
+
+
+// List Icons
+// -------------------------
+
+.@{fa-css-prefix}-ul {
+ padding-left: 0;
+ margin-left: @fa-li-width;
+ list-style-type: none;
+ > li { position: relative; }
+}
+.@{fa-css-prefix}-li {
+ position: absolute;
+ left: -@fa-li-width;
+ width: @fa-li-width;
+ top: (2em / 14);
+ text-align: center;
+ &.@{fa-css-prefix}-lg {
+ left: (-@fa-li-width + (4em / 14));
+ }
+}
+
+
+
+// Bordered & Pulled
+// -------------------------
+
+.@{fa-css-prefix}-border {
+ padding: .2em .25em .15em;
+ border: solid .08em @fa-border-color;
+ border-radius: .1em;
+}
+
+.pull-right { float: right; }
+.pull-left { float: left; }
+
+.@{fa-css-prefix} {
+ &.pull-left { margin-right: .3em; }
+ &.pull-right { margin-left: .3em; }
+}
+
+
+// Animated Icons
+// --------------------------
+
+.@{fa-css-prefix}-spin {
+ -webkit-animation: fa-spin 2s infinite linear;
+ animation: fa-spin 2s infinite linear;
+}
+
+.@{fa-css-prefix}-pulse {
+ -webkit-animation: fa-spin 1s infinite steps(8);
+ animation: fa-spin 1s infinite steps(8);
+}
+
+@-webkit-keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+
+@keyframes fa-spin {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(359deg);
+ transform: rotate(359deg);
+ }
+}
+
+
+// Rotated & Flipped Icons
+// -------------------------
+
+.@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); }
+.@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); }
+.@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); }
+
+.@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); }
+.@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); }
+
+// Hook for IE8-9
+// -------------------------
+
+:root .@{fa-css-prefix}-rotate-90,
+:root .@{fa-css-prefix}-rotate-180,
+:root .@{fa-css-prefix}-rotate-270,
+:root .@{fa-css-prefix}-flip-horizontal,
+:root .@{fa-css-prefix}-flip-vertical {
+ filter: none;
+}
+
+
+// Stacked Icons
+// -------------------------
+
+.@{fa-css-prefix}-stack {
+ position: relative;
+ display: inline-block;
+ width: 2em;
+ height: 2em;
+ line-height: 2em;
+ vertical-align: middle;
+}
+.@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x {
+ position: absolute;
+ left: 0;
+ width: 100%;
+ text-align: center;
+}
+.@{fa-css-prefix}-stack-1x { line-height: inherit; }
+.@{fa-css-prefix}-stack-2x { font-size: 2em; }
+.@{fa-css-prefix}-inverse { color: @fa-inverse; }
+
+
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */
+
+.@{fa-css-prefix}-glass:before { content: @fa-var-glass; }
+.@{fa-css-prefix}-music:before { content: @fa-var-music; }
+.@{fa-css-prefix}-search:before { content: @fa-var-search; }
+.@{fa-css-prefix}-envelope-o:before { content: @fa-var-envelope-o; }
+.@{fa-css-prefix}-heart:before { content: @fa-var-heart; }
+.@{fa-css-prefix}-star:before { content: @fa-var-star; }
+.@{fa-css-prefix}-star-o:before { content: @fa-var-star-o; }
+.@{fa-css-prefix}-user:before { content: @fa-var-user; }
+.@{fa-css-prefix}-film:before { content: @fa-var-film; }
+.@{fa-css-prefix}-th-large:before { content: @fa-var-th-large; }
+.@{fa-css-prefix}-th:before { content: @fa-var-th; }
+.@{fa-css-prefix}-th-list:before { content: @fa-var-th-list; }
+.@{fa-css-prefix}-check:before { content: @fa-var-check; }
+.@{fa-css-prefix}-remove:before,
+.@{fa-css-prefix}-close:before,
+.@{fa-css-prefix}-times:before { content: @fa-var-times; }
+.@{fa-css-prefix}-search-plus:before { content: @fa-var-search-plus; }
+.@{fa-css-prefix}-search-minus:before { content: @fa-var-search-minus; }
+.@{fa-css-prefix}-power-off:before { content: @fa-var-power-off; }
+.@{fa-css-prefix}-signal:before { content: @fa-var-signal; }
+.@{fa-css-prefix}-gear:before,
+.@{fa-css-prefix}-cog:before { content: @fa-var-cog; }
+.@{fa-css-prefix}-trash-o:before { content: @fa-var-trash-o; }
+.@{fa-css-prefix}-home:before { content: @fa-var-home; }
+.@{fa-css-prefix}-file-o:before { content: @fa-var-file-o; }
+.@{fa-css-prefix}-clock-o:before { content: @fa-var-clock-o; }
+.@{fa-css-prefix}-road:before { content: @fa-var-road; }
+.@{fa-css-prefix}-download:before { content: @fa-var-download; }
+.@{fa-css-prefix}-arrow-circle-o-down:before { content: @fa-var-arrow-circle-o-down; }
+.@{fa-css-prefix}-arrow-circle-o-up:before { content: @fa-var-arrow-circle-o-up; }
+.@{fa-css-prefix}-inbox:before { content: @fa-var-inbox; }
+.@{fa-css-prefix}-play-circle-o:before { content: @fa-var-play-circle-o; }
+.@{fa-css-prefix}-rotate-right:before,
+.@{fa-css-prefix}-repeat:before { content: @fa-var-repeat; }
+.@{fa-css-prefix}-refresh:before { content: @fa-var-refresh; }
+.@{fa-css-prefix}-list-alt:before { content: @fa-var-list-alt; }
+.@{fa-css-prefix}-lock:before { content: @fa-var-lock; }
+.@{fa-css-prefix}-flag:before { content: @fa-var-flag; }
+.@{fa-css-prefix}-headphones:before { content: @fa-var-headphones; }
+.@{fa-css-prefix}-volume-off:before { content: @fa-var-volume-off; }
+.@{fa-css-prefix}-volume-down:before { content: @fa-var-volume-down; }
+.@{fa-css-prefix}-volume-up:before { content: @fa-var-volume-up; }
+.@{fa-css-prefix}-qrcode:before { content: @fa-var-qrcode; }
+.@{fa-css-prefix}-barcode:before { content: @fa-var-barcode; }
+.@{fa-css-prefix}-tag:before { content: @fa-var-tag; }
+.@{fa-css-prefix}-tags:before { content: @fa-var-tags; }
+.@{fa-css-prefix}-book:before { content: @fa-var-book; }
+.@{fa-css-prefix}-bookmark:before { content: @fa-var-bookmark; }
+.@{fa-css-prefix}-print:before { content: @fa-var-print; }
+.@{fa-css-prefix}-camera:before { content: @fa-var-camera; }
+.@{fa-css-prefix}-font:before { content: @fa-var-font; }
+.@{fa-css-prefix}-bold:before { content: @fa-var-bold; }
+.@{fa-css-prefix}-italic:before { content: @fa-var-italic; }
+.@{fa-css-prefix}-text-height:before { content: @fa-var-text-height; }
+.@{fa-css-prefix}-text-width:before { content: @fa-var-text-width; }
+.@{fa-css-prefix}-align-left:before { content: @fa-var-align-left; }
+.@{fa-css-prefix}-align-center:before { content: @fa-var-align-center; }
+.@{fa-css-prefix}-align-right:before { content: @fa-var-align-right; }
+.@{fa-css-prefix}-align-justify:before { content: @fa-var-align-justify; }
+.@{fa-css-prefix}-list:before { content: @fa-var-list; }
+.@{fa-css-prefix}-dedent:before,
+.@{fa-css-prefix}-outdent:before { content: @fa-var-outdent; }
+.@{fa-css-prefix}-indent:before { content: @fa-var-indent; }
+.@{fa-css-prefix}-video-camera:before { content: @fa-var-video-camera; }
+.@{fa-css-prefix}-photo:before,
+.@{fa-css-prefix}-image:before,
+.@{fa-css-prefix}-picture-o:before { content: @fa-var-picture-o; }
+.@{fa-css-prefix}-pencil:before { content: @fa-var-pencil; }
+.@{fa-css-prefix}-map-marker:before { content: @fa-var-map-marker; }
+.@{fa-css-prefix}-adjust:before { content: @fa-var-adjust; }
+.@{fa-css-prefix}-tint:before { content: @fa-var-tint; }
+.@{fa-css-prefix}-edit:before,
+.@{fa-css-prefix}-pencil-square-o:before { content: @fa-var-pencil-square-o; }
+.@{fa-css-prefix}-share-square-o:before { content: @fa-var-share-square-o; }
+.@{fa-css-prefix}-check-square-o:before { content: @fa-var-check-square-o; }
+.@{fa-css-prefix}-arrows:before { content: @fa-var-arrows; }
+.@{fa-css-prefix}-step-backward:before { content: @fa-var-step-backward; }
+.@{fa-css-prefix}-fast-backward:before { content: @fa-var-fast-backward; }
+.@{fa-css-prefix}-backward:before { content: @fa-var-backward; }
+.@{fa-css-prefix}-play:before { content: @fa-var-play; }
+.@{fa-css-prefix}-pause:before { content: @fa-var-pause; }
+.@{fa-css-prefix}-stop:before { content: @fa-var-stop; }
+.@{fa-css-prefix}-forward:before { content: @fa-var-forward; }
+.@{fa-css-prefix}-fast-forward:before { content: @fa-var-fast-forward; }
+.@{fa-css-prefix}-step-forward:before { content: @fa-var-step-forward; }
+.@{fa-css-prefix}-eject:before { content: @fa-var-eject; }
+.@{fa-css-prefix}-chevron-left:before { content: @fa-var-chevron-left; }
+.@{fa-css-prefix}-chevron-right:before { content: @fa-var-chevron-right; }
+.@{fa-css-prefix}-plus-circle:before { content: @fa-var-plus-circle; }
+.@{fa-css-prefix}-minus-circle:before { content: @fa-var-minus-circle; }
+.@{fa-css-prefix}-times-circle:before { content: @fa-var-times-circle; }
+.@{fa-css-prefix}-check-circle:before { content: @fa-var-check-circle; }
+.@{fa-css-prefix}-question-circle:before { content: @fa-var-question-circle; }
+.@{fa-css-prefix}-info-circle:before { content: @fa-var-info-circle; }
+.@{fa-css-prefix}-crosshairs:before { content: @fa-var-crosshairs; }
+.@{fa-css-prefix}-times-circle-o:before { content: @fa-var-times-circle-o; }
+.@{fa-css-prefix}-check-circle-o:before { content: @fa-var-check-circle-o; }
+.@{fa-css-prefix}-ban:before { content: @fa-var-ban; }
+.@{fa-css-prefix}-arrow-left:before { content: @fa-var-arrow-left; }
+.@{fa-css-prefix}-arrow-right:before { content: @fa-var-arrow-right; }
+.@{fa-css-prefix}-arrow-up:before { content: @fa-var-arrow-up; }
+.@{fa-css-prefix}-arrow-down:before { content: @fa-var-arrow-down; }
+.@{fa-css-prefix}-mail-forward:before,
+.@{fa-css-prefix}-share:before { content: @fa-var-share; }
+.@{fa-css-prefix}-expand:before { content: @fa-var-expand; }
+.@{fa-css-prefix}-compress:before { content: @fa-var-compress; }
+.@{fa-css-prefix}-plus:before { content: @fa-var-plus; }
+.@{fa-css-prefix}-minus:before { content: @fa-var-minus; }
+.@{fa-css-prefix}-asterisk:before { content: @fa-var-asterisk; }
+.@{fa-css-prefix}-exclamation-circle:before { content: @fa-var-exclamation-circle; }
+.@{fa-css-prefix}-gift:before { content: @fa-var-gift; }
+.@{fa-css-prefix}-leaf:before { content: @fa-var-leaf; }
+.@{fa-css-prefix}-fire:before { content: @fa-var-fire; }
+.@{fa-css-prefix}-eye:before { content: @fa-var-eye; }
+.@{fa-css-prefix}-eye-slash:before { content: @fa-var-eye-slash; }
+.@{fa-css-prefix}-warning:before,
+.@{fa-css-prefix}-exclamation-triangle:before { content: @fa-var-exclamation-triangle; }
+.@{fa-css-prefix}-plane:before { content: @fa-var-plane; }
+.@{fa-css-prefix}-calendar:before { content: @fa-var-calendar; }
+.@{fa-css-prefix}-random:before { content: @fa-var-random; }
+.@{fa-css-prefix}-comment:before { content: @fa-var-comment; }
+.@{fa-css-prefix}-magnet:before { content: @fa-var-magnet; }
+.@{fa-css-prefix}-chevron-up:before { content: @fa-var-chevron-up; }
+.@{fa-css-prefix}-chevron-down:before { content: @fa-var-chevron-down; }
+.@{fa-css-prefix}-retweet:before { content: @fa-var-retweet; }
+.@{fa-css-prefix}-shopping-cart:before { content: @fa-var-shopping-cart; }
+.@{fa-css-prefix}-folder:before { content: @fa-var-folder; }
+.@{fa-css-prefix}-folder-open:before { content: @fa-var-folder-open; }
+.@{fa-css-prefix}-arrows-v:before { content: @fa-var-arrows-v; }
+.@{fa-css-prefix}-arrows-h:before { content: @fa-var-arrows-h; }
+.@{fa-css-prefix}-bar-chart-o:before,
+.@{fa-css-prefix}-bar-chart:before { content: @fa-var-bar-chart; }
+.@{fa-css-prefix}-twitter-square:before { content: @fa-var-twitter-square; }
+.@{fa-css-prefix}-facebook-square:before { content: @fa-var-facebook-square; }
+.@{fa-css-prefix}-camera-retro:before { content: @fa-var-camera-retro; }
+.@{fa-css-prefix}-key:before { content: @fa-var-key; }
+.@{fa-css-prefix}-gears:before,
+.@{fa-css-prefix}-cogs:before { content: @fa-var-cogs; }
+.@{fa-css-prefix}-comments:before { content: @fa-var-comments; }
+.@{fa-css-prefix}-thumbs-o-up:before { content: @fa-var-thumbs-o-up; }
+.@{fa-css-prefix}-thumbs-o-down:before { content: @fa-var-thumbs-o-down; }
+.@{fa-css-prefix}-star-half:before { content: @fa-var-star-half; }
+.@{fa-css-prefix}-heart-o:before { content: @fa-var-heart-o; }
+.@{fa-css-prefix}-sign-out:before { content: @fa-var-sign-out; }
+.@{fa-css-prefix}-linkedin-square:before { content: @fa-var-linkedin-square; }
+.@{fa-css-prefix}-thumb-tack:before { content: @fa-var-thumb-tack; }
+.@{fa-css-prefix}-external-link:before { content: @fa-var-external-link; }
+.@{fa-css-prefix}-sign-in:before { content: @fa-var-sign-in; }
+.@{fa-css-prefix}-trophy:before { content: @fa-var-trophy; }
+.@{fa-css-prefix}-github-square:before { content: @fa-var-github-square; }
+.@{fa-css-prefix}-upload:before { content: @fa-var-upload; }
+.@{fa-css-prefix}-lemon-o:before { content: @fa-var-lemon-o; }
+.@{fa-css-prefix}-phone:before { content: @fa-var-phone; }
+.@{fa-css-prefix}-square-o:before { content: @fa-var-square-o; }
+.@{fa-css-prefix}-bookmark-o:before { content: @fa-var-bookmark-o; }
+.@{fa-css-prefix}-phone-square:before { content: @fa-var-phone-square; }
+.@{fa-css-prefix}-twitter:before { content: @fa-var-twitter; }
+.@{fa-css-prefix}-facebook-f:before,
+.@{fa-css-prefix}-facebook:before { content: @fa-var-facebook; }
+.@{fa-css-prefix}-github:before { content: @fa-var-github; }
+.@{fa-css-prefix}-unlock:before { content: @fa-var-unlock; }
+.@{fa-css-prefix}-credit-card:before { content: @fa-var-credit-card; }
+.@{fa-css-prefix}-rss:before { content: @fa-var-rss; }
+.@{fa-css-prefix}-hdd-o:before { content: @fa-var-hdd-o; }
+.@{fa-css-prefix}-bullhorn:before { content: @fa-var-bullhorn; }
+.@{fa-css-prefix}-bell:before { content: @fa-var-bell; }
+.@{fa-css-prefix}-certificate:before { content: @fa-var-certificate; }
+.@{fa-css-prefix}-hand-o-right:before { content: @fa-var-hand-o-right; }
+.@{fa-css-prefix}-hand-o-left:before { content: @fa-var-hand-o-left; }
+.@{fa-css-prefix}-hand-o-up:before { content: @fa-var-hand-o-up; }
+.@{fa-css-prefix}-hand-o-down:before { content: @fa-var-hand-o-down; }
+.@{fa-css-prefix}-arrow-circle-left:before { content: @fa-var-arrow-circle-left; }
+.@{fa-css-prefix}-arrow-circle-right:before { content: @fa-var-arrow-circle-right; }
+.@{fa-css-prefix}-arrow-circle-up:before { content: @fa-var-arrow-circle-up; }
+.@{fa-css-prefix}-arrow-circle-down:before { content: @fa-var-arrow-circle-down; }
+.@{fa-css-prefix}-globe:before { content: @fa-var-globe; }
+.@{fa-css-prefix}-wrench:before { content: @fa-var-wrench; }
+.@{fa-css-prefix}-tasks:before { content: @fa-var-tasks; }
+.@{fa-css-prefix}-filter:before { content: @fa-var-filter; }
+.@{fa-css-prefix}-briefcase:before { content: @fa-var-briefcase; }
+.@{fa-css-prefix}-arrows-alt:before { content: @fa-var-arrows-alt; }
+.@{fa-css-prefix}-group:before,
+.@{fa-css-prefix}-users:before { content: @fa-var-users; }
+.@{fa-css-prefix}-chain:before,
+.@{fa-css-prefix}-link:before { content: @fa-var-link; }
+.@{fa-css-prefix}-cloud:before { content: @fa-var-cloud; }
+.@{fa-css-prefix}-flask:before { content: @fa-var-flask; }
+.@{fa-css-prefix}-cut:before,
+.@{fa-css-prefix}-scissors:before { content: @fa-var-scissors; }
+.@{fa-css-prefix}-copy:before,
+.@{fa-css-prefix}-files-o:before { content: @fa-var-files-o; }
+.@{fa-css-prefix}-paperclip:before { content: @fa-var-paperclip; }
+.@{fa-css-prefix}-save:before,
+.@{fa-css-prefix}-floppy-o:before { content: @fa-var-floppy-o; }
+.@{fa-css-prefix}-square:before { content: @fa-var-square; }
+.@{fa-css-prefix}-navicon:before,
+.@{fa-css-prefix}-reorder:before,
+.@{fa-css-prefix}-bars:before { content: @fa-var-bars; }
+.@{fa-css-prefix}-list-ul:before { content: @fa-var-list-ul; }
+.@{fa-css-prefix}-list-ol:before { content: @fa-var-list-ol; }
+.@{fa-css-prefix}-strikethrough:before { content: @fa-var-strikethrough; }
+.@{fa-css-prefix}-underline:before { content: @fa-var-underline; }
+.@{fa-css-prefix}-table:before { content: @fa-var-table; }
+.@{fa-css-prefix}-magic:before { content: @fa-var-magic; }
+.@{fa-css-prefix}-truck:before { content: @fa-var-truck; }
+.@{fa-css-prefix}-pinterest:before { content: @fa-var-pinterest; }
+.@{fa-css-prefix}-pinterest-square:before { content: @fa-var-pinterest-square; }
+.@{fa-css-prefix}-google-plus-square:before { content: @fa-var-google-plus-square; }
+.@{fa-css-prefix}-google-plus:before { content: @fa-var-google-plus; }
+.@{fa-css-prefix}-money:before { content: @fa-var-money; }
+.@{fa-css-prefix}-caret-down:before { content: @fa-var-caret-down; }
+.@{fa-css-prefix}-caret-up:before { content: @fa-var-caret-up; }
+.@{fa-css-prefix}-caret-left:before { content: @fa-var-caret-left; }
+.@{fa-css-prefix}-caret-right:before { content: @fa-var-caret-right; }
+.@{fa-css-prefix}-columns:before { content: @fa-var-columns; }
+.@{fa-css-prefix}-unsorted:before,
+.@{fa-css-prefix}-sort:before { content: @fa-var-sort; }
+.@{fa-css-prefix}-sort-down:before,
+.@{fa-css-prefix}-sort-desc:before { content: @fa-var-sort-desc; }
+.@{fa-css-prefix}-sort-up:before,
+.@{fa-css-prefix}-sort-asc:before { content: @fa-var-sort-asc; }
+.@{fa-css-prefix}-envelope:before { content: @fa-var-envelope; }
+.@{fa-css-prefix}-linkedin:before { content: @fa-var-linkedin; }
+.@{fa-css-prefix}-rotate-left:before,
+.@{fa-css-prefix}-undo:before { content: @fa-var-undo; }
+.@{fa-css-prefix}-legal:before,
+.@{fa-css-prefix}-gavel:before { content: @fa-var-gavel; }
+.@{fa-css-prefix}-dashboard:before,
+.@{fa-css-prefix}-tachometer:before { content: @fa-var-tachometer; }
+.@{fa-css-prefix}-comment-o:before { content: @fa-var-comment-o; }
+.@{fa-css-prefix}-comments-o:before { content: @fa-var-comments-o; }
+.@{fa-css-prefix}-flash:before,
+.@{fa-css-prefix}-bolt:before { content: @fa-var-bolt; }
+.@{fa-css-prefix}-sitemap:before { content: @fa-var-sitemap; }
+.@{fa-css-prefix}-umbrella:before { content: @fa-var-umbrella; }
+.@{fa-css-prefix}-paste:before,
+.@{fa-css-prefix}-clipboard:before { content: @fa-var-clipboard; }
+.@{fa-css-prefix}-lightbulb-o:before { content: @fa-var-lightbulb-o; }
+.@{fa-css-prefix}-exchange:before { content: @fa-var-exchange; }
+.@{fa-css-prefix}-cloud-download:before { content: @fa-var-cloud-download; }
+.@{fa-css-prefix}-cloud-upload:before { content: @fa-var-cloud-upload; }
+.@{fa-css-prefix}-user-md:before { content: @fa-var-user-md; }
+.@{fa-css-prefix}-stethoscope:before { content: @fa-var-stethoscope; }
+.@{fa-css-prefix}-suitcase:before { content: @fa-var-suitcase; }
+.@{fa-css-prefix}-bell-o:before { content: @fa-var-bell-o; }
+.@{fa-css-prefix}-coffee:before { content: @fa-var-coffee; }
+.@{fa-css-prefix}-cutlery:before { content: @fa-var-cutlery; }
+.@{fa-css-prefix}-file-text-o:before { content: @fa-var-file-text-o; }
+.@{fa-css-prefix}-building-o:before { content: @fa-var-building-o; }
+.@{fa-css-prefix}-hospital-o:before { content: @fa-var-hospital-o; }
+.@{fa-css-prefix}-ambulance:before { content: @fa-var-ambulance; }
+.@{fa-css-prefix}-medkit:before { content: @fa-var-medkit; }
+.@{fa-css-prefix}-fighter-jet:before { content: @fa-var-fighter-jet; }
+.@{fa-css-prefix}-beer:before { content: @fa-var-beer; }
+.@{fa-css-prefix}-h-square:before { content: @fa-var-h-square; }
+.@{fa-css-prefix}-plus-square:before { content: @fa-var-plus-square; }
+.@{fa-css-prefix}-angle-double-left:before { content: @fa-var-angle-double-left; }
+.@{fa-css-prefix}-angle-double-right:before { content: @fa-var-angle-double-right; }
+.@{fa-css-prefix}-angle-double-up:before { content: @fa-var-angle-double-up; }
+.@{fa-css-prefix}-angle-double-down:before { content: @fa-var-angle-double-down; }
+.@{fa-css-prefix}-angle-left:before { content: @fa-var-angle-left; }
+.@{fa-css-prefix}-angle-right:before { content: @fa-var-angle-right; }
+.@{fa-css-prefix}-angle-up:before { content: @fa-var-angle-up; }
+.@{fa-css-prefix}-angle-down:before { content: @fa-var-angle-down; }
+.@{fa-css-prefix}-desktop:before { content: @fa-var-desktop; }
+.@{fa-css-prefix}-laptop:before { content: @fa-var-laptop; }
+.@{fa-css-prefix}-tablet:before { content: @fa-var-tablet; }
+.@{fa-css-prefix}-mobile-phone:before,
+.@{fa-css-prefix}-mobile:before { content: @fa-var-mobile; }
+.@{fa-css-prefix}-circle-o:before { content: @fa-var-circle-o; }
+.@{fa-css-prefix}-quote-left:before { content: @fa-var-quote-left; }
+.@{fa-css-prefix}-quote-right:before { content: @fa-var-quote-right; }
+.@{fa-css-prefix}-spinner:before { content: @fa-var-spinner; }
+.@{fa-css-prefix}-circle:before { content: @fa-var-circle; }
+.@{fa-css-prefix}-mail-reply:before,
+.@{fa-css-prefix}-reply:before { content: @fa-var-reply; }
+.@{fa-css-prefix}-github-alt:before { content: @fa-var-github-alt; }
+.@{fa-css-prefix}-folder-o:before { content: @fa-var-folder-o; }
+.@{fa-css-prefix}-folder-open-o:before { content: @fa-var-folder-open-o; }
+.@{fa-css-prefix}-smile-o:before { content: @fa-var-smile-o; }
+.@{fa-css-prefix}-frown-o:before { content: @fa-var-frown-o; }
+.@{fa-css-prefix}-meh-o:before { content: @fa-var-meh-o; }
+.@{fa-css-prefix}-gamepad:before { content: @fa-var-gamepad; }
+.@{fa-css-prefix}-keyboard-o:before { content: @fa-var-keyboard-o; }
+.@{fa-css-prefix}-flag-o:before { content: @fa-var-flag-o; }
+.@{fa-css-prefix}-flag-checkered:before { content: @fa-var-flag-checkered; }
+.@{fa-css-prefix}-terminal:before { content: @fa-var-terminal; }
+.@{fa-css-prefix}-code:before { content: @fa-var-code; }
+.@{fa-css-prefix}-mail-reply-all:before,
+.@{fa-css-prefix}-reply-all:before { content: @fa-var-reply-all; }
+.@{fa-css-prefix}-star-half-empty:before,
+.@{fa-css-prefix}-star-half-full:before,
+.@{fa-css-prefix}-star-half-o:before { content: @fa-var-star-half-o; }
+.@{fa-css-prefix}-location-arrow:before { content: @fa-var-location-arrow; }
+.@{fa-css-prefix}-crop:before { content: @fa-var-crop; }
+.@{fa-css-prefix}-code-fork:before { content: @fa-var-code-fork; }
+.@{fa-css-prefix}-unlink:before,
+.@{fa-css-prefix}-chain-broken:before { content: @fa-var-chain-broken; }
+.@{fa-css-prefix}-question:before { content: @fa-var-question; }
+.@{fa-css-prefix}-info:before { content: @fa-var-info; }
+.@{fa-css-prefix}-exclamation:before { content: @fa-var-exclamation; }
+.@{fa-css-prefix}-superscript:before { content: @fa-var-superscript; }
+.@{fa-css-prefix}-subscript:before { content: @fa-var-subscript; }
+.@{fa-css-prefix}-eraser:before { content: @fa-var-eraser; }
+.@{fa-css-prefix}-puzzle-piece:before { content: @fa-var-puzzle-piece; }
+.@{fa-css-prefix}-microphone:before { content: @fa-var-microphone; }
+.@{fa-css-prefix}-microphone-slash:before { content: @fa-var-microphone-slash; }
+.@{fa-css-prefix}-shield:before { content: @fa-var-shield; }
+.@{fa-css-prefix}-calendar-o:before { content: @fa-var-calendar-o; }
+.@{fa-css-prefix}-fire-extinguisher:before { content: @fa-var-fire-extinguisher; }
+.@{fa-css-prefix}-rocket:before { content: @fa-var-rocket; }
+.@{fa-css-prefix}-maxcdn:before { content: @fa-var-maxcdn; }
+.@{fa-css-prefix}-chevron-circle-left:before { content: @fa-var-chevron-circle-left; }
+.@{fa-css-prefix}-chevron-circle-right:before { content: @fa-var-chevron-circle-right; }
+.@{fa-css-prefix}-chevron-circle-up:before { content: @fa-var-chevron-circle-up; }
+.@{fa-css-prefix}-chevron-circle-down:before { content: @fa-var-chevron-circle-down; }
+.@{fa-css-prefix}-html5:before { content: @fa-var-html5; }
+.@{fa-css-prefix}-css3:before { content: @fa-var-css3; }
+.@{fa-css-prefix}-anchor:before { content: @fa-var-anchor; }
+.@{fa-css-prefix}-unlock-alt:before { content: @fa-var-unlock-alt; }
+.@{fa-css-prefix}-bullseye:before { content: @fa-var-bullseye; }
+.@{fa-css-prefix}-ellipsis-h:before { content: @fa-var-ellipsis-h; }
+.@{fa-css-prefix}-ellipsis-v:before { content: @fa-var-ellipsis-v; }
+.@{fa-css-prefix}-rss-square:before { content: @fa-var-rss-square; }
+.@{fa-css-prefix}-play-circle:before { content: @fa-var-play-circle; }
+.@{fa-css-prefix}-ticket:before { content: @fa-var-ticket; }
+.@{fa-css-prefix}-minus-square:before { content: @fa-var-minus-square; }
+.@{fa-css-prefix}-minus-square-o:before { content: @fa-var-minus-square-o; }
+.@{fa-css-prefix}-level-up:before { content: @fa-var-level-up; }
+.@{fa-css-prefix}-level-down:before { content: @fa-var-level-down; }
+.@{fa-css-prefix}-check-square:before { content: @fa-var-check-square; }
+.@{fa-css-prefix}-pencil-square:before { content: @fa-var-pencil-square; }
+.@{fa-css-prefix}-external-link-square:before { content: @fa-var-external-link-square; }
+.@{fa-css-prefix}-share-square:before { content: @fa-var-share-square; }
+.@{fa-css-prefix}-compass:before { content: @fa-var-compass; }
+.@{fa-css-prefix}-toggle-down:before,
+.@{fa-css-prefix}-caret-square-o-down:before { content: @fa-var-caret-square-o-down; }
+.@{fa-css-prefix}-toggle-up:before,
+.@{fa-css-prefix}-caret-square-o-up:before { content: @fa-var-caret-square-o-up; }
+.@{fa-css-prefix}-toggle-right:before,
+.@{fa-css-prefix}-caret-square-o-right:before { content: @fa-var-caret-square-o-right; }
+.@{fa-css-prefix}-euro:before,
+.@{fa-css-prefix}-eur:before { content: @fa-var-eur; }
+.@{fa-css-prefix}-gbp:before { content: @fa-var-gbp; }
+.@{fa-css-prefix}-dollar:before,
+.@{fa-css-prefix}-usd:before { content: @fa-var-usd; }
+.@{fa-css-prefix}-rupee:before,
+.@{fa-css-prefix}-inr:before { content: @fa-var-inr; }
+.@{fa-css-prefix}-cny:before,
+.@{fa-css-prefix}-rmb:before,
+.@{fa-css-prefix}-yen:before,
+.@{fa-css-prefix}-jpy:before { content: @fa-var-jpy; }
+.@{fa-css-prefix}-ruble:before,
+.@{fa-css-prefix}-rouble:before,
+.@{fa-css-prefix}-rub:before { content: @fa-var-rub; }
+.@{fa-css-prefix}-won:before,
+.@{fa-css-prefix}-krw:before { content: @fa-var-krw; }
+.@{fa-css-prefix}-bitcoin:before,
+.@{fa-css-prefix}-btc:before { content: @fa-var-btc; }
+.@{fa-css-prefix}-file:before { content: @fa-var-file; }
+.@{fa-css-prefix}-file-text:before { content: @fa-var-file-text; }
+.@{fa-css-prefix}-sort-alpha-asc:before { content: @fa-var-sort-alpha-asc; }
+.@{fa-css-prefix}-sort-alpha-desc:before { content: @fa-var-sort-alpha-desc; }
+.@{fa-css-prefix}-sort-amount-asc:before { content: @fa-var-sort-amount-asc; }
+.@{fa-css-prefix}-sort-amount-desc:before { content: @fa-var-sort-amount-desc; }
+.@{fa-css-prefix}-sort-numeric-asc:before { content: @fa-var-sort-numeric-asc; }
+.@{fa-css-prefix}-sort-numeric-desc:before { content: @fa-var-sort-numeric-desc; }
+.@{fa-css-prefix}-thumbs-up:before { content: @fa-var-thumbs-up; }
+.@{fa-css-prefix}-thumbs-down:before { content: @fa-var-thumbs-down; }
+.@{fa-css-prefix}-youtube-square:before { content: @fa-var-youtube-square; }
+.@{fa-css-prefix}-youtube:before { content: @fa-var-youtube; }
+.@{fa-css-prefix}-xing:before { content: @fa-var-xing; }
+.@{fa-css-prefix}-xing-square:before { content: @fa-var-xing-square; }
+.@{fa-css-prefix}-youtube-play:before { content: @fa-var-youtube-play; }
+.@{fa-css-prefix}-dropbox:before { content: @fa-var-dropbox; }
+.@{fa-css-prefix}-stack-overflow:before { content: @fa-var-stack-overflow; }
+.@{fa-css-prefix}-instagram:before { content: @fa-var-instagram; }
+.@{fa-css-prefix}-flickr:before { content: @fa-var-flickr; }
+.@{fa-css-prefix}-adn:before { content: @fa-var-adn; }
+.@{fa-css-prefix}-bitbucket:before { content: @fa-var-bitbucket; }
+.@{fa-css-prefix}-bitbucket-square:before { content: @fa-var-bitbucket-square; }
+.@{fa-css-prefix}-tumblr:before { content: @fa-var-tumblr; }
+.@{fa-css-prefix}-tumblr-square:before { content: @fa-var-tumblr-square; }
+.@{fa-css-prefix}-long-arrow-down:before { content: @fa-var-long-arrow-down; }
+.@{fa-css-prefix}-long-arrow-up:before { content: @fa-var-long-arrow-up; }
+.@{fa-css-prefix}-long-arrow-left:before { content: @fa-var-long-arrow-left; }
+.@{fa-css-prefix}-long-arrow-right:before { content: @fa-var-long-arrow-right; }
+.@{fa-css-prefix}-apple:before { content: @fa-var-apple; }
+.@{fa-css-prefix}-windows:before { content: @fa-var-windows; }
+.@{fa-css-prefix}-android:before { content: @fa-var-android; }
+.@{fa-css-prefix}-linux:before { content: @fa-var-linux; }
+.@{fa-css-prefix}-dribbble:before { content: @fa-var-dribbble; }
+.@{fa-css-prefix}-skype:before { content: @fa-var-skype; }
+.@{fa-css-prefix}-foursquare:before { content: @fa-var-foursquare; }
+.@{fa-css-prefix}-trello:before { content: @fa-var-trello; }
+.@{fa-css-prefix}-female:before { content: @fa-var-female; }
+.@{fa-css-prefix}-male:before { content: @fa-var-male; }
+.@{fa-css-prefix}-gittip:before,
+.@{fa-css-prefix}-gratipay:before { content: @fa-var-gratipay; }
+.@{fa-css-prefix}-sun-o:before { content: @fa-var-sun-o; }
+.@{fa-css-prefix}-moon-o:before { content: @fa-var-moon-o; }
+.@{fa-css-prefix}-archive:before { content: @fa-var-archive; }
+.@{fa-css-prefix}-bug:before { content: @fa-var-bug; }
+.@{fa-css-prefix}-vk:before { content: @fa-var-vk; }
+.@{fa-css-prefix}-weibo:before { content: @fa-var-weibo; }
+.@{fa-css-prefix}-renren:before { content: @fa-var-renren; }
+.@{fa-css-prefix}-pagelines:before { content: @fa-var-pagelines; }
+.@{fa-css-prefix}-stack-exchange:before { content: @fa-var-stack-exchange; }
+.@{fa-css-prefix}-arrow-circle-o-right:before { content: @fa-var-arrow-circle-o-right; }
+.@{fa-css-prefix}-arrow-circle-o-left:before { content: @fa-var-arrow-circle-o-left; }
+.@{fa-css-prefix}-toggle-left:before,
+.@{fa-css-prefix}-caret-square-o-left:before { content: @fa-var-caret-square-o-left; }
+.@{fa-css-prefix}-dot-circle-o:before { content: @fa-var-dot-circle-o; }
+.@{fa-css-prefix}-wheelchair:before { content: @fa-var-wheelchair; }
+.@{fa-css-prefix}-vimeo-square:before { content: @fa-var-vimeo-square; }
+.@{fa-css-prefix}-turkish-lira:before,
+.@{fa-css-prefix}-try:before { content: @fa-var-try; }
+.@{fa-css-prefix}-plus-square-o:before { content: @fa-var-plus-square-o; }
+.@{fa-css-prefix}-space-shuttle:before { content: @fa-var-space-shuttle; }
+.@{fa-css-prefix}-slack:before { content: @fa-var-slack; }
+.@{fa-css-prefix}-envelope-square:before { content: @fa-var-envelope-square; }
+.@{fa-css-prefix}-wordpress:before { content: @fa-var-wordpress; }
+.@{fa-css-prefix}-openid:before { content: @fa-var-openid; }
+.@{fa-css-prefix}-institution:before,
+.@{fa-css-prefix}-bank:before,
+.@{fa-css-prefix}-university:before { content: @fa-var-university; }
+.@{fa-css-prefix}-mortar-board:before,
+.@{fa-css-prefix}-graduation-cap:before { content: @fa-var-graduation-cap; }
+.@{fa-css-prefix}-yahoo:before { content: @fa-var-yahoo; }
+.@{fa-css-prefix}-google:before { content: @fa-var-google; }
+.@{fa-css-prefix}-reddit:before { content: @fa-var-reddit; }
+.@{fa-css-prefix}-reddit-square:before { content: @fa-var-reddit-square; }
+.@{fa-css-prefix}-stumbleupon-circle:before { content: @fa-var-stumbleupon-circle; }
+.@{fa-css-prefix}-stumbleupon:before { content: @fa-var-stumbleupon; }
+.@{fa-css-prefix}-delicious:before { content: @fa-var-delicious; }
+.@{fa-css-prefix}-digg:before { content: @fa-var-digg; }
+.@{fa-css-prefix}-pied-piper:before { content: @fa-var-pied-piper; }
+.@{fa-css-prefix}-pied-piper-alt:before { content: @fa-var-pied-piper-alt; }
+.@{fa-css-prefix}-drupal:before { content: @fa-var-drupal; }
+.@{fa-css-prefix}-joomla:before { content: @fa-var-joomla; }
+.@{fa-css-prefix}-language:before { content: @fa-var-language; }
+.@{fa-css-prefix}-fax:before { content: @fa-var-fax; }
+.@{fa-css-prefix}-building:before { content: @fa-var-building; }
+.@{fa-css-prefix}-child:before { content: @fa-var-child; }
+.@{fa-css-prefix}-paw:before { content: @fa-var-paw; }
+.@{fa-css-prefix}-spoon:before { content: @fa-var-spoon; }
+.@{fa-css-prefix}-cube:before { content: @fa-var-cube; }
+.@{fa-css-prefix}-cubes:before { content: @fa-var-cubes; }
+.@{fa-css-prefix}-behance:before { content: @fa-var-behance; }
+.@{fa-css-prefix}-behance-square:before { content: @fa-var-behance-square; }
+.@{fa-css-prefix}-steam:before { content: @fa-var-steam; }
+.@{fa-css-prefix}-steam-square:before { content: @fa-var-steam-square; }
+.@{fa-css-prefix}-recycle:before { content: @fa-var-recycle; }
+.@{fa-css-prefix}-automobile:before,
+.@{fa-css-prefix}-car:before { content: @fa-var-car; }
+.@{fa-css-prefix}-cab:before,
+.@{fa-css-prefix}-taxi:before { content: @fa-var-taxi; }
+.@{fa-css-prefix}-tree:before { content: @fa-var-tree; }
+.@{fa-css-prefix}-spotify:before { content: @fa-var-spotify; }
+.@{fa-css-prefix}-deviantart:before { content: @fa-var-deviantart; }
+.@{fa-css-prefix}-soundcloud:before { content: @fa-var-soundcloud; }
+.@{fa-css-prefix}-database:before { content: @fa-var-database; }
+.@{fa-css-prefix}-file-pdf-o:before { content: @fa-var-file-pdf-o; }
+.@{fa-css-prefix}-file-word-o:before { content: @fa-var-file-word-o; }
+.@{fa-css-prefix}-file-excel-o:before { content: @fa-var-file-excel-o; }
+.@{fa-css-prefix}-file-powerpoint-o:before { content: @fa-var-file-powerpoint-o; }
+.@{fa-css-prefix}-file-photo-o:before,
+.@{fa-css-prefix}-file-picture-o:before,
+.@{fa-css-prefix}-file-image-o:before { content: @fa-var-file-image-o; }
+.@{fa-css-prefix}-file-zip-o:before,
+.@{fa-css-prefix}-file-archive-o:before { content: @fa-var-file-archive-o; }
+.@{fa-css-prefix}-file-sound-o:before,
+.@{fa-css-prefix}-file-audio-o:before { content: @fa-var-file-audio-o; }
+.@{fa-css-prefix}-file-movie-o:before,
+.@{fa-css-prefix}-file-video-o:before { content: @fa-var-file-video-o; }
+.@{fa-css-prefix}-file-code-o:before { content: @fa-var-file-code-o; }
+.@{fa-css-prefix}-vine:before { content: @fa-var-vine; }
+.@{fa-css-prefix}-codepen:before { content: @fa-var-codepen; }
+.@{fa-css-prefix}-jsfiddle:before { content: @fa-var-jsfiddle; }
+.@{fa-css-prefix}-life-bouy:before,
+.@{fa-css-prefix}-life-buoy:before,
+.@{fa-css-prefix}-life-saver:before,
+.@{fa-css-prefix}-support:before,
+.@{fa-css-prefix}-life-ring:before { content: @fa-var-life-ring; }
+.@{fa-css-prefix}-circle-o-notch:before { content: @fa-var-circle-o-notch; }
+.@{fa-css-prefix}-ra:before,
+.@{fa-css-prefix}-rebel:before { content: @fa-var-rebel; }
+.@{fa-css-prefix}-ge:before,
+.@{fa-css-prefix}-empire:before { content: @fa-var-empire; }
+.@{fa-css-prefix}-git-square:before { content: @fa-var-git-square; }
+.@{fa-css-prefix}-git:before { content: @fa-var-git; }
+.@{fa-css-prefix}-hacker-news:before { content: @fa-var-hacker-news; }
+.@{fa-css-prefix}-tencent-weibo:before { content: @fa-var-tencent-weibo; }
+.@{fa-css-prefix}-qq:before { content: @fa-var-qq; }
+.@{fa-css-prefix}-wechat:before,
+.@{fa-css-prefix}-weixin:before { content: @fa-var-weixin; }
+.@{fa-css-prefix}-send:before,
+.@{fa-css-prefix}-paper-plane:before { content: @fa-var-paper-plane; }
+.@{fa-css-prefix}-send-o:before,
+.@{fa-css-prefix}-paper-plane-o:before { content: @fa-var-paper-plane-o; }
+.@{fa-css-prefix}-history:before { content: @fa-var-history; }
+.@{fa-css-prefix}-genderless:before,
+.@{fa-css-prefix}-circle-thin:before { content: @fa-var-circle-thin; }
+.@{fa-css-prefix}-header:before { content: @fa-var-header; }
+.@{fa-css-prefix}-paragraph:before { content: @fa-var-paragraph; }
+.@{fa-css-prefix}-sliders:before { content: @fa-var-sliders; }
+.@{fa-css-prefix}-share-alt:before { content: @fa-var-share-alt; }
+.@{fa-css-prefix}-share-alt-square:before { content: @fa-var-share-alt-square; }
+.@{fa-css-prefix}-bomb:before { content: @fa-var-bomb; }
+.@{fa-css-prefix}-soccer-ball-o:before,
+.@{fa-css-prefix}-futbol-o:before { content: @fa-var-futbol-o; }
+.@{fa-css-prefix}-tty:before { content: @fa-var-tty; }
+.@{fa-css-prefix}-binoculars:before { content: @fa-var-binoculars; }
+.@{fa-css-prefix}-plug:before { content: @fa-var-plug; }
+.@{fa-css-prefix}-slideshare:before { content: @fa-var-slideshare; }
+.@{fa-css-prefix}-twitch:before { content: @fa-var-twitch; }
+.@{fa-css-prefix}-yelp:before { content: @fa-var-yelp; }
+.@{fa-css-prefix}-newspaper-o:before { content: @fa-var-newspaper-o; }
+.@{fa-css-prefix}-wifi:before { content: @fa-var-wifi; }
+.@{fa-css-prefix}-calculator:before { content: @fa-var-calculator; }
+.@{fa-css-prefix}-paypal:before { content: @fa-var-paypal; }
+.@{fa-css-prefix}-google-wallet:before { content: @fa-var-google-wallet; }
+.@{fa-css-prefix}-cc-visa:before { content: @fa-var-cc-visa; }
+.@{fa-css-prefix}-cc-mastercard:before { content: @fa-var-cc-mastercard; }
+.@{fa-css-prefix}-cc-discover:before { content: @fa-var-cc-discover; }
+.@{fa-css-prefix}-cc-amex:before { content: @fa-var-cc-amex; }
+.@{fa-css-prefix}-cc-paypal:before { content: @fa-var-cc-paypal; }
+.@{fa-css-prefix}-cc-stripe:before { content: @fa-var-cc-stripe; }
+.@{fa-css-prefix}-bell-slash:before { content: @fa-var-bell-slash; }
+.@{fa-css-prefix}-bell-slash-o:before { content: @fa-var-bell-slash-o; }
+.@{fa-css-prefix}-trash:before { content: @fa-var-trash; }
+.@{fa-css-prefix}-copyright:before { content: @fa-var-copyright; }
+.@{fa-css-prefix}-at:before { content: @fa-var-at; }
+.@{fa-css-prefix}-eyedropper:before { content: @fa-var-eyedropper; }
+.@{fa-css-prefix}-paint-brush:before { content: @fa-var-paint-brush; }
+.@{fa-css-prefix}-birthday-cake:before { content: @fa-var-birthday-cake; }
+.@{fa-css-prefix}-area-chart:before { content: @fa-var-area-chart; }
+.@{fa-css-prefix}-pie-chart:before { content: @fa-var-pie-chart; }
+.@{fa-css-prefix}-line-chart:before { content: @fa-var-line-chart; }
+.@{fa-css-prefix}-lastfm:before { content: @fa-var-lastfm; }
+.@{fa-css-prefix}-lastfm-square:before { content: @fa-var-lastfm-square; }
+.@{fa-css-prefix}-toggle-off:before { content: @fa-var-toggle-off; }
+.@{fa-css-prefix}-toggle-on:before { content: @fa-var-toggle-on; }
+.@{fa-css-prefix}-bicycle:before { content: @fa-var-bicycle; }
+.@{fa-css-prefix}-bus:before { content: @fa-var-bus; }
+.@{fa-css-prefix}-ioxhost:before { content: @fa-var-ioxhost; }
+.@{fa-css-prefix}-angellist:before { content: @fa-var-angellist; }
+.@{fa-css-prefix}-cc:before { content: @fa-var-cc; }
+.@{fa-css-prefix}-shekel:before,
+.@{fa-css-prefix}-sheqel:before,
+.@{fa-css-prefix}-ils:before { content: @fa-var-ils; }
+.@{fa-css-prefix}-meanpath:before { content: @fa-var-meanpath; }
+.@{fa-css-prefix}-buysellads:before { content: @fa-var-buysellads; }
+.@{fa-css-prefix}-connectdevelop:before { content: @fa-var-connectdevelop; }
+.@{fa-css-prefix}-dashcube:before { content: @fa-var-dashcube; }
+.@{fa-css-prefix}-forumbee:before { content: @fa-var-forumbee; }
+.@{fa-css-prefix}-leanpub:before { content: @fa-var-leanpub; }
+.@{fa-css-prefix}-sellsy:before { content: @fa-var-sellsy; }
+.@{fa-css-prefix}-shirtsinbulk:before { content: @fa-var-shirtsinbulk; }
+.@{fa-css-prefix}-simplybuilt:before { content: @fa-var-simplybuilt; }
+.@{fa-css-prefix}-skyatlas:before { content: @fa-var-skyatlas; }
+.@{fa-css-prefix}-cart-plus:before { content: @fa-var-cart-plus; }
+.@{fa-css-prefix}-cart-arrow-down:before { content: @fa-var-cart-arrow-down; }
+.@{fa-css-prefix}-diamond:before { content: @fa-var-diamond; }
+.@{fa-css-prefix}-ship:before { content: @fa-var-ship; }
+.@{fa-css-prefix}-user-secret:before { content: @fa-var-user-secret; }
+.@{fa-css-prefix}-motorcycle:before { content: @fa-var-motorcycle; }
+.@{fa-css-prefix}-street-view:before { content: @fa-var-street-view; }
+.@{fa-css-prefix}-heartbeat:before { content: @fa-var-heartbeat; }
+.@{fa-css-prefix}-venus:before { content: @fa-var-venus; }
+.@{fa-css-prefix}-mars:before { content: @fa-var-mars; }
+.@{fa-css-prefix}-mercury:before { content: @fa-var-mercury; }
+.@{fa-css-prefix}-transgender:before { content: @fa-var-transgender; }
+.@{fa-css-prefix}-transgender-alt:before { content: @fa-var-transgender-alt; }
+.@{fa-css-prefix}-venus-double:before { content: @fa-var-venus-double; }
+.@{fa-css-prefix}-mars-double:before { content: @fa-var-mars-double; }
+.@{fa-css-prefix}-venus-mars:before { content: @fa-var-venus-mars; }
+.@{fa-css-prefix}-mars-stroke:before { content: @fa-var-mars-stroke; }
+.@{fa-css-prefix}-mars-stroke-v:before { content: @fa-var-mars-stroke-v; }
+.@{fa-css-prefix}-mars-stroke-h:before { content: @fa-var-mars-stroke-h; }
+.@{fa-css-prefix}-neuter:before { content: @fa-var-neuter; }
+.@{fa-css-prefix}-facebook-official:before { content: @fa-var-facebook-official; }
+.@{fa-css-prefix}-pinterest-p:before { content: @fa-var-pinterest-p; }
+.@{fa-css-prefix}-whatsapp:before { content: @fa-var-whatsapp; }
+.@{fa-css-prefix}-server:before { content: @fa-var-server; }
+.@{fa-css-prefix}-user-plus:before { content: @fa-var-user-plus; }
+.@{fa-css-prefix}-user-times:before { content: @fa-var-user-times; }
+.@{fa-css-prefix}-hotel:before,
+.@{fa-css-prefix}-bed:before { content: @fa-var-bed; }
+.@{fa-css-prefix}-viacoin:before { content: @fa-var-viacoin; }
+.@{fa-css-prefix}-train:before { content: @fa-var-train; }
+.@{fa-css-prefix}-subway:before { content: @fa-var-subway; }
+.@{fa-css-prefix}-medium:before { content: @fa-var-medium; }
+
diff --git a/client/assets/css/forms.less b/client/assets/css/forms.less
new file mode 100644
index 0000000..479614f
--- /dev/null
+++ b/client/assets/css/forms.less
@@ -0,0 +1,438 @@
+//
+// Forms
+// --------------------------------------------------
+
+
+// Normalize non-controls
+//
+// Restyle and baseline non-control form elements.
+
+fieldset {
+ padding: 0;
+ margin: 0;
+ border: 0;
+ // Chrome and Firefox set a `min-width: -webkit-min-content;` on fieldsets,
+ // so we reset that to ensure it behaves more like a standard block element.
+ // See https://github.com/twbs/bootstrap/issues/12359.
+ min-width: 0;
+}
+
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: @line-height-computed;
+ font-size: (@font-size-base * 1.5);
+ line-height: inherit;
+ color: @legend-color;
+ border: 0;
+ border-bottom: 1px solid @legend-border-color;
+}
+
+label {
+ display: inline-block;
+ margin-bottom: 5px;
+ font-weight: bold;
+}
+
+
+// Normalize form controls
+//
+// While most of our form styles require extra classes, some basic normalization
+// is required to ensure optimum display with or without those classes to better
+// address browser inconsistencies.
+
+// Override content-box in Normalize (* isn't specific enough)
+input[type="search"] {
+ .box-sizing(border-box);
+}
+
+// Position radios and checkboxes better
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 4px 0 0;
+ margin-top: 1px \9; /* IE8-9 */
+ line-height: normal;
+}
+
+// Set the height of file controls to match text inputs
+input[type="file"] {
+ display: block;
+}
+
+// Make range inputs behave like textual form controls
+input[type="range"] {
+ display: block;
+ width: 100%;
+}
+
+// Make multiple select elements height not fixed
+select[multiple],
+select[size] {
+ height: auto;
+}
+
+// Focus for file, radio, and checkbox
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+ .tab-focus();
+}
+
+// Adjust output element
+output {
+ display: block;
+ padding-top: (@padding-base-vertical + 1);
+ font-size: @font-size-base;
+ line-height: @line-height-base;
+ color: @input-color;
+}
+
+
+// Common form controls
+//
+// Shared size and type resets for form controls. Apply `.form-control` to any
+// of the following form controls:
+//
+// select
+// textarea
+// input[type="text"]
+// input[type="password"]
+// input[type="datetime"]
+// input[type="datetime-local"]
+// input[type="date"]
+// input[type="month"]
+// input[type="time"]
+// input[type="week"]
+// input[type="number"]
+// input[type="email"]
+// input[type="url"]
+// input[type="search"]
+// input[type="tel"]
+// input[type="color"]
+
+.form-control {
+ display: block;
+ width: 100%;
+ height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)
+ padding: @padding-base-vertical @padding-base-horizontal;
+ font-size: @font-size-base;
+ line-height: @line-height-base;
+ color: @input-color;
+ background-color: @input-bg;
+ background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214
+ border: 1px solid @input-border;
+ border-radius: @input-border-radius;
+ .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));
+ .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s");
+
+ // Customize the `:focus` state to imitate native WebKit styles.
+ .form-control-focus();
+
+ // Placeholder
+ .placeholder();
+
+ // Disabled and read-only inputs
+ //
+ // HTML5 says that controls under a fieldset > legend:first-child won't be
+ // disabled if the fieldset is disabled. Due to implementation difficulty, we
+ // don't honor that edge case; we style them as disabled anyway.
+ &[disabled],
+ &[readonly],
+ fieldset[disabled] & {
+ cursor: not-allowed;
+ background-color: @input-bg-disabled;
+ opacity: 1; // iOS fix for unreadable disabled content
+ }
+
+ // Reset height for `textarea`s
+ textarea& {
+ height: auto;
+ }
+}
+
+
+// Search inputs in iOS
+//
+// This overrides the extra rounded corners on search inputs in iOS so that our
+// `.form-control` class can properly style them. Note that this cannot simply
+// be added to `.form-control` as it's not specific enough. For details, see
+// https://github.com/twbs/bootstrap/issues/11586.
+
+input[type="search"] {
+ -webkit-appearance: none;
+}
+
+
+// Special styles for iOS date input
+//
+// In Mobile Safari, date inputs require a pixel line-height that matches the
+// given height of the input.
+
+input[type="date"] {
+ line-height: @input-height-base;
+}
+
+
+// Form groups
+//
+// Designed to help with the organization and spacing of vertical forms. For
+// horizontal forms, use the predefined grid classes.
+
+.form-group {
+ margin-bottom: 15px;
+}
+
+
+// Checkboxes and radios
+//
+// Indent the labels to position radios/checkboxes as hanging controls.
+
+.radio,
+.checkbox {
+ display: block;
+ min-height: @line-height-computed; // clear the floating input if there is no label text
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding-left: 20px;
+ label {
+ display: inline;
+ font-weight: normal;
+ cursor: pointer;
+ }
+}
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+ float: left;
+ margin-left: -20px;
+}
+.radio + .radio,
+.checkbox + .checkbox {
+ margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing
+}
+
+// Radios and checkboxes on same line
+.radio-inline,
+.checkbox-inline {
+ display: inline-block;
+ padding-left: 20px;
+ margin-bottom: 0;
+ vertical-align: middle;
+ font-weight: normal;
+ cursor: pointer;
+}
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+ margin-top: 0;
+ margin-left: 10px; // space out consecutive inline controls
+}
+
+// Apply same disabled cursor tweak as for inputs
+//
+// Note: Neither radios nor checkboxes can be readonly.
+input[type="radio"],
+input[type="checkbox"],
+.radio,
+.radio-inline,
+.checkbox,
+.checkbox-inline {
+ &[disabled],
+ fieldset[disabled] & {
+ cursor: not-allowed;
+ }
+}
+
+
+// Form control sizing
+//
+// Build on `.form-control` with modifier classes to decrease or increase the
+// height and font-size of form controls.
+
+.input-sm {
+ .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);
+}
+
+.input-lg {
+ .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);
+}
+
+
+// Form control feedback states
+//
+// Apply contextual and semantic states to individual form controls.
+
+.has-feedback {
+ // Enable absolute positioning
+ position: relative;
+
+ // Ensure icons don't overlap text
+ .form-control {
+ padding-right: (@input-height-base * 1.25);
+ }
+
+ // Feedback icon (requires .glyphicon classes)
+ .form-control-feedback {
+ position: absolute;
+ top: (@line-height-computed + 5); // Height of the `label` and its margin
+ right: 0;
+ display: block;
+ width: @input-height-base;
+ height: @input-height-base;
+ line-height: @input-height-base;
+ text-align: center;
+ }
+}
+
+// Feedback states
+.has-success {
+ .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);
+}
+.has-warning {
+ .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);
+}
+.has-error {
+ .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);
+}
+
+
+// Static form control text
+//
+// Apply class to a `p` element to make any string of text align with labels in
+// a horizontal form layout.
+
+.form-control-static {
+ margin-bottom: 0; // Remove default margin from `p`
+}
+
+
+// Help text
+//
+// Apply to any element you wish to create light text for placement immediately
+// below a form control. Use for general help, formatting, or instructional text.
+
+.help-block {
+ display: block; // account for any element using help-block
+ margin-top: 5px;
+ margin-bottom: 10px;
+ color: lighten(@text-color, 25%); // lighten the text some for contrast
+}
+
+
+
+// Inline forms
+//
+// Make forms appear inline(-block) by adding the `.form-inline` class. Inline
+// forms begin stacked on extra small (mobile) devices and then go inline when
+// viewports reach <768px.
+//
+// Requires wrapping inputs and labels with `.form-group` for proper display of
+// default HTML form controls and our custom form controls (e.g., input groups).
+//
+// Heads up! This is mixin-ed into `.navbar-form` in navbars.less.
+
+.form-inline {
+
+ // Kick in the inline
+ @media (min-width: @screen-sm-min) {
+ // Inline-block all the things for "inline"
+ .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+
+ // In navbar-form, allow folks to *not* use `.form-group`
+ .form-control {
+ display: inline-block;
+ width: auto; // Prevent labels from stacking above inputs in `.form-group`
+ vertical-align: middle;
+ }
+ // Input groups need that 100% width though
+ .input-group > .form-control {
+ width: 100%;
+ }
+
+ .control-label {
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+
+ // Remove default margin on radios/checkboxes that were used for stacking, and
+ // then undo the floating of radios and checkboxes to match (which also avoids
+ // a bug in WebKit: https://github.com/twbs/bootstrap/issues/1969).
+ .radio,
+ .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-left: 0;
+ vertical-align: middle;
+ }
+ .radio input[type="radio"],
+ .checkbox input[type="checkbox"] {
+ float: none;
+ margin-left: 0;
+ }
+
+ // Validation states
+ //
+ // Reposition the icon because it's now within a grid column and columns have
+ // `position: relative;` on them. Also accounts for the grid gutter padding.
+ .has-feedback .form-control-feedback {
+ top: 0;
+ }
+ }
+}
+
+
+// Horizontal forms
+//
+// Horizontal forms are built on grid classes and allow you to create forms with
+// labels on the left and inputs on the right.
+
+.form-horizontal {
+
+ // Consistent vertical alignment of labels, radios, and checkboxes
+ .control-label,
+ .radio,
+ .checkbox,
+ .radio-inline,
+ .checkbox-inline {
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-top: (@padding-base-vertical + 1); // Default padding plus a border
+ }
+ // Account for padding we're adding to ensure the alignment and of help text
+ // and other content below items
+ .radio,
+ .checkbox {
+ min-height: (@line-height-computed + (@padding-base-vertical + 1));
+ }
+
+ // Make form groups behave like rows
+ .form-group {
+ .make-row();
+ }
+
+ .form-control-static {
+ padding-top: (@padding-base-vertical + 1);
+ }
+
+ // Only right align form labels here when the columns stop stacking
+ @media (min-width: @screen-sm-min) {
+ .control-label {
+ text-align: right;
+ }
+ }
+
+ // Validation states
+ //
+ // Reposition the icon because it's now within a grid column and columns have
+ // `position: relative;` on them. Also accounts for the grid gutter padding.
+ .has-feedback .form-control-feedback {
+ top: 0;
+ right: (@grid-gutter-width / 2);
+ }
+}
diff --git a/client/assets/css/glyphicons.less b/client/assets/css/glyphicons.less
new file mode 100644
index 0000000..2bb0baf
--- /dev/null
+++ b/client/assets/css/glyphicons.less
@@ -0,0 +1,233 @@
+//
+// Glyphicons for Bootstrap
+//
+// Since icons are fonts, they can be placed anywhere text is placed and are
+// thus automatically sized to match the surrounding child. To use, create an
+// inline element with the appropriate classes, like so:
+//
+// Star
+
+// Import the fonts
+@font-face {
+ font-family: 'Glyphicons Halflings';
+ src: ~"url('@{icon-font-path}@{icon-font-name}.eot')";
+ src: ~"url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype')",
+ ~"url('@{icon-font-path}@{icon-font-name}.woff') format('woff')",
+ ~"url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype')",
+ ~"url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg')";
+}
+
+// Catchall baseclass
+.glyphicon {
+ position: relative;
+ top: 1px;
+ display: inline-block;
+ font-family: 'Glyphicons Halflings';
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+// Individual icons
+.glyphicon-asterisk { &:before { content: "\2a"; } }
+.glyphicon-plus { &:before { content: "\2b"; } }
+.glyphicon-euro { &:before { content: "\20ac"; } }
+.glyphicon-minus { &:before { content: "\2212"; } }
+.glyphicon-cloud { &:before { content: "\2601"; } }
+.glyphicon-envelope { &:before { content: "\2709"; } }
+.glyphicon-pencil { &:before { content: "\270f"; } }
+.glyphicon-glass { &:before { content: "\e001"; } }
+.glyphicon-music { &:before { content: "\e002"; } }
+.glyphicon-search { &:before { content: "\e003"; } }
+.glyphicon-heart { &:before { content: "\e005"; } }
+.glyphicon-star { &:before { content: "\e006"; } }
+.glyphicon-star-empty { &:before { content: "\e007"; } }
+.glyphicon-user { &:before { content: "\e008"; } }
+.glyphicon-film { &:before { content: "\e009"; } }
+.glyphicon-th-large { &:before { content: "\e010"; } }
+.glyphicon-th { &:before { content: "\e011"; } }
+.glyphicon-th-list { &:before { content: "\e012"; } }
+.glyphicon-ok { &:before { content: "\e013"; } }
+.glyphicon-remove { &:before { content: "\e014"; } }
+.glyphicon-zoom-in { &:before { content: "\e015"; } }
+.glyphicon-zoom-out { &:before { content: "\e016"; } }
+.glyphicon-off { &:before { content: "\e017"; } }
+.glyphicon-signal { &:before { content: "\e018"; } }
+.glyphicon-cog { &:before { content: "\e019"; } }
+.glyphicon-trash { &:before { content: "\e020"; } }
+.glyphicon-home { &:before { content: "\e021"; } }
+.glyphicon-file { &:before { content: "\e022"; } }
+.glyphicon-time { &:before { content: "\e023"; } }
+.glyphicon-road { &:before { content: "\e024"; } }
+.glyphicon-download-alt { &:before { content: "\e025"; } }
+.glyphicon-download { &:before { content: "\e026"; } }
+.glyphicon-upload { &:before { content: "\e027"; } }
+.glyphicon-inbox { &:before { content: "\e028"; } }
+.glyphicon-play-circle { &:before { content: "\e029"; } }
+.glyphicon-repeat { &:before { content: "\e030"; } }
+.glyphicon-refresh { &:before { content: "\e031"; } }
+.glyphicon-list-alt { &:before { content: "\e032"; } }
+.glyphicon-lock { &:before { content: "\e033"; } }
+.glyphicon-flag { &:before { content: "\e034"; } }
+.glyphicon-headphones { &:before { content: "\e035"; } }
+.glyphicon-volume-off { &:before { content: "\e036"; } }
+.glyphicon-volume-down { &:before { content: "\e037"; } }
+.glyphicon-volume-up { &:before { content: "\e038"; } }
+.glyphicon-qrcode { &:before { content: "\e039"; } }
+.glyphicon-barcode { &:before { content: "\e040"; } }
+.glyphicon-tag { &:before { content: "\e041"; } }
+.glyphicon-tags { &:before { content: "\e042"; } }
+.glyphicon-book { &:before { content: "\e043"; } }
+.glyphicon-bookmark { &:before { content: "\e044"; } }
+.glyphicon-print { &:before { content: "\e045"; } }
+.glyphicon-camera { &:before { content: "\e046"; } }
+.glyphicon-font { &:before { content: "\e047"; } }
+.glyphicon-bold { &:before { content: "\e048"; } }
+.glyphicon-italic { &:before { content: "\e049"; } }
+.glyphicon-text-height { &:before { content: "\e050"; } }
+.glyphicon-text-width { &:before { content: "\e051"; } }
+.glyphicon-align-left { &:before { content: "\e052"; } }
+.glyphicon-align-center { &:before { content: "\e053"; } }
+.glyphicon-align-right { &:before { content: "\e054"; } }
+.glyphicon-align-justify { &:before { content: "\e055"; } }
+.glyphicon-list { &:before { content: "\e056"; } }
+.glyphicon-indent-left { &:before { content: "\e057"; } }
+.glyphicon-indent-right { &:before { content: "\e058"; } }
+.glyphicon-facetime-video { &:before { content: "\e059"; } }
+.glyphicon-picture { &:before { content: "\e060"; } }
+.glyphicon-map-marker { &:before { content: "\e062"; } }
+.glyphicon-adjust { &:before { content: "\e063"; } }
+.glyphicon-tint { &:before { content: "\e064"; } }
+.glyphicon-edit { &:before { content: "\e065"; } }
+.glyphicon-share { &:before { content: "\e066"; } }
+.glyphicon-check { &:before { content: "\e067"; } }
+.glyphicon-move { &:before { content: "\e068"; } }
+.glyphicon-step-backward { &:before { content: "\e069"; } }
+.glyphicon-fast-backward { &:before { content: "\e070"; } }
+.glyphicon-backward { &:before { content: "\e071"; } }
+.glyphicon-play { &:before { content: "\e072"; } }
+.glyphicon-pause { &:before { content: "\e073"; } }
+.glyphicon-stop { &:before { content: "\e074"; } }
+.glyphicon-forward { &:before { content: "\e075"; } }
+.glyphicon-fast-forward { &:before { content: "\e076"; } }
+.glyphicon-step-forward { &:before { content: "\e077"; } }
+.glyphicon-eject { &:before { content: "\e078"; } }
+.glyphicon-chevron-left { &:before { content: "\e079"; } }
+.glyphicon-chevron-right { &:before { content: "\e080"; } }
+.glyphicon-plus-sign { &:before { content: "\e081"; } }
+.glyphicon-minus-sign { &:before { content: "\e082"; } }
+.glyphicon-remove-sign { &:before { content: "\e083"; } }
+.glyphicon-ok-sign { &:before { content: "\e084"; } }
+.glyphicon-question-sign { &:before { content: "\e085"; } }
+.glyphicon-info-sign { &:before { content: "\e086"; } }
+.glyphicon-screenshot { &:before { content: "\e087"; } }
+.glyphicon-remove-circle { &:before { content: "\e088"; } }
+.glyphicon-ok-circle { &:before { content: "\e089"; } }
+.glyphicon-ban-circle { &:before { content: "\e090"; } }
+.glyphicon-arrow-left { &:before { content: "\e091"; } }
+.glyphicon-arrow-right { &:before { content: "\e092"; } }
+.glyphicon-arrow-up { &:before { content: "\e093"; } }
+.glyphicon-arrow-down { &:before { content: "\e094"; } }
+.glyphicon-share-alt { &:before { content: "\e095"; } }
+.glyphicon-resize-full { &:before { content: "\e096"; } }
+.glyphicon-resize-small { &:before { content: "\e097"; } }
+.glyphicon-exclamation-sign { &:before { content: "\e101"; } }
+.glyphicon-gift { &:before { content: "\e102"; } }
+.glyphicon-leaf { &:before { content: "\e103"; } }
+.glyphicon-fire { &:before { content: "\e104"; } }
+.glyphicon-eye-open { &:before { content: "\e105"; } }
+.glyphicon-eye-close { &:before { content: "\e106"; } }
+.glyphicon-warning-sign { &:before { content: "\e107"; } }
+.glyphicon-plane { &:before { content: "\e108"; } }
+.glyphicon-calendar { &:before { content: "\e109"; } }
+.glyphicon-random { &:before { content: "\e110"; } }
+.glyphicon-comment { &:before { content: "\e111"; } }
+.glyphicon-magnet { &:before { content: "\e112"; } }
+.glyphicon-chevron-up { &:before { content: "\e113"; } }
+.glyphicon-chevron-down { &:before { content: "\e114"; } }
+.glyphicon-retweet { &:before { content: "\e115"; } }
+.glyphicon-shopping-cart { &:before { content: "\e116"; } }
+.glyphicon-folder-close { &:before { content: "\e117"; } }
+.glyphicon-folder-open { &:before { content: "\e118"; } }
+.glyphicon-resize-vertical { &:before { content: "\e119"; } }
+.glyphicon-resize-horizontal { &:before { content: "\e120"; } }
+.glyphicon-hdd { &:before { content: "\e121"; } }
+.glyphicon-bullhorn { &:before { content: "\e122"; } }
+.glyphicon-bell { &:before { content: "\e123"; } }
+.glyphicon-certificate { &:before { content: "\e124"; } }
+.glyphicon-thumbs-up { &:before { content: "\e125"; } }
+.glyphicon-thumbs-down { &:before { content: "\e126"; } }
+.glyphicon-hand-right { &:before { content: "\e127"; } }
+.glyphicon-hand-left { &:before { content: "\e128"; } }
+.glyphicon-hand-up { &:before { content: "\e129"; } }
+.glyphicon-hand-down { &:before { content: "\e130"; } }
+.glyphicon-circle-arrow-right { &:before { content: "\e131"; } }
+.glyphicon-circle-arrow-left { &:before { content: "\e132"; } }
+.glyphicon-circle-arrow-up { &:before { content: "\e133"; } }
+.glyphicon-circle-arrow-down { &:before { content: "\e134"; } }
+.glyphicon-globe { &:before { content: "\e135"; } }
+.glyphicon-wrench { &:before { content: "\e136"; } }
+.glyphicon-tasks { &:before { content: "\e137"; } }
+.glyphicon-filter { &:before { content: "\e138"; } }
+.glyphicon-briefcase { &:before { content: "\e139"; } }
+.glyphicon-fullscreen { &:before { content: "\e140"; } }
+.glyphicon-dashboard { &:before { content: "\e141"; } }
+.glyphicon-paperclip { &:before { content: "\e142"; } }
+.glyphicon-heart-empty { &:before { content: "\e143"; } }
+.glyphicon-link { &:before { content: "\e144"; } }
+.glyphicon-phone { &:before { content: "\e145"; } }
+.glyphicon-pushpin { &:before { content: "\e146"; } }
+.glyphicon-usd { &:before { content: "\e148"; } }
+.glyphicon-gbp { &:before { content: "\e149"; } }
+.glyphicon-sort { &:before { content: "\e150"; } }
+.glyphicon-sort-by-alphabet { &:before { content: "\e151"; } }
+.glyphicon-sort-by-alphabet-alt { &:before { content: "\e152"; } }
+.glyphicon-sort-by-order { &:before { content: "\e153"; } }
+.glyphicon-sort-by-order-alt { &:before { content: "\e154"; } }
+.glyphicon-sort-by-attributes { &:before { content: "\e155"; } }
+.glyphicon-sort-by-attributes-alt { &:before { content: "\e156"; } }
+.glyphicon-unchecked { &:before { content: "\e157"; } }
+.glyphicon-expand { &:before { content: "\e158"; } }
+.glyphicon-collapse-down { &:before { content: "\e159"; } }
+.glyphicon-collapse-up { &:before { content: "\e160"; } }
+.glyphicon-log-in { &:before { content: "\e161"; } }
+.glyphicon-flash { &:before { content: "\e162"; } }
+.glyphicon-log-out { &:before { content: "\e163"; } }
+.glyphicon-new-window { &:before { content: "\e164"; } }
+.glyphicon-record { &:before { content: "\e165"; } }
+.glyphicon-save { &:before { content: "\e166"; } }
+.glyphicon-open { &:before { content: "\e167"; } }
+.glyphicon-saved { &:before { content: "\e168"; } }
+.glyphicon-import { &:before { content: "\e169"; } }
+.glyphicon-export { &:before { content: "\e170"; } }
+.glyphicon-send { &:before { content: "\e171"; } }
+.glyphicon-floppy-disk { &:before { content: "\e172"; } }
+.glyphicon-floppy-saved { &:before { content: "\e173"; } }
+.glyphicon-floppy-remove { &:before { content: "\e174"; } }
+.glyphicon-floppy-save { &:before { content: "\e175"; } }
+.glyphicon-floppy-open { &:before { content: "\e176"; } }
+.glyphicon-credit-card { &:before { content: "\e177"; } }
+.glyphicon-transfer { &:before { content: "\e178"; } }
+.glyphicon-cutlery { &:before { content: "\e179"; } }
+.glyphicon-header { &:before { content: "\e180"; } }
+.glyphicon-compressed { &:before { content: "\e181"; } }
+.glyphicon-earphone { &:before { content: "\e182"; } }
+.glyphicon-phone-alt { &:before { content: "\e183"; } }
+.glyphicon-tower { &:before { content: "\e184"; } }
+.glyphicon-stats { &:before { content: "\e185"; } }
+.glyphicon-sd-video { &:before { content: "\e186"; } }
+.glyphicon-hd-video { &:before { content: "\e187"; } }
+.glyphicon-subtitles { &:before { content: "\e188"; } }
+.glyphicon-sound-stereo { &:before { content: "\e189"; } }
+.glyphicon-sound-dolby { &:before { content: "\e190"; } }
+.glyphicon-sound-5-1 { &:before { content: "\e191"; } }
+.glyphicon-sound-6-1 { &:before { content: "\e192"; } }
+.glyphicon-sound-7-1 { &:before { content: "\e193"; } }
+.glyphicon-copyright-mark { &:before { content: "\e194"; } }
+.glyphicon-registration-mark { &:before { content: "\e195"; } }
+.glyphicon-cloud-download { &:before { content: "\e197"; } }
+.glyphicon-cloud-upload { &:before { content: "\e198"; } }
+.glyphicon-tree-conifer { &:before { content: "\e199"; } }
+.glyphicon-tree-deciduous { &:before { content: "\e200"; } }
diff --git a/client/assets/css/grid.less b/client/assets/css/grid.less
new file mode 100644
index 0000000..02a950c
--- /dev/null
+++ b/client/assets/css/grid.less
@@ -0,0 +1,84 @@
+//
+// Grid system
+// --------------------------------------------------
+
+
+// Container widths
+//
+// Set the container width, and override it for fixed navbars in media queries.
+
+.container {
+ .container-fixed();
+
+ @media (min-width: @screen-sm-min) {
+ width: @container-sm;
+ }
+ @media (min-width: @screen-md-min) {
+ width: @container-md;
+ }
+ @media (min-width: @screen-lg-min) {
+ width: @container-lg;
+ }
+}
+
+
+// Fluid container
+//
+// Utilizes the mixin meant for fixed width containers, but without any defined
+// width for fluid, full width layouts.
+
+.container-fluid {
+ .container-fixed();
+}
+
+
+// Row
+//
+// Rows contain and clear the floats of your columns.
+
+.row {
+ .make-row();
+}
+
+
+// Columns
+//
+// Common styles for small and large grid columns
+
+.make-grid-columns();
+
+
+// Extra small grid
+//
+// Columns, offsets, pushes, and pulls for extra small devices like
+// smartphones.
+
+.make-grid(xs);
+
+
+// Small grid
+//
+// Columns, offsets, pushes, and pulls for the small device range, from phones
+// to tablets.
+
+@media (min-width: @screen-sm-min) {
+ .make-grid(sm);
+}
+
+
+// Medium grid
+//
+// Columns, offsets, pushes, and pulls for the desktop device range.
+
+@media (min-width: @screen-md-min) {
+ .make-grid(md);
+}
+
+
+// Large grid
+//
+// Columns, offsets, pushes, and pulls for the large desktop device range.
+
+@media (min-width: @screen-lg-min) {
+ .make-grid(lg);
+}
diff --git a/client/assets/css/input-groups.less b/client/assets/css/input-groups.less
new file mode 100644
index 0000000..2954955
--- /dev/null
+++ b/client/assets/css/input-groups.less
@@ -0,0 +1,162 @@
+//
+// Input groups
+// --------------------------------------------------
+
+// Base styles
+// -------------------------
+.input-group {
+ position: relative; // For dropdowns
+ display: table;
+ border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table
+
+ // Undo padding and float of grid classes
+ &[class*="col-"] {
+ float: none;
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ .form-control {
+ // Ensure that the input is always above the *appended* addon button for
+ // proper border colors.
+ position: relative;
+ z-index: 2;
+
+ // IE9 fubars the placeholder attribute in text inputs and the arrows on
+ // select elements in input groups. To fix it, we float the input. Details:
+ // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855
+ float: left;
+
+ width: 100%;
+ margin-bottom: 0;
+ }
+}
+
+// Sizing options
+//
+// Remix the default form control sizing classes into new ones for easier
+// manipulation.
+
+.input-group-lg > .form-control,
+.input-group-lg > .input-group-addon,
+.input-group-lg > .input-group-btn > .btn { .input-lg(); }
+.input-group-sm > .form-control,
+.input-group-sm > .input-group-addon,
+.input-group-sm > .input-group-btn > .btn { .input-sm(); }
+
+
+// Display as table-cell
+// -------------------------
+.input-group-addon,
+.input-group-btn,
+.input-group .form-control {
+ display: table-cell;
+
+ &:not(:first-child):not(:last-child) {
+ border-radius: 0;
+ }
+}
+// Addon and addon wrapper for buttons
+.input-group-addon,
+.input-group-btn {
+ width: 1%;
+ white-space: nowrap;
+ vertical-align: middle; // Match the inputs
+}
+
+// Text input groups
+// -------------------------
+.input-group-addon {
+ padding: @padding-base-vertical @padding-base-horizontal;
+ font-size: @font-size-base;
+ font-weight: normal;
+ line-height: 1;
+ color: @input-color;
+ text-align: center;
+ background-color: @input-group-addon-bg;
+ border: 1px solid @input-group-addon-border-color;
+ border-radius: @border-radius-base;
+
+ // Sizing
+ &.input-sm {
+ padding: @padding-small-vertical @padding-small-horizontal;
+ font-size: @font-size-small;
+ border-radius: @border-radius-small;
+ }
+ &.input-lg {
+ padding: @padding-large-vertical @padding-large-horizontal;
+ font-size: @font-size-large;
+ border-radius: @border-radius-large;
+ }
+
+ // Nuke default margins from checkboxes and radios to vertically center within.
+ input[type="radio"],
+ input[type="checkbox"] {
+ margin-top: 0;
+ }
+}
+
+// Reset rounded corners
+.input-group .form-control:first-child,
+.input-group-addon:first-child,
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group > .btn,
+.input-group-btn:first-child > .dropdown-toggle,
+.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),
+.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
+ .border-right-radius(0);
+}
+.input-group-addon:first-child {
+ border-right: 0;
+}
+.input-group .form-control:last-child,
+.input-group-addon:last-child,
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group > .btn,
+.input-group-btn:last-child > .dropdown-toggle,
+.input-group-btn:first-child > .btn:not(:first-child),
+.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
+ .border-left-radius(0);
+}
+.input-group-addon:last-child {
+ border-left: 0;
+}
+
+// Button input groups
+// -------------------------
+.input-group-btn {
+ position: relative;
+ // Jankily prevent input button groups from wrapping with `white-space` and
+ // `font-size` in combination with `inline-block` on buttons.
+ font-size: 0;
+ white-space: nowrap;
+
+ // Negative margin for spacing, position for bringing hovered/focused/actived
+ // element above the siblings.
+ > .btn {
+ position: relative;
+ + .btn {
+ margin-left: -1px;
+ }
+ // Bring the "active" button to the front
+ &:hover,
+ &:focus,
+ &:active {
+ z-index: 2;
+ }
+ }
+
+ // Negative margin to only have a 1px border between the two
+ &:first-child {
+ > .btn,
+ > .btn-group {
+ margin-right: -1px;
+ }
+ }
+ &:last-child {
+ > .btn,
+ > .btn-group {
+ margin-left: -1px;
+ }
+ }
+}
diff --git a/client/assets/css/jumbotron.less b/client/assets/css/jumbotron.less
new file mode 100644
index 0000000..16f1ef3
--- /dev/null
+++ b/client/assets/css/jumbotron.less
@@ -0,0 +1,44 @@
+//
+// Jumbotron
+// --------------------------------------------------
+
+
+.jumbotron {
+ padding: @jumbotron-padding;
+ margin-bottom: @jumbotron-padding;
+ color: @jumbotron-color;
+ background-color: @jumbotron-bg;
+
+ h1,
+ .h1 {
+ color: @jumbotron-heading-color;
+ }
+ p {
+ margin-bottom: (@jumbotron-padding / 2);
+ font-size: @jumbotron-font-size;
+ font-weight: 200;
+ }
+
+ .container & {
+ border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container
+ }
+
+ .container {
+ max-width: 100%;
+ }
+
+ @media screen and (min-width: @screen-sm-min) {
+ padding-top: (@jumbotron-padding * 1.6);
+ padding-bottom: (@jumbotron-padding * 1.6);
+
+ .container & {
+ padding-left: (@jumbotron-padding * 2);
+ padding-right: (@jumbotron-padding * 2);
+ }
+
+ h1,
+ .h1 {
+ font-size: (@font-size-base * 4.5);
+ }
+ }
+}
diff --git a/client/assets/css/labels.less b/client/assets/css/labels.less
new file mode 100644
index 0000000..5fc1be8
--- /dev/null
+++ b/client/assets/css/labels.less
@@ -0,0 +1,64 @@
+//
+// Labels
+// --------------------------------------------------
+
+.label {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: bold;
+ line-height: 1;
+ color: @label-color;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em;
+
+ // Add hover effects, but only for links
+ &[href] {
+ &:hover,
+ &:focus {
+ color: @label-link-hover-color;
+ text-decoration: none;
+ cursor: pointer;
+ }
+ }
+
+ // Empty labels collapse automatically (not available in IE8)
+ &:empty {
+ display: none;
+ }
+
+ // Quick fix for labels in buttons
+ .btn & {
+ position: relative;
+ top: -1px;
+ }
+}
+
+// Colors
+// Contextual variations (linked labels get darker on :hover)
+
+.label-default {
+ .label-variant(@label-default-bg);
+}
+
+.label-primary {
+ .label-variant(@label-primary-bg);
+}
+
+.label-success {
+ .label-variant(@label-success-bg);
+}
+
+.label-info {
+ .label-variant(@label-info-bg);
+}
+
+.label-warning {
+ .label-variant(@label-warning-bg);
+}
+
+.label-danger {
+ .label-variant(@label-danger-bg);
+}
diff --git a/client/assets/css/list-group.less b/client/assets/css/list-group.less
new file mode 100644
index 0000000..4782a20
--- /dev/null
+++ b/client/assets/css/list-group.less
@@ -0,0 +1,110 @@
+//
+// List groups
+// --------------------------------------------------
+
+
+// Base class
+//
+// Easily usable on , , or .
+
+.list-group {
+ // No need to set list-style: none; since .list-group-item is block level
+ margin-bottom: 20px;
+ padding-left: 0; // reset padding because ul and ol
+}
+
+
+// Individual list items
+//
+// Use on `li`s or `div`s within the `.list-group` parent.
+
+.list-group-item {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
+ // Place the border on the list items and negative margin up for better styling
+ margin-bottom: -1px;
+ background-color: @list-group-bg;
+ border: 1px solid @list-group-border;
+
+ // Round the first and last items
+ &:first-child {
+ .border-top-radius(@list-group-border-radius);
+ }
+ &:last-child {
+ margin-bottom: 0;
+ .border-bottom-radius(@list-group-border-radius);
+ }
+
+ // Align badges within list items
+ > .badge {
+ float: right;
+ }
+ > .badge + .badge {
+ margin-right: 5px;
+ }
+}
+
+
+// Linked list items
+//
+// Use anchor elements instead of `li`s or `div`s to create linked list items.
+// Includes an extra `.active` modifier class for showing selected items.
+
+a.list-group-item {
+ color: @list-group-link-color;
+
+ .list-group-item-heading {
+ color: @list-group-link-heading-color;
+ }
+
+ // Hover state
+ &:hover,
+ &:focus {
+ text-decoration: none;
+ background-color: @list-group-hover-bg;
+ }
+
+ // Active class on item itself, not parent
+ &.active,
+ &.active:hover,
+ &.active:focus {
+ z-index: 2; // Place active items above their siblings for proper border styling
+ color: @list-group-active-color;
+ background-color: @list-group-active-bg;
+ border-color: @list-group-active-border;
+
+ // Force color to inherit for custom content
+ .list-group-item-heading {
+ color: inherit;
+ }
+ .list-group-item-text {
+ color: @list-group-active-text-color;
+ }
+ }
+}
+
+
+// Contextual variants
+//
+// Add modifier classes to change text and background color on individual items.
+// Organizationally, this must come after the `:hover` states.
+
+.list-group-item-variant(success; @state-success-bg; @state-success-text);
+.list-group-item-variant(info; @state-info-bg; @state-info-text);
+.list-group-item-variant(warning; @state-warning-bg; @state-warning-text);
+.list-group-item-variant(danger; @state-danger-bg; @state-danger-text);
+
+
+// Custom content options
+//
+// Extra classes for creating well-formatted content within `.list-group-item`s.
+
+.list-group-item-heading {
+ margin-top: 0;
+ margin-bottom: 5px;
+}
+.list-group-item-text {
+ margin-bottom: 0;
+ line-height: 1.3;
+}
diff --git a/client/assets/css/main.less b/client/assets/css/main.less
new file mode 100644
index 0000000..3838114
--- /dev/null
+++ b/client/assets/css/main.less
@@ -0,0 +1,33 @@
+/**
+ * This is the main application stylesheet. It should include or import all
+ * stylesheets used throughout the application as this is the only stylesheet in
+ * the Grunt configuration that is automatically processed.
+ */
+
+
+/**
+ * First, we include the Twitter Bootstrap LESS files. Only the ones used in the
+ * project should be imported as the rest are just wasting space.
+ */
+
+@import 'bootstrap.less';
+
+/**
+ * This is our main variables file. We must include it last so we can overwrite any variable
+ * definitions in our imported stylesheets.
+ */
+
+@import 'variables.less';
+
+
+/**
+ * Typography
+ */
+
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ src: local('Roboto Regular'), local('Roboto-Regular'), url(fonts/Roboto-Regular.woff) format('woff');
+}
+
diff --git a/client/assets/css/media.less b/client/assets/css/media.less
new file mode 100644
index 0000000..38617d9
--- /dev/null
+++ b/client/assets/css/media.less
@@ -0,0 +1,56 @@
+// Media objects
+// Source: http://stubbornella.org/content/?p=497
+// --------------------------------------------------
+
+
+// Common styles
+// -------------------------
+
+// Clear the floats
+.media,
+.media-body {
+ overflow: hidden;
+ zoom: 1;
+}
+
+// Proper spacing between instances of .media
+.media,
+.media .media {
+ margin-top: 15px;
+}
+.media:first-child {
+ margin-top: 0;
+}
+
+// For images and videos, set to block
+.media-object {
+ display: block;
+}
+
+// Reset margins on headings for tighter default spacing
+.media-heading {
+ margin: 0 0 5px;
+}
+
+
+// Media image alignment
+// -------------------------
+
+.media {
+ > .pull-left {
+ margin-right: 10px;
+ }
+ > .pull-right {
+ margin-left: 10px;
+ }
+}
+
+
+// Media list variation
+// -------------------------
+
+// Undo default ul/ol styles
+.media-list {
+ padding-left: 0;
+ list-style: none;
+}
diff --git a/client/assets/css/mixins.less b/client/assets/css/mixins.less
new file mode 100644
index 0000000..67a2cba
--- /dev/null
+++ b/client/assets/css/mixins.less
@@ -0,0 +1,929 @@
+//
+// Mixins
+// --------------------------------------------------
+
+
+// Utilities
+// -------------------------
+
+// Clearfix
+// Source: http://nicolasgallagher.com/micro-clearfix-hack/
+//
+// For modern browsers
+// 1. The space content is one way to avoid an Opera bug when the
+// contenteditable attribute is included anywhere else in the document.
+// Otherwise it causes space to appear at the top and bottom of elements
+// that are clearfixed.
+// 2. The use of `table` rather than `block` is only necessary if using
+// `:before` to contain the top-margins of child elements.
+.clearfix() {
+ &:before,
+ &:after {
+ content: " "; // 1
+ display: table; // 2
+ }
+ &:after {
+ clear: both;
+ }
+}
+
+// WebKit-style focus
+.tab-focus() {
+ // Default
+ outline: thin dotted;
+ // WebKit
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+// Center-align a block level element
+.center-block() {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+// Sizing shortcuts
+.size(@width; @height) {
+ width: @width;
+ height: @height;
+}
+.square(@size) {
+ .size(@size; @size);
+}
+
+// Placeholder text
+.placeholder(@color: @input-color-placeholder) {
+ &::-moz-placeholder { color: @color; // Firefox
+ opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526
+ &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+
+ &::-webkit-input-placeholder { color: @color; } // Safari and Chrome
+}
+
+// Text overflow
+// Requires inline-block or block for proper styling
+.text-overflow() {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+// CSS image replacement
+//
+// Heads up! v3 launched with with only `.hide-text()`, but per our pattern for
+// mixins being reused as classes with the same name, this doesn't hold up. As
+// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`. Note
+// that we cannot chain the mixins together in Less, so they are repeated.
+//
+// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757
+
+// Deprecated as of v3.0.1 (will be removed in v4)
+.hide-text() {
+ font: ~"0/0" a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+// New mixin to use as of v3.0.1
+.text-hide() {
+ .hide-text();
+}
+
+
+
+// CSS3 PROPERTIES
+// --------------------------------------------------
+
+// Single side border-radius
+.border-top-radius(@radius) {
+ border-top-right-radius: @radius;
+ border-top-left-radius: @radius;
+}
+.border-right-radius(@radius) {
+ border-bottom-right-radius: @radius;
+ border-top-right-radius: @radius;
+}
+.border-bottom-radius(@radius) {
+ border-bottom-right-radius: @radius;
+ border-bottom-left-radius: @radius;
+}
+.border-left-radius(@radius) {
+ border-bottom-left-radius: @radius;
+ border-top-left-radius: @radius;
+}
+
+// Drop shadows
+//
+// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's
+// supported browsers that have box shadow capabilities now support the
+// standard `box-shadow` property.
+.box-shadow(@shadow) {
+ -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1
+ box-shadow: @shadow;
+}
+
+// Transitions
+.transition(@transition) {
+ -webkit-transition: @transition;
+ transition: @transition;
+}
+.transition-property(@transition-property) {
+ -webkit-transition-property: @transition-property;
+ transition-property: @transition-property;
+}
+.transition-delay(@transition-delay) {
+ -webkit-transition-delay: @transition-delay;
+ transition-delay: @transition-delay;
+}
+.transition-duration(@transition-duration) {
+ -webkit-transition-duration: @transition-duration;
+ transition-duration: @transition-duration;
+}
+.transition-transform(@transition) {
+ -webkit-transition: -webkit-transform @transition;
+ -moz-transition: -moz-transform @transition;
+ -o-transition: -o-transform @transition;
+ transition: transform @transition;
+}
+
+// Transformations
+.rotate(@degrees) {
+ -webkit-transform: rotate(@degrees);
+ -ms-transform: rotate(@degrees); // IE9 only
+ transform: rotate(@degrees);
+}
+.scale(@ratio; @ratio-y...) {
+ -webkit-transform: scale(@ratio, @ratio-y);
+ -ms-transform: scale(@ratio, @ratio-y); // IE9 only
+ transform: scale(@ratio, @ratio-y);
+}
+.translate(@x; @y) {
+ -webkit-transform: translate(@x, @y);
+ -ms-transform: translate(@x, @y); // IE9 only
+ transform: translate(@x, @y);
+}
+.skew(@x; @y) {
+ -webkit-transform: skew(@x, @y);
+ -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+
+ transform: skew(@x, @y);
+}
+.translate3d(@x; @y; @z) {
+ -webkit-transform: translate3d(@x, @y, @z);
+ transform: translate3d(@x, @y, @z);
+}
+
+.rotateX(@degrees) {
+ -webkit-transform: rotateX(@degrees);
+ -ms-transform: rotateX(@degrees); // IE9 only
+ transform: rotateX(@degrees);
+}
+.rotateY(@degrees) {
+ -webkit-transform: rotateY(@degrees);
+ -ms-transform: rotateY(@degrees); // IE9 only
+ transform: rotateY(@degrees);
+}
+.perspective(@perspective) {
+ -webkit-perspective: @perspective;
+ -moz-perspective: @perspective;
+ perspective: @perspective;
+}
+.perspective-origin(@perspective) {
+ -webkit-perspective-origin: @perspective;
+ -moz-perspective-origin: @perspective;
+ perspective-origin: @perspective;
+}
+.transform-origin(@origin) {
+ -webkit-transform-origin: @origin;
+ -moz-transform-origin: @origin;
+ -ms-transform-origin: @origin; // IE9 only
+ transform-origin: @origin;
+}
+
+// Animations
+.animation(@animation) {
+ -webkit-animation: @animation;
+ animation: @animation;
+}
+.animation-name(@name) {
+ -webkit-animation-name: @name;
+ animation-name: @name;
+}
+.animation-duration(@duration) {
+ -webkit-animation-duration: @duration;
+ animation-duration: @duration;
+}
+.animation-timing-function(@timing-function) {
+ -webkit-animation-timing-function: @timing-function;
+ animation-timing-function: @timing-function;
+}
+.animation-delay(@delay) {
+ -webkit-animation-delay: @delay;
+ animation-delay: @delay;
+}
+.animation-iteration-count(@iteration-count) {
+ -webkit-animation-iteration-count: @iteration-count;
+ animation-iteration-count: @iteration-count;
+}
+.animation-direction(@direction) {
+ -webkit-animation-direction: @direction;
+ animation-direction: @direction;
+}
+
+// Backface visibility
+// Prevent browsers from flickering when using CSS 3D transforms.
+// Default value is `visible`, but can be changed to `hidden`
+.backface-visibility(@visibility){
+ -webkit-backface-visibility: @visibility;
+ -moz-backface-visibility: @visibility;
+ backface-visibility: @visibility;
+}
+
+// Box sizing
+.box-sizing(@boxmodel) {
+ -webkit-box-sizing: @boxmodel;
+ -moz-box-sizing: @boxmodel;
+ box-sizing: @boxmodel;
+}
+
+// User select
+// For selecting text on the page
+.user-select(@select) {
+ -webkit-user-select: @select;
+ -moz-user-select: @select;
+ -ms-user-select: @select; // IE10+
+ user-select: @select;
+}
+
+// Resize anything
+.resizable(@direction) {
+ resize: @direction; // Options: horizontal, vertical, both
+ overflow: auto; // Safari fix
+}
+
+// CSS3 Content Columns
+.content-columns(@column-count; @column-gap: @grid-gutter-width) {
+ -webkit-column-count: @column-count;
+ -moz-column-count: @column-count;
+ column-count: @column-count;
+ -webkit-column-gap: @column-gap;
+ -moz-column-gap: @column-gap;
+ column-gap: @column-gap;
+}
+
+// Optional hyphenation
+.hyphens(@mode: auto) {
+ word-wrap: break-word;
+ -webkit-hyphens: @mode;
+ -moz-hyphens: @mode;
+ -ms-hyphens: @mode; // IE10+
+ -o-hyphens: @mode;
+ hyphens: @mode;
+}
+
+// Opacity
+.opacity(@opacity) {
+ opacity: @opacity;
+ // IE8 filter
+ @opacity-ie: (@opacity * 100);
+ filter: ~"alpha(opacity=@{opacity-ie})";
+}
+
+
+
+// GRADIENTS
+// --------------------------------------------------
+
+#gradient {
+
+ // Horizontal gradient, from left to right
+ //
+ // Creates two color stops, start and end, by specifying a color and position for each color stop.
+ // Color stops are not available in IE9 and below.
+ .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {
+ background-image: -webkit-linear-gradient(left, color-stop(@start-color @start-percent), color-stop(@end-color @end-percent)); // Safari 5.1-6, Chrome 10+
+ background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+
+ background-repeat: repeat-x;
+ filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@start-color),argb(@end-color))); // IE9 and down
+ }
+
+ // Vertical gradient, from top to bottom
+ //
+ // Creates two color stops, start and end, by specifying a color and position for each color stop.
+ // Color stops are not available in IE9 and below.
+ .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {
+ background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+
+ background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+
+ background-repeat: repeat-x;
+ filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@start-color),argb(@end-color))); // IE9 and down
+ }
+
+ .directional(@start-color: #555; @end-color: #333; @deg: 45deg) {
+ background-repeat: repeat-x;
+ background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+
+ background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+
+ }
+ .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {
+ background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);
+ background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);
+ background-repeat: no-repeat;
+ filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback
+ }
+ .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {
+ background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);
+ background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);
+ background-repeat: no-repeat;
+ filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback
+ }
+ .radial(@inner-color: #555; @outer-color: #333) {
+ background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);
+ background-image: radial-gradient(circle, @inner-color, @outer-color);
+ background-repeat: no-repeat;
+ }
+ .striped(@color: rgba(255,255,255,.15); @angle: 45deg) {
+ background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);
+ background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);
+ }
+}
+
+// Reset filters for IE
+//
+// When you need to remove a gradient background, do not forget to use this to reset
+// the IE filter for IE9 and below.
+.reset-filter() {
+ filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)"));
+}
+
+
+
+// Retina images
+//
+// Short retina mixin for setting background-image and -size
+
+.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {
+ background-image: url("@{file-1x}");
+
+ @media
+ only screen and (-webkit-min-device-pixel-ratio: 2),
+ only screen and ( min--moz-device-pixel-ratio: 2),
+ only screen and ( -o-min-device-pixel-ratio: 2/1),
+ only screen and ( min-device-pixel-ratio: 2),
+ only screen and ( min-resolution: 192dpi),
+ only screen and ( min-resolution: 2dppx) {
+ background-image: url("@{file-2x}");
+ background-size: @width-1x @height-1x;
+ }
+}
+
+
+// Responsive image
+//
+// Keep images from scaling beyond the width of their parents.
+
+.img-responsive(@display: block) {
+ display: @display;
+ max-width: 100%; // Part 1: Set a maximum relative to the parent
+ height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching
+}
+
+
+// COMPONENT MIXINS
+// --------------------------------------------------
+
+// Horizontal dividers
+// -------------------------
+// Dividers (basically an hr) within dropdowns and nav lists
+.nav-divider(@color: #e5e5e5) {
+ height: 1px;
+ margin: ((@line-height-computed / 2) - 1) 0;
+ overflow: hidden;
+ background-color: @color;
+}
+
+// Panels
+// -------------------------
+.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {
+ border-color: @border;
+
+ & > .panel-heading {
+ color: @heading-text-color;
+ background-color: @heading-bg-color;
+ border-color: @heading-border;
+
+ + .panel-collapse .panel-body {
+ border-top-color: @border;
+ }
+ }
+ & > .panel-footer {
+ + .panel-collapse .panel-body {
+ border-bottom-color: @border;
+ }
+ }
+}
+
+// Alerts
+// -------------------------
+.alert-variant(@background; @border; @text-color) {
+ background-color: @background;
+ border-color: @border;
+ color: @text-color;
+
+ hr {
+ border-top-color: darken(@border, 5%);
+ }
+ .alert-link {
+ color: darken(@text-color, 10%);
+ }
+}
+
+// Tables
+// -------------------------
+.table-row-variant(@state; @background) {
+ // Exact selectors below required to override `.table-striped` and prevent
+ // inheritance to nested tables.
+ .table > thead > tr,
+ .table > tbody > tr,
+ .table > tfoot > tr {
+ > td.@{state},
+ > th.@{state},
+ &.@{state} > td,
+ &.@{state} > th {
+ background-color: @background;
+ }
+ }
+
+ // Hover states for `.table-hover`
+ // Note: this is not available for cells or rows within `thead` or `tfoot`.
+ .table-hover > tbody > tr {
+ > td.@{state}:hover,
+ > th.@{state}:hover,
+ &.@{state}:hover > td,
+ &.@{state}:hover > th {
+ background-color: darken(@background, 5%);
+ }
+ }
+}
+
+// List Groups
+// -------------------------
+.list-group-item-variant(@state; @background; @color) {
+ .list-group-item-@{state} {
+ color: @color;
+ background-color: @background;
+
+ a& {
+ color: @color;
+
+ .list-group-item-heading { color: inherit; }
+
+ &:hover,
+ &:focus {
+ color: @color;
+ background-color: darken(@background, 5%);
+ }
+ &.active,
+ &.active:hover,
+ &.active:focus {
+ color: #fff;
+ background-color: @color;
+ border-color: @color;
+ }
+ }
+ }
+}
+
+// Button variants
+// -------------------------
+// Easily pump out default styles, as well as :hover, :focus, :active,
+// and disabled options for all buttons
+.button-variant(@color; @background; @border) {
+ color: @color;
+ background-color: @background;
+ border-color: @border;
+
+ &:hover,
+ &:focus,
+ &:active,
+ &.active,
+ .open .dropdown-toggle& {
+ color: @color;
+ background-color: darken(@background, 8%);
+ border-color: darken(@border, 12%);
+ }
+ &:active,
+ &.active,
+ .open .dropdown-toggle& {
+ background-image: none;
+ }
+ &.disabled,
+ &[disabled],
+ fieldset[disabled] & {
+ &,
+ &:hover,
+ &:focus,
+ &:active,
+ &.active {
+ background-color: @background;
+ border-color: @border;
+ }
+ }
+
+ .badge {
+ color: @background;
+ background-color: @color;
+ }
+}
+
+// Button sizes
+// -------------------------
+.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {
+ padding: @padding-vertical @padding-horizontal;
+ font-size: @font-size;
+ line-height: @line-height;
+ border-radius: @border-radius;
+}
+
+// Pagination
+// -------------------------
+.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @border-radius) {
+ > li {
+ > a,
+ > span {
+ padding: @padding-vertical @padding-horizontal;
+ font-size: @font-size;
+ }
+ &:first-child {
+ > a,
+ > span {
+ .border-left-radius(@border-radius);
+ }
+ }
+ &:last-child {
+ > a,
+ > span {
+ .border-right-radius(@border-radius);
+ }
+ }
+ }
+}
+
+// Labels
+// -------------------------
+.label-variant(@color) {
+ background-color: @color;
+ &[href] {
+ &:hover,
+ &:focus {
+ background-color: darken(@color, 10%);
+ }
+ }
+}
+
+// Contextual backgrounds
+// -------------------------
+.bg-variant(@color) {
+ background-color: @color;
+ a&:hover {
+ background-color: darken(@color, 10%);
+ }
+}
+
+// Typography
+// -------------------------
+.text-emphasis-variant(@color) {
+ color: @color;
+ a&:hover {
+ color: darken(@color, 10%);
+ }
+}
+
+// Navbar vertical align
+// -------------------------
+// Vertically center elements in the navbar.
+// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.
+.navbar-vertical-align(@element-height) {
+ margin-top: ((@navbar-height - @element-height) / 2);
+ margin-bottom: ((@navbar-height - @element-height) / 2);
+}
+
+// Progress bars
+// -------------------------
+.progress-bar-variant(@color) {
+ background-color: @color;
+ .progress-striped & {
+ #gradient > .striped();
+ }
+}
+
+// Responsive utilities
+// -------------------------
+// More easily include all the states for responsive-utilities.less.
+.responsive-visibility() {
+ display: block !important;
+ table& { display: table; }
+ tr& { display: table-row !important; }
+ th&,
+ td& { display: table-cell !important; }
+}
+
+.responsive-invisibility() {
+ display: none !important;
+}
+
+
+// Grid System
+// -----------
+
+// Centered container element
+.container-fixed() {
+ margin-right: auto;
+ margin-left: auto;
+ padding-left: (@grid-gutter-width / 2);
+ padding-right: (@grid-gutter-width / 2);
+ &:extend(.clearfix all);
+}
+
+// Creates a wrapper for a series of columns
+.make-row(@gutter: @grid-gutter-width) {
+ margin-left: (@gutter / -2);
+ margin-right: (@gutter / -2);
+ &:extend(.clearfix all);
+}
+
+// Generate the extra small columns
+.make-xs-column(@columns; @gutter: @grid-gutter-width) {
+ position: relative;
+ float: left;
+ width: percentage((@columns / @grid-columns));
+ min-height: 1px;
+ padding-left: (@gutter / 2);
+ padding-right: (@gutter / 2);
+}
+.make-xs-column-offset(@columns) {
+ @media (min-width: @screen-xs-min) {
+ margin-left: percentage((@columns / @grid-columns));
+ }
+}
+.make-xs-column-push(@columns) {
+ @media (min-width: @screen-xs-min) {
+ left: percentage((@columns / @grid-columns));
+ }
+}
+.make-xs-column-pull(@columns) {
+ @media (min-width: @screen-xs-min) {
+ right: percentage((@columns / @grid-columns));
+ }
+}
+
+
+// Generate the small columns
+.make-sm-column(@columns; @gutter: @grid-gutter-width) {
+ position: relative;
+ min-height: 1px;
+ padding-left: (@gutter / 2);
+ padding-right: (@gutter / 2);
+
+ @media (min-width: @screen-sm-min) {
+ float: left;
+ width: percentage((@columns / @grid-columns));
+ }
+}
+.make-sm-column-offset(@columns) {
+ @media (min-width: @screen-sm-min) {
+ margin-left: percentage((@columns / @grid-columns));
+ }
+}
+.make-sm-column-push(@columns) {
+ @media (min-width: @screen-sm-min) {
+ left: percentage((@columns / @grid-columns));
+ }
+}
+.make-sm-column-pull(@columns) {
+ @media (min-width: @screen-sm-min) {
+ right: percentage((@columns / @grid-columns));
+ }
+}
+
+
+// Generate the medium columns
+.make-md-column(@columns; @gutter: @grid-gutter-width) {
+ position: relative;
+ min-height: 1px;
+ padding-left: (@gutter / 2);
+ padding-right: (@gutter / 2);
+
+ @media (min-width: @screen-md-min) {
+ float: left;
+ width: percentage((@columns / @grid-columns));
+ }
+}
+.make-md-column-offset(@columns) {
+ @media (min-width: @screen-md-min) {
+ margin-left: percentage((@columns / @grid-columns));
+ }
+}
+.make-md-column-push(@columns) {
+ @media (min-width: @screen-md-min) {
+ left: percentage((@columns / @grid-columns));
+ }
+}
+.make-md-column-pull(@columns) {
+ @media (min-width: @screen-md-min) {
+ right: percentage((@columns / @grid-columns));
+ }
+}
+
+
+// Generate the large columns
+.make-lg-column(@columns; @gutter: @grid-gutter-width) {
+ position: relative;
+ min-height: 1px;
+ padding-left: (@gutter / 2);
+ padding-right: (@gutter / 2);
+
+ @media (min-width: @screen-lg-min) {
+ float: left;
+ width: percentage((@columns / @grid-columns));
+ }
+}
+.make-lg-column-offset(@columns) {
+ @media (min-width: @screen-lg-min) {
+ margin-left: percentage((@columns / @grid-columns));
+ }
+}
+.make-lg-column-push(@columns) {
+ @media (min-width: @screen-lg-min) {
+ left: percentage((@columns / @grid-columns));
+ }
+}
+.make-lg-column-pull(@columns) {
+ @media (min-width: @screen-lg-min) {
+ right: percentage((@columns / @grid-columns));
+ }
+}
+
+
+// Framework grid generation
+//
+// Used only by Bootstrap to generate the correct number of grid classes given
+// any value of `@grid-columns`.
+
+.make-grid-columns() {
+ // Common styles for all sizes of grid columns, widths 1-12
+ .col(@index) when (@index = 1) { // initial
+ @item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}";
+ .col((@index + 1), @item);
+ }
+ .col(@index, @list) when (@index =< @grid-columns) { // general; "=<" isn't a typo
+ @item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}";
+ .col((@index + 1), ~"@{list}, @{item}");
+ }
+ .col(@index, @list) when (@index > @grid-columns) { // terminal
+ @{list} {
+ position: relative;
+ // Prevent columns from collapsing when empty
+ min-height: 1px;
+ // Inner gutter via padding
+ padding-left: (@grid-gutter-width / 2);
+ padding-right: (@grid-gutter-width / 2);
+ }
+ }
+ .col(1); // kickstart it
+}
+
+.float-grid-columns(@class) {
+ .col(@index) when (@index = 1) { // initial
+ @item: ~".col-@{class}-@{index}";
+ .col((@index + 1), @item);
+ }
+ .col(@index, @list) when (@index =< @grid-columns) { // general
+ @item: ~".col-@{class}-@{index}";
+ .col((@index + 1), ~"@{list}, @{item}");
+ }
+ .col(@index, @list) when (@index > @grid-columns) { // terminal
+ @{list} {
+ float: left;
+ }
+ }
+ .col(1); // kickstart it
+}
+
+.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {
+ .col-@{class}-@{index} {
+ width: percentage((@index / @grid-columns));
+ }
+}
+.calc-grid-column(@index, @class, @type) when (@type = push) {
+ .col-@{class}-push-@{index} {
+ left: percentage((@index / @grid-columns));
+ }
+}
+.calc-grid-column(@index, @class, @type) when (@type = pull) {
+ .col-@{class}-pull-@{index} {
+ right: percentage((@index / @grid-columns));
+ }
+}
+.calc-grid-column(@index, @class, @type) when (@type = offset) {
+ .col-@{class}-offset-@{index} {
+ margin-left: percentage((@index / @grid-columns));
+ }
+}
+
+// Basic looping in LESS
+.loop-grid-columns(@index, @class, @type) when (@index >= 0) {
+ .calc-grid-column(@index, @class, @type);
+ // next iteration
+ .loop-grid-columns((@index - 1), @class, @type);
+}
+
+// Create grid for specific class
+.make-grid(@class) {
+ .float-grid-columns(@class);
+ .loop-grid-columns(@grid-columns, @class, width);
+ .loop-grid-columns(@grid-columns, @class, pull);
+ .loop-grid-columns(@grid-columns, @class, push);
+ .loop-grid-columns(@grid-columns, @class, offset);
+}
+
+// Form validation states
+//
+// Used in forms.less to generate the form validation CSS for warnings, errors,
+// and successes.
+
+.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {
+ // Color the label and help text
+ .help-block,
+ .control-label,
+ .radio,
+ .checkbox,
+ .radio-inline,
+ .checkbox-inline {
+ color: @text-color;
+ }
+ // Set the border and box shadow on specific inputs to match
+ .form-control {
+ border-color: @border-color;
+ .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
+ &:focus {
+ border-color: darken(@border-color, 10%);
+ @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);
+ .box-shadow(@shadow);
+ }
+ }
+ // Set validation states also for addons
+ .input-group-addon {
+ color: @text-color;
+ border-color: @border-color;
+ background-color: @background-color;
+ }
+ // Optional feedback icon
+ .form-control-feedback {
+ color: @text-color;
+ }
+}
+
+// Form control focus state
+//
+// Generate a customized focus state and for any input with the specified color,
+// which defaults to the `@input-focus-border` variable.
+//
+// We highly encourage you to not customize the default value, but instead use
+// this to tweak colors on an as-needed basis. This aesthetic change is based on
+// WebKit's default styles, but applicable to a wider range of browsers. Its
+// usability and accessibility should be taken into account with any change.
+//
+// Example usage: change the default blue border and shadow to white for better
+// contrast against a dark gray background.
+
+.form-control-focus(@color: @input-border-focus) {
+ @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);
+ &:focus {
+ border-color: @color;
+ outline: 0;
+ .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}");
+ }
+}
+
+// Form control sizing
+//
+// Relative text size, padding, and border-radii changes for form controls. For
+// horizontal sizing, wrap controls in the predefined grid classes. ` |