`, and ``.\n@font-family-monospace: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n@font-family-base: @font-family-sans-serif;\n\n@font-size-base: 14px;\n@font-size-large: ceil((@font-size-base * 1.25)); // ~18px\n@font-size-small: ceil((@font-size-base * 0.85)); // ~12px\n\n@font-size-h1: floor((@font-size-base * 2.6)); // ~36px\n@font-size-h2: floor((@font-size-base * 2.15)); // ~30px\n@font-size-h3: ceil((@font-size-base * 1.7)); // ~24px\n@font-size-h4: ceil((@font-size-base * 1.25)); // ~18px\n@font-size-h5: @font-size-base;\n@font-size-h6: ceil((@font-size-base * 0.85)); // ~12px\n\n//** Unit-less `line-height` for use in components like buttons.\n@line-height-base: 1.428571429; // 20/14\n//** Computed \"line-height\" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.\n@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px\n\n//** By default, this inherits from the ``.\n@headings-font-family: inherit;\n@headings-font-weight: 500;\n@headings-line-height: 1.1;\n@headings-color: inherit;\n\n\n//-- Iconography\n//\n//## Specify custom locations of the include Glyphicons icon font. Useful for those including Bootstrap via Bower.\n\n@icon-font-path: \"../fonts/\";\n@icon-font-name: \"glyphicons-halflings-regular\";\n@icon-font-svg-id: \"glyphicons_halflingsregular\";\n\n//== Components\n//\n//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).\n\n@padding-base-vertical: 6px;\n@padding-base-horizontal: 12px;\n\n@padding-large-vertical: 10px;\n@padding-large-horizontal: 16px;\n\n@padding-small-vertical: 5px;\n@padding-small-horizontal: 10px;\n\n@padding-xs-vertical: 1px;\n@padding-xs-horizontal: 5px;\n\n@line-height-large: 1.33;\n@line-height-small: 1.5;\n\n@border-radius-base: 4px;\n@border-radius-large: 6px;\n@border-radius-small: 3px;\n\n//** Global color for active items (e.g., navs or dropdowns).\n@component-active-color: #fff;\n//** Global background color for active items (e.g., navs or dropdowns).\n@component-active-bg: @brand-primary;\n\n//** Width of the `border` for generating carets that indicator dropdowns.\n@caret-width-base: 4px;\n//** Carets increase slightly in size for larger components.\n@caret-width-large: 5px;\n\n\n//== Tables\n//\n//## Customizes the `.table` component with basic values, each used across all table variations.\n\n//** Padding for ``s and ` `s.\n@table-cell-padding: 8px;\n//** Padding for cells in `.table-condensed`.\n@table-condensed-cell-padding: 5px;\n\n//** Default background color used for all tables.\n@table-bg: transparent;\n//** Background color used for `.table-striped`.\n@table-bg-accent: #f9f9f9;\n//** Background color used for `.table-hover`.\n@table-bg-hover: #f5f5f5;\n@table-bg-active: @table-bg-hover;\n\n//** Border color for table and cell borders.\n@table-border-color: #ddd;\n\n\n//== Buttons\n//\n//## For each of Bootstrap's buttons, define text, background and border color.\n\n@btn-font-weight: normal;\n\n@btn-default-color: #333;\n@btn-default-bg: #fff;\n@btn-default-border: #ccc;\n\n@btn-primary-color: #fff;\n@btn-primary-bg: @brand-primary;\n@btn-primary-border: darken(@btn-primary-bg, 5%);\n\n@btn-success-color: #fff;\n@btn-success-bg: @brand-success;\n@btn-success-border: darken(@btn-success-bg, 5%);\n\n@btn-info-color: #fff;\n@btn-info-bg: @brand-info;\n@btn-info-border: darken(@btn-info-bg, 5%);\n\n@btn-warning-color: #fff;\n@btn-warning-bg: @brand-warning;\n@btn-warning-border: darken(@btn-warning-bg, 5%);\n\n@btn-danger-color: #fff;\n@btn-danger-bg: @brand-danger;\n@btn-danger-border: darken(@btn-danger-bg, 5%);\n\n@btn-link-disabled-color: @gray-light;\n\n\n//== Forms\n//\n//##\n\n//** ` ` background color\n@input-bg: #fff;\n//** ` ` background color\n@input-bg-disabled: @gray-lighter;\n\n//** Text color for ` `s\n@input-color: @gray;\n//** ` ` border color\n@input-border: #ccc;\n//** ` ` border radius\n@input-border-radius: @border-radius-base;\n//** Border color for inputs on focus\n@input-border-focus: #66afe9;\n\n//** Placeholder text color\n@input-color-placeholder: @gray-light;\n\n//** Default `.form-control` height\n@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);\n//** Large `.form-control` height\n@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);\n//** Small `.form-control` height\n@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);\n\n@legend-color: @gray-dark;\n@legend-border-color: #e5e5e5;\n\n//** Background color for textual input addons\n@input-group-addon-bg: @gray-lighter;\n//** Border color for textual input addons\n@input-group-addon-border-color: @input-border;\n\n\n//== Dropdowns\n//\n//## Dropdown menu container and contents.\n\n//** Background for the dropdown menu.\n@dropdown-bg: #fff;\n//** Dropdown menu `border-color`.\n@dropdown-border: rgba(0,0,0,.15);\n//** Dropdown menu `border-color` **for IE8**.\n@dropdown-fallback-border: #ccc;\n//** Divider color for between dropdown items.\n@dropdown-divider-bg: #e5e5e5;\n\n//** Dropdown link text color.\n@dropdown-link-color: @gray-dark;\n//** Hover color for dropdown links.\n@dropdown-link-hover-color: darken(@gray-dark, 5%);\n//** Hover background for dropdown links.\n@dropdown-link-hover-bg: #f5f5f5;\n\n//** Active dropdown menu item text color.\n@dropdown-link-active-color: @component-active-color;\n//** Active dropdown menu item background color.\n@dropdown-link-active-bg: @component-active-bg;\n\n//** Disabled dropdown menu item background color.\n@dropdown-link-disabled-color: @gray-light;\n\n//** Text color for headers within dropdown menus.\n@dropdown-header-color: @gray-light;\n\n// Note: Deprecated @dropdown-caret-color as of v3.1.0\n@dropdown-caret-color: #000;\n\n\n//-- Z-index master list\n//\n// Warning: Avoid customizing these values. They're used for a bird's eye view\n// of components dependent on the z-axis and are designed to all work together.\n//\n// Note: These variables are not generated into the Customizer.\n\n@zindex-navbar: 1000;\n@zindex-dropdown: 1000;\n@zindex-popover: 1010;\n@zindex-tooltip: 1030;\n@zindex-navbar-fixed: 1030;\n@zindex-modal-background: 1040;\n@zindex-modal: 1050;\n\n\n//== Media queries breakpoints\n//\n//## Define the breakpoints at which your layout will change, adapting to different screen sizes.\n\n// Extra small screen / phone\n// Note: Deprecated @screen-xs and @screen-phone as of v3.0.1\n@screen-xs: 480px;\n@screen-xs-min: @screen-xs;\n@screen-phone: @screen-xs-min;\n\n// Small screen / tablet\n// Note: Deprecated @screen-sm and @screen-tablet as of v3.0.1\n@screen-sm: 768px;\n@screen-sm-min: @screen-sm;\n@screen-tablet: @screen-sm-min;\n\n// Medium screen / desktop\n// Note: Deprecated @screen-md and @screen-desktop as of v3.0.1\n@screen-md: 992px;\n@screen-md-min: @screen-md;\n@screen-desktop: @screen-md-min;\n\n// Large screen / wide desktop\n// Note: Deprecated @screen-lg and @screen-lg-desktop as of v3.0.1\n@screen-lg: 1200px;\n@screen-lg-min: @screen-lg;\n@screen-lg-desktop: @screen-lg-min;\n\n// So media queries don't overlap when required, provide a maximum\n@screen-xs-max: (@screen-sm-min - 1);\n@screen-sm-max: (@screen-md-min - 1);\n@screen-md-max: (@screen-lg-min - 1);\n\n\n//== Grid system\n//\n//## Define your custom responsive grid.\n\n//** Number of columns in the grid.\n@grid-columns: 12;\n//** Padding between columns. Gets divided in half for the left and right.\n@grid-gutter-width: 30px;\n// Navbar collapse\n//** Point at which the navbar becomes uncollapsed.\n@grid-float-breakpoint: @screen-sm-min;\n//** Point at which the navbar begins collapsing.\n@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);\n\n\n//== Container sizes\n//\n//## Define the maximum width of `.container` for different screen sizes.\n\n// Small screen / tablet\n@container-tablet: ((720px + @grid-gutter-width));\n//** For `@screen-sm-min` and up.\n@container-sm: @container-tablet;\n\n// Medium screen / desktop\n@container-desktop: ((940px + @grid-gutter-width));\n//** For `@screen-md-min` and up.\n@container-md: @container-desktop;\n\n// Large screen / wide desktop\n@container-large-desktop: ((1140px + @grid-gutter-width));\n//** For `@screen-lg-min` and up.\n@container-lg: @container-large-desktop;\n\n\n//== Navbar\n//\n//##\n\n// Basics of a navbar\n@navbar-height: 50px;\n@navbar-margin-bottom: @line-height-computed;\n@navbar-border-radius: @border-radius-base;\n@navbar-padding-horizontal: floor((@grid-gutter-width / 2));\n@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);\n@navbar-collapse-max-height: 340px;\n\n@navbar-default-color: #777;\n@navbar-default-bg: #f8f8f8;\n@navbar-default-border: darken(@navbar-default-bg, 6.5%);\n\n// Navbar links\n@navbar-default-link-color: #777;\n@navbar-default-link-hover-color: #333;\n@navbar-default-link-hover-bg: transparent;\n@navbar-default-link-active-color: #555;\n@navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%);\n@navbar-default-link-disabled-color: #ccc;\n@navbar-default-link-disabled-bg: transparent;\n\n// Navbar brand label\n@navbar-default-brand-color: @navbar-default-link-color;\n@navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%);\n@navbar-default-brand-hover-bg: transparent;\n\n// Navbar toggle\n@navbar-default-toggle-hover-bg: #ddd;\n@navbar-default-toggle-icon-bar-bg: #888;\n@navbar-default-toggle-border-color: #ddd;\n\n\n// Inverted navbar\n// Reset inverted navbar basics\n@navbar-inverse-color: @gray-light;\n@navbar-inverse-bg: #222;\n@navbar-inverse-border: darken(@navbar-inverse-bg, 10%);\n\n// Inverted navbar links\n@navbar-inverse-link-color: @gray-light;\n@navbar-inverse-link-hover-color: #fff;\n@navbar-inverse-link-hover-bg: transparent;\n@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color;\n@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%);\n@navbar-inverse-link-disabled-color: #444;\n@navbar-inverse-link-disabled-bg: transparent;\n\n// Inverted navbar brand label\n@navbar-inverse-brand-color: @navbar-inverse-link-color;\n@navbar-inverse-brand-hover-color: #fff;\n@navbar-inverse-brand-hover-bg: transparent;\n\n// Inverted navbar toggle\n@navbar-inverse-toggle-hover-bg: #333;\n@navbar-inverse-toggle-icon-bar-bg: #fff;\n@navbar-inverse-toggle-border-color: #333;\n\n\n//== Navs\n//\n//##\n\n//=== Shared nav styles\n@nav-link-padding: 10px 15px;\n@nav-link-hover-bg: @gray-lighter;\n\n@nav-disabled-link-color: @gray-light;\n@nav-disabled-link-hover-color: @gray-light;\n\n@nav-open-link-hover-color: #fff;\n\n//== Tabs\n@nav-tabs-border-color: #ddd;\n\n@nav-tabs-link-hover-border-color: @gray-lighter;\n\n@nav-tabs-active-link-hover-bg: @body-bg;\n@nav-tabs-active-link-hover-color: @gray;\n@nav-tabs-active-link-hover-border-color: #ddd;\n\n@nav-tabs-justified-link-border-color: #ddd;\n@nav-tabs-justified-active-link-border-color: @body-bg;\n\n//== Pills\n@nav-pills-border-radius: @border-radius-base;\n@nav-pills-active-link-hover-bg: @component-active-bg;\n@nav-pills-active-link-hover-color: @component-active-color;\n\n\n//== Pagination\n//\n//##\n\n@pagination-color: @link-color;\n@pagination-bg: #fff;\n@pagination-border: #ddd;\n\n@pagination-hover-color: @link-hover-color;\n@pagination-hover-bg: @gray-lighter;\n@pagination-hover-border: #ddd;\n\n@pagination-active-color: #fff;\n@pagination-active-bg: @brand-primary;\n@pagination-active-border: @brand-primary;\n\n@pagination-disabled-color: @gray-light;\n@pagination-disabled-bg: #fff;\n@pagination-disabled-border: #ddd;\n\n\n//== Pager\n//\n//##\n\n@pager-bg: @pagination-bg;\n@pager-border: @pagination-border;\n@pager-border-radius: 15px;\n\n@pager-hover-bg: @pagination-hover-bg;\n\n@pager-active-bg: @pagination-active-bg;\n@pager-active-color: @pagination-active-color;\n\n@pager-disabled-color: @pagination-disabled-color;\n\n\n//== Jumbotron\n//\n//##\n\n@jumbotron-padding: 30px;\n@jumbotron-color: inherit;\n@jumbotron-bg: @gray-lighter;\n@jumbotron-heading-color: inherit;\n@jumbotron-font-size: ceil((@font-size-base * 1.5));\n\n\n//== Form states and alerts\n//\n//## Define colors for form feedback states and, by default, alerts.\n\n@state-success-text: #3c763d;\n@state-success-bg: #dff0d8;\n@state-success-border: darken(spin(@state-success-bg, -10), 5%);\n\n@state-info-text: #31708f;\n@state-info-bg: #d9edf7;\n@state-info-border: darken(spin(@state-info-bg, -10), 7%);\n\n@state-warning-text: #8a6d3b;\n@state-warning-bg: #fcf8e3;\n@state-warning-border: darken(spin(@state-warning-bg, -10), 5%);\n\n@state-danger-text: #a94442;\n@state-danger-bg: #f2dede;\n@state-danger-border: darken(spin(@state-danger-bg, -10), 5%);\n\n\n//== Tooltips\n//\n//##\n\n//** Tooltip max width\n@tooltip-max-width: 200px;\n//** Tooltip text color\n@tooltip-color: #fff;\n//** Tooltip background color\n@tooltip-bg: #000;\n@tooltip-opacity: .9;\n\n//** Tooltip arrow width\n@tooltip-arrow-width: 5px;\n//** Tooltip arrow color\n@tooltip-arrow-color: @tooltip-bg;\n\n\n//== Popovers\n//\n//##\n\n//** Popover body background color\n@popover-bg: #fff;\n//** Popover maximum width\n@popover-max-width: 276px;\n//** Popover border color\n@popover-border-color: rgba(0,0,0,.2);\n//** Popover fallback border color\n@popover-fallback-border-color: #ccc;\n\n//** Popover title background color\n@popover-title-bg: darken(@popover-bg, 3%);\n\n//** Popover arrow width\n@popover-arrow-width: 10px;\n//** Popover arrow color\n@popover-arrow-color: #fff;\n\n//** Popover outer arrow width\n@popover-arrow-outer-width: (@popover-arrow-width + 1);\n//** Popover outer arrow color\n@popover-arrow-outer-color: fadein(@popover-border-color, 5%);\n//** Popover outer arrow fallback color\n@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%);\n\n\n//== Labels\n//\n//##\n\n//** Default label background color\n@label-default-bg: @gray-light;\n//** Primary label background color\n@label-primary-bg: @brand-primary;\n//** Success label background color\n@label-success-bg: @brand-success;\n//** Info label background color\n@label-info-bg: @brand-info;\n//** Warning label background color\n@label-warning-bg: @brand-warning;\n//** Danger label background color\n@label-danger-bg: @brand-danger;\n\n//** Default label text color\n@label-color: #fff;\n//** Default text color of a linked label\n@label-link-hover-color: #fff;\n\n\n//== Modals\n//\n//##\n\n//** Padding applied to the modal body\n@modal-inner-padding: 20px;\n\n//** Padding applied to the modal title\n@modal-title-padding: 15px;\n//** Modal title line-height\n@modal-title-line-height: @line-height-base;\n\n//** Background color of modal content area\n@modal-content-bg: #fff;\n//** Modal content border color\n@modal-content-border-color: rgba(0,0,0,.2);\n//** Modal content border color **for IE8**\n@modal-content-fallback-border-color: #999;\n\n//** Modal backdrop background color\n@modal-backdrop-bg: #000;\n//** Modal backdrop opacity\n@modal-backdrop-opacity: .5;\n//** Modal header border color\n@modal-header-border-color: #e5e5e5;\n//** Modal footer border color\n@modal-footer-border-color: @modal-header-border-color;\n\n@modal-lg: 900px;\n@modal-md: 600px;\n@modal-sm: 300px;\n\n\n//== Alerts\n//\n//## Define alert colors, border radius, and padding.\n\n@alert-padding: 15px;\n@alert-border-radius: @border-radius-base;\n@alert-link-font-weight: bold;\n\n@alert-success-bg: @state-success-bg;\n@alert-success-text: @state-success-text;\n@alert-success-border: @state-success-border;\n\n@alert-info-bg: @state-info-bg;\n@alert-info-text: @state-info-text;\n@alert-info-border: @state-info-border;\n\n@alert-warning-bg: @state-warning-bg;\n@alert-warning-text: @state-warning-text;\n@alert-warning-border: @state-warning-border;\n\n@alert-danger-bg: @state-danger-bg;\n@alert-danger-text: @state-danger-text;\n@alert-danger-border: @state-danger-border;\n\n\n//== Progress bars\n//\n//##\n\n//** Background color of the whole progress component\n@progress-bg: #f5f5f5;\n//** Progress bar text color\n@progress-bar-color: #fff;\n\n//** Default progress bar color\n@progress-bar-bg: @brand-primary;\n//** Success progress bar color\n@progress-bar-success-bg: @brand-success;\n//** Warning progress bar color\n@progress-bar-warning-bg: @brand-warning;\n//** Danger progress bar color\n@progress-bar-danger-bg: @brand-danger;\n//** Info progress bar color\n@progress-bar-info-bg: @brand-info;\n\n\n//== List group\n//\n//##\n\n//** Background color on `.list-group-item`\n@list-group-bg: #fff;\n//** `.list-group-item` border color\n@list-group-border: #ddd;\n//** List group border radius\n@list-group-border-radius: @border-radius-base;\n\n//** Background color of single list elements on hover\n@list-group-hover-bg: #f5f5f5;\n//** Text color of active list elements\n@list-group-active-color: @component-active-color;\n//** Background color of active list elements\n@list-group-active-bg: @component-active-bg;\n//** Border color of active list elements\n@list-group-active-border: @list-group-active-bg;\n@list-group-active-text-color: lighten(@list-group-active-bg, 40%);\n\n@list-group-link-color: #555;\n@list-group-link-heading-color: #333;\n\n\n//== Panels\n//\n//##\n\n@panel-bg: #fff;\n@panel-body-padding: 15px;\n@panel-border-radius: @border-radius-base;\n\n//** Border color for elements within panels\n@panel-inner-border: #ddd;\n@panel-footer-bg: #f5f5f5;\n\n@panel-default-text: @gray-dark;\n@panel-default-border: #ddd;\n@panel-default-heading-bg: #f5f5f5;\n\n@panel-primary-text: #fff;\n@panel-primary-border: @brand-primary;\n@panel-primary-heading-bg: @brand-primary;\n\n@panel-success-text: @state-success-text;\n@panel-success-border: @state-success-border;\n@panel-success-heading-bg: @state-success-bg;\n\n@panel-info-text: @state-info-text;\n@panel-info-border: @state-info-border;\n@panel-info-heading-bg: @state-info-bg;\n\n@panel-warning-text: @state-warning-text;\n@panel-warning-border: @state-warning-border;\n@panel-warning-heading-bg: @state-warning-bg;\n\n@panel-danger-text: @state-danger-text;\n@panel-danger-border: @state-danger-border;\n@panel-danger-heading-bg: @state-danger-bg;\n\n\n//== Thumbnails\n//\n//##\n\n//** Padding around the thumbnail image\n@thumbnail-padding: 4px;\n//** Thumbnail background color\n@thumbnail-bg: @body-bg;\n//** Thumbnail border color\n@thumbnail-border: #ddd;\n//** Thumbnail border radius\n@thumbnail-border-radius: @border-radius-base;\n\n//** Custom text color for thumbnail captions\n@thumbnail-caption-color: @text-color;\n//** Padding around the thumbnail caption\n@thumbnail-caption-padding: 9px;\n\n\n//== Wells\n//\n//##\n\n@well-bg: #f5f5f5;\n@well-border: darken(@well-bg, 7%);\n\n\n//== Badges\n//\n//##\n\n@badge-color: #fff;\n//** Linked badge text color on hover\n@badge-link-hover-color: #fff;\n@badge-bg: @gray-light;\n\n//** Badge text color in active nav link\n@badge-active-color: @link-color;\n//** Badge background color in active nav link\n@badge-active-bg: #fff;\n\n@badge-font-weight: bold;\n@badge-line-height: 1;\n@badge-border-radius: 10px;\n\n\n//== Breadcrumbs\n//\n//##\n\n@breadcrumb-padding-vertical: 8px;\n@breadcrumb-padding-horizontal: 15px;\n//** Breadcrumb background color\n@breadcrumb-bg: #f5f5f5;\n//** Breadcrumb text color\n@breadcrumb-color: #ccc;\n//** Text color of current page in the breadcrumb\n@breadcrumb-active-color: @gray-light;\n//** Textual separator for between breadcrumb elements\n@breadcrumb-separator: \"/\";\n\n\n//== Carousel\n//\n//##\n\n@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6);\n\n@carousel-control-color: #fff;\n@carousel-control-width: 15%;\n@carousel-control-opacity: .5;\n@carousel-control-font-size: 20px;\n\n@carousel-indicator-active-bg: #fff;\n@carousel-indicator-border-color: #fff;\n\n@carousel-caption-color: #fff;\n\n\n//== Close\n//\n//##\n\n@close-font-weight: bold;\n@close-color: #000;\n@close-text-shadow: 0 1px 0 #fff;\n\n\n//== Code\n//\n//##\n\n@code-color: #c7254e;\n@code-bg: #f9f2f4;\n\n@kbd-color: #fff;\n@kbd-bg: #333;\n\n@pre-bg: #f5f5f5;\n@pre-color: @gray-dark;\n@pre-border-color: #ccc;\n@pre-scrollable-max-height: 340px;\n\n\n//== Type\n//\n//##\n\n//** Text muted color\n@text-muted: @gray-light;\n//** Abbreviations and acronyms border color\n@abbr-border-color: @gray-light;\n//** Headings small color\n@headings-small-color: @gray-light;\n//** Blockquote small color\n@blockquote-small-color: @gray-light;\n//** Blockquote font size\n@blockquote-font-size: (@font-size-base * 1.25);\n//** Blockquote border color\n@blockquote-border-color: @gray-lighter;\n//** Page header border color\n@page-header-border-color: @gray-lighter;\n\n\n//== Miscellaneous\n//\n//##\n\n//** Horizontal line color.\n@hr-border: @gray-lighter;\n\n//** Horizontal offset for forms and lists.\n@component-offset-horizontal: 180px;\n","//\n// Thumbnails\n// --------------------------------------------------\n\n\n// Mixin and adjust the regular image class\n.thumbnail {\n display: block;\n padding: @thumbnail-padding;\n margin-bottom: @line-height-computed;\n line-height: @line-height-base;\n background-color: @thumbnail-bg;\n border: 1px solid @thumbnail-border;\n border-radius: @thumbnail-border-radius;\n .transition(all .2s ease-in-out);\n\n > img,\n a > img {\n &:extend(.img-responsive);\n margin-left: auto;\n margin-right: auto;\n }\n\n // Add a hover state for linked versions only\n a&:hover,\n a&:focus,\n a&.active {\n border-color: @link-color;\n }\n\n // Image captions\n .caption {\n padding: @thumbnail-caption-padding;\n color: @thumbnail-caption-color;\n }\n}\n","//\n// Carousel\n// --------------------------------------------------\n\n\n// Wrapper for the slide container and indicators\n.carousel {\n position: relative;\n}\n\n.carousel-inner {\n position: relative;\n overflow: hidden;\n width: 100%;\n\n > .item {\n display: none;\n position: relative;\n .transition(.6s ease-in-out left);\n\n // Account for jankitude on images\n > img,\n > a > img {\n &:extend(.img-responsive);\n line-height: 1;\n }\n }\n\n > .active,\n > .next,\n > .prev { display: block; }\n\n > .active {\n left: 0;\n }\n\n > .next,\n > .prev {\n position: absolute;\n top: 0;\n width: 100%;\n }\n\n > .next {\n left: 100%;\n }\n > .prev {\n left: -100%;\n }\n > .next.left,\n > .prev.right {\n left: 0;\n }\n\n > .active.left {\n left: -100%;\n }\n > .active.right {\n left: 100%;\n }\n\n}\n\n// Left/right controls for nav\n// ---------------------------\n\n.carousel-control {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n width: @carousel-control-width;\n .opacity(@carousel-control-opacity);\n font-size: @carousel-control-font-size;\n color: @carousel-control-color;\n text-align: center;\n text-shadow: @carousel-text-shadow;\n // We can't have this transition here because WebKit cancels the carousel\n // animation if you trip this while in the middle of another animation.\n\n // Set gradients for backgrounds\n &.left {\n #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001));\n }\n &.right {\n left: auto;\n right: 0;\n #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5));\n }\n\n // Hover/focus state\n &:hover,\n &:focus {\n outline: none;\n color: @carousel-control-color;\n text-decoration: none;\n .opacity(.9);\n }\n\n // Toggles\n .icon-prev,\n .icon-next,\n .glyphicon-chevron-left,\n .glyphicon-chevron-right {\n position: absolute;\n top: 50%;\n z-index: 5;\n display: inline-block;\n }\n .icon-prev,\n .glyphicon-chevron-left {\n left: 50%;\n }\n .icon-next,\n .glyphicon-chevron-right {\n right: 50%;\n }\n .icon-prev,\n .icon-next {\n width: 20px;\n height: 20px;\n margin-top: -10px;\n margin-left: -10px;\n font-family: serif;\n }\n\n .icon-prev {\n &:before {\n content: '\\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039)\n }\n }\n .icon-next {\n &:before {\n content: '\\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A)\n }\n }\n}\n\n// Optional indicator pips\n//\n// Add an unordered list with the following class and add a list item for each\n// slide your carousel holds.\n\n.carousel-indicators {\n position: absolute;\n bottom: 10px;\n left: 50%;\n z-index: 15;\n width: 60%;\n margin-left: -30%;\n padding-left: 0;\n list-style: none;\n text-align: center;\n\n li {\n display: inline-block;\n width: 10px;\n height: 10px;\n margin: 1px;\n text-indent: -999px;\n border: 1px solid @carousel-indicator-border-color;\n border-radius: 10px;\n cursor: pointer;\n\n // IE8-9 hack for event handling\n //\n // Internet Explorer 8-9 does not support clicks on elements without a set\n // `background-color`. We cannot use `filter` since that's not viewed as a\n // background color by the browser. Thus, a hack is needed.\n //\n // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we\n // set alpha transparency for the best results possible.\n background-color: #000 \\9; // IE8\n background-color: rgba(0,0,0,0); // IE9\n }\n .active {\n margin: 0;\n width: 12px;\n height: 12px;\n background-color: @carousel-indicator-active-bg;\n }\n}\n\n// Optional captions\n// -----------------------------\n// Hidden by default for smaller viewports\n.carousel-caption {\n position: absolute;\n left: 15%;\n right: 15%;\n bottom: 20px;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: @carousel-caption-color;\n text-align: center;\n text-shadow: @carousel-text-shadow;\n & .btn {\n text-shadow: none; // No shadow for button elements in carousel-caption\n }\n}\n\n\n// Scale up controls for tablets and up\n@media screen and (min-width: @screen-sm-min) {\n\n // Scale up the controls a smidge\n .carousel-control {\n .glyphicon-chevron-left,\n .glyphicon-chevron-right,\n .icon-prev,\n .icon-next {\n width: 30px;\n height: 30px;\n margin-top: -15px;\n margin-left: -15px;\n font-size: 30px;\n }\n }\n\n // Show and left align the captions\n .carousel-caption {\n left: 20%;\n right: 20%;\n padding-bottom: 30px;\n }\n\n // Move up the indicators\n .carousel-indicators {\n bottom: 20px;\n }\n}\n","//\n// Typography\n// --------------------------------------------------\n\n\n// Headings\n// -------------------------\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n font-family: @headings-font-family;\n font-weight: @headings-font-weight;\n line-height: @headings-line-height;\n color: @headings-color;\n\n small,\n .small {\n font-weight: normal;\n line-height: 1;\n color: @headings-small-color;\n }\n}\n\nh1, .h1,\nh2, .h2,\nh3, .h3 {\n margin-top: @line-height-computed;\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 65%;\n }\n}\nh4, .h4,\nh5, .h5,\nh6, .h6 {\n margin-top: (@line-height-computed / 2);\n margin-bottom: (@line-height-computed / 2);\n\n small,\n .small {\n font-size: 75%;\n }\n}\n\nh1, .h1 { font-size: @font-size-h1; }\nh2, .h2 { font-size: @font-size-h2; }\nh3, .h3 { font-size: @font-size-h3; }\nh4, .h4 { font-size: @font-size-h4; }\nh5, .h5 { font-size: @font-size-h5; }\nh6, .h6 { font-size: @font-size-h6; }\n\n\n// Body text\n// -------------------------\n\np {\n margin: 0 0 (@line-height-computed / 2);\n}\n\n.lead {\n margin-bottom: @line-height-computed;\n font-size: floor((@font-size-base * 1.15));\n font-weight: 200;\n line-height: 1.4;\n\n @media (min-width: @screen-sm-min) {\n font-size: (@font-size-base * 1.5);\n }\n}\n\n\n// Emphasis & misc\n// -------------------------\n\n// Ex: 14px base font * 85% = about 12px\nsmall,\n.small { font-size: 85%; }\n\n// Undo browser default styling\ncite { font-style: normal; }\n\n// Alignment\n.text-left { text-align: left; }\n.text-right { text-align: right; }\n.text-center { text-align: center; }\n.text-justify { text-align: justify; }\n\n// Contextual colors\n.text-muted {\n color: @text-muted;\n}\n.text-primary {\n .text-emphasis-variant(@brand-primary);\n}\n.text-success {\n .text-emphasis-variant(@state-success-text);\n}\n.text-info {\n .text-emphasis-variant(@state-info-text);\n}\n.text-warning {\n .text-emphasis-variant(@state-warning-text);\n}\n.text-danger {\n .text-emphasis-variant(@state-danger-text);\n}\n\n// Contextual backgrounds\n// For now we'll leave these alongside the text classes until v4 when we can\n// safely shift things around (per SemVer rules).\n.bg-primary {\n // Given the contrast here, this is the only class to have its color inverted\n // automatically.\n color: #fff;\n .bg-variant(@brand-primary);\n}\n.bg-success {\n .bg-variant(@state-success-bg);\n}\n.bg-info {\n .bg-variant(@state-info-bg);\n}\n.bg-warning {\n .bg-variant(@state-warning-bg);\n}\n.bg-danger {\n .bg-variant(@state-danger-bg);\n}\n\n\n// Page header\n// -------------------------\n\n.page-header {\n padding-bottom: ((@line-height-computed / 2) - 1);\n margin: (@line-height-computed * 2) 0 @line-height-computed;\n border-bottom: 1px solid @page-header-border-color;\n}\n\n\n// Lists\n// --------------------------------------------------\n\n// Unordered and Ordered lists\nul,\nol {\n margin-top: 0;\n margin-bottom: (@line-height-computed / 2);\n ul,\n ol {\n margin-bottom: 0;\n }\n}\n\n// List options\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n .list-unstyled();\n margin-left: -5px;\n\n > li {\n display: inline-block;\n padding-left: 5px;\n padding-right: 5px;\n }\n}\n\n// Description Lists\ndl {\n margin-top: 0; // Remove browser default\n margin-bottom: @line-height-computed;\n}\ndt,\ndd {\n line-height: @line-height-base;\n}\ndt {\n font-weight: bold;\n}\ndd {\n margin-left: 0; // Undo browser default\n}\n\n// Horizontal description lists\n//\n// Defaults to being stacked without any of the below styles applied, until the\n// grid breakpoint is reached (default of ~768px).\n\n@media (min-width: @grid-float-breakpoint) {\n .dl-horizontal {\n dt {\n float: left;\n width: (@component-offset-horizontal - 20);\n clear: left;\n text-align: right;\n .text-overflow();\n }\n dd {\n margin-left: @component-offset-horizontal;\n &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present\n }\n }\n}\n\n// MISC\n// ----\n\n// Abbreviations and acronyms\nabbr[title],\n// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257\nabbr[data-original-title] {\n cursor: help;\n border-bottom: 1px dotted @abbr-border-color;\n}\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n// Blockquotes\nblockquote {\n padding: (@line-height-computed / 2) @line-height-computed;\n margin: 0 0 @line-height-computed;\n font-size: @blockquote-font-size;\n border-left: 5px solid @blockquote-border-color;\n\n p,\n ul,\n ol {\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Note: Deprecated small and .small as of v3.1.0\n // Context: https://github.com/twbs/bootstrap/issues/11660\n footer,\n small,\n .small {\n display: block;\n font-size: 80%; // back to default font-size\n line-height: @line-height-base;\n color: @blockquote-small-color;\n\n &:before {\n content: '\\2014 \\00A0'; // em dash, nbsp\n }\n }\n}\n\n// Opposite alignment of blockquote\n//\n// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0.\n.blockquote-reverse,\nblockquote.pull-right {\n padding-right: 15px;\n padding-left: 0;\n border-right: 5px solid @blockquote-border-color;\n border-left: 0;\n text-align: right;\n\n // Account for citation\n footer,\n small,\n .small {\n &:before { content: ''; }\n &:after {\n content: '\\00A0 \\2014'; // nbsp, em dash\n }\n }\n}\n\n// Quotes\nblockquote:before,\nblockquote:after {\n content: \"\";\n}\n\n// Addresses\naddress {\n margin-bottom: @line-height-computed;\n font-style: normal;\n line-height: @line-height-base;\n}\n","//\n// Code (inline and block)\n// --------------------------------------------------\n\n\n// Inline and block code styles\ncode,\nkbd,\npre,\nsamp {\n font-family: @font-family-monospace;\n}\n\n// Inline code\ncode {\n padding: 2px 4px;\n font-size: 90%;\n color: @code-color;\n background-color: @code-bg;\n white-space: nowrap;\n border-radius: @border-radius-base;\n}\n\n// User input typically entered via keyboard\nkbd {\n padding: 2px 4px;\n font-size: 90%;\n color: @kbd-color;\n background-color: @kbd-bg;\n border-radius: @border-radius-small;\n box-shadow: inset 0 -1px 0 rgba(0,0,0,.25);\n}\n\n// Blocks of code\npre {\n display: block;\n padding: ((@line-height-computed - 1) / 2);\n margin: 0 0 (@line-height-computed / 2);\n font-size: (@font-size-base - 1); // 14px to 13px\n line-height: @line-height-base;\n word-break: break-all;\n word-wrap: break-word;\n color: @pre-color;\n background-color: @pre-bg;\n border: 1px solid @pre-border-color;\n border-radius: @border-radius-base;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n padding: 0;\n font-size: inherit;\n color: inherit;\n white-space: pre-wrap;\n background-color: transparent;\n border-radius: 0;\n }\n}\n\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: @pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","//\n// Grid system\n// --------------------------------------------------\n\n\n// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n.container {\n .container-fixed();\n\n @media (min-width: @screen-sm-min) {\n width: @container-sm;\n }\n @media (min-width: @screen-md-min) {\n width: @container-md;\n }\n @media (min-width: @screen-lg-min) {\n width: @container-lg;\n }\n}\n\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but without any defined\n// width for fluid, full width layouts.\n\n.container-fluid {\n .container-fixed();\n}\n\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n.row {\n .make-row();\n}\n\n\n// Columns\n//\n// Common styles for small and large grid columns\n\n.make-grid-columns();\n\n\n// Extra small grid\n//\n// Columns, offsets, pushes, and pulls for extra small devices like\n// smartphones.\n\n.make-grid(xs);\n\n\n// Small grid\n//\n// Columns, offsets, pushes, and pulls for the small device range, from phones\n// to tablets.\n\n@media (min-width: @screen-sm-min) {\n .make-grid(sm);\n}\n\n\n// Medium grid\n//\n// Columns, offsets, pushes, and pulls for the desktop device range.\n\n@media (min-width: @screen-md-min) {\n .make-grid(md);\n}\n\n\n// Large grid\n//\n// Columns, offsets, pushes, and pulls for the large desktop device range.\n\n@media (min-width: @screen-lg-min) {\n .make-grid(lg);\n}\n","//\n// Tables\n// --------------------------------------------------\n\n\ntable {\n max-width: 100%;\n background-color: @table-bg;\n}\nth {\n text-align: left;\n}\n\n\n// Baseline styles\n\n.table {\n width: 100%;\n margin-bottom: @line-height-computed;\n // Cells\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-cell-padding;\n line-height: @line-height-base;\n vertical-align: top;\n border-top: 1px solid @table-border-color;\n }\n }\n }\n // Bottom align for column headings\n > thead > tr > th {\n vertical-align: bottom;\n border-bottom: 2px solid @table-border-color;\n }\n // Remove top border from thead by default\n > caption + thead,\n > colgroup + thead,\n > thead:first-child {\n > tr:first-child {\n > th,\n > td {\n border-top: 0;\n }\n }\n }\n // Account for multiple tbody instances\n > tbody + tbody {\n border-top: 2px solid @table-border-color;\n }\n\n // Nesting\n .table {\n background-color: @body-bg;\n }\n}\n\n\n// Condensed table w/ half padding\n\n.table-condensed {\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n padding: @table-condensed-cell-padding;\n }\n }\n }\n}\n\n\n// Bordered version\n//\n// Add borders all around the table and between all the columns.\n\n.table-bordered {\n border: 1px solid @table-border-color;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n border: 1px solid @table-border-color;\n }\n }\n }\n > thead > tr {\n > th,\n > td {\n border-bottom-width: 2px;\n }\n }\n}\n\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n > tbody > tr:nth-child(odd) {\n > td,\n > th {\n background-color: @table-bg-accent;\n }\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n > tbody > tr:hover {\n > td,\n > th {\n background-color: @table-bg-hover;\n }\n }\n}\n\n\n// Table cell sizing\n//\n// Reset default table behavior\n\ntable col[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-column;\n}\ntable {\n td,\n th {\n &[class*=\"col-\"] {\n position: static; // Prevent border hiding in Firefox and IE9/10 (see https://github.com/twbs/bootstrap/issues/11623)\n float: none;\n display: table-cell;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n// Generate the contextual variants\n.table-row-variant(active; @table-bg-active);\n.table-row-variant(success; @state-success-bg);\n.table-row-variant(info; @state-info-bg);\n.table-row-variant(warning; @state-warning-bg);\n.table-row-variant(danger; @state-danger-bg);\n\n\n// Responsive tables\n//\n// Wrap your tables in `.table-responsive` and we'll make them mobile friendly\n// by enabling horizontal scrolling. Only applies <768px. Everything above that\n// will display normally.\n\n@media (max-width: @screen-xs-max) {\n .table-responsive {\n width: 100%;\n margin-bottom: (@line-height-computed * 0.75);\n overflow-y: hidden;\n overflow-x: scroll;\n -ms-overflow-style: -ms-autohiding-scrollbar;\n border: 1px solid @table-border-color;\n -webkit-overflow-scrolling: touch;\n\n // Tighten up spacing\n > .table {\n margin-bottom: 0;\n\n // Ensure the content doesn't wrap\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th,\n > td {\n white-space: nowrap;\n }\n }\n }\n }\n\n // Special overrides for the bordered tables\n > .table-bordered {\n border: 0;\n\n // Nuke the appropriate borders so that the parent can handle them\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n\n // Only nuke the last row's bottom-border in `tbody` and `tfoot` since\n // chances are there will be only one `tr` in a `thead` and that would\n // remove the border altogether.\n > tbody,\n > tfoot {\n > tr:last-child {\n > th,\n > td {\n border-bottom: 0;\n }\n }\n }\n\n }\n }\n}\n","//\n// Forms\n// --------------------------------------------------\n\n\n// Normalize non-controls\n//\n// Restyle and baseline non-control form elements.\n\nfieldset {\n padding: 0;\n margin: 0;\n border: 0;\n // Chrome and Firefox set a `min-width: -webkit-min-content;` on fieldsets,\n // so we reset that to ensure it behaves more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359.\n min-width: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n padding: 0;\n margin-bottom: @line-height-computed;\n font-size: (@font-size-base * 1.5);\n line-height: inherit;\n color: @legend-color;\n border: 0;\n border-bottom: 1px solid @legend-border-color;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 5px;\n font-weight: bold;\n}\n\n\n// Normalize form controls\n//\n// While most of our form styles require extra classes, some basic normalization\n// is required to ensure optimum display with or without those classes to better\n// address browser inconsistencies.\n\n// Override content-box in Normalize (* isn't specific enough)\ninput[type=\"search\"] {\n .box-sizing(border-box);\n}\n\n// Position radios and checkboxes better\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n margin: 4px 0 0;\n margin-top: 1px \\9; /* IE8-9 */\n line-height: normal;\n}\n\n// Set the height of file controls to match text inputs\ninput[type=\"file\"] {\n display: block;\n}\n\n// Make range inputs behave like textual form controls\ninput[type=\"range\"] {\n display: block;\n width: 100%;\n}\n\n// Make multiple select elements height not fixed\nselect[multiple],\nselect[size] {\n height: auto;\n}\n\n// Focus for file, radio, and checkbox\ninput[type=\"file\"]:focus,\ninput[type=\"radio\"]:focus,\ninput[type=\"checkbox\"]:focus {\n .tab-focus();\n}\n\n// Adjust output element\noutput {\n display: block;\n padding-top: (@padding-base-vertical + 1);\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n}\n\n\n// Common form controls\n//\n// Shared size and type resets for form controls. Apply `.form-control` to any\n// of the following form controls:\n//\n// select\n// textarea\n// input[type=\"text\"]\n// input[type=\"password\"]\n// input[type=\"datetime\"]\n// input[type=\"datetime-local\"]\n// input[type=\"date\"]\n// input[type=\"month\"]\n// input[type=\"time\"]\n// input[type=\"week\"]\n// input[type=\"number\"]\n// input[type=\"email\"]\n// input[type=\"url\"]\n// input[type=\"search\"]\n// input[type=\"tel\"]\n// input[type=\"color\"]\n\n.form-control {\n display: block;\n width: 100%;\n height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border)\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n line-height: @line-height-base;\n color: @input-color;\n background-color: @input-bg;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid @input-border;\n border-radius: @input-border-radius;\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.075));\n .transition(~\"border-color ease-in-out .15s, box-shadow ease-in-out .15s\");\n\n // Customize the `:focus` state to imitate native WebKit styles.\n .form-control-focus();\n\n // Placeholder\n .placeholder();\n\n // Disabled and read-only inputs\n //\n // HTML5 says that controls under a fieldset > legend:first-child won't be\n // disabled if the fieldset is disabled. Due to implementation difficulty, we\n // don't honor that edge case; we style them as disabled anyway.\n &[disabled],\n &[readonly],\n fieldset[disabled] & {\n cursor: not-allowed;\n background-color: @input-bg-disabled;\n opacity: 1; // iOS fix for unreadable disabled content\n }\n\n // Reset height for `textarea`s\n textarea& {\n height: auto;\n }\n}\n\n\n// Search inputs in iOS\n//\n// This overrides the extra rounded corners on search inputs in iOS so that our\n// `.form-control` class can properly style them. Note that this cannot simply\n// be added to `.form-control` as it's not specific enough. For details, see\n// https://github.com/twbs/bootstrap/issues/11586.\n\ninput[type=\"search\"] {\n -webkit-appearance: none;\n}\n\n\n// Special styles for iOS date input\n//\n// In Mobile Safari, date inputs require a pixel line-height that matches the\n// given height of the input.\n\ninput[type=\"date\"] {\n line-height: @input-height-base;\n}\n\n\n// Form groups\n//\n// Designed to help with the organization and spacing of vertical forms. For\n// horizontal forms, use the predefined grid classes.\n\n.form-group {\n margin-bottom: 15px;\n}\n\n\n// Checkboxes and radios\n//\n// Indent the labels to position radios/checkboxes as hanging controls.\n\n.radio,\n.checkbox {\n display: block;\n min-height: @line-height-computed; // clear the floating input if there is no label text\n margin-top: 10px;\n margin-bottom: 10px;\n padding-left: 20px;\n label {\n display: inline;\n font-weight: normal;\n cursor: pointer;\n }\n}\n.radio input[type=\"radio\"],\n.radio-inline input[type=\"radio\"],\n.checkbox input[type=\"checkbox\"],\n.checkbox-inline input[type=\"checkbox\"] {\n float: left;\n margin-left: -20px;\n}\n.radio + .radio,\n.checkbox + .checkbox {\n margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing\n}\n\n// Radios and checkboxes on same line\n.radio-inline,\n.checkbox-inline {\n display: inline-block;\n padding-left: 20px;\n margin-bottom: 0;\n vertical-align: middle;\n font-weight: normal;\n cursor: pointer;\n}\n.radio-inline + .radio-inline,\n.checkbox-inline + .checkbox-inline {\n margin-top: 0;\n margin-left: 10px; // space out consecutive inline controls\n}\n\n// Apply same disabled cursor tweak as for inputs\n//\n// Note: Neither radios nor checkboxes can be readonly.\ninput[type=\"radio\"],\ninput[type=\"checkbox\"],\n.radio,\n.radio-inline,\n.checkbox,\n.checkbox-inline {\n &[disabled],\n fieldset[disabled] & {\n cursor: not-allowed;\n }\n}\n\n\n// Form control sizing\n//\n// Build on `.form-control` with modifier classes to decrease or increase the\n// height and font-size of form controls.\n\n.input-sm {\n .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n\n.input-lg {\n .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n\n\n// Form control feedback states\n//\n// Apply contextual and semantic states to individual form controls.\n\n.has-feedback {\n // Enable absolute positioning\n position: relative;\n\n // Ensure icons don't overlap text\n .form-control {\n padding-right: (@input-height-base * 1.25);\n }\n\n // Feedback icon (requires .glyphicon classes)\n .form-control-feedback {\n position: absolute;\n top: (@line-height-computed + 5); // Height of the `label` and its margin\n right: 0;\n display: block;\n width: @input-height-base;\n height: @input-height-base;\n line-height: @input-height-base;\n text-align: center;\n }\n}\n\n// Feedback states\n.has-success {\n .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);\n}\n.has-warning {\n .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);\n}\n.has-error {\n .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);\n}\n\n\n// Static form control text\n//\n// Apply class to a `p` element to make any string of text align with labels in\n// a horizontal form layout.\n\n.form-control-static {\n margin-bottom: 0; // Remove default margin from `p`\n}\n\n\n// Help text\n//\n// Apply to any element you wish to create light text for placement immediately\n// below a form control. Use for general help, formatting, or instructional text.\n\n.help-block {\n display: block; // account for any element using help-block\n margin-top: 5px;\n margin-bottom: 10px;\n color: lighten(@text-color, 25%); // lighten the text some for contrast\n}\n\n\n\n// Inline forms\n//\n// Make forms appear inline(-block) by adding the `.form-inline` class. Inline\n// forms begin stacked on extra small (mobile) devices and then go inline when\n// viewports reach <768px.\n//\n// Requires wrapping inputs and labels with `.form-group` for proper display of\n// default HTML form controls and our custom form controls (e.g., input groups).\n//\n// Heads up! This is mixin-ed into `.navbar-form` in navbars.less.\n\n.form-inline {\n\n // Kick in the inline\n @media (min-width: @screen-sm-min) {\n // Inline-block all the things for \"inline\"\n .form-group {\n display: inline-block;\n margin-bottom: 0;\n vertical-align: middle;\n }\n\n // In navbar-form, allow folks to *not* use `.form-group`\n .form-control {\n display: inline-block;\n width: auto; // Prevent labels from stacking above inputs in `.form-group`\n vertical-align: middle;\n }\n // Input groups need that 100% width though\n .input-group > .form-control {\n width: 100%;\n }\n\n .control-label {\n margin-bottom: 0;\n vertical-align: middle;\n }\n\n // Remove default margin on radios/checkboxes that were used for stacking, and\n // then undo the floating of radios and checkboxes to match (which also avoids\n // a bug in WebKit: https://github.com/twbs/bootstrap/issues/1969).\n .radio,\n .checkbox {\n display: inline-block;\n margin-top: 0;\n margin-bottom: 0;\n padding-left: 0;\n vertical-align: middle;\n }\n .radio input[type=\"radio\"],\n .checkbox input[type=\"checkbox\"] {\n float: none;\n margin-left: 0;\n }\n\n // Validation states\n //\n // Reposition the icon because it's now within a grid column and columns have\n // `position: relative;` on them. Also accounts for the grid gutter padding.\n .has-feedback .form-control-feedback {\n top: 0;\n }\n }\n}\n\n\n// Horizontal forms\n//\n// Horizontal forms are built on grid classes and allow you to create forms with\n// labels on the left and inputs on the right.\n\n.form-horizontal {\n\n // Consistent vertical alignment of labels, radios, and checkboxes\n .control-label,\n .radio,\n .checkbox,\n .radio-inline,\n .checkbox-inline {\n margin-top: 0;\n margin-bottom: 0;\n padding-top: (@padding-base-vertical + 1); // Default padding plus a border\n }\n // Account for padding we're adding to ensure the alignment and of help text\n // and other content below items\n .radio,\n .checkbox {\n min-height: (@line-height-computed + (@padding-base-vertical + 1));\n }\n\n // Make form groups behave like rows\n .form-group {\n .make-row();\n }\n\n .form-control-static {\n padding-top: (@padding-base-vertical + 1);\n }\n\n // Only right align form labels here when the columns stop stacking\n @media (min-width: @screen-sm-min) {\n .control-label {\n text-align: right;\n }\n }\n\n // Validation states\n //\n // Reposition the icon because it's now within a grid column and columns have\n // `position: relative;` on them. Also accounts for the grid gutter padding.\n .has-feedback .form-control-feedback {\n top: 0;\n right: (@grid-gutter-width / 2);\n }\n}\n","//\n// Buttons\n// --------------------------------------------------\n\n\n// Base styles\n// --------------------------------------------------\n\n.btn {\n display: inline-block;\n margin-bottom: 0; // For input.btn\n font-weight: @btn-font-weight;\n text-align: center;\n vertical-align: middle;\n cursor: pointer;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n white-space: nowrap;\n .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base);\n .user-select(none);\n\n &,\n &:active,\n &.active {\n &:focus {\n .tab-focus();\n }\n }\n\n &:hover,\n &:focus {\n color: @btn-default-color;\n text-decoration: none;\n }\n\n &:active,\n &.active {\n outline: 0;\n background-image: none;\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n }\n\n &.disabled,\n &[disabled],\n fieldset[disabled] & {\n cursor: not-allowed;\n pointer-events: none; // Future-proof disabling of clicks\n .opacity(.65);\n .box-shadow(none);\n }\n}\n\n\n// Alternate buttons\n// --------------------------------------------------\n\n.btn-default {\n .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);\n}\n.btn-primary {\n .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border);\n}\n// Success appears as green\n.btn-success {\n .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border);\n}\n// Info appears as blue-green\n.btn-info {\n .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border);\n}\n// Warning appears as orange\n.btn-warning {\n .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border);\n}\n// Danger and error appear as red\n.btn-danger {\n .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border);\n}\n\n\n// Link buttons\n// -------------------------\n\n// Make a button look and behave like a link\n.btn-link {\n color: @link-color;\n font-weight: normal;\n cursor: pointer;\n border-radius: 0;\n\n &,\n &:active,\n &[disabled],\n fieldset[disabled] & {\n background-color: transparent;\n .box-shadow(none);\n }\n &,\n &:hover,\n &:focus,\n &:active {\n border-color: transparent;\n }\n &:hover,\n &:focus {\n color: @link-hover-color;\n text-decoration: underline;\n background-color: transparent;\n }\n &[disabled],\n fieldset[disabled] & {\n &:hover,\n &:focus {\n color: @btn-link-disabled-color;\n text-decoration: none;\n }\n }\n}\n\n\n// Button Sizes\n// --------------------------------------------------\n\n.btn-lg {\n // line-height: ensure even-numbered height of button next to large input\n .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);\n}\n.btn-sm {\n // line-height: ensure proper height of button next to small input\n .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n.btn-xs {\n .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small);\n}\n\n\n// Block button\n// --------------------------------------------------\n\n.btn-block {\n display: block;\n width: 100%;\n padding-left: 0;\n padding-right: 0;\n}\n\n// Vertically space out multiple block buttons\n.btn-block + .btn-block {\n margin-top: 5px;\n}\n\n// Specificity overrides\ninput[type=\"submit\"],\ninput[type=\"reset\"],\ninput[type=\"button\"] {\n &.btn-block {\n width: 100%;\n }\n}\n","//\n// Button groups\n// --------------------------------------------------\n\n// Make the div behave like a button\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-block;\n vertical-align: middle; // match .btn alignment given font-size hack above\n > .btn {\n position: relative;\n float: left;\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active,\n &.active {\n z-index: 2;\n }\n &:focus {\n // Remove focus outline when dropdown JS adds it after closing the menu\n outline: none;\n }\n }\n}\n\n// Prevent double borders when buttons are next to each other\n.btn-group {\n .btn + .btn,\n .btn + .btn-group,\n .btn-group + .btn,\n .btn-group + .btn-group {\n margin-left: -1px;\n }\n}\n\n// Optional: Group multiple button groups together for a toolbar\n.btn-toolbar {\n margin-left: -5px; // Offset the first child's margin\n &:extend(.clearfix all);\n\n .btn-group,\n .input-group {\n float: left;\n }\n > .btn,\n > .btn-group,\n > .input-group {\n margin-left: 5px;\n }\n}\n\n.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {\n border-radius: 0;\n}\n\n// 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\n.btn-group > .btn:first-child {\n margin-left: 0;\n &:not(:last-child):not(.dropdown-toggle) {\n .border-right-radius(0);\n }\n}\n// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it\n.btn-group > .btn:last-child:not(:first-child),\n.btn-group > .dropdown-toggle:not(:first-child) {\n .border-left-radius(0);\n}\n\n// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group)\n.btn-group > .btn-group {\n float: left;\n}\n.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group > .btn-group:first-child {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-right-radius(0);\n }\n}\n.btn-group > .btn-group:last-child > .btn:first-child {\n .border-left-radius(0);\n}\n\n// On active and open, don't show outline\n.btn-group .dropdown-toggle:active,\n.btn-group.open .dropdown-toggle {\n outline: 0;\n}\n\n\n// Sizing\n//\n// Remix the default button sizing classes into new ones for easier manipulation.\n\n.btn-group-xs > .btn { &:extend(.btn-xs); }\n.btn-group-sm > .btn { &:extend(.btn-sm); }\n.btn-group-lg > .btn { &:extend(.btn-lg); }\n\n\n// Split button dropdowns\n// ----------------------\n\n// Give the line between buttons some depth\n.btn-group > .btn + .dropdown-toggle {\n padding-left: 8px;\n padding-right: 8px;\n}\n.btn-group > .btn-lg + .dropdown-toggle {\n padding-left: 12px;\n padding-right: 12px;\n}\n\n// The clickable button for toggling the menu\n// Remove the gradient and set the same inset shadow as the :active state\n.btn-group.open .dropdown-toggle {\n .box-shadow(inset 0 3px 5px rgba(0,0,0,.125));\n\n // Show no shadow for `.btn-link` since it has no other button styles.\n &.btn-link {\n .box-shadow(none);\n }\n}\n\n\n// Reposition the caret\n.btn .caret {\n margin-left: 0;\n}\n// Carets in other button sizes\n.btn-lg .caret {\n border-width: @caret-width-large @caret-width-large 0;\n border-bottom-width: 0;\n}\n// Upside down carets for .dropup\n.dropup .btn-lg .caret {\n border-width: 0 @caret-width-large @caret-width-large;\n}\n\n\n// Vertical button groups\n// ----------------------\n\n.btn-group-vertical {\n > .btn,\n > .btn-group,\n > .btn-group > .btn {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n }\n\n // Clear floats so dropdown menus can be properly placed\n > .btn-group {\n &:extend(.clearfix all);\n > .btn {\n float: none;\n }\n }\n\n > .btn + .btn,\n > .btn + .btn-group,\n > .btn-group + .btn,\n > .btn-group + .btn-group {\n margin-top: -1px;\n margin-left: 0;\n }\n}\n\n.btn-group-vertical > .btn {\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n &:first-child:not(:last-child) {\n border-top-right-radius: @border-radius-base;\n .border-bottom-radius(0);\n }\n &:last-child:not(:first-child) {\n border-bottom-left-radius: @border-radius-base;\n .border-top-radius(0);\n }\n}\n.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {\n border-radius: 0;\n}\n.btn-group-vertical > .btn-group:first-child:not(:last-child) {\n > .btn:last-child,\n > .dropdown-toggle {\n .border-bottom-radius(0);\n }\n}\n.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {\n .border-top-radius(0);\n}\n\n\n\n// Justified button groups\n// ----------------------\n\n.btn-group-justified {\n display: table;\n width: 100%;\n table-layout: fixed;\n border-collapse: separate;\n > .btn,\n > .btn-group {\n float: none;\n display: table-cell;\n width: 1%;\n }\n > .btn-group .btn {\n width: 100%;\n }\n}\n\n\n// Checkbox and radio options\n[data-toggle=\"buttons\"] > .btn > input[type=\"radio\"],\n[data-toggle=\"buttons\"] > .btn > input[type=\"checkbox\"] {\n display: none;\n}\n","//\n// Component animations\n// --------------------------------------------------\n\n// Heads up!\n//\n// We don't use the `.opacity()` mixin here since it causes a bug with text\n// fields in IE7-8. Source: https://github.com/twitter/bootstrap/pull/3552.\n\n.fade {\n opacity: 0;\n .transition(opacity .15s linear);\n &.in {\n opacity: 1;\n }\n}\n\n.collapse {\n display: none;\n &.in {\n display: block;\n }\n}\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n .transition(height .35s ease);\n}\n","//\n// Glyphicons for Bootstrap\n//\n// Since icons are fonts, they can be placed anywhere text is placed and are\n// thus automatically sized to match the surrounding child. To use, create an\n// inline element with the appropriate classes, like so:\n//\n// Star \n\n// Import the fonts\n@font-face {\n font-family: 'Glyphicons Halflings';\n src: ~\"url('@{icon-font-path}@{icon-font-name}.eot')\";\n src: ~\"url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype')\",\n ~\"url('@{icon-font-path}@{icon-font-name}.woff') format('woff')\",\n ~\"url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype')\",\n ~\"url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg')\";\n}\n\n// Catchall baseclass\n.glyphicon {\n position: relative;\n top: 1px;\n display: inline-block;\n font-family: 'Glyphicons Halflings';\n font-style: normal;\n font-weight: normal;\n line-height: 1;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Individual icons\n.glyphicon-asterisk { &:before { content: \"\\2a\"; } }\n.glyphicon-plus { &:before { content: \"\\2b\"; } }\n.glyphicon-euro { &:before { content: \"\\20ac\"; } }\n.glyphicon-minus { &:before { content: \"\\2212\"; } }\n.glyphicon-cloud { &:before { content: \"\\2601\"; } }\n.glyphicon-envelope { &:before { content: \"\\2709\"; } }\n.glyphicon-pencil { &:before { content: \"\\270f\"; } }\n.glyphicon-glass { &:before { content: \"\\e001\"; } }\n.glyphicon-music { &:before { content: \"\\e002\"; } }\n.glyphicon-search { &:before { content: \"\\e003\"; } }\n.glyphicon-heart { &:before { content: \"\\e005\"; } }\n.glyphicon-star { &:before { content: \"\\e006\"; } }\n.glyphicon-star-empty { &:before { content: \"\\e007\"; } }\n.glyphicon-user { &:before { content: \"\\e008\"; } }\n.glyphicon-film { &:before { content: \"\\e009\"; } }\n.glyphicon-th-large { &:before { content: \"\\e010\"; } }\n.glyphicon-th { &:before { content: \"\\e011\"; } }\n.glyphicon-th-list { &:before { content: \"\\e012\"; } }\n.glyphicon-ok { &:before { content: \"\\e013\"; } }\n.glyphicon-remove { &:before { content: \"\\e014\"; } }\n.glyphicon-zoom-in { &:before { content: \"\\e015\"; } }\n.glyphicon-zoom-out { &:before { content: \"\\e016\"; } }\n.glyphicon-off { &:before { content: \"\\e017\"; } }\n.glyphicon-signal { &:before { content: \"\\e018\"; } }\n.glyphicon-cog { &:before { content: \"\\e019\"; } }\n.glyphicon-trash { &:before { content: \"\\e020\"; } }\n.glyphicon-home { &:before { content: \"\\e021\"; } }\n.glyphicon-file { &:before { content: \"\\e022\"; } }\n.glyphicon-time { &:before { content: \"\\e023\"; } }\n.glyphicon-road { &:before { content: \"\\e024\"; } }\n.glyphicon-download-alt { &:before { content: \"\\e025\"; } }\n.glyphicon-download { &:before { content: \"\\e026\"; } }\n.glyphicon-upload { &:before { content: \"\\e027\"; } }\n.glyphicon-inbox { &:before { content: \"\\e028\"; } }\n.glyphicon-play-circle { &:before { content: \"\\e029\"; } }\n.glyphicon-repeat { &:before { content: \"\\e030\"; } }\n.glyphicon-refresh { &:before { content: \"\\e031\"; } }\n.glyphicon-list-alt { &:before { content: \"\\e032\"; } }\n.glyphicon-lock { &:before { content: \"\\e033\"; } }\n.glyphicon-flag { &:before { content: \"\\e034\"; } }\n.glyphicon-headphones { &:before { content: \"\\e035\"; } }\n.glyphicon-volume-off { &:before { content: \"\\e036\"; } }\n.glyphicon-volume-down { &:before { content: \"\\e037\"; } }\n.glyphicon-volume-up { &:before { content: \"\\e038\"; } }\n.glyphicon-qrcode { &:before { content: \"\\e039\"; } }\n.glyphicon-barcode { &:before { content: \"\\e040\"; } }\n.glyphicon-tag { &:before { content: \"\\e041\"; } }\n.glyphicon-tags { &:before { content: \"\\e042\"; } }\n.glyphicon-book { &:before { content: \"\\e043\"; } }\n.glyphicon-bookmark { &:before { content: \"\\e044\"; } }\n.glyphicon-print { &:before { content: \"\\e045\"; } }\n.glyphicon-camera { &:before { content: \"\\e046\"; } }\n.glyphicon-font { &:before { content: \"\\e047\"; } }\n.glyphicon-bold { &:before { content: \"\\e048\"; } }\n.glyphicon-italic { &:before { content: \"\\e049\"; } }\n.glyphicon-text-height { &:before { content: \"\\e050\"; } }\n.glyphicon-text-width { &:before { content: \"\\e051\"; } }\n.glyphicon-align-left { &:before { content: \"\\e052\"; } }\n.glyphicon-align-center { &:before { content: \"\\e053\"; } }\n.glyphicon-align-right { &:before { content: \"\\e054\"; } }\n.glyphicon-align-justify { &:before { content: \"\\e055\"; } }\n.glyphicon-list { &:before { content: \"\\e056\"; } }\n.glyphicon-indent-left { &:before { content: \"\\e057\"; } }\n.glyphicon-indent-right { &:before { content: \"\\e058\"; } }\n.glyphicon-facetime-video { &:before { content: \"\\e059\"; } }\n.glyphicon-picture { &:before { content: \"\\e060\"; } }\n.glyphicon-map-marker { &:before { content: \"\\e062\"; } }\n.glyphicon-adjust { &:before { content: \"\\e063\"; } }\n.glyphicon-tint { &:before { content: \"\\e064\"; } }\n.glyphicon-edit { &:before { content: \"\\e065\"; } }\n.glyphicon-share { &:before { content: \"\\e066\"; } }\n.glyphicon-check { &:before { content: \"\\e067\"; } }\n.glyphicon-move { &:before { content: \"\\e068\"; } }\n.glyphicon-step-backward { &:before { content: \"\\e069\"; } }\n.glyphicon-fast-backward { &:before { content: \"\\e070\"; } }\n.glyphicon-backward { &:before { content: \"\\e071\"; } }\n.glyphicon-play { &:before { content: \"\\e072\"; } }\n.glyphicon-pause { &:before { content: \"\\e073\"; } }\n.glyphicon-stop { &:before { content: \"\\e074\"; } }\n.glyphicon-forward { &:before { content: \"\\e075\"; } }\n.glyphicon-fast-forward { &:before { content: \"\\e076\"; } }\n.glyphicon-step-forward { &:before { content: \"\\e077\"; } }\n.glyphicon-eject { &:before { content: \"\\e078\"; } }\n.glyphicon-chevron-left { &:before { content: \"\\e079\"; } }\n.glyphicon-chevron-right { &:before { content: \"\\e080\"; } }\n.glyphicon-plus-sign { &:before { content: \"\\e081\"; } }\n.glyphicon-minus-sign { &:before { content: \"\\e082\"; } }\n.glyphicon-remove-sign { &:before { content: \"\\e083\"; } }\n.glyphicon-ok-sign { &:before { content: \"\\e084\"; } }\n.glyphicon-question-sign { &:before { content: \"\\e085\"; } }\n.glyphicon-info-sign { &:before { content: \"\\e086\"; } }\n.glyphicon-screenshot { &:before { content: \"\\e087\"; } }\n.glyphicon-remove-circle { &:before { content: \"\\e088\"; } }\n.glyphicon-ok-circle { &:before { content: \"\\e089\"; } }\n.glyphicon-ban-circle { &:before { content: \"\\e090\"; } }\n.glyphicon-arrow-left { &:before { content: \"\\e091\"; } }\n.glyphicon-arrow-right { &:before { content: \"\\e092\"; } }\n.glyphicon-arrow-up { &:before { content: \"\\e093\"; } }\n.glyphicon-arrow-down { &:before { content: \"\\e094\"; } }\n.glyphicon-share-alt { &:before { content: \"\\e095\"; } }\n.glyphicon-resize-full { &:before { content: \"\\e096\"; } }\n.glyphicon-resize-small { &:before { content: \"\\e097\"; } }\n.glyphicon-exclamation-sign { &:before { content: \"\\e101\"; } }\n.glyphicon-gift { &:before { content: \"\\e102\"; } }\n.glyphicon-leaf { &:before { content: \"\\e103\"; } }\n.glyphicon-fire { &:before { content: \"\\e104\"; } }\n.glyphicon-eye-open { &:before { content: \"\\e105\"; } }\n.glyphicon-eye-close { &:before { content: \"\\e106\"; } }\n.glyphicon-warning-sign { &:before { content: \"\\e107\"; } }\n.glyphicon-plane { &:before { content: \"\\e108\"; } }\n.glyphicon-calendar { &:before { content: \"\\e109\"; } }\n.glyphicon-random { &:before { content: \"\\e110\"; } }\n.glyphicon-comment { &:before { content: \"\\e111\"; } }\n.glyphicon-magnet { &:before { content: \"\\e112\"; } }\n.glyphicon-chevron-up { &:before { content: \"\\e113\"; } }\n.glyphicon-chevron-down { &:before { content: \"\\e114\"; } }\n.glyphicon-retweet { &:before { content: \"\\e115\"; } }\n.glyphicon-shopping-cart { &:before { content: \"\\e116\"; } }\n.glyphicon-folder-close { &:before { content: \"\\e117\"; } }\n.glyphicon-folder-open { &:before { content: \"\\e118\"; } }\n.glyphicon-resize-vertical { &:before { content: \"\\e119\"; } }\n.glyphicon-resize-horizontal { &:before { content: \"\\e120\"; } }\n.glyphicon-hdd { &:before { content: \"\\e121\"; } }\n.glyphicon-bullhorn { &:before { content: \"\\e122\"; } }\n.glyphicon-bell { &:before { content: \"\\e123\"; } }\n.glyphicon-certificate { &:before { content: \"\\e124\"; } }\n.glyphicon-thumbs-up { &:before { content: \"\\e125\"; } }\n.glyphicon-thumbs-down { &:before { content: \"\\e126\"; } }\n.glyphicon-hand-right { &:before { content: \"\\e127\"; } }\n.glyphicon-hand-left { &:before { content: \"\\e128\"; } }\n.glyphicon-hand-up { &:before { content: \"\\e129\"; } }\n.glyphicon-hand-down { &:before { content: \"\\e130\"; } }\n.glyphicon-circle-arrow-right { &:before { content: \"\\e131\"; } }\n.glyphicon-circle-arrow-left { &:before { content: \"\\e132\"; } }\n.glyphicon-circle-arrow-up { &:before { content: \"\\e133\"; } }\n.glyphicon-circle-arrow-down { &:before { content: \"\\e134\"; } }\n.glyphicon-globe { &:before { content: \"\\e135\"; } }\n.glyphicon-wrench { &:before { content: \"\\e136\"; } }\n.glyphicon-tasks { &:before { content: \"\\e137\"; } }\n.glyphicon-filter { &:before { content: \"\\e138\"; } }\n.glyphicon-briefcase { &:before { content: \"\\e139\"; } }\n.glyphicon-fullscreen { &:before { content: \"\\e140\"; } }\n.glyphicon-dashboard { &:before { content: \"\\e141\"; } }\n.glyphicon-paperclip { &:before { content: \"\\e142\"; } }\n.glyphicon-heart-empty { &:before { content: \"\\e143\"; } }\n.glyphicon-link { &:before { content: \"\\e144\"; } }\n.glyphicon-phone { &:before { content: \"\\e145\"; } }\n.glyphicon-pushpin { &:before { content: \"\\e146\"; } }\n.glyphicon-usd { &:before { content: \"\\e148\"; } }\n.glyphicon-gbp { &:before { content: \"\\e149\"; } }\n.glyphicon-sort { &:before { content: \"\\e150\"; } }\n.glyphicon-sort-by-alphabet { &:before { content: \"\\e151\"; } }\n.glyphicon-sort-by-alphabet-alt { &:before { content: \"\\e152\"; } }\n.glyphicon-sort-by-order { &:before { content: \"\\e153\"; } }\n.glyphicon-sort-by-order-alt { &:before { content: \"\\e154\"; } }\n.glyphicon-sort-by-attributes { &:before { content: \"\\e155\"; } }\n.glyphicon-sort-by-attributes-alt { &:before { content: \"\\e156\"; } }\n.glyphicon-unchecked { &:before { content: \"\\e157\"; } }\n.glyphicon-expand { &:before { content: \"\\e158\"; } }\n.glyphicon-collapse-down { &:before { content: \"\\e159\"; } }\n.glyphicon-collapse-up { &:before { content: \"\\e160\"; } }\n.glyphicon-log-in { &:before { content: \"\\e161\"; } }\n.glyphicon-flash { &:before { content: \"\\e162\"; } }\n.glyphicon-log-out { &:before { content: \"\\e163\"; } }\n.glyphicon-new-window { &:before { content: \"\\e164\"; } }\n.glyphicon-record { &:before { content: \"\\e165\"; } }\n.glyphicon-save { &:before { content: \"\\e166\"; } }\n.glyphicon-open { &:before { content: \"\\e167\"; } }\n.glyphicon-saved { &:before { content: \"\\e168\"; } }\n.glyphicon-import { &:before { content: \"\\e169\"; } }\n.glyphicon-export { &:before { content: \"\\e170\"; } }\n.glyphicon-send { &:before { content: \"\\e171\"; } }\n.glyphicon-floppy-disk { &:before { content: \"\\e172\"; } }\n.glyphicon-floppy-saved { &:before { content: \"\\e173\"; } }\n.glyphicon-floppy-remove { &:before { content: \"\\e174\"; } }\n.glyphicon-floppy-save { &:before { content: \"\\e175\"; } }\n.glyphicon-floppy-open { &:before { content: \"\\e176\"; } }\n.glyphicon-credit-card { &:before { content: \"\\e177\"; } }\n.glyphicon-transfer { &:before { content: \"\\e178\"; } }\n.glyphicon-cutlery { &:before { content: \"\\e179\"; } }\n.glyphicon-header { &:before { content: \"\\e180\"; } }\n.glyphicon-compressed { &:before { content: \"\\e181\"; } }\n.glyphicon-earphone { &:before { content: \"\\e182\"; } }\n.glyphicon-phone-alt { &:before { content: \"\\e183\"; } }\n.glyphicon-tower { &:before { content: \"\\e184\"; } }\n.glyphicon-stats { &:before { content: \"\\e185\"; } }\n.glyphicon-sd-video { &:before { content: \"\\e186\"; } }\n.glyphicon-hd-video { &:before { content: \"\\e187\"; } }\n.glyphicon-subtitles { &:before { content: \"\\e188\"; } }\n.glyphicon-sound-stereo { &:before { content: \"\\e189\"; } }\n.glyphicon-sound-dolby { &:before { content: \"\\e190\"; } }\n.glyphicon-sound-5-1 { &:before { content: \"\\e191\"; } }\n.glyphicon-sound-6-1 { &:before { content: \"\\e192\"; } }\n.glyphicon-sound-7-1 { &:before { content: \"\\e193\"; } }\n.glyphicon-copyright-mark { &:before { content: \"\\e194\"; } }\n.glyphicon-registration-mark { &:before { content: \"\\e195\"; } }\n.glyphicon-cloud-download { &:before { content: \"\\e197\"; } }\n.glyphicon-cloud-upload { &:before { content: \"\\e198\"; } }\n.glyphicon-tree-conifer { &:before { content: \"\\e199\"; } }\n.glyphicon-tree-deciduous { &:before { content: \"\\e200\"; } }\n","//\n// Dropdown menus\n// --------------------------------------------------\n\n\n// Dropdown arrow/caret\n.caret {\n display: inline-block;\n width: 0;\n height: 0;\n margin-left: 2px;\n vertical-align: middle;\n border-top: @caret-width-base solid;\n border-right: @caret-width-base solid transparent;\n border-left: @caret-width-base solid transparent;\n}\n\n// The dropdown wrapper (div)\n.dropdown {\n position: relative;\n}\n\n// Prevent the focus on the dropdown toggle when closing dropdowns\n.dropdown-toggle:focus {\n outline: 0;\n}\n\n// The dropdown menu (ul)\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: @zindex-dropdown;\n display: none; // none by default, but block on \"open\" of the menu\n float: left;\n min-width: 160px;\n padding: 5px 0;\n margin: 2px 0 0; // override default ul\n list-style: none;\n font-size: @font-size-base;\n background-color: @dropdown-bg;\n border: 1px solid @dropdown-fallback-border; // IE8 fallback\n border: 1px solid @dropdown-border;\n border-radius: @border-radius-base;\n .box-shadow(0 6px 12px rgba(0,0,0,.175));\n background-clip: padding-box;\n\n // Aligns the dropdown menu to right\n //\n // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]`\n &.pull-right {\n right: 0;\n left: auto;\n }\n\n // Dividers (basically an hr) within the dropdown\n .divider {\n .nav-divider(@dropdown-divider-bg);\n }\n\n // Links within the dropdown menu\n > li > a {\n display: block;\n padding: 3px 20px;\n clear: both;\n font-weight: normal;\n line-height: @line-height-base;\n color: @dropdown-link-color;\n white-space: nowrap; // prevent links from randomly breaking onto new lines\n }\n}\n\n// Hover/Focus state\n.dropdown-menu > li > a {\n &:hover,\n &:focus {\n text-decoration: none;\n color: @dropdown-link-hover-color;\n background-color: @dropdown-link-hover-bg;\n }\n}\n\n// Active state\n.dropdown-menu > .active > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-active-color;\n text-decoration: none;\n outline: 0;\n background-color: @dropdown-link-active-bg;\n }\n}\n\n// Disabled state\n//\n// Gray out text and ensure the hover/focus state remains gray\n\n.dropdown-menu > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @dropdown-link-disabled-color;\n }\n}\n// Nuke hover/focus effects\n.dropdown-menu > .disabled > a {\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: transparent;\n background-image: none; // Remove CSS gradient\n .reset-filter();\n cursor: not-allowed;\n }\n}\n\n// Open state for the dropdown\n.open {\n // Show the menu\n > .dropdown-menu {\n display: block;\n }\n\n // Remove the outline when :focus is triggered\n > a {\n outline: 0;\n }\n}\n\n// Menu positioning\n//\n// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown\n// menu with the parent.\n.dropdown-menu-right {\n left: auto; // Reset the default from `.dropdown-menu`\n right: 0;\n}\n// With v3, we enabled auto-flipping if you have a dropdown within a right\n// aligned nav component. To enable the undoing of that, we provide an override\n// to restore the default dropdown menu alignment.\n//\n// This is only for left-aligning a dropdown menu within a `.navbar-right` or\n// `.pull-right` nav component.\n.dropdown-menu-left {\n left: 0;\n right: auto;\n}\n\n// Dropdown section headers\n.dropdown-header {\n display: block;\n padding: 3px 20px;\n font-size: @font-size-small;\n line-height: @line-height-base;\n color: @dropdown-header-color;\n}\n\n// Backdrop to catch body clicks on mobile, etc.\n.dropdown-backdrop {\n position: fixed;\n left: 0;\n right: 0;\n bottom: 0;\n top: 0;\n z-index: (@zindex-dropdown - 10);\n}\n\n// Right aligned dropdowns\n.pull-right > .dropdown-menu {\n right: 0;\n left: auto;\n}\n\n// Allow for dropdowns to go bottom up (aka, dropup-menu)\n//\n// Just add .dropup after the standard .dropdown class and you're set, bro.\n// TODO: abstract this so that the navbar fixed styles are not placed here?\n\n.dropup,\n.navbar-fixed-bottom .dropdown {\n // Reverse the caret\n .caret {\n border-top: 0;\n border-bottom: @caret-width-base solid;\n content: \"\";\n }\n // Different positioning for bottom up menu\n .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-bottom: 1px;\n }\n}\n\n\n// Component alignment\n//\n// Reiterate per navbar.less and the modified component alignment there.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-right {\n .dropdown-menu {\n .dropdown-menu-right();\n }\n // Necessary for overrides of the default right aligned menu.\n // Will remove come v4 in all likelihood.\n .dropdown-menu-left {\n .dropdown-menu-left();\n }\n }\n}\n\n","//\n// Input groups\n// --------------------------------------------------\n\n// Base styles\n// -------------------------\n.input-group {\n position: relative; // For dropdowns\n display: table;\n border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table\n\n // Undo padding and float of grid classes\n &[class*=\"col-\"] {\n float: none;\n padding-left: 0;\n padding-right: 0;\n }\n\n .form-control {\n // Ensure that the input is always above the *appended* addon button for\n // proper border colors.\n position: relative;\n z-index: 2;\n\n // IE9 fubars the placeholder attribute in text inputs and the arrows on\n // select elements in input groups. To fix it, we float the input. Details:\n // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855\n float: left;\n\n width: 100%;\n margin-bottom: 0;\n }\n}\n\n// Sizing options\n//\n// Remix the default form control sizing classes into new ones for easier\n// manipulation.\n\n.input-group-lg > .form-control,\n.input-group-lg > .input-group-addon,\n.input-group-lg > .input-group-btn > .btn { .input-lg(); }\n.input-group-sm > .form-control,\n.input-group-sm > .input-group-addon,\n.input-group-sm > .input-group-btn > .btn { .input-sm(); }\n\n\n// Display as table-cell\n// -------------------------\n.input-group-addon,\n.input-group-btn,\n.input-group .form-control {\n display: table-cell;\n\n &:not(:first-child):not(:last-child) {\n border-radius: 0;\n }\n}\n// Addon and addon wrapper for buttons\n.input-group-addon,\n.input-group-btn {\n width: 1%;\n white-space: nowrap;\n vertical-align: middle; // Match the inputs\n}\n\n// Text input groups\n// -------------------------\n.input-group-addon {\n padding: @padding-base-vertical @padding-base-horizontal;\n font-size: @font-size-base;\n font-weight: normal;\n line-height: 1;\n color: @input-color;\n text-align: center;\n background-color: @input-group-addon-bg;\n border: 1px solid @input-group-addon-border-color;\n border-radius: @border-radius-base;\n\n // Sizing\n &.input-sm {\n padding: @padding-small-vertical @padding-small-horizontal;\n font-size: @font-size-small;\n border-radius: @border-radius-small;\n }\n &.input-lg {\n padding: @padding-large-vertical @padding-large-horizontal;\n font-size: @font-size-large;\n border-radius: @border-radius-large;\n }\n\n // Nuke default margins from checkboxes and radios to vertically center within.\n input[type=\"radio\"],\n input[type=\"checkbox\"] {\n margin-top: 0;\n }\n}\n\n// Reset rounded corners\n.input-group .form-control:first-child,\n.input-group-addon:first-child,\n.input-group-btn:first-child > .btn,\n.input-group-btn:first-child > .btn-group > .btn,\n.input-group-btn:first-child > .dropdown-toggle,\n.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {\n .border-right-radius(0);\n}\n.input-group-addon:first-child {\n border-right: 0;\n}\n.input-group .form-control:last-child,\n.input-group-addon:last-child,\n.input-group-btn:last-child > .btn,\n.input-group-btn:last-child > .btn-group > .btn,\n.input-group-btn:last-child > .dropdown-toggle,\n.input-group-btn:first-child > .btn:not(:first-child),\n.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {\n .border-left-radius(0);\n}\n.input-group-addon:last-child {\n border-left: 0;\n}\n\n// Button input groups\n// -------------------------\n.input-group-btn {\n position: relative;\n // Jankily prevent input button groups from wrapping with `white-space` and\n // `font-size` in combination with `inline-block` on buttons.\n font-size: 0;\n white-space: nowrap;\n\n // Negative margin for spacing, position for bringing hovered/focused/actived\n // element above the siblings.\n > .btn {\n position: relative;\n + .btn {\n margin-left: -1px;\n }\n // Bring the \"active\" button to the front\n &:hover,\n &:focus,\n &:active {\n z-index: 2;\n }\n }\n\n // Negative margin to only have a 1px border between the two\n &:first-child {\n > .btn,\n > .btn-group {\n margin-right: -1px;\n }\n }\n &:last-child {\n > .btn,\n > .btn-group {\n margin-left: -1px;\n }\n }\n}\n","//\n// Navs\n// --------------------------------------------------\n\n\n// Base class\n// --------------------------------------------------\n\n.nav {\n margin-bottom: 0;\n padding-left: 0; // Override default ul/ol\n list-style: none;\n &:extend(.clearfix all);\n\n > li {\n position: relative;\n display: block;\n\n > a {\n position: relative;\n display: block;\n padding: @nav-link-padding;\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: @nav-link-hover-bg;\n }\n }\n\n // Disabled state sets text to gray and nukes hover/tab effects\n &.disabled > a {\n color: @nav-disabled-link-color;\n\n &:hover,\n &:focus {\n color: @nav-disabled-link-hover-color;\n text-decoration: none;\n background-color: transparent;\n cursor: not-allowed;\n }\n }\n }\n\n // Open dropdowns\n .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @nav-link-hover-bg;\n border-color: @link-color;\n }\n }\n\n // Nav dividers (deprecated with v3.0.1)\n //\n // This should have been removed in v3 with the dropping of `.nav-list`, but\n // we missed it. We don't currently support this anywhere, but in the interest\n // of maintaining backward compatibility in case you use it, it's deprecated.\n .nav-divider {\n .nav-divider();\n }\n\n // Prevent IE8 from misplacing imgs\n //\n // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989\n > li > a > img {\n max-width: none;\n }\n}\n\n\n// Tabs\n// -------------------------\n\n// Give the tabs something to sit on\n.nav-tabs {\n border-bottom: 1px solid @nav-tabs-border-color;\n > li {\n float: left;\n // Make the list-items overlay the bottom border\n margin-bottom: -1px;\n\n // Actual tabs (as links)\n > a {\n margin-right: 2px;\n line-height: @line-height-base;\n border: 1px solid transparent;\n border-radius: @border-radius-base @border-radius-base 0 0;\n &:hover {\n border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color;\n }\n }\n\n // Active state, and its :hover to override normal :hover\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-tabs-active-link-hover-color;\n background-color: @nav-tabs-active-link-hover-bg;\n border: 1px solid @nav-tabs-active-link-hover-border-color;\n border-bottom-color: transparent;\n cursor: default;\n }\n }\n }\n // pulling this in mainly for less shorthand\n &.nav-justified {\n .nav-justified();\n .nav-tabs-justified();\n }\n}\n\n\n// Pills\n// -------------------------\n.nav-pills {\n > li {\n float: left;\n\n // Links rendered as pills\n > a {\n border-radius: @nav-pills-border-radius;\n }\n + li {\n margin-left: 2px;\n }\n\n // Active state\n &.active > a {\n &,\n &:hover,\n &:focus {\n color: @nav-pills-active-link-hover-color;\n background-color: @nav-pills-active-link-hover-bg;\n }\n }\n }\n}\n\n\n// Stacked pills\n.nav-stacked {\n > li {\n float: none;\n + li {\n margin-top: 2px;\n margin-left: 0; // no need for this gap between nav items\n }\n }\n}\n\n\n// Nav variations\n// --------------------------------------------------\n\n// Justified nav links\n// -------------------------\n\n.nav-justified {\n width: 100%;\n\n > li {\n float: none;\n > a {\n text-align: center;\n margin-bottom: 5px;\n }\n }\n\n > .dropdown .dropdown-menu {\n top: auto;\n left: auto;\n }\n\n @media (min-width: @screen-sm-min) {\n > li {\n display: table-cell;\n width: 1%;\n > a {\n margin-bottom: 0;\n }\n }\n }\n}\n\n// Move borders to anchors instead of bottom of list\n//\n// Mixin for adding on top the shared `.nav-justified` styles for our tabs\n.nav-tabs-justified {\n border-bottom: 0;\n\n > li > a {\n // Override margin from .nav-tabs\n margin-right: 0;\n border-radius: @border-radius-base;\n }\n\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border: 1px solid @nav-tabs-justified-link-border-color;\n }\n\n @media (min-width: @screen-sm-min) {\n > li > a {\n border-bottom: 1px solid @nav-tabs-justified-link-border-color;\n border-radius: @border-radius-base @border-radius-base 0 0;\n }\n > .active > a,\n > .active > a:hover,\n > .active > a:focus {\n border-bottom-color: @nav-tabs-justified-active-link-border-color;\n }\n }\n}\n\n\n// Tabbable tabs\n// -------------------------\n\n// Hide tabbable panes to start, show them when `.active`\n.tab-content {\n > .tab-pane {\n display: none;\n }\n > .active {\n display: block;\n }\n}\n\n\n// Dropdowns\n// -------------------------\n\n// Specific dropdowns\n.nav-tabs .dropdown-menu {\n // make dropdown border overlap tab border\n margin-top: -1px;\n // Remove the top rounded corners here since there is a hard edge above the menu\n .border-top-radius(0);\n}\n","//\n// Navbars\n// --------------------------------------------------\n\n\n// Wrapper and base class\n//\n// Provide a static navbar from which we expand to create full-width, fixed, and\n// other navbar variations.\n\n.navbar {\n position: relative;\n min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode)\n margin-bottom: @navbar-margin-bottom;\n border: 1px solid transparent;\n\n // Prevent floats from breaking the navbar\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: @navbar-border-radius;\n }\n}\n\n\n// Navbar heading\n//\n// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy\n// styling of responsive aspects.\n\n.navbar-header {\n &:extend(.clearfix all);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n }\n}\n\n\n// Navbar collapse (body)\n//\n// Group your navbar content into this for easy collapsing and expanding across\n// various device sizes. By default, this content is collapsed when <768px, but\n// will expand past that for a horizontal display.\n//\n// To start (on mobile devices) the navbar links, forms, and buttons are stacked\n// vertically and include a `max-height` to overflow in case you have too much\n// content for the user's viewport.\n\n.navbar-collapse {\n max-height: @navbar-collapse-max-height;\n overflow-x: visible;\n padding-right: @navbar-padding-horizontal;\n padding-left: @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n box-shadow: inset 0 1px 0 rgba(255,255,255,.1);\n &:extend(.clearfix all);\n -webkit-overflow-scrolling: touch;\n\n &.in {\n overflow-y: auto;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border-top: 0;\n box-shadow: none;\n\n &.collapse {\n display: block !important;\n height: auto !important;\n padding-bottom: 0; // Override default setting\n overflow: visible !important;\n }\n\n &.in {\n overflow-y: visible;\n }\n\n // Undo the collapse side padding for navbars with containers to ensure\n // alignment of right-aligned contents.\n .navbar-fixed-top &,\n .navbar-static-top &,\n .navbar-fixed-bottom & {\n padding-left: 0;\n padding-right: 0;\n }\n }\n}\n\n\n// Both navbar header and collapse\n//\n// When a container is present, change the behavior of the header and collapse.\n\n.container,\n.container-fluid {\n > .navbar-header,\n > .navbar-collapse {\n margin-right: -@navbar-padding-horizontal;\n margin-left: -@navbar-padding-horizontal;\n\n @media (min-width: @grid-float-breakpoint) {\n margin-right: 0;\n margin-left: 0;\n }\n }\n}\n\n\n//\n// Navbar alignment options\n//\n// Display the navbar across the entirety of the page or fixed it to the top or\n// bottom of the page.\n\n// Static top (unfixed, but 100% wide) navbar\n.navbar-static-top {\n z-index: @zindex-navbar;\n border-width: 0 0 1px;\n\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n\n// Fix the top/bottom navbars when screen real estate supports it\n.navbar-fixed-top,\n.navbar-fixed-bottom {\n position: fixed;\n right: 0;\n left: 0;\n z-index: @zindex-navbar-fixed;\n\n // Undo the rounded corners\n @media (min-width: @grid-float-breakpoint) {\n border-radius: 0;\n }\n}\n.navbar-fixed-top {\n top: 0;\n border-width: 0 0 1px;\n}\n.navbar-fixed-bottom {\n bottom: 0;\n margin-bottom: 0; // override .navbar defaults\n border-width: 1px 0 0;\n}\n\n\n// Brand/project name\n\n.navbar-brand {\n float: left;\n padding: @navbar-padding-vertical @navbar-padding-horizontal;\n font-size: @font-size-large;\n line-height: @line-height-computed;\n height: @navbar-height;\n\n &:hover,\n &:focus {\n text-decoration: none;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n .navbar > .container &,\n .navbar > .container-fluid & {\n margin-left: -@navbar-padding-horizontal;\n }\n }\n}\n\n\n// Navbar toggle\n//\n// Custom button for toggling the `.navbar-collapse`, powered by the collapse\n// JavaScript plugin.\n\n.navbar-toggle {\n position: relative;\n float: right;\n margin-right: @navbar-padding-horizontal;\n padding: 9px 10px;\n .navbar-vertical-align(34px);\n background-color: transparent;\n background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214\n border: 1px solid transparent;\n border-radius: @border-radius-base;\n\n // We remove the `outline` here, but later compensate by attaching `:hover`\n // styles to `:focus`.\n &:focus {\n outline: none;\n }\n\n // Bars\n .icon-bar {\n display: block;\n width: 22px;\n height: 2px;\n border-radius: 1px;\n }\n .icon-bar + .icon-bar {\n margin-top: 4px;\n }\n\n @media (min-width: @grid-float-breakpoint) {\n display: none;\n }\n}\n\n\n// Navbar nav links\n//\n// Builds on top of the `.nav` components with its own modifier class to make\n// the nav the full height of the horizontal nav (above 768px).\n\n.navbar-nav {\n margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal;\n\n > li > a {\n padding-top: 10px;\n padding-bottom: 10px;\n line-height: @line-height-computed;\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n position: static;\n float: none;\n width: auto;\n margin-top: 0;\n background-color: transparent;\n border: 0;\n box-shadow: none;\n > li > a,\n .dropdown-header {\n padding: 5px 15px 5px 25px;\n }\n > li > a {\n line-height: @line-height-computed;\n &:hover,\n &:focus {\n background-image: none;\n }\n }\n }\n }\n\n // Uncollapse the nav\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin: 0;\n\n > li {\n float: left;\n > a {\n padding-top: @navbar-padding-vertical;\n padding-bottom: @navbar-padding-vertical;\n }\n }\n\n &.navbar-right:last-child {\n margin-right: -@navbar-padding-horizontal;\n }\n }\n}\n\n\n// Component alignment\n//\n// Repurpose the pull utilities as their own navbar utilities to avoid specificity\n// issues with parents and chaining. Only do this when the navbar is uncollapsed\n// though so that navbar contents properly stack and align in mobile.\n\n@media (min-width: @grid-float-breakpoint) {\n .navbar-left { .pull-left(); }\n .navbar-right { .pull-right(); }\n}\n\n\n// Navbar form\n//\n// Extension of the `.form-inline` with some extra flavor for optimum display in\n// our navbars.\n\n.navbar-form {\n margin-left: -@navbar-padding-horizontal;\n margin-right: -@navbar-padding-horizontal;\n padding: 10px @navbar-padding-horizontal;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1);\n .box-shadow(@shadow);\n\n // Mixin behavior for optimum display\n .form-inline();\n\n .form-group {\n @media (max-width: @grid-float-breakpoint-max) {\n margin-bottom: 5px;\n }\n }\n\n // Vertically center in expanded, horizontal navbar\n .navbar-vertical-align(@input-height-base);\n\n // Undo 100% width for pull classes\n @media (min-width: @grid-float-breakpoint) {\n width: auto;\n border: 0;\n margin-left: 0;\n margin-right: 0;\n padding-top: 0;\n padding-bottom: 0;\n .box-shadow(none);\n\n // Outdent the form if last child to line up with content down the page\n &.navbar-right:last-child {\n margin-right: -@navbar-padding-horizontal;\n }\n }\n}\n\n\n// Dropdown menus\n\n// Menu position and menu carets\n.navbar-nav > li > .dropdown-menu {\n margin-top: 0;\n .border-top-radius(0);\n}\n// Menu position and menu caret support for dropups via extra dropup class\n.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {\n .border-bottom-radius(0);\n}\n\n\n// Buttons in navbars\n//\n// Vertically center a button within a navbar (when *not* in a form).\n\n.navbar-btn {\n .navbar-vertical-align(@input-height-base);\n\n &.btn-sm {\n .navbar-vertical-align(@input-height-small);\n }\n &.btn-xs {\n .navbar-vertical-align(22);\n }\n}\n\n\n// Text in navbars\n//\n// Add a class to make any element properly align itself vertically within the navbars.\n\n.navbar-text {\n .navbar-vertical-align(@line-height-computed);\n\n @media (min-width: @grid-float-breakpoint) {\n float: left;\n margin-left: @navbar-padding-horizontal;\n margin-right: @navbar-padding-horizontal;\n\n // Outdent the form if last child to line up with content down the page\n &.navbar-right:last-child {\n margin-right: 0;\n }\n }\n}\n\n// Alternate navbars\n// --------------------------------------------------\n\n// Default navbar\n.navbar-default {\n background-color: @navbar-default-bg;\n border-color: @navbar-default-border;\n\n .navbar-brand {\n color: @navbar-default-brand-color;\n &:hover,\n &:focus {\n color: @navbar-default-brand-hover-color;\n background-color: @navbar-default-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-default-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-default-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n\n .navbar-toggle {\n border-color: @navbar-default-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-default-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-default-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: @navbar-default-border;\n }\n\n // Dropdown menu items\n .navbar-nav {\n // Remove background color from open dropdown\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-default-link-active-bg;\n color: @navbar-default-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display when collapsed\n .open .dropdown-menu {\n > li > a {\n color: @navbar-default-link-color;\n &:hover,\n &:focus {\n color: @navbar-default-link-hover-color;\n background-color: @navbar-default-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-active-color;\n background-color: @navbar-default-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-default-link-disabled-color;\n background-color: @navbar-default-link-disabled-bg;\n }\n }\n }\n }\n }\n\n\n // Links in navbars\n //\n // Add a class to ensure links outside the navbar nav are colored correctly.\n\n .navbar-link {\n color: @navbar-default-link-color;\n &:hover {\n color: @navbar-default-link-hover-color;\n }\n }\n\n}\n\n// Inverse navbar\n\n.navbar-inverse {\n background-color: @navbar-inverse-bg;\n border-color: @navbar-inverse-border;\n\n .navbar-brand {\n color: @navbar-inverse-brand-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-brand-hover-color;\n background-color: @navbar-inverse-brand-hover-bg;\n }\n }\n\n .navbar-text {\n color: @navbar-inverse-color;\n }\n\n .navbar-nav {\n > li > a {\n color: @navbar-inverse-link-color;\n\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n\n // Darken the responsive nav toggle\n .navbar-toggle {\n border-color: @navbar-inverse-toggle-border-color;\n &:hover,\n &:focus {\n background-color: @navbar-inverse-toggle-hover-bg;\n }\n .icon-bar {\n background-color: @navbar-inverse-toggle-icon-bar-bg;\n }\n }\n\n .navbar-collapse,\n .navbar-form {\n border-color: darken(@navbar-inverse-bg, 7%);\n }\n\n // Dropdowns\n .navbar-nav {\n > .open > a {\n &,\n &:hover,\n &:focus {\n background-color: @navbar-inverse-link-active-bg;\n color: @navbar-inverse-link-active-color;\n }\n }\n\n @media (max-width: @grid-float-breakpoint-max) {\n // Dropdowns get custom display\n .open .dropdown-menu {\n > .dropdown-header {\n border-color: @navbar-inverse-border;\n }\n .divider {\n background-color: @navbar-inverse-border;\n }\n > li > a {\n color: @navbar-inverse-link-color;\n &:hover,\n &:focus {\n color: @navbar-inverse-link-hover-color;\n background-color: @navbar-inverse-link-hover-bg;\n }\n }\n > .active > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-active-color;\n background-color: @navbar-inverse-link-active-bg;\n }\n }\n > .disabled > a {\n &,\n &:hover,\n &:focus {\n color: @navbar-inverse-link-disabled-color;\n background-color: @navbar-inverse-link-disabled-bg;\n }\n }\n }\n }\n }\n\n .navbar-link {\n color: @navbar-inverse-link-color;\n &:hover {\n color: @navbar-inverse-link-hover-color;\n }\n }\n\n}\n","//\n// Utility classes\n// --------------------------------------------------\n\n\n// Floats\n// -------------------------\n\n.clearfix {\n .clearfix();\n}\n.center-block {\n .center-block();\n}\n.pull-right {\n float: right !important;\n}\n.pull-left {\n float: left !important;\n}\n\n\n// Toggling content\n// -------------------------\n\n// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1\n.hide {\n display: none !important;\n}\n.show {\n display: block !important;\n}\n.invisible {\n visibility: hidden;\n}\n.text-hide {\n .text-hide();\n}\n\n\n// Hide from screenreaders and browsers\n//\n// Credit: HTML5 Boilerplate\n\n.hidden {\n display: none !important;\n visibility: hidden !important;\n}\n\n\n// For Affix plugin\n// -------------------------\n\n.affix {\n position: fixed;\n}\n","//\n// Breadcrumbs\n// --------------------------------------------------\n\n\n.breadcrumb {\n padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal;\n margin-bottom: @line-height-computed;\n list-style: none;\n background-color: @breadcrumb-bg;\n border-radius: @border-radius-base;\n\n > li {\n display: inline-block;\n\n + li:before {\n content: \"@{breadcrumb-separator}\\00a0\"; // Unicode space added since inline-block means non-collapsing white-space\n padding: 0 5px;\n color: @breadcrumb-color;\n }\n }\n\n > .active {\n color: @breadcrumb-active-color;\n }\n}\n","//\n// Pagination (multiple pages)\n// --------------------------------------------------\n.pagination {\n display: inline-block;\n padding-left: 0;\n margin: @line-height-computed 0;\n border-radius: @border-radius-base;\n\n > li {\n display: inline; // Remove list-style and block-level defaults\n > a,\n > span {\n position: relative;\n float: left; // Collapse white-space\n padding: @padding-base-vertical @padding-base-horizontal;\n line-height: @line-height-base;\n text-decoration: none;\n color: @pagination-color;\n background-color: @pagination-bg;\n border: 1px solid @pagination-border;\n margin-left: -1px;\n }\n &:first-child {\n > a,\n > span {\n margin-left: 0;\n .border-left-radius(@border-radius-base);\n }\n }\n &:last-child {\n > a,\n > span {\n .border-right-radius(@border-radius-base);\n }\n }\n }\n\n > li > a,\n > li > span {\n &:hover,\n &:focus {\n color: @pagination-hover-color;\n background-color: @pagination-hover-bg;\n border-color: @pagination-hover-border;\n }\n }\n\n > .active > a,\n > .active > span {\n &,\n &:hover,\n &:focus {\n z-index: 2;\n color: @pagination-active-color;\n background-color: @pagination-active-bg;\n border-color: @pagination-active-border;\n cursor: default;\n }\n }\n\n > .disabled {\n > span,\n > span:hover,\n > span:focus,\n > a,\n > a:hover,\n > a:focus {\n color: @pagination-disabled-color;\n background-color: @pagination-disabled-bg;\n border-color: @pagination-disabled-border;\n cursor: not-allowed;\n }\n }\n}\n\n// Sizing\n// --------------------------------------------------\n\n// Large\n.pagination-lg {\n .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @border-radius-large);\n}\n\n// Small\n.pagination-sm {\n .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @border-radius-small);\n}\n","//\n// Pager pagination\n// --------------------------------------------------\n\n\n.pager {\n padding-left: 0;\n margin: @line-height-computed 0;\n list-style: none;\n text-align: center;\n &:extend(.clearfix all);\n li {\n display: inline;\n > a,\n > span {\n display: inline-block;\n padding: 5px 14px;\n background-color: @pager-bg;\n border: 1px solid @pager-border;\n border-radius: @pager-border-radius;\n }\n\n > a:hover,\n > a:focus {\n text-decoration: none;\n background-color: @pager-hover-bg;\n }\n }\n\n .next {\n > a,\n > span {\n float: right;\n }\n }\n\n .previous {\n > a,\n > span {\n float: left;\n }\n }\n\n .disabled {\n > a,\n > a:hover,\n > a:focus,\n > span {\n color: @pager-disabled-color;\n background-color: @pager-bg;\n cursor: not-allowed;\n }\n }\n\n}\n","//\n// Labels\n// --------------------------------------------------\n\n.label {\n display: inline;\n padding: .2em .6em .3em;\n font-size: 75%;\n font-weight: bold;\n line-height: 1;\n color: @label-color;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: .25em;\n\n // Add hover effects, but only for links\n &[href] {\n &:hover,\n &:focus {\n color: @label-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n }\n\n // Empty labels collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for labels in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n}\n\n// Colors\n// Contextual variations (linked labels get darker on :hover)\n\n.label-default {\n .label-variant(@label-default-bg);\n}\n\n.label-primary {\n .label-variant(@label-primary-bg);\n}\n\n.label-success {\n .label-variant(@label-success-bg);\n}\n\n.label-info {\n .label-variant(@label-info-bg);\n}\n\n.label-warning {\n .label-variant(@label-warning-bg);\n}\n\n.label-danger {\n .label-variant(@label-danger-bg);\n}\n","//\n// Badges\n// --------------------------------------------------\n\n\n// Base classes\n.badge {\n display: inline-block;\n min-width: 10px;\n padding: 3px 7px;\n font-size: @font-size-small;\n font-weight: @badge-font-weight;\n color: @badge-color;\n line-height: @badge-line-height;\n vertical-align: baseline;\n white-space: nowrap;\n text-align: center;\n background-color: @badge-bg;\n border-radius: @badge-border-radius;\n\n // Empty badges collapse automatically (not available in IE8)\n &:empty {\n display: none;\n }\n\n // Quick fix for badges in buttons\n .btn & {\n position: relative;\n top: -1px;\n }\n .btn-xs & {\n top: 0;\n padding: 1px 5px;\n }\n}\n\n// Hover state, but only for links\na.badge {\n &:hover,\n &:focus {\n color: @badge-link-hover-color;\n text-decoration: none;\n cursor: pointer;\n }\n}\n\n// Account for counters in navs\na.list-group-item.active > .badge,\n.nav-pills > .active > a > .badge {\n color: @badge-active-color;\n background-color: @badge-active-bg;\n}\n.nav-pills > li > a > .badge {\n margin-left: 3px;\n}\n","//\n// Jumbotron\n// --------------------------------------------------\n\n\n.jumbotron {\n padding: @jumbotron-padding;\n margin-bottom: @jumbotron-padding;\n color: @jumbotron-color;\n background-color: @jumbotron-bg;\n\n h1,\n .h1 {\n color: @jumbotron-heading-color;\n }\n p {\n margin-bottom: (@jumbotron-padding / 2);\n font-size: @jumbotron-font-size;\n font-weight: 200;\n }\n\n .container & {\n border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container\n }\n\n .container {\n max-width: 100%;\n }\n\n @media screen and (min-width: @screen-sm-min) {\n padding-top: (@jumbotron-padding * 1.6);\n padding-bottom: (@jumbotron-padding * 1.6);\n\n .container & {\n padding-left: (@jumbotron-padding * 2);\n padding-right: (@jumbotron-padding * 2);\n }\n\n h1,\n .h1 {\n font-size: (@font-size-base * 4.5);\n }\n }\n}\n","//\n// Alerts\n// --------------------------------------------------\n\n\n// Base styles\n// -------------------------\n\n.alert {\n padding: @alert-padding;\n margin-bottom: @line-height-computed;\n border: 1px solid transparent;\n border-radius: @alert-border-radius;\n\n // Headings for larger alerts\n h4 {\n margin-top: 0;\n // Specified for the h4 to prevent conflicts of changing @headings-color\n color: inherit;\n }\n // Provide class for links that match alerts\n .alert-link {\n font-weight: @alert-link-font-weight;\n }\n\n // Improve alignment and spacing of inner content\n > p,\n > ul {\n margin-bottom: 0;\n }\n > p + p {\n margin-top: 5px;\n }\n}\n\n// Dismissable alerts\n//\n// Expand the right padding and account for the close button's positioning.\n\n.alert-dismissable {\n padding-right: (@alert-padding + 20);\n\n // Adjust close link position\n .close {\n position: relative;\n top: -2px;\n right: -21px;\n color: inherit;\n }\n}\n\n// Alternate styles\n//\n// Generate contextual modifier classes for colorizing the alert.\n\n.alert-success {\n .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text);\n}\n.alert-info {\n .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text);\n}\n.alert-warning {\n .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text);\n}\n.alert-danger {\n .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text);\n}\n","//\n// Progress bars\n// --------------------------------------------------\n\n\n// Bar animations\n// -------------------------\n\n// WebKit\n@-webkit-keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n// Spec and IE10+\n@keyframes progress-bar-stripes {\n from { background-position: 40px 0; }\n to { background-position: 0 0; }\n}\n\n\n\n// Bar itself\n// -------------------------\n\n// Outer container\n.progress {\n overflow: hidden;\n height: @line-height-computed;\n margin-bottom: @line-height-computed;\n background-color: @progress-bg;\n border-radius: @border-radius-base;\n .box-shadow(inset 0 1px 2px rgba(0,0,0,.1));\n}\n\n// Bar of progress\n.progress-bar {\n float: left;\n width: 0%;\n height: 100%;\n font-size: @font-size-small;\n line-height: @line-height-computed;\n color: @progress-bar-color;\n text-align: center;\n background-color: @progress-bar-bg;\n .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));\n .transition(width .6s ease);\n}\n\n// Striped bars\n.progress-striped .progress-bar {\n #gradient > .striped();\n background-size: 40px 40px;\n}\n\n// Call animation for the active one\n.progress.active .progress-bar {\n .animation(progress-bar-stripes 2s linear infinite);\n}\n\n\n\n// Variations\n// -------------------------\n\n.progress-bar-success {\n .progress-bar-variant(@progress-bar-success-bg);\n}\n\n.progress-bar-info {\n .progress-bar-variant(@progress-bar-info-bg);\n}\n\n.progress-bar-warning {\n .progress-bar-variant(@progress-bar-warning-bg);\n}\n\n.progress-bar-danger {\n .progress-bar-variant(@progress-bar-danger-bg);\n}\n","// Media objects\n// Source: http://stubbornella.org/content/?p=497\n// --------------------------------------------------\n\n\n// Common styles\n// -------------------------\n\n// Clear the floats\n.media,\n.media-body {\n overflow: hidden;\n zoom: 1;\n}\n\n// Proper spacing between instances of .media\n.media,\n.media .media {\n margin-top: 15px;\n}\n.media:first-child {\n margin-top: 0;\n}\n\n// For images and videos, set to block\n.media-object {\n display: block;\n}\n\n// Reset margins on headings for tighter default spacing\n.media-heading {\n margin: 0 0 5px;\n}\n\n\n// Media image alignment\n// -------------------------\n\n.media {\n > .pull-left {\n margin-right: 10px;\n }\n > .pull-right {\n margin-left: 10px;\n }\n}\n\n\n// Media list variation\n// -------------------------\n\n// Undo default ul/ol styles\n.media-list {\n padding-left: 0;\n list-style: none;\n}\n","//\n// List groups\n// --------------------------------------------------\n\n\n// Base class\n//\n// Easily usable on , , or .\n\n.list-group {\n // No need to set list-style: none; since .list-group-item is block level\n margin-bottom: 20px;\n padding-left: 0; // reset padding because ul and ol\n}\n\n\n// Individual list items\n//\n// Use on `li`s or `div`s within the `.list-group` parent.\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 10px 15px;\n // Place the border on the list items and negative margin up for better styling\n margin-bottom: -1px;\n background-color: @list-group-bg;\n border: 1px solid @list-group-border;\n\n // Round the first and last items\n &:first-child {\n .border-top-radius(@list-group-border-radius);\n }\n &:last-child {\n margin-bottom: 0;\n .border-bottom-radius(@list-group-border-radius);\n }\n\n // Align badges within list items\n > .badge {\n float: right;\n }\n > .badge + .badge {\n margin-right: 5px;\n }\n}\n\n\n// Linked list items\n//\n// Use anchor elements instead of `li`s or `div`s to create linked list items.\n// Includes an extra `.active` modifier class for showing selected items.\n\na.list-group-item {\n color: @list-group-link-color;\n\n .list-group-item-heading {\n color: @list-group-link-heading-color;\n }\n\n // Hover state\n &:hover,\n &:focus {\n text-decoration: none;\n background-color: @list-group-hover-bg;\n }\n\n // Active class on item itself, not parent\n &.active,\n &.active:hover,\n &.active:focus {\n z-index: 2; // Place active items above their siblings for proper border styling\n color: @list-group-active-color;\n background-color: @list-group-active-bg;\n border-color: @list-group-active-border;\n\n // Force color to inherit for custom content\n .list-group-item-heading {\n color: inherit;\n }\n .list-group-item-text {\n color: @list-group-active-text-color;\n }\n }\n}\n\n\n// Contextual variants\n//\n// Add modifier classes to change text and background color on individual items.\n// Organizationally, this must come after the `:hover` states.\n\n.list-group-item-variant(success; @state-success-bg; @state-success-text);\n.list-group-item-variant(info; @state-info-bg; @state-info-text);\n.list-group-item-variant(warning; @state-warning-bg; @state-warning-text);\n.list-group-item-variant(danger; @state-danger-bg; @state-danger-text);\n\n\n// Custom content options\n//\n// Extra classes for creating well-formatted content within `.list-group-item`s.\n\n.list-group-item-heading {\n margin-top: 0;\n margin-bottom: 5px;\n}\n.list-group-item-text {\n margin-bottom: 0;\n line-height: 1.3;\n}\n","//\n// Panels\n// --------------------------------------------------\n\n\n// Base class\n.panel {\n margin-bottom: @line-height-computed;\n background-color: @panel-bg;\n border: 1px solid transparent;\n border-radius: @panel-border-radius;\n .box-shadow(0 1px 1px rgba(0,0,0,.05));\n}\n\n// Panel contents\n.panel-body {\n padding: @panel-body-padding;\n &:extend(.clearfix all);\n}\n\n// Optional heading\n.panel-heading {\n padding: 10px 15px;\n border-bottom: 1px solid transparent;\n .border-top-radius((@panel-border-radius - 1));\n\n > .dropdown .dropdown-toggle {\n color: inherit;\n }\n}\n\n// Within heading, strip any `h*` tag of its default margins for spacing.\n.panel-title {\n margin-top: 0;\n margin-bottom: 0;\n font-size: ceil((@font-size-base * 1.125));\n color: inherit;\n\n > a {\n color: inherit;\n }\n}\n\n// Optional footer (stays gray in every modifier class)\n.panel-footer {\n padding: 10px 15px;\n background-color: @panel-footer-bg;\n border-top: 1px solid @panel-inner-border;\n .border-bottom-radius((@panel-border-radius - 1));\n}\n\n\n// List groups in panels\n//\n// By default, space out list group content from panel headings to account for\n// any kind of custom content between the two.\n\n.panel {\n > .list-group {\n margin-bottom: 0;\n\n .list-group-item {\n border-width: 1px 0;\n border-radius: 0;\n }\n\n // Add border top radius for first one\n &:first-child {\n .list-group-item:first-child {\n border-top: 0;\n .border-top-radius((@panel-border-radius - 1));\n }\n }\n // Add border bottom radius for last one\n &:last-child {\n .list-group-item:last-child {\n border-bottom: 0;\n .border-bottom-radius((@panel-border-radius - 1));\n }\n }\n }\n}\n// Collapse space between when there's no additional content.\n.panel-heading + .list-group {\n .list-group-item:first-child {\n border-top-width: 0;\n }\n}\n\n\n// Tables in panels\n//\n// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and\n// watch it go full width.\n\n.panel {\n > .table,\n > .table-responsive > .table {\n margin-bottom: 0;\n }\n // Add border top radius for first one\n > .table:first-child,\n > .table-responsive:first-child > .table:first-child {\n .border-top-radius((@panel-border-radius - 1));\n\n > thead:first-child,\n > tbody:first-child {\n > tr:first-child {\n td:first-child,\n th:first-child {\n border-top-left-radius: (@panel-border-radius - 1);\n }\n td:last-child,\n th:last-child {\n border-top-right-radius: (@panel-border-radius - 1);\n }\n }\n }\n }\n // Add border bottom radius for last one\n > .table:last-child,\n > .table-responsive:last-child > .table:last-child {\n .border-bottom-radius((@panel-border-radius - 1));\n\n > tbody:last-child,\n > tfoot:last-child {\n > tr:last-child {\n td:first-child,\n th:first-child {\n border-bottom-left-radius: (@panel-border-radius - 1);\n }\n td:last-child,\n th:last-child {\n border-bottom-right-radius: (@panel-border-radius - 1);\n }\n }\n }\n }\n > .panel-body + .table,\n > .panel-body + .table-responsive {\n border-top: 1px solid @table-border-color;\n }\n > .table > tbody:first-child > tr:first-child th,\n > .table > tbody:first-child > tr:first-child td {\n border-top: 0;\n }\n > .table-bordered,\n > .table-responsive > .table-bordered {\n border: 0;\n > thead,\n > tbody,\n > tfoot {\n > tr {\n > th:first-child,\n > td:first-child {\n border-left: 0;\n }\n > th:last-child,\n > td:last-child {\n border-right: 0;\n }\n }\n }\n > thead,\n > tbody {\n > tr:first-child {\n > td,\n > th {\n border-bottom: 0;\n }\n }\n }\n > tbody,\n > tfoot {\n > tr:last-child {\n > td,\n > th {\n border-bottom: 0;\n }\n }\n }\n }\n > .table-responsive {\n border: 0;\n margin-bottom: 0;\n }\n}\n\n\n// Collapsable panels (aka, accordion)\n//\n// Wrap a series of panels in `.panel-group` to turn them into an accordion with\n// the help of our collapse JavaScript plugin.\n\n.panel-group {\n margin-bottom: @line-height-computed;\n\n // Tighten up margin so it's only between panels\n .panel {\n margin-bottom: 0;\n border-radius: @panel-border-radius;\n overflow: hidden; // crop contents when collapsed\n + .panel {\n margin-top: 5px;\n }\n }\n\n .panel-heading {\n border-bottom: 0;\n + .panel-collapse .panel-body {\n border-top: 1px solid @panel-inner-border;\n }\n }\n .panel-footer {\n border-top: 0;\n + .panel-collapse .panel-body {\n border-bottom: 1px solid @panel-inner-border;\n }\n }\n}\n\n\n// Contextual variations\n.panel-default {\n .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border);\n}\n.panel-primary {\n .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border);\n}\n.panel-success {\n .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border);\n}\n.panel-info {\n .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border);\n}\n.panel-warning {\n .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border);\n}\n.panel-danger {\n .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border);\n}\n","//\n// Wells\n// --------------------------------------------------\n\n\n// Base class\n.well {\n min-height: 20px;\n padding: 19px;\n margin-bottom: 20px;\n background-color: @well-bg;\n border: 1px solid @well-border;\n border-radius: @border-radius-base;\n .box-shadow(inset 0 1px 1px rgba(0,0,0,.05));\n blockquote {\n border-color: #ddd;\n border-color: rgba(0,0,0,.15);\n }\n}\n\n// Sizes\n.well-lg {\n padding: 24px;\n border-radius: @border-radius-large;\n}\n.well-sm {\n padding: 9px;\n border-radius: @border-radius-small;\n}\n","//\n// Close icons\n// --------------------------------------------------\n\n\n.close {\n float: right;\n font-size: (@font-size-base * 1.5);\n font-weight: @close-font-weight;\n line-height: 1;\n color: @close-color;\n text-shadow: @close-text-shadow;\n .opacity(.2);\n\n &:hover,\n &:focus {\n color: @close-color;\n text-decoration: none;\n cursor: pointer;\n .opacity(.5);\n }\n\n // Additional properties for button version\n // iOS requires the button element instead of an anchor tag.\n // If you want the anchor version, it requires `href=\"#\"`.\n button& {\n padding: 0;\n cursor: pointer;\n background: transparent;\n border: 0;\n -webkit-appearance: none;\n }\n}\n","//\n// Modals\n// --------------------------------------------------\n\n// .modal-open - body class for killing the scroll\n// .modal - container to scroll within\n// .modal-dialog - positioning shell for the actual modal\n// .modal-content - actual modal w/ bg and corners and shit\n\n// Kill the scroll on the body\n.modal-open {\n overflow: hidden;\n}\n\n// Container that the modal scrolls within\n.modal {\n display: none;\n overflow: auto;\n overflow-y: scroll;\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: @zindex-modal;\n -webkit-overflow-scrolling: touch;\n\n // Prevent Chrome on Windows from adding a focus outline. For details, see\n // https://github.com/twbs/bootstrap/pull/10951.\n outline: 0;\n\n // When fading in the modal, animate it to slide down\n &.fade .modal-dialog {\n .translate(0, -25%);\n .transition-transform(~\"0.3s ease-out\");\n }\n &.in .modal-dialog { .translate(0, 0)}\n}\n\n// Shell div to position the modal with bottom padding\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 10px;\n}\n\n// Actual modal\n.modal-content {\n position: relative;\n background-color: @modal-content-bg;\n border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc)\n border: 1px solid @modal-content-border-color;\n border-radius: @border-radius-large;\n .box-shadow(0 3px 9px rgba(0,0,0,.5));\n background-clip: padding-box;\n // Remove focus outline from opened modal\n outline: none;\n}\n\n// Modal background\n.modal-backdrop {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: @zindex-modal-background;\n background-color: @modal-backdrop-bg;\n // Fade for backdrop\n &.fade { .opacity(0); }\n &.in { .opacity(@modal-backdrop-opacity); }\n}\n\n// Modal header\n// Top section of the modal w/ title and dismiss\n.modal-header {\n padding: @modal-title-padding;\n border-bottom: 1px solid @modal-header-border-color;\n min-height: (@modal-title-padding + @modal-title-line-height);\n}\n// Close icon\n.modal-header .close {\n margin-top: -2px;\n}\n\n// Title text within header\n.modal-title {\n margin: 0;\n line-height: @modal-title-line-height;\n}\n\n// Modal body\n// Where all modal content resides (sibling of .modal-header and .modal-footer)\n.modal-body {\n position: relative;\n padding: @modal-inner-padding;\n}\n\n// Footer (for actions)\n.modal-footer {\n margin-top: 15px;\n padding: (@modal-inner-padding - 1) @modal-inner-padding @modal-inner-padding;\n text-align: right; // right align buttons\n border-top: 1px solid @modal-footer-border-color;\n &:extend(.clearfix all); // clear it in case folks use .pull-* classes on buttons\n\n // Properly space out buttons\n .btn + .btn {\n margin-left: 5px;\n margin-bottom: 0; // account for input[type=\"submit\"] which gets the bottom margin like all other inputs\n }\n // but override that for button groups\n .btn-group .btn + .btn {\n margin-left: -1px;\n }\n // and override it for block buttons as well\n .btn-block + .btn-block {\n margin-left: 0;\n }\n}\n\n// Scale up the modal\n@media (min-width: @screen-sm-min) {\n // Automatically set modal's width for larger viewports\n .modal-dialog {\n width: @modal-md;\n margin: 30px auto;\n }\n .modal-content {\n .box-shadow(0 5px 15px rgba(0,0,0,.5));\n }\n\n // Modal sizes\n .modal-sm { width: @modal-sm; }\n}\n\n@media (min-width: @screen-md-min) {\n .modal-lg { width: @modal-lg; }\n}\n","//\n// Tooltips\n// --------------------------------------------------\n\n\n// Base class\n.tooltip {\n position: absolute;\n z-index: @zindex-tooltip;\n display: block;\n visibility: visible;\n font-size: @font-size-small;\n line-height: 1.4;\n .opacity(0);\n\n &.in { .opacity(@tooltip-opacity); }\n &.top { margin-top: -3px; padding: @tooltip-arrow-width 0; }\n &.right { margin-left: 3px; padding: 0 @tooltip-arrow-width; }\n &.bottom { margin-top: 3px; padding: @tooltip-arrow-width 0; }\n &.left { margin-left: -3px; padding: 0 @tooltip-arrow-width; }\n}\n\n// Wrapper for the tooltip content\n.tooltip-inner {\n max-width: @tooltip-max-width;\n padding: 3px 8px;\n color: @tooltip-color;\n text-align: center;\n text-decoration: none;\n background-color: @tooltip-bg;\n border-radius: @border-radius-base;\n}\n\n// Arrows\n.tooltip-arrow {\n position: absolute;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n}\n.tooltip {\n &.top .tooltip-arrow {\n bottom: 0;\n left: 50%;\n margin-left: -@tooltip-arrow-width;\n border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n border-top-color: @tooltip-arrow-color;\n }\n &.top-left .tooltip-arrow {\n bottom: 0;\n left: @tooltip-arrow-width;\n border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n border-top-color: @tooltip-arrow-color;\n }\n &.top-right .tooltip-arrow {\n bottom: 0;\n right: @tooltip-arrow-width;\n border-width: @tooltip-arrow-width @tooltip-arrow-width 0;\n border-top-color: @tooltip-arrow-color;\n }\n &.right .tooltip-arrow {\n top: 50%;\n left: 0;\n margin-top: -@tooltip-arrow-width;\n border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0;\n border-right-color: @tooltip-arrow-color;\n }\n &.left .tooltip-arrow {\n top: 50%;\n right: 0;\n margin-top: -@tooltip-arrow-width;\n border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width;\n border-left-color: @tooltip-arrow-color;\n }\n &.bottom .tooltip-arrow {\n top: 0;\n left: 50%;\n margin-left: -@tooltip-arrow-width;\n border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n border-bottom-color: @tooltip-arrow-color;\n }\n &.bottom-left .tooltip-arrow {\n top: 0;\n left: @tooltip-arrow-width;\n border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n border-bottom-color: @tooltip-arrow-color;\n }\n &.bottom-right .tooltip-arrow {\n top: 0;\n right: @tooltip-arrow-width;\n border-width: 0 @tooltip-arrow-width @tooltip-arrow-width;\n border-bottom-color: @tooltip-arrow-color;\n }\n}\n","//\n// Popovers\n// --------------------------------------------------\n\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: @zindex-popover;\n display: none;\n max-width: @popover-max-width;\n padding: 1px;\n text-align: left; // Reset given new insertion method\n background-color: @popover-bg;\n background-clip: padding-box;\n border: 1px solid @popover-fallback-border-color;\n border: 1px solid @popover-border-color;\n border-radius: @border-radius-large;\n .box-shadow(0 5px 10px rgba(0,0,0,.2));\n\n // Overrides for proper insertion\n white-space: normal;\n\n // Offset the popover to account for the popover arrow\n &.top { margin-top: -@popover-arrow-width; }\n &.right { margin-left: @popover-arrow-width; }\n &.bottom { margin-top: @popover-arrow-width; }\n &.left { margin-left: -@popover-arrow-width; }\n}\n\n.popover-title {\n margin: 0; // reset heading margin\n padding: 8px 14px;\n font-size: @font-size-base;\n font-weight: normal;\n line-height: 18px;\n background-color: @popover-title-bg;\n border-bottom: 1px solid darken(@popover-title-bg, 5%);\n border-radius: 5px 5px 0 0;\n}\n\n.popover-content {\n padding: 9px 14px;\n}\n\n// Arrows\n//\n// .arrow is outer, .arrow:after is inner\n\n.popover > .arrow {\n &,\n &:after {\n position: absolute;\n display: block;\n width: 0;\n height: 0;\n border-color: transparent;\n border-style: solid;\n }\n}\n.popover > .arrow {\n border-width: @popover-arrow-outer-width;\n}\n.popover > .arrow:after {\n border-width: @popover-arrow-width;\n content: \"\";\n}\n\n.popover {\n &.top > .arrow {\n left: 50%;\n margin-left: -@popover-arrow-outer-width;\n border-bottom-width: 0;\n border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n border-top-color: @popover-arrow-outer-color;\n bottom: -@popover-arrow-outer-width;\n &:after {\n content: \" \";\n bottom: 1px;\n margin-left: -@popover-arrow-width;\n border-bottom-width: 0;\n border-top-color: @popover-arrow-color;\n }\n }\n &.right > .arrow {\n top: 50%;\n left: -@popover-arrow-outer-width;\n margin-top: -@popover-arrow-outer-width;\n border-left-width: 0;\n border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n border-right-color: @popover-arrow-outer-color;\n &:after {\n content: \" \";\n left: 1px;\n bottom: -@popover-arrow-width;\n border-left-width: 0;\n border-right-color: @popover-arrow-color;\n }\n }\n &.bottom > .arrow {\n left: 50%;\n margin-left: -@popover-arrow-outer-width;\n border-top-width: 0;\n border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n border-bottom-color: @popover-arrow-outer-color;\n top: -@popover-arrow-outer-width;\n &:after {\n content: \" \";\n top: 1px;\n margin-left: -@popover-arrow-width;\n border-top-width: 0;\n border-bottom-color: @popover-arrow-color;\n }\n }\n\n &.left > .arrow {\n top: 50%;\n right: -@popover-arrow-outer-width;\n margin-top: -@popover-arrow-outer-width;\n border-right-width: 0;\n border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback\n border-left-color: @popover-arrow-outer-color;\n &:after {\n content: \" \";\n right: 1px;\n border-right-width: 0;\n border-left-color: @popover-arrow-color;\n bottom: -@popover-arrow-width;\n }\n }\n\n}\n","//\n// Responsive: Utility classes\n// --------------------------------------------------\n\n\n// IE10 in Windows (Phone) 8\n//\n// Support for responsive views via media queries is kind of borked in IE10, for\n// Surface/desktop in split view and for Windows Phone 8. This particular fix\n// must be accompanied by a snippet of JavaScript to sniff the user agent and\n// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at\n// our Getting Started page for more information on this bug.\n//\n// For more information, see the following:\n//\n// Issue: https://github.com/twbs/bootstrap/issues/10497\n// Docs: http://getbootstrap.com/getting-started/#browsers\n// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/\n\n@-ms-viewport {\n width: device-width;\n}\n\n\n// Visibility utilities\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n .responsive-invisibility();\n}\n\n.visible-xs {\n @media (max-width: @screen-xs-max) {\n .responsive-visibility();\n }\n}\n.visible-sm {\n @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n .responsive-visibility();\n }\n}\n.visible-md {\n @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n .responsive-visibility();\n }\n}\n.visible-lg {\n @media (min-width: @screen-lg-min) {\n .responsive-visibility();\n }\n}\n\n.hidden-xs {\n @media (max-width: @screen-xs-max) {\n .responsive-invisibility();\n }\n}\n.hidden-sm {\n @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n .responsive-invisibility();\n }\n}\n.hidden-md {\n @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n .responsive-invisibility();\n }\n}\n.hidden-lg {\n @media (min-width: @screen-lg-min) {\n .responsive-invisibility();\n }\n}\n\n\n// Print utilities\n//\n// Media queries are placed on the inside to be mixin-friendly.\n\n.visible-print {\n .responsive-invisibility();\n\n @media print {\n .responsive-visibility();\n }\n}\n\n.hidden-print {\n @media print {\n .responsive-invisibility();\n }\n}\n"]}
\ No newline at end of file
diff --git a/li.strolch.website/www.strolch.li/css/bootstrap.min.css b/li.strolch.website/www.strolch.li/css/bootstrap.min.css
deleted file mode 100644
index 679272d25..000000000
--- a/li.strolch.website/www.strolch.li/css/bootstrap.min.css
+++ /dev/null
@@ -1,7 +0,0 @@
-/*!
- * Bootstrap v3.1.1 (http://getbootstrap.com)
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */
-
-/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*{text-shadow:none!important;color:#000!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-muted{color:#999}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;white-space:nowrap;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:0}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:0}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:0}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:0}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:0}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:0}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:0}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:0}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*=col-]{position:static;float:none;display:table-column}table td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date]{line-height:34px}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;margin-top:10px;margin-bottom:10px;padding-left:20px}.radio label,.checkbox label{display:inline;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:400;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.has-feedback .form-control-feedback{position:absolute;top:25px;right:0;display:block;width:34px;height:34px;line-height:34px;text-align:center}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{float:none;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-control-static{padding-top:7px}@media (min-width:768px){.form-horizontal .control-label{text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:400;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle=buttons]>.btn>input[type=radio],[data-toggle=buttons]>.btn>input[type=checkbox]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{float:none;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:gray}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#999;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px;overflow:hidden}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#faebcc}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#ebccd1}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ebccd1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:auto;overflow-y:scroll;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{margin-top:15px;padding:19px 20px 20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;right:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);white-space:normal}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.5) 0),color-stop(rgba(0,0,0,.0001) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.0001) 0),color-stop(rgba(0,0,0,.5) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file
diff --git a/li.strolch.website/www.strolch.li/css/custom.css b/li.strolch.website/www.strolch.li/css/custom.css
deleted file mode 100644
index 728267f79..000000000
--- a/li.strolch.website/www.strolch.li/css/custom.css
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Globals
- */
-
-/*
- * Override Bootstrap's default container.
- */
-
-.content {
- padding: 0px 2em 3em;
-}
-
-blockquote {
- font-size: 1.0em;
-}
-
-@media (min-width: 1200px) {
- .container {
- width: 970px;
- }
-}
-
-.page-header {
- padding-top: 20px;
-}
-
-.page-title {
- margin-top: 30px;
- margin-bottom: 0;
- font-size: 2.2em;
- font-weight: normal;
-}
-
-.page-description {
- padding-top: 20px;
- font-size: 1.6em;
- color: #999;
-}
-
-#footer {
- font-size: 0.8em;
-}
-
-.image {
- display: block;
- margin-left: auto;
- margin-right: auto;
- margin-bottom: 10px;
-}
\ No newline at end of file
diff --git a/li.strolch.website/www.strolch.li/css/downloads.css b/li.strolch.website/www.strolch.li/css/downloads.css
deleted file mode 100644
index b232e94f0..000000000
--- a/li.strolch.website/www.strolch.li/css/downloads.css
+++ /dev/null
@@ -1,14 +0,0 @@
-.release, .snapshot {
- float: left;
- padding: 20px;
-}
-.release {
- width: 350px;
-}
-.snapshot {
- width: 350px;
-}
-
-.previousRelease {
- clear: both;
-}
\ No newline at end of file
diff --git a/li.strolch.website/www.strolch.li/development.html b/li.strolch.website/www.strolch.li/development.html
deleted file mode 100644
index 2efe67013..000000000
--- a/li.strolch.website/www.strolch.li/development.html
+++ /dev/null
@@ -1,199 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Development
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Prerequisites
- To start developing Strolch you need an installed:
-
- Java JDK 8
- Apache Maven 3
-
-
-
Building Strolch
-
Setting up Strolch is just a few lines:
-
-git clone https://github.com/4treesCH/strolch.git
-cd strolch
-mvn clean install -DskipTests
-
-
Note: To run the tests you will need to configure the PostgreSQL Databases. See the README in the
- module.
-
-
After running the Maven build, you will have a full build of all Strolch projects. Now you can start
- modifying the projects, and add your own features, or, far more interesting, start developing your projects
- using the Strolch agent.
-
-
Creating a Strolch App
-
To create your own Strolch App, you can use Maven's archetype generation. There are two versions, one is a
- simple Java App which you can use to directly access the Strolch runtime, and the second is to create a Java
- Web App, which is the usual way to run Strolch runtimes.
-
-
Note: you need to have installed Strolch to your local maven repo, otherwise the archetype won't be
- available.
-
-
Creating a simple Java Strolch App
-
The following shows the maven command to create the new maven project. Note that you should replace the
- placeholders in the brackets:
-
-mvn archetype:generate \
- -DarchetypeGroupId=li.strolch \
- -DarchetypeArtifactId=li.strolch.mvn.archetype.main \
- -DarchetypeVersion=1.6.0-SNAPSHOT \
- -DgroupId=<my.groupid> \
- -DartifactId=<my-artifactId> \
- -Dversion=<my.version> \
- -DappName="<my app name>"
-
-
You change into the directory of the new project and then build the project by calling:
-
-cd <my-artifactId>
-mvn clean package
-
-
Start the program using:
-
-mvn exec:java
-
-
Happy coding =))
-
-
Creating a Java Strolch Web App
-
The following shows the maven command to create the new maven project. Note that you should replace the
- placeholders in the brackets:
-
-mvn archetype:generate \
- -DarchetypeGroupId=li.strolch \
- -DarchetypeArtifactId=li.strolch.mvn.archetype.webapp \
- -DarchetypeVersion=1.6.0-SNAPSHOT \
- -DgroupId=<my.groupid> \
- -DartifactId=<my-artifactId> \
- -Dversion=<my.version> \
- -DappName="<my app name>"
-
-
Install the web dependencies
-
The Strolch Web App uses NodeJS v11.x to build
- the web dependencies. Please download the relevant platform's package, unpack it, and add the
- bin
directory to your path variable.
-
Once NodeJS is installed, then you can prepare the web dependencies:
-
-cd src/main/webapp/
-npm install gulp -g
-npm install
-gulp
-
-
Note: Whenever the bower.json
is changed then you should again call npm
- install
- inside the webapp folder.
-
-
Building the WAR
-
Building the WAR uses the package
maven goal, but to have the optimized WAR use the release
- profile:
-
-mvn clean package -Prelease
-
-
Happy coding =))
-
-
-
-
Tools used
- The following tools are used to develop Strolch and Strolch-based projects:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-architecture.html b/li.strolch.website/www.strolch.li/documentation-architecture.html
deleted file mode 100644
index bcd4ff32d..000000000
--- a/li.strolch.website/www.strolch.li/documentation-architecture.html
+++ /dev/null
@@ -1,159 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Architecture
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Birds View
-
-
A Strolch agent's architecture can be seen as a simple three-tier architecture. The presentation layer is
- mostly a web frontend, where the communication with the agent is done via REST API calls.
-
-
The agent itself implements the business logic using Services, Commands, Queries etc.
-
-
The agent can communicate with other 3rd systems using any API, where it is preferred to use JSON over
- REST.
-
-
The agent can use a standard RDBMS as a storage system, where currently DAOs have been implemented only for
- PostgreSQL. Should it be required, then any JDBC cabable RDBMS can be used, as no PostgreSQL specific SQL
- commands are used.
-
-
The following diagram helps visualize this:
-
-
-
-
-
-
Squirrel View
-
-
The following diagram shows a more detailed view of the architecture of a Strolch Agent.
-
-
-
-
-
-
A Strolch agent consists of the following main parts:
-
- REST Endpoints → expose an API to access the Strolch agent outside of the Java VM
- Services and Commands → implements business logic
- Searches → implements specific queries against the Strolch model
- Components → Implements additional logic, which is best implement as a component. E.g. active
- components which have threads, etc.
-
- Realms → implements multi-tenant capabilities
-
-
-
In addition to the main parts, Strolch contains inherit capabilities, which gives Strolch unique features
- when compared to other Java Frameworks:
-
- Policies → Policies allow injecting different algorithms. All root elements can store Policy
- definitions, so that a service can delegate to a Policy and thus behave differently, depending on the
- element being accessed.
-
- Transactions → Transactions handle locking/unlocking of objects, updating the model and are the
- central API for the developer.
-
- Privileges → Strolch is user agnostic and any action, i.e. Service, Query, etc. is authorized
- against the authenticated user.
-
- Observers → modifications to the model are propagated to listeners using the observer pattern.
- Audits → Every access (read, modify) of the model are audited
- Versioning → modifications to objects are versioned and thus can be rolled back at a later time.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-components.html b/li.strolch.website/www.strolch.li/documentation-components.html
deleted file mode 100644
index 2281ec1cb..000000000
--- a/li.strolch.website/www.strolch.li/documentation-components.html
+++ /dev/null
@@ -1,209 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Components
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
A Strolch agent can be easily extended with arbitrary components. An agent is basically a container for
- classes extending StrolchComponent
. Theses classes mostly implement an interface which describes
- the operations that are supported by the component.
-
-
The following represents a list of the most used components:
-
- RealmHandler
: li.strolch.agent.impl.DefaultRealmHandler
- PrivilegeHandler
: li.strolch.runtime.privilege.DefaultStrolchPrivilegeHandler
- EnumHandler
: li.strolch.runtime.query.enums.DefaultEnumHandler
- PolicyHandler
: li.strolch.policy.DefaultPolicyHandler
- ServiceHandler
: li.strolch.service.api.DefaultServiceHandler
- StrolchSessionHandler
: li.strolch.rest.DefaultStrolchSessionHandler
- PersistenceHandler
: multiple implementations
- PostInitializer
: project specific implementation
- MailHandler
: li.strolch.handler.mail.SmtpMailHandler
-
-
-
A component has a life-cycle, which is governed by the Agent's own life-cycle. The life-cycle is as
- follows:
-
-
- setup -> initialize -> start <-> stop -> destroy
-
-
The setup step is used to instantiate the component, the initialize step is used to validate configuration
- parameters, and the run step is used to start the component, i.e. start threads, etc. The stop step stops
- these threads and also allows the component to be started again. The destroy step destroys the instance and
- makes it unusable anymore, i.e. shutdown of the agent.
-
-
Each component has its own configuration parameters. A component is registered in the StrolchConfiguration.xml
- file with a
-
- name
- api class name
- implementation class name
- configuration parameters
- any required dependencies
-
-
-
The dependencies is an important feature as the dependencies of a component are always started before the
- actual component.
-
-
By example of the MailHandler
we shall show how a strolch component would be implemented.
-
-
First define an interface:
-
- public interface MailHandler {
- public void sendMail(String subject, String text, String recipient);
- }
-
-
-
-
Then implement a concrete MailHandler
:
-
-
-public class SmtpMailHandler extends StrolchComponent implements MailHandler {
-
- // instance fields with configuration properties to send the mail
-
- public SmtpMailHandler(ComponentContainer container, String componentName) {
- super(container, componentName);
- }
-
- @Override
- public void initialize(ComponentConfiguration configuration) throws Exception {
-
- // store any properties needed from the configuration
-
- super.initialize(configuration);
- }
-
- @Override
- public void sendMail(String subject, String text, String recipient) {
-
- // send the e-mail using SMTP, or store in stack to send by thread
- }
-}
-
-
-
Now that the component is written, it must be registered on the component, so that it is loaded when the
- agent is started. For this the StrolchConfiguration.xml
file must be modified to include a
- component element:
-
-
-<StrolchConfiguration>
- <env id="dev">
- ...
- <Component>
- <name>MailHandler</name>
- <api>li.strolch.handler.mail.MailHandler</api>
- <impl>li.strolch.handler.mail.SmtpMailHandler</impl>
- <Properties>
- <username>test</username>
- <password>test</password>
- <hostName>localhost</hostName>
- ...
- </Properties>
- </Component>
- ...
- </env>
-</StrolchConfiguration>
-
-
-
Now when the agent is started, the component can be retrieved and used. E.g from inside a
- Service
:
-
-
-MailHandler mailHandler = getComponent(MailHandler.class);
-mailHandler.sendMail("My Subject", "Hello World", "test@test.ch");
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-do-and-dont.html b/li.strolch.website/www.strolch.li/documentation-do-and-dont.html
deleted file mode 100644
index 2d54e1744..000000000
--- a/li.strolch.website/www.strolch.li/documentation-do-and-dont.html
+++ /dev/null
@@ -1,123 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Do and Don't
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Dos and Don'ts
-
- 1 Service
per use-case, should mostly delegate to Commands
.
- Commands
implement use-cases or parts of it, and are thus reusable.
- Subclass ResourceSearch
, OrderSearch
and ActivitySearch
when
- implementing use-case specific search - this allows privilege checking.
-
- One Transaction at a time - no TX inside of another TX.
- Commands are added to TXs and performed on close: tx.addCommand(Command)
and then tx.commitOnClose()
-
- Use tx.flush() if you need to perform first some work and then as late as possible call tx.commitOnClose()
-
- Only access ElementMap
s if really no other way, mostly use tx.get*By()
, tx.findBy()
- and queries - if a specific get is missing, then add the method to StrolchTransaction
and
- send a pull request!
-
- Use tx.stream*()
methods to iterate over all elements, if you don't want to use a search.
-
- Don't write logic in REST API beans. Delegate to other services, making your code reusable!
- Transform to JSON using the StrolchElementToJsonVisitor
.
- References between objects is done by adding a ParameterBag
with the id
- relations
to the object and then StringParameters
with the value being the ID,
- the UOM set to the type of element being referenced and the Interpretation set to the class type being
- referenced.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-model.html b/li.strolch.website/www.strolch.li/documentation-model.html
deleted file mode 100644
index feac3b550..000000000
--- a/li.strolch.website/www.strolch.li/documentation-model.html
+++ /dev/null
@@ -1,391 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Model
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Before we dive into the entire model, let's show an example and how it would be modelled in Strolch and use
- in Strolch:
-
-
A possible model would look as follows:
-
-<?xml version="1.0" encoding="UTF-8" ?>
-<StrolchModel xmlns="https://strolch.li/xsd/StrolchModel-1.6.xsd">
-
- <Resource Id="Product" Name="Product Template" Type="Template">
- <ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
- <Parameter Id="description" Name="Description" Type="String" Value=""/>
- <Parameter Id="color" Name="Color" Type="String" Value=""/>
- <Parameter Id="form" Name="Form" Type="String" Value=""/>
- </ParameterBag>
- <ParameterBag Id="relations" Name="Relations" Type="Relations">
- <Parameter Id="articles" Name="Articles" Type="StringList" Interpretation="Resource-Ref" Uom="Article" Value=""/>
- </ParameterBag>
- </Resource>
-
- <Resource Id="Article" Name="Article Template" Type="Template">
- <ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
- <Parameter Id="description" Name="Description" Type="String" Value=""/>
- <Parameter Id="barcode" Name="Barcode" Type="String" Value=""/>
- </ParameterBag>
- <ParameterBag Id="relations" Name="Relations" Type="Relations">
- <Parameter Id="product" Name="Product" Type="String" Interpretation="Resource-Ref" Uom="Product" Value=""/>
- </ParameterBag>
- </Resource>
-
- <Resource Id="Customer" Name="Customer Template" Type="Template">
- <ParameterBag Id="address" Name="Address" Type="Address">
- <Parameter Id="street" Name="Street" Type="String" Value=""/>
- <Parameter Id="zip" Name="Zip" Type="String" Value=""/>
- <Parameter Id="city" Name="City" Type="String" Value=""/>
- <Parameter Id="country" Name="Country" Type="String" Value=""/>
- </ParameterBag>
- </Resource>
-
- <Order Id="Order" Name="Order" Type="Template">
- <ParameterBag Id="quantities" Name="Quantities per Article Id" Type="Quantities">
- <Parameter Id="quantity" Name="Quantity" Type="Float" Value="0"/>
- </ParameterBag>
- <ParameterBag Id="relations" Name="Relations" Type="Relations">
- <Parameter Id="articles" Name="Articles" Type="StringList" Interpretation="Resource-Ref" Uom="Article" Value=""/>
- <Parameter Id="customer" Name="Customer" Type="String" Interpretation="Resource-Ref" Uom="Customer" Value=""/>
- </ParameterBag>
- </Order>
-
-</StrolchModel>
-
Let's go through this model:
-
- In the above model we see that the id
and name
fields are always on the
- element, and thus aren't added as parameters. Further most elements have a parameters
- ParameterBag, with one or more parameters, modelling the fields.
- Note that in this example the Type
of all the elements is Template
. Strolch
- has API support to create a clone of these elements, so that they have a unique ID, and the proper
- type for persistence.
-
- The Product
element has three parameters: description
, color
- and form
. In this case they are all of type String. Further the relations
- ParameterBag defines the relationships, i.e. the product knows its articles. Note how the relation is
- first defined in a relations
ParameterBag and that the Parameter has Interpretation="Resource-Ref"
- Uom="Product"
- attributes. Strolch has API support for this, making it trivial to retrieve a dependency.
- The Article
element has two parameters description
and barcode
.
- Further it has a reference to its Product.
- The Order
element doesn't model the date
and state
fields as
- parameters, as these are inherently part of an Order element. The Order does define two references to
- customer
and articles
. A special case is the quantities
- ParameterBag. This bag of parameters is used to store the per article quantity for this order. With
- ParameterBags, you can eliminate the use of simple aggregate classes, as is commonly used in
- object-oriented programming.
-
- The Customer
element models a address
ParameterBag to store the address of a
- customer. Using a separate bag allows for further more direct fields to stored on the default parameters
- bag.
-
-
-
-
Now that we have a basic understanding of te model, it is of far more interest in how to create and interact
- with these elements at runtime. The following listing will perform simple operations:
-
-try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, false)) {
-
- /*
- * create a new product
- */
- Resource dafalgan = tx.getResourceTemplate("Product", true);
- dafalgan.setName("Dafalgan 100mg");
- dafalgan.getParameter("description", true).setValue("Dafalgan is for pain.");
- dafalgan.getParameter("color", true).setValue("Yellow");
- dafalgan.getParameter("form", true).setValue("flat");
-
- StringListParameter articlesP = dafalgan.getRelationsParam("articles", true);
-
- /*
- * create articles
- */
- Resource dafalgan1 = tx.getResourceTemplate("Article", true);
- dafalgan1.setName("Dafalgan 100mg 10 pce");
- dafalgan1.getParameter("description", true).setValue("This is pack with 10 pieces.");
- dafalgan1.getParameter("barcode", true).setValue("654654");
-
- Resource dafalgan2 = tx.getResourceTemplate("Article", true);
- dafalgan2.setName("Dafalgan 100mg 20 pce");
- dafalgan2.getParameter("description", true).setValue("This is pack with 20 pieces.");
- dafalgan2.getParameter("barcode", true).setValue("654655");
-
- /*
- * add reference to product
- */
- dafalgan1.getRelationParam("product").setValue(dafalgan.getId());
- articlesP.addValue(dafalgan1.getId());
- dafalgan2.getRelationParam("product").setValue(dafalgan.getId());
- articlesP.addValue(dafalgan2.getId());
-
- /*
- * create a new customer
- */
- Resource customer1 = tx.getResourceTemplate("Customer", true);
- customer1.setName("John Doe");
-
- // set address
- ParameterBag addressBag = customer1.getParameterBag("address", true);
- addressBag.getParameter("street", true).setValue("Main Str. 1");
- addressBag.getParameter("zip", true).setValue("1234");
- addressBag.getParameter("city", true).setValue("Hometown");
- addressBag.getParameter("country", true).setValue("Switzerland");
-
- /*
- * create a new order
- */
- Order order = tx.getOrderTemplate("Order", true);
- order.setName("Order for " + customer1.getName());
- order.setDate(LocalDate.of(2021, 2, 1));
- order.setState(State.PLANNED);
-
- // store reference to customer
- order.getRelationParam("customer", true).setValue(customer1.getId());
-
- StringListParameter orderArticlesP = order.getRelationsParam("articles", true);
- ParameterBag quantitiesBag = order.getParameterBag("quantities", true);
- FloatParameter quantityT = quantitiesBag.removeParameter("quantity");
-
- // order quantity of 20 for Dafalgan 1
- FloatParameter q1P = quantityT.getClone();
- q1P.setId(dafalgan1.getId());
- q1P.setValue(20);
- quantitiesBag.addParameter(q1P);
- orderArticlesP.addValue(dafalgan1.getId());
-
- // set order quantity of 10 for Dafalgan 2
- FloatParameter q2P = quantityT.getClone();
- orderArticlesP.addValue(dafalgan2.getId());
- q2P.setId(dafalgan2.getId());
- q2P.setValue(20);
- quantitiesBag.addParameter(q2P);
-
- // keep IDs for later use
- dafalganId = dafalgan.getId();
- dafalgan1Id = dafalgan1.getId();
- dafalgan2Id = dafalgan2.getId();
- customerId = customer1.getId();
- orderId = order.getId();
-
- /*
- * persist
- */
- tx.add(dafalgan);
- tx.add(dafalgan1);
- tx.add(dafalgan2);
- tx.add(customer1);
- tx.add(order);
-
- // commit
- tx.commitOnClose();
-}
-
-try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true)) {
-
- // get order
- Order order = tx.getOrderBy("Order", orderId, true);
- assertNotNull(orderId);
- assertEquals("Order for John Doe", order.getName());
-
- // get customer
- Resource customer = tx.getResourceByRelation(order, "customer", true);
- assertNotNull(customer);
- assertEquals("John Doe", customer.getName());
-
- // get articles
- List<Resource> articles = tx.getResourcesByRelation(order, "articles", true);
- assertEquals(2, articles.size());
-
- // get products
- List<Resource> products = articles.stream().map(a -> tx.getResourceByRelation(a, "product", true))
- .distinct().collect(Collectors.toList());
- assertEquals(1, products.size());
-
- // search for all orders in state PLANNED and with customer
- List<Order> orders = new OrderSearch().types("Order").stateIsIn(State.PLANNED)
- .where(ExpressionsSupport.relationParam("customer").isEqualTo(customerId)).search(tx).toList();
- assertEquals(1, orders.size());
-}
-
-
Note: Checkout
- example-model.xml
- and
- SimpleModelTest.java
- for these examples.
-
-
There is a XML Schema which defines the model in XML:
- StrolchModel-1.6.xsd
-
-
- Here is an example of all the possible elements in Strolch:
-
-<StrolchModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns="https://strolch.li/xsd/StrolchModel-1.6.xsd"
- xsi:schemaLocation="https://strolch.li/xsd/StrolchModel-1.6.xsd StrolchModel-1.6.xsd">
-
- <IncludeFile file="Include1.xml"/>
-
- <Order Id="@test1" Name="Test Order" Type="Order">
- <Version Version="0" CreatedBy="test" CreatedAt="2012-11-30T18:12:05.628+01:00" Deleted="false"/>
- <ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
- <Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
- </ParameterBag>
- <ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
- <Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
- </ParameterBag>
- <Policies>
- <Policy Type="PlanningPolicy" Value="key:SimplePlanning"/>
- <Policy Type="ConfirmationPolicy" Value="key:NoConfirmation"/>
- </Policies>
- </Order>
-
- <Resource Id="MyTestResource" Name="Test Name" Type="TestType">
- <Version Version="0" CreatedBy="test" CreatedAt="2012-11-30T18:12:05.628+01:00" Deleted="false"/>
- <ParameterBag Id="@bag01" Name="Test Bag 01" Type="TestBag">
- <Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
- </ParameterBag>
- <ParameterBag Id="@bag02" Name="Test Bag 02" Type="TestBag">
- <Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
- </ParameterBag>
- <TimedState Id="@booleanState" Name="Boolean State" Type="Boolean">
- <Value Time="1970-01-01T00:02:00.000+01:00" Value="false"/>
- </TimedState>
- <Policies>
- <Policy Type="PlanningPolicy" Value="key:SimplePlanning"/>
- <Policy Type="ConfirmationPolicy" Value="key:NoConfirmation"/>
- </Policies>
- </Resource>
-
- <Activity Id="activity_1" Name="Activity" Type="parentType" TimeOrdering="Series">
- <Version Version="0" CreatedBy="test" CreatedAt="2012-11-30T18:12:05.628+01:00" Deleted="false"/>
- <ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
- <Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
- </ParameterBag>
- <Policies>
- <Policy Type="PlanningPolicy" Value="key:SimplePlanning"/>
- <Policy Type="ConfirmationPolicy" Value="key:NoConfirmation"/>
- </Policies>
- <Action Id="action_1" Name="Action 1" ResourceId="dummyId" ResourceType="dummyType" State="Created" Type="Use">
- <ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
- <Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
- </ParameterBag>
- <Policies>
- <Policy Type="PlanningPolicy" Value="key:SimplePlanning"/>
- <Policy Type="ConfirmationPolicy" Value="key:NoConfirmation"/>
- </Policies>
- <ValueChange StateId="dummyId" Time="2012-11-30T18:12:05.628+01:00" Value="5" Type="Integer"/>
- <ValueChange StateId="dummyId" Time="2012-11-30T18:12:06.628+01:00" Value="6" Type="Integer"/>
- </Action>
- <Activity Id="child_activity" Name="Child Activity" Type="childType" TimeOrdering="Series">
- <ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
- <Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
- </ParameterBag>
- <Policies>
- <Policy Type="PlanningPolicy" Value="key:SimplePlanning"/>
- <Policy Type="ConfirmationPolicy" Value="key:NoConfirmation"/>
- </Policies>
- <Action Id="action_2" Name="Action 2" ResourceId="dummyId" ResourceType="dummyType" State="Planned"
- Type="Use">
- <ValueChange StateId="dummyId" Time="2012-11-30T18:12:05.628+01:00" Value="5" Type="Integer"/>
- <ValueChange StateId="dummyId" Time="2012-11-30T18:12:06.628+01:00" Value="6" Type="Integer"/>
- </Action>
- <Action Id="action_3" Name="Action 3" ResourceId="dummyId" ResourceType="dummyType" State="Created"
- Type="Use"/>
- </Activity>
- </Activity>
-
-</StrolchModel>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-observers.html b/li.strolch.website/www.strolch.li/documentation-observers.html
deleted file mode 100644
index 21d4d95bd..000000000
--- a/li.strolch.website/www.strolch.li/documentation-observers.html
+++ /dev/null
@@ -1,151 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Observers
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
All changes done in a Strolch transaction are recorded and then propagated to any registered observers.
-
-
The observer feature is opt-in and is configured for each realm. In the StrolchConfiguration.xml
- file enable observers by adding the enableObserverUpdates
propery per realm:
-
-<StrolchConfiguration>
- <env id="dev">
- ...
- <Component>
- <name>RealmHandler</name>
- <api>li.strolch.agent.api.RealmHandler</api>
- <impl>li.strolch.agent.impl.DefaultRealmHandler</impl>
- <depends>PrivilegeHandler</depends>
- <Properties>
- <realms>defaultRealm, otherRealm</realms>
- <enableObserverUpdates>true</enableObserverUpdates>
- <dataStoreMode>TRANSIENT</dataStoreMode>
- <dataStoreFile>StrolchModel.xml</dataStoreFile>
- <enableObserverUpdates.otherRealm>true</enableObserverUpdates.otherRealm>
- <dataStoreMode.otherRealm>TRANSIENT</dataStoreMode.otherRealm>
- <dataStoreFile.otherRealm>StrolchModel.xml</dataStoreFile.otherRealm>
- </Properties>
- </Component>
- </env>
- ...
-</StrolchConfiguration>
-
-
-
Registering for updates is done by registering an Observer
on the ObserverHandler
- of the realm itself:
-
-ObserverHandler observerHandler = container.getRealm(StrolchConstants.DEFAULT_REALM).getObserverHandler();
-observerHandler.registerObserver(Tags.RESOURCE, new Observer() {
-
- @Override
- public void update(String key, List<StrolchRootElement> elements) {
- logger.info(elements.size() + " resources were updated!");
- }
-
- @Override
- public void remove(String key, List<StrolchRootElement> elements) {
- logger.info(elements.size() + " resources were removed!");
- }
-
- @Override
- public void add(String key, List<StrolchRootElement> elements) {
- logger.info(elements.size() + " resources were added!");
- }
-});
-
-
-
Observer updates can be suppressed on the transaction by calling tx.setSuppressUpdates()
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-policies.html b/li.strolch.website/www.strolch.li/documentation-policies.html
deleted file mode 100644
index d17f43389..000000000
--- a/li.strolch.website/www.strolch.li/documentation-policies.html
+++ /dev/null
@@ -1,179 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Policies
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Policies are an integral part when writing business logic in Strolch. In many cases it would suffice to write
- all such logic in Services
and Commands
, but as soon as behaviour can change,
- depending on the element being accessed, then this would quickly lead to many if/else blocks.
-
-
Since writing large if/else blocks is not maintanable in the long run, Strolch offers a different approach.
- All Strolch elements can store Policy definitions. This is a simple key/value store where the key defines the
- type of policy, and the value references the policy to use.
-
-
Currently there are two ways to reference a policy in Strolch, either via a key which defines a further
- lookup in the PolicyHandler
, or directly as the name of the class to instantiate.
-
-
Using policies in Strolch gives the additional possibility of easily changing the behaviour at runtime, as a
- Service and/or Command would delegate the behaviour to the currently configured policy on the releveant
- element.
-
-
Policies are implemented by defining an abstract class and extends StrolchPolicy
. This abstract
- class then defines the API of the actual policy. A concrete class then extends this abstract class and
- implements the concrete methods.
-
-
Policies are registered on Resources, Orders, Activities and Actions. The following shows defining two
- policies on a Resource, a PlanningPolicy, an ExecutionPolicy in XML:
-
-<Resource ...>
- ...
- <Policies>
- <Policy Type="PlanningPolicy" Value="key:SimplePlanning" />
- <Policy Type="ExecutionPolicy" Value="java:li.strolch.policytest.TestSimulatedExecutionPolicy" />
- </Policies>
-</Resource>
-
-
Note how the PlanningPolicy has a value of key:SimplePlanning
and the ExecutionPolicy
- defines a reference to an actual class.
-
-
-
To use the PolicyHandler, it must be configured in the StrolchConfiguration.xml as follows:
-
-<Component>
- <name>PolicyHandler</name>
- <api>li.strolch.policy.PolicyHandler</api>
- <impl>li.strolch.policy.DefaultPolicyHandler</impl>
- <Properties>
- <readPolicyFile>true</readPolicyFile>
- <policyConfigFile>StrolchPolicies.xml</policyConfigFile>
- </Properties>
-</Component>
-
-
-
And this policy handler implementation requires a file where the lookups for the policies is defined,
- e.g.:
-
-<StrolchPolicies>
-
- <PolicyType Type="PlanningPolicy" Api="li.strolch.policytest.TestPlanningPolicy">
- <Policy Key="SimplePlanning" Class="li.strolch.policytest.TestSimplePlanningPolicy" />
- </PolicyType>
-
- <PolicyType Type="ConfirmationPolicy" Api="li.strolch.policytest.TestConfirmationPolicy">
- <Policy Key="NoConfirmation" Class="li.strolch.policytest.TestNoConfirmationPolicy" />
- </PolicyType>
-
-</StrolchPolicies>
-
-
-
Now at runtime we can access the policies:
-
-PolicyHandler policyHandler = getComponent(PolicyHandler.class);
-
-try (StrolchTransaction tx = openTx()) {
-
- Resource res = tx.getResourceBy("TestType", "MyTestResource");
- PolicyDefs policyDefs = res.getPolicyDefs();
-
- PolicyDef planningPolicyDef = policyDefs.getPolicyDef("PlanningPolicy");
- PlanningPolicy planningPolicy = policyHandler.getPolicy(planningPolicyDef, tx);
- planningPolicy.plan(...);
-
- PolicyDef executionPolicyDef = res.getPolicyDefs().getPolicyDef("ExecutionPolicy");
- ExecutionPolicy executionPolicy = policyHandler.getPolicy(executionPolicyDef, tx);
- executionPolicy.execute(...);
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-privileges.html b/li.strolch.website/www.strolch.li/documentation-privileges.html
deleted file mode 100644
index 514e50b0d..000000000
--- a/li.strolch.website/www.strolch.li/documentation-privileges.html
+++ /dev/null
@@ -1,211 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Privileges
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
No framework is complete without user management and privilege validation. The basic form would be Users and
- Roles, and then validating that an authenticated user has a given role. In Strolch we go a step further: A
- User has roles assigned, and each role has a set of Privileges. The privileges can overlap, a validation is
- performed to make sure that the one role doesn't deny and another role allows a specific action.
-
-
As explained in the Privilege Configuration section,
- users are defined in the PrivilegeUsers.xml
file, and roles are defined in the PrivilegeRoles.xml
- file.
-
-
Let's assume the following user and role definition:
-
-
-<Users>
- <User userId="1" username="jill" password="$PBKDF2WithHmacSHA512,10000,256$61646d696e$cb69962946617da006a2f95776d78b49e5ec7941d2bdb2d25cdb05f957f64344">
- <Firstname>Jill</Firstname>
- <Lastname>Someone</Lastname>
- <State>ENABLED</State>
- <Locale>en-GB</Locale>
- <Roles>
- <Role>AppUser</Role>
- </Roles>
- <Properties>
- <Property name="realm" value="execution" />
- </Properties>
- </User>
-</Users>
-
-<Roles>
- <Role name="AppUser">
- <Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="li.strolch.model.query.StrolchQuery" policy="DefaultPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Role>
-<Roles>
-
-
-
This configuration contains one user and one role. The user jill
has the role
- AppUser
and the role AppUser
has three privileges assigned.
-
-
Note how the user's password is configured similar to a unix password definition: Using the dollar sign
- &
first the hashing algorithm is configured (algorithm, iterations, key length), then the
- salt, followed by the password hash.
-
-
Note:
The password can also still be saved using two separate fields: a salt and password field.
- This configuration will be immediately changed to the unix form, so won't be described
- further here.
-
-
Further a user always has a firstname and last name. Optionally a locale can be set, otherwise the system
- locale is used. The user's state must be defined as one of NEW
, ENABLED
, DISABLED
,
- EXPIRED
or SYSTEM
. A user can only authenticate/login with the state
- ENABLED
. A user can have any number of properties, which can then be used at runtime. A user
- can also reference any number of roles, the assigned privilege can overlap, a global configuration mode
- defines how duplicate privileges are handled.
-
-
Roles have a name and any number of Privilege
definitions. A Privilege has a name, which in many
- cases is the name of Java class/interface on which an action is being invoked. The policy
value
- defines which policy to use when evaluating the privilege access. The privilege definition is defined in the
- PrivilegeConfig.xml
and is the name of a class to call for privilege validation.
-
-
Further the privilege definitions can have a AllAllowed
boolean flag, or any number of Allow
- or Deny
values. How these values are interpreted is defined in the policy implementation. A
- policy takes three input parameters:
-
- PrivilegeContext
→ supplied by privilege and gives access to the
- Certificate
, thus identifying the user for which privilege access is to be validated.
-
- IPrivilege
→ Contains the privilege values: AllAllowed
,
- Allow
and Deny
- Restrictable
→ An interface from which the privilege name is retrieved, and the
- associated value. The value is an object, and is cast to the relevant input in
- the concrete privilege policy.
-
-
-
-
The following privilege policies are already implemented:
-
-
- DefaultPrivilege → simple policy where the passed Restrictable
is expected to return a
- String value, which is compared with allow and deny values.
-
-
-
- Internal: RoleAccessPrivilege → policy used for the internal
- privileges PrivilegeGetRole
, PrivilegeAddRole
,
- PrivilegeModifyRole
or PrivilegeModifyRole
- Internal: UserAccessPrivilege → policy used for the internal privileges PrivilegeGetUser
,
- PrivilegeAddUser
, PrivilegeRemoveUser
, PrivilegeModifyUser
,
- PrivilegeAddRoleToUser
, PrivilegeRemoveRoleFromUser
, PrivilegeSetUserState
,
- PrivilegeSetUserLocale
or PrivilegeSetUserPassword
- Internal: UserAccessWithSameOrganisationPrivilege → Same as the
- UserAccessPrivilege
but expects the authenticated user to have a property organisation
- and validates that the user being modified is in the same organisation.
-
- Internal: UsernameFromCertificatePrivilege → This policy expects a Restrictable
- to return the certificate of another authenticated user and is used when modifying an
- authenticated user, i.e. killing a session, or modifying its current state, e.g. locale
- etc.
-
- Internal: UsernameFromCertificateWithSameOrganisationPrivilege → Same as UsernameFromCertificatePrivilege
- but expects the authenticated user to have a property organisation
and
- validates that the user being modified is in the same organisation.
-
-
-
-
Note: As a rule, the sequence is AllAllowed
→ Allow
→
- Deny
→ default deny
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-queries.html b/li.strolch.website/www.strolch.li/documentation-queries.html
deleted file mode 100644
index 7f5b5b3fb..000000000
--- a/li.strolch.website/www.strolch.li/documentation-queries.html
+++ /dev/null
@@ -1,202 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Queries
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
The Query API is deprecated and the search API should be used instead.
-
-
-
As is custom for every framework, querying the model must be possible. Strolch queries are implemented using
- the StrolchQuery
interface and one of its concrete implementations: ResourceQuery
,
- OrderQuery
, ActivityQuery
.
-
-
A Strolch element always has two identifiers: Type
and Id
. The type is important as
- it classifies an element. So if a car and a house would be modelled in Strolch, then those would both be a
- Resource
, but one of type Car
and the other of type House
. Both would
- have different parameters.
-
-
Thus one of the inputs for every query is it's type, which is defined as the navigation. It is said that we
- navigate to the Cars, or Houses. Thus when instantiating a ResourceQuery, pass the navigation to the type of
- Resource as well. Same applies for Orders and Activities.
-
-
Further input for a StrolchQuery are the selections. These selections get translated into RDBMS
- WHERE
clauses. Selections support boolean operations thus allowing for complex querying.
-
-
StrolchQueries also support Ordering and object transformation. Following classes provide the most used
- scenarios:
-
- OrderById
- OrderByName
- OrderByParameter
- *ToDomVisitor
- *ToSaxVisitor
- *ToJsonVisitor
- *ToFlatJsonVisitor
-
-
-
-
Example: Query all resources of type Car:
-
-try (StrolchTransaction tx = openTx()) {
- ResourceQuery<Resource> query = ResourceQuery.query("Car");
- query.withAny();
- List<Resource> cars = tx.doQuery(query);
-}
-
-
-
Example: Query all resources of type Car, order by Name and transform to JSON:
-
-try (StrolchTransaction tx = openTx()) {
- ResourceQuery<JsonObject> query = ResourceQuery.query("Car", new ResourceToJsonVisitor(),
- new OrderByName());
- query.withAny();
- List<JsonObject> cars = tx.doQuery(query);
-}
-
-
-
the previous example can also be written as follows:
-
-try (StrolchTransaction tx = openTx()) {
- ResourceQuery<JsonObject> query = new ResourceQuery<>();
- query.setNavigation(new StrolchTypeNavigation("Car"));
- query.setResourceVisitor(new ResourceToJsonVisitor());
- query.withAny();
- List<JsonObject> cars = tx.doQuery(query);
-}
-
-
-
Example: Query all resources of type Car with color blue:
-
-try (StrolchTransaction tx = openTx()) {
- ResourceQuery<Resource> query = ResourceQuery.query("Car");
- query.with(ParameterSelection.stringSelection("parameters", "color", "blue", StringMatchMode.es()));
- List<Resource> cars = tx.doQuery(query);
-}
-
-
-
Example: Query all resources of type Car which are not blue:
-
-try (StrolchTransaction tx = openTx()) {
- ResourceQuery<Resource> query = ResourceQuery.query("Car");
- query.not(ParameterSelection.stringSelection("parameters", "color", "blue", StringMatchMode.es()));
- List<Resource> cars = tx.doQuery(query);
-}
-
-
-
Example: Query all resources of type Car with color blue or yellow:
-
-try (StrolchTransaction tx = openTx()) {
- ResourceQuery<Resource> query = ResourceQuery.query("Car");
- query.or().with(
- ParameterSelection.stringSelection("parameters", "color", "blue", StringMatchMode.es()),
- ParameterSelection.stringSelection("parameters", "color", "yellow", StringMatchMode.es()));
- List<Resource> cars = tx.doQuery(query);
-}
-
-
-
Example: Query all resources of type Car with color blue or yellow owned by Jill:
-
-try (StrolchTransaction tx = openTx()) {
- ResourceQuery<Resource> query = ResourceQuery.query("Car");
-
- StringParameterSelection owner = ParameterSelection.stringSelection("parameters", "owner", "Jill", StringMatchMode.es());
- OrSelection colors = new OrSelection().with(
- ParameterSelection.stringSelection("parameters", "color", "blue", StringMatchMode.es()),
- ParameterSelection.stringSelection("parameters", "color", "yellow", StringMatchMode.es()));
-
- query.and().with(owner, colors);
-
- List<Resource> cars = tx.doQuery(query);
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-realms.html b/li.strolch.website/www.strolch.li/documentation-realms.html
deleted file mode 100644
index 52212e59b..000000000
--- a/li.strolch.website/www.strolch.li/documentation-realms.html
+++ /dev/null
@@ -1,205 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Realms
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Realms implement multi-tenant capabilities. A Strolch agent can have an arbitrary number of realms configured
- and each realm has its own persistence configuration, allowing to separate mandates completely.
-
- A realm can run in one of the following modes:
-
- EMPTY This is a transient data store mode, where no model changes are persisted - they are only
- kept in memory. When the Strolch agent is started, this realm is empty as no data is loaded.
-
- TRANSIENT This is the same as EMPTY, but with the difference that when the Strolch agent is
- started, a model file is parsed and the in-memory realm is populated with the elements parsed from the
- model file.
-
- CACHED In this mode, all data is stored in-memory, and any changes made are written back to the
- persistence layer. This allows for fast in-memory qeuries, but makes sure no data is lost when the agent
- is restarted.
-
-
-
-
Realms are mostly hidden from a developer as a StrolchTransaction
exposes all important
- operations needed to access Strolch objects. A developer will however need to configure the realms for their
- specific project. If the project only requires one realm, then the defaultRealm
can be used,
- where the developer only is required to configure the mode and any relevant model file.
-
-
If the mode is CACHED, then the PersistenceHandler
component is required to be configured, so
- that the DAOs know how to access the underlying database.
-
-
The configuration in the StrolchConfiguration.xml
file is as follows:
-
-
-<StrolchConfiguration>
- <env id="dev">
- ...
- <Component>
- <name>RealmHandler</name>
- <api>li.strolch.agent.api.RealmHandler</api>
- <impl>li.strolch.agent.impl.DefaultRealmHandler</impl>
- <depends>PrivilegeHandler</depends>
- <!-- if CACHED: -->
- <!--depends>PersistenceHandler</depends-->
- <Properties>
- <dataStoreMode>EMPTY|TRANSIENT|CACHED</dataStoreMode>
- <dataStoreFile>StrolchModel.xml</dataStoreFile>
- </Properties>
- </Component>
- ...
- </env>
-</StrolchConfiguration>
-
-
-
A multi-realm configuration would be as follows. Note how the defaultRealm
is still enabled, and
- has its configuration as before. Further the PostgreSQL PersistenceHandler
is configured to show
- how the realms are connected to the persistence handler:
-
-
-<StrolchConfiguration>
- <env id="dev">
- ...
- <Component>
- <name>RealmHandler</name>
- <api>li.strolch.agent.api.RealmHandler</api>
- <impl>li.strolch.agent.impl.DefaultRealmHandler</impl>
- <depends>PrivilegeHandler</depends>
- <depends>PersistenceHandler</depends>
- <Properties>
- <realms>defaultRealm, cachedRealm</realms>
- <dataStoreMode>TRANSIENT</dataStoreMode>
- <dataStoreFile>DefaultRealm.xml</dataStoreFile>
- <dataStoreMode.cachedRealm>CACHED</dataStoreMode.cachedRealm>
- <dataStoreMode.emptyRealm>EMPTY</dataStoreMode.emptyRealm>
- </Properties>
- </Component>
-
- <Component>
- <name>PersistenceHandler</name>
- <api>li.strolch.persistence.api.PersistenceHandler</api>
- <impl>li.strolch.persistence.postgresql.PostgreSqlPersistenceHandler</impl>
- <Properties>
- <allowSchemaCreation>true</allowSchemaCreation>
- <allowSchemaDrop>true</allowSchemaDrop>
-
- <db.url.cachedRealm>jdbc:postgresql://localhost/testdb2</db.url.cachedRealm>
- <db.username.cachedRealm>testuser2</db.username.cachedRealm>
- <db.password.cachedRealm>test</db.password.cachedRealm>
- <db.pool.maximumPoolSize.cachedRealm>1</db.pool.maximumPoolSize.cachedRealm>
- <db.pool.keepaliveTime.cachedRealm>600000</db.pool.keepaliveTime.cachedRealm>
- </Properties>
- </Component>
- ...
- </env>
-</StrolchConfiguration>
-
-
-
Accessing a realm is done in multiple ways. Important is to note, that a user should use the StrolchTransaction
- object, instead of accessing the Realm directly.
-
-
Opening a transaction is done from a Service
by calling one of the openTx()
-methods.
- Nevertheless, the realm can be accessed as follows:
-
-
-ComponentContainer container = getAgent().getContainer();
-StrolchRealm realm = container.getRealm(StrolchConstants.DEFAULT_REALM);
-try(StrolchTransaction tx = realm.openTx()) {
- Resource resource = tx.getResourceBy("TestType", "MyTestResource");
- ...
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-reports.html b/li.strolch.website/www.strolch.li/documentation-reports.html
deleted file mode 100644
index a6ac5606a..000000000
--- a/li.strolch.website/www.strolch.li/documentation-reports.html
+++ /dev/null
@@ -1,367 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Reports
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Since Strolch has a generic model, it was rather straight forward to create a simple API for writing reports.
- In Strolch a report is defined by using its own model, i.e. a Report is a Resource
of type
- Report
.
-
-
A report consists of the following parts:
-
- policy definition, thus allowing extensions
- basic configuration like base object type, order direction, etc.
- column definitions
- joins
- ordering definition
- filters
-
-
-
An example of a report is as follows:
-
-<Resource Id="stockReport" Name="Stock Report" Type="Report">
-
- <ParameterBag Id="parameters" Name="parameters" Type="Parameters">
- <Parameter Id="objectType" Index="20" Hidden="false" Name="Object Type" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="Player"/>
- <Parameter Id="descending" Name="Descending order" Type="Boolean" Value="true"/>
- </ParameterBag>
-
- <ParameterBag Id="ordering" Name="Ordering" Type="Ordering">
- <Parameter Id="name" Name="Name" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/>
- </ParameterBag>
-
- <ParameterBag Id="noTeamFilter" Name="Filter" Type="Filter">
- <Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:Equals" Value="!"/>
- <Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Slot" Value="Bags/relations/team"/>
- </ParameterBag>
-
- <ParameterBag Id="columns" Name="Display Columns" Type="Display">
- <Parameter Id="name" Name="Player" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/>
- <Parameter Id="birthDate" Name="Birth date" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="Bags/parameters/birthDate"/>
- <Parameter Id="team" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="$name"/>
- </ParameterBag>
-
- <ParameterBag Id="joins" Name="Joins" Type="Joins">
- <Parameter Id="Team" Index="10" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Player"/>
- </ParameterBag>
-
- <Policies>
- <Policy Type="ReportPolicy" Value="java:li.strolch.report.policy.GenericReport"/>
- </Policies>
-
-</Resource>
-
-
This report
-
- shows all Resources of type player (parameter objectType
) → marks the object type to
- be show in the filter criteria (default), and that its sorting index is at 20.
-
- orders the report by player's name (parameter bag ordering
)
- filters out all players with no team assigned (parameter bag noTeamFilter
)
- defines three columns: Player, Birth date, Team (paramger bag columns
)
- joins in the resource of type Team
- Uses the GenericReport
class to generate the report
-
-
-
GenericReport
-
- The default generic report implemented in Strolch has the following features and options:
-
-
Parameters
-
The parameters bag can contain the following parameters:
-
- objectType
→ the base type of object to get the input for the report. This means that
- the Interpretation
is set to one of:
-
- Resource-Ref
- Order-Ref
- Activity-Ref
-
- and that the UOM
and value
of the parameter is set to
- the type of element with which to retrieve the elements from the strolch model.
- Note: that the attributes Hidden
and Index
define the visibility
- and sorting index as filter criteria respectively.
-
- descending
→ boolean flag to define if sorting is in descending order
- allowMissingColumns
→ flag to define if no exception should be thrown if a column is
- missing
-
- dateRangeSel
→ defines a lookup parameter to use as a date range selector. This
- requires input when executing the report
-
-
-
-
Lookups
-
Many of the features of the generic report rely on looking up a value on the referenced element. The
- following shows the ways that a lookup can be performed:
-
- $id
→ lookup the ID of the element
- $name
→ lookup the name of the element
- $type
→ lookup the type of the element
- $date
→ lookup the date of the element (only possible on Order
and Activity
- elements)
-
- $state
→ lookup the state of the element (only possible on Order
and
- Activity
elements)
-
- Bags/<bag_id>/<param_id>
→
- $search:<parent_ref_id>:Bags/<bag_id>/<param_id>
→ searches for a
- parameter with the given
- bag and parameter, and
- if it does not exist,
- looks for the parent
- with the given parent_ref_id
- on the element. This
- allows a recursive
- search up a tree of
- elements which all have
- the same parameter
- referencing a parent.
- relations
bag
-
-
-
-
Note: these definitions are set as the value of a Parameter, and the Interpretation
and
- UOM
of the parameter is used to find the element on which to perform the lookup. I.e. the
- following definition:
-
<Parameter Id="name" Name="Player" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/>
-
defines that we want to lookup the name of the resource of type Player.
-
-
Ordering
-
Ordering, i.e sorting is done by adding the parameter bag with the id ordering
and each
- parameter defines a column to order by. The sequence of the ordering is defined by the index value assigned
- to each parameter.
-
-
Filtering
-
Filtering use additional Strolch Policies which implement the operator function. I.e. performing an equals,
- etc. The following ReportFilterPolicy
are available and should be added in your StrolchPolicies.xml
- file:
-
-<StrolchPolicies>
- ...
- <PolicyType Type="ReportFilterPolicy" Api="li.strolch.report.policy.ReportFilterPolicy">
- <Policy Key="GreaterThan" Class="li.strolch.report.policy.GreaterThanReportFilter" />
- <Policy Key="LessThan" Class="li.strolch.report.policy.LessThanReportFilter" />
- <Policy Key="Equals" Class="li.strolch.report.policy.EqualsReportFilter" />
- </PolicyType>
- ...
-<StrolchPolicies>
-
-
From this we can see that we can perform a GreaterThan
, LessThan
and
- Equals
filtering. These filters can also be negated by prefixing the filter value with an
- exclamation mark (!).
-
-
A special case for the filter values are filters on dates. If you are filtering on a date, then you can use
- the special operator now()
. This filter will use the current date and time and will add/subtract
- the ISO8601 period passed as an argument to the operator.
-
-
The following shows examples of these filters:
-
-<ParameterBag Id="minQtyFilter" Name="Filter" Type="Filter">
- <Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:GreaterThan" Value="10"/>
- <Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="Bags/parameters/quantity"/>
-</ParameterBag>
-
-<ParameterBag Id="notEmptyFilter" Name="Filter" Type="Filter">
- <Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:Equals" Value="!"/>
- <Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Bags/relations/team"/>
-</ParameterBag>
-
-<ParameterBag Id="threeMonthsAgoFilter" Name="Filter" Type="Filter">
- <Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:LessThan" Value="now(-P3M)"/>
- <Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="FromStock" Value="$date"/>
-</ParameterBag>
-
-
Note: One parameter defines which policy gets used and the key:<name>
value
- references a policy defined in the StrolchPolicies.xml
file. Further the lookup is
- defined in the fieldRef
parameter.
-
-
Joins
-
To add columns from data which is not on the element denoted by the base object type, we can join further
- elements. This is done by adding the parameter bag joins
and then each parameter references an
- element to join. The joining is done as follows:
-
- The Intepretation
and UOM
define which object we want to join, i.e. resource
- of type foo
-
- The value of the parameter defines the type of element on which to find the reference
- The join ordering is not relevant, as the tree is traversed accordingly
- At least one join must reference the base object type
- The lookup of the join is done by finding a parameter with any ID, which has the same Interpretation
- and UOM
as the join definition
-
- The attributes Hidden
and Index
define the visibility and sorting index as
- filter criteria respectively.
-
-
-
-
Thus the following:
-
-
-<ParameterBag Id="joins" Name="Joins" Type="Joins">
- <Parameter Id="Team" Index="10" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Player"/>
- <Parameter Id="Country" Index="5" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Country" Value="Team"/>
-</ParameterBag>
-
-
Performs two joins: First we join a resource of type Team
by finding the relevant parameter on
- the Player
resource, and then we lookup a resource of type Country
on the
- previously joined Team
resource.
-
-
-
Execution of Reports
-
To execute a reports, we must instantiate the Report and can then directly generate a JsonObject stream,
- which we can then pipe to a browser, file, etc.:
-
-Stream<JsonObject> jsonObjectStream = new Report(tx, reportId).doReportAsJson();
-
-
If you prefer a CSV report:
-
-try (CSVPrinter csvP = new CSVPrinter(new OutputStreamWriter(out),
- CSVFormat.DEFAULT.withHeader(headers).withDelimiter(';'))) {
-
- // do report without AsJson, and then iterating each row and sending to a CSV writer
- report.doReport().forEach(row -> {
- try {
- csvP.printRecord(row.valueStream().collect(Collectors.toList())); // add to CSV
- } catch (Exception e) {
- logger.error("Could not write CSV row", e);
- }
- });
-}
-
-
-
Filter Criteria
-
Predefining filters is a good start, but in some case you only want a portion of the actual filtered data.
- For instance if you make a stock report, you might only want one location. This information is dynamic and
- thus not stored on the report definition.
-
-
To perform these dynamic filterings, one would call the filter()
method on the report, passing
- the type of element to be filtered, and to which element IDs to reduce the report data to. The following
- reduces the report to only return the rows with the product01
Product and
- location02
Location elements:
-
-new Report(tx, "stockReport")
- .filter("Product", "product01")
- .filter("Location", "location02")
- .doReportAsJson()
-
-
It is possible to find the possible filter criteria dynamically using the
- generateFilterCriteria()
method.
-
-
-
Date Range Filtering
-
-
The last option to filter dynamically is using a date range selector. Define the dateRangeSel
- lookup parameter, and then set the date range on the instantiated report:
-
-<ParameterBag Id="parameters" Name="parameters" Type="Parameters">
- ...
- <Parameter Id="dateRangeSel" Name="Date Range Selector" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="Bags/parameters/expirationDate"/>
- ...
-</ParameterBag>
-
-Date from = new Date(LocalDate.of(2016, 1, 1).toEpochDay() * 86400000);
-Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000);
-DateRange dateRange = new DateRange().from(from, true).to(to, false);
-List<JsonObject> result = new Report(tx, "stockReport") //
- .filter("Product", "product01") //
- .dateRange(dateRange) //
- .doReportAsJson()
-
-
-
Note: See the
- GenericReportTest for examples.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-runtime.html b/li.strolch.website/www.strolch.li/documentation-runtime.html
deleted file mode 100644
index 6cd59334a..000000000
--- a/li.strolch.website/www.strolch.li/documentation-runtime.html
+++ /dev/null
@@ -1,395 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Runtime Configuration
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
A Strolch runtime configuration comprises two parts: a configuration part, and a model part. The
- configuration are files located in the ..config/
folder, and the model are files located in the
- ../data
folder.
-
-
-
In an absolute minimal configuration, the Strolch runtime requires the following folder structure:
-
- ../config/
-
- StrolchConfiguration.xml
- → configures the Strolch agent
- PrivilegeConfig.xml
- → configures user management
- PrivilegeUsers.xml
- → contains the users in an XML based user management file
- PrivilegeRoles.xml
- → contains the roles and privileges in an XML based user management
-
-
-
-
-
StrolchConfiguration.xml
-
-
The StrolchConfiguration.xml file configures the Strolch agent. The StrolchConfiguration.xml
- defines the following:
-
- <StrolchConfiguration>
root element
-
- <env id="xxx">
different environments with the possibility of having a global
- environment for configuration valid in multiple environments.
-
- <Runtime>
element which defines the agents name and a few other properties
- e.g. locale
and verbose
:
-
- <applicationName>
the agent's name
-
- <Properties>
-
- <locale>
the agent's internal locale for log messages etc.
- <verbose>
the logging level for some internal logging. (Logging is
- mostly done using log4j over slf4j)
-
-
-
-
-
-
- <Component>
elements for each component used in the agent. A component is configured
- by defining the following child elements:
-
- <name>
the name of the component, use when defining dependencies between
- components. The name is mostly set to the simple name of the interface
- of the component
-
-
- <api>
the full class name to the interface of the component. During runtime
- this interface will be used to access the component e.g.: ServiceHandler
- svcHandler
- =
- agent.getContainer().getComponent(ServiceHandler.class);
-
-
- <impl>
the full class name of the concrete implementation of the component.
- During initialization this class will be instantiated and registered
- under the component name and interface. This class must extend the
- class li.strolch.agent.api.StrolchComponent
-
-
- <depends>
any number of these elements, where the content is the name of
- another component, on which this component depends. Depending
- components are initialized and started after the component they
- depend on and are stopped and destroyed before
-
-
- <Properties>
-
- <...>
any number of properties which the component requires. The
- element's name will be the key with which the value can be
- accessed at runtime.
-
-
-
-
-
-
-
-
Note: When a property is missing, and the component has a hard coded default value, then when the
- component is initialized, the use of the default value and its key is logged. This makes it easy
- to see which new properties can be configured. Should the component not define a default value,
- then the component will thrown an exception on initialization. In this case it can be a good
- moment to read the JavaDoc (or source code) for the component in question to see how it is
- configured.
-
-
Privilege Configuration
-
-
In Strolch authentication and authorization is baked in. To open a transaction, and thus access the Strolch
- model, a Certificate object is required, which means the user has been authenticated and possibly
- authorized.
-
-
The PrivilegeConfig.xml defines the following:
-
- <Privilege>
root element
-
- <Container>
configures the individual Privilege components
-
- <Parameters>
base configuration properties for Privilege
- <EncryptionHandler>
configures the hashing algorithms and other
- encryption specific configuration
-
- <PersistenceHandler>
configures the persistence of the roles and
- users
-
- <UserChallengeHandler>
configures a challenge handler so that a user
- can reset their password. The default
- challenge handler is the li.strolch.privilege.handler.MailUserChallengeHandler
- which sends a challenge to the user's defined
- e-mail address.
-
- <SsoHandler>
the SSO Handler is used to implement a SingleSignOn and
- can be used to start a session using a LDAP token, etc.
- There is no default implementation as this is project
- specific.
-
-
-
- <Policies>
configures the available privilege policies at runtime, the name
- is referenced from the model file
-
-
-
-
-
-
The PrivilegeUsers.xml
and PrivilegeRoles.xml
define the users and roles and is
- used when in PrvilegeConfig.xml
the PersistenceHandler
is set to ch.eitchnet.privilege.handler.XmlPersistenceHandler
:
-
-
- <Users>
configures all users
-
- <User>
configures a specific user
-
- <Firstname>
configures a user's first name
- <Lastname>
configure a user's last name
- <State>
configures the user's state, see ch.eitchnet.privilege.model.UserState
-
- <Locale>
configure the user's locale
- <Roles>
configures the user's roles
-
- <Role>
adds a role to the user
-
-
- <Properties>
configures user specific properties. What properties are
- used is not specified and is dependent on the concrete
- agent
-
- <Property>
defines a single property
-
-
-
-
-
-
-
- <Roles>
configures all roles
-
- <Role>
configures a specific role
-
- <Privilege>
configures a specific privilege for this role
-
- <AllAllowed>
if set to true, then defines that all values
- associated with this privilege are allowed
-
- <Allow>
defines one allowed value for this privilege
-
- <Deny>
defines one denied value for this privilege
-
-
-
-
-
-
-
-
-
-
Implementing a StrolchComponent
-
-
Implementing a strolch component requires an interface, which defines the component's API and a concrete
- class which implements the interface and extends the class StrolchComponent
.
-
-
The StrolchComponent class adds the state model to the class, which transitions as follows: UNDEFINED
- =>
- SETUP
- =>
- INITIALIZED
- =>
- STARTED
- <=>
- STOPPED
- =>
- DESTROYED
-
-
-
Components can switch between STARTED
and STOPPED
, but once DESTROYED
- no further state change is possible. The component's state is changed by changes to the agent's lifecycle.
-
-
-
A component's state is changed by a call to the appropriate method on the component, override the methods as
- necessary. Note that it is good practice that the initialize()
-method is used to get all the
- configuration properties, and that they should there be evaluated and that the method so return quickly. The
- start()
-method is called after the agent's initialization and should be where additional
- threads are started. Correctly implementing these methods allows to quickly detect a wrongly configured
- agent, which might take longer to start for whatever reason.
-
-
The following shows a basic implementation of a component on the basis of a post initializer (a component
- which performs some actions in its start()
-method which should be done after everything else is
- started in the agent).
-
-
-public class SimplePostInitializer
- extends StrolchComponent
- implements PostInitializer {
-
- public SimplePostInitializer(ComponentContainer container,
- String componentName) {
- super(container, componentName);
- }
-
- @Override
- public void initialize(ComponentConfiguration configuration) {
- // do some initialization, validate configuration values, etc.
- // now call super, to update state
- super.initialize(configuration);
- }
-
- @Override
- public void start() {
- // start any threads, or perform long running start work
- // now call super, to update state
- super.start();
- }
-
- @Override
- public void stop() {
- // stop threads and timers, but be ready to start again
- // now call super, to update state
- super.stop();
- }
-
- @Override
- public void destroy() {
- // destroy this component, release all resources and don't worry about
- // being called to start again now call super, to update state
- super.destroy();
- }
-}
-
-
-
The new component would then be registered in the StrolchConfiguration.xml
as follows:
-
-<StrolchConfiguration>
- <env id="...">
- ...
- <Component>
- <name>SimplePostInitializer</name>
- <api>li.strolch.agent.api.PostInitializer</api>
- <impl>li.strolch.documentation.SimplePostInitializer</impl>
- </Component>
- ...
- </env>
-</StrolchConfiguration>
-
-
-
And can be access at runtime using:
-
-PostInitializer postInitializer = getContainer().getComponent(PostInitializer.class);
-
-
-
Starting the agent
-
-
When a Strolch runtime is started, then the root path to the runtime configuration must be passed. In Java
- this is done by calling:
-
-StrolchAgent agent = new StrolchAgent();
-agent.setup(environment, rootPath);
-agent.initialize();
-agent.start();
-
-
-
In Servlet 3.0 applications one would implement the javax.servlet.ServletContextListener
- interface, add the @WebListener
annotation to the class and in the
- contextInitialized()
-method start Strolch:
-
-String realPath = sce.getServletContext().getRealPath("/WEB-INF");
-String environment = StrolchEnvironment.getEnvironmentFromEnvProperties(pathF);
-this.agent = new StrolchAgent();
-this.agent.setup(environment, new File(realPath));
-this.agent.initialize();
-this.agent.start();
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-searches.html b/li.strolch.website/www.strolch.li/documentation-searches.html
deleted file mode 100644
index c0f81b3da..000000000
--- a/li.strolch.website/www.strolch.li/documentation-searches.html
+++ /dev/null
@@ -1,183 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Searches
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
As is custom for every framework, querying, or searching, the model must be possible. Strolch searches are
- implemented using the StrolchSearch
class and one of its concrete implementations: ResourceSearch
,
- OrderSearch
, ActivitySearch
.
-
-
A Strolch element always has two identifiers: Type
and Id
. The type is important as
- it classifies an element. So if a car and a house would be modelled in Strolch, then those would both be a
- Resource
, but one of type Car
and the other of type House
. Both would
- have different parameters. Thus when searching for objects, the first thing to do is define the type of
- object being searched.
-
-
The Strolch search API is very expressive and offers multiple ways to perform the same search. The search API
- consists of three components: The search classes, the search expressions and the search predicates. The
- concept was taken from the Apache Camel project.
-
-
There are four main search classes:
-
- RootElementSearch
- search for any of Resource
, Order
or Activity
- elements
-
- ResourceSearch
- search for Resources
- OrderSearch
- search for Orders
- ActivitySearch
- search for Activities
-
-
-
No search is useful without a where
clause, which are called search expressions. When writing a
- search, there are multiple ways to add such where clauses. Either
-
- override the define()
method in your sub class and add the where clauses by calling the
- where()
method, or
-
- define special methods on the class e.g. byColor()
which also calls the
- where()
method to add a search expression, or
-
- directly call the where()
method after instantiating a search.
-
-
-
When extending the class, then the search expressions are available as methods on the super class, otherwise
- you can statically import them from
- ExpressionsSupport .
-
-
And of course a where clause needs operators, which are called search predicates. Just as search expressions
- are available in sub classes, so are search predicates and can also be statically imported through
- PredicatesSupport .
-
-
Examples of search expressions with search predicates follow:
-
-ResourceSearch search = new ResourceSearch();
-
-// predicate either as parameter, or chained
-search.where(id().isEqualTo("myId"));
-search.where(id(isEqualTo("myId")));
-
-// negating
-search.where(id(isEqualTo("myId")).not());
-
-search.where(param("bagId", "paramId").isIn(Arrays.asList("red", "blue", "green")));
-
-search.where(paramNull("bagId", "paramId")));
-
-// boolean operations
-search.where(id(isEqualTo("myId")) //
- .or(name(isEqualTo("myName"))));
-
-
-
Note how the predicates can be chained to the search expression, or passed as a parameter to the
- expression.
-
-
In addition to using predefined search search expressions, one can also just pass a lambda expression which
- performs a custom filter:
-
-personSearch.where(person -> person.getName().length() == 3);
-
-
See the
- StrolchSearchTest for many ways in which you can implement tests.
-
-
Note that strolch searches requires privileges. Thus when you use a strolch search, add it to the role of the
- user in PrivilegeRoles.xml
:
-
-<Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
- <Allow>internal</Allow> <!-- internal used for when the search is done in an internal service -->
- <Allow>li.strolch.bookshop.search.BookSearch</Allow>
-</Privilege>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-services-and-commands.html b/li.strolch.website/www.strolch.li/documentation-services-and-commands.html
deleted file mode 100644
index 8d84fd85c..000000000
--- a/li.strolch.website/www.strolch.li/documentation-services-and-commands.html
+++ /dev/null
@@ -1,213 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Services and Commands
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Services
are written to implement a specific use-case. Commands
are written to
- implemente re-usable parts of a use-case. The use-case can be abstract e.g. AddResourceService
- or very specific e.g. CreatePatientService
.
-
-
Should the use-case be re-usable in different scenarios, then commands should implement the logic, and the
- services should then execute the commands. E.g. The CreatePatientService
would use a CreatePatientResourceCommand
- and then use an AddResourceCommand
in a single transaction, so that the task of creating the
- actual Patient Resource can be re-used somewhere else.
-
-
Services extend the abstract class AbstractService
and then implement the method internalDoService(ServiceArgument)
.
- AbstractService defines generic template arguments with which the concrete service can define a specific
- input ServiceArgument class and output ServiceResult class.
-
-
The AbstractService class has multiple helper methods:
-
- openTx():StrolchTransaction
- to open a transaction
- runPrivileged()
- to perform a SystemUserAction
- getComponent():V
- to retrieve a specific StrolchComponent
-
-
there are more - check the JavaDocs
-
-
-
Commands extend the Command
class and then implement the method doCommand()
.
- Commands have helper methods:
-
- tx()
- to get the current transaction
- getPolicy()
- to retrieve a policy instance
- runPrivileged()
- to perform a SystemUserAction
-
-
there are more - check the JavaDocs
-
-
The following code snippets shows how a Service and Command are used to perform the task of adding a new
- Order. Note how:
-
- the Service opens the transaction
- adds the command to the TX
- calls tx.commitOnClose()
- the command validates its input
- locks the object
- performs the work
- and implements an undo
-
-
-
AddOrderService:
-
-public class AddOrderService extends AbstractService<AddOrderService.AddOrderArg, ServiceResult> {
-
- @Override
- protected ServiceResult getResultInstance() {
- return new ServiceResult();
- }
-
- @Override
- protected ServiceResult internalDoService(AddOrderArg arg) {
-
- try (StrolchTransaction tx = openTx(arg.realm)) {
- AddOrderCommand command = new AddOrderCommand(tx);
- command.setOrder(arg.order);
- tx.addCommand(command);
- tx.commitOnClose();
- }
-
- return ServiceResult.success();
- }
-
- public static class AddOrderArg extends ServiceArgument {
- public Order order;
- }
-}
-
-
-
AddOrderCommand:
-
-public class AddOrderCommand extends Command {
-
- private Order order;
-
- public AddOrderCommand(StrolchTransaction tx) {
- super(tx);
- }
-
- public void setOrder(Order order) {
- this.order = order;
- }
-
- @Override
- public void validate() {
- DBC.PRE.assertNotNull("Order may not be null!", this.order);
- }
-
- @Override
- public void doCommand() {
-
- tx().lock(this.order);
-
- OrderMap orderMap = tx().getOrderMap();
- if (orderMap.hasElement(tx(), this.order.getType(), this.order.getId())) {
- String msg = MessageFormat.format("The Order {0} already exists!", this.order.getLocator());
- throw new StrolchException(msg);
- }
-
- orderMap.add(tx(), this.order);
- }
-
- @Override
- public void undo() {
- if (this.order != null && tx().isRollingBack()) {
- OrderMap orderMap = tx().getOrderMap();
- if (orderMap.hasElement(tx(), this.order.getType(), this.order.getId()))
- orderMap.remove(tx(), this.order);
- }
- }
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-transactions.html b/li.strolch.website/www.strolch.li/documentation-transactions.html
deleted file mode 100644
index 0839f305a..000000000
--- a/li.strolch.website/www.strolch.li/documentation-transactions.html
+++ /dev/null
@@ -1,191 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Transactions
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Strolch Transactions play a central role in a Strolch agent. A transaction is opened for a realm, and grants
- access to the model of the agent. Transactions are implemented as a Java try-with-resources
by
- implementing the AutoCloseable
interface. This makes it trivial to understand the scope of a
- transaction.
-
-
Transactions handle the following:
-
- Opening and closing database connections
- Releasing locks to strolch elements, if tx.lock(StrolchRootElement)
or tx.lock(Locator)
- was called
-
- Performing Commands by executing them in the added order, and validating them first.
- Exception handling
- Auditing
- Updating observers
-
-
-
When a transaction is opened, it is by default read-only, i.e. does not perform any commands when it is
- closed. Should the TX perform commands, then it is important to call tx.commitOnClose()
, but
- only at the end of the work, so that exception handling can properly work if something goes wrong.
-
-
StrolchTransaction
offers a myriad of methods:
-
- find element by its Locator
- get methods for elements by type and id, or using a StringParameter
or StringListParameter
- references
-
- methods to add, update or remove elements
- assert privilege access
- get a new element by its template
- check if an element exists by type and id
- get streams for elements
- add commands for execution
-
-
-
Transactions are opened by accessing the realm, but there are convenience methods depending on the
- use-case:
-
- In Services: by calling one of the openTx()
-methods
- In Commands: Transactions are already open, use method tx()
to get instance. Note:
- don't open a new TX inside a TX for the same realm!
-
- REST API: RestfulStrolchComponent.openTx()
-
-
-
Important is to always open the transaction as a try-with-resource block and to define if the TX should
- commit, or not:
-
-try (StrolchTransaction tx = openTx(...)) {
-
- // read lock our object
- Locator ferrariLoc = Resource.locatorFor("Car", "ferrari");
- tx.lock(ferrariLoc);
-
- // find a car by locator
- Resource ferrari = tx.findElement(ferrariLoc);
-
- // get a car by ID
- Resource opel = tx.getResourceBy("Car", "opel", true);
-
- // modify ball
- opel.setName("Opel Corsa");
- tx.update(opel);
-
- // get by string reference
- StringParameter ownerP = ferrari.getParameter("relations", "owner", true);
- Resource owner = tx.getResourceBy(ownerP, true);
-
- // get by string list reference
- StringListParameter previousOwnersP = opel.getParameter("relations", "previousOwners", true);
- List<Resource> previousOwners = tx.getResourcesBy(previousOwnersP, true);
-
- // check resource exists
- if (tx.hasResource("Car", "audi")) {
- Resource audi = tx.getResourceBy("Car", "audi", true);
-
- // assert has privilege to remove a car
- tx.assertHasPrivilege(Operation.REMOVE, audi);
-
- // remove the car
- tx.remove(audi);
- }
-
- // iterate all cars
- tx.streamResources("Car").forEach(car -> {
- logger.info("Car: " + car.getId());
- });
-
- // commit if TX was changed
- if (tx.needsCommit())
- tx.commitOnClose();
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation-versioning.html b/li.strolch.website/www.strolch.li/documentation-versioning.html
deleted file mode 100644
index 01d33f344..000000000
--- a/li.strolch.website/www.strolch.li/documentation-versioning.html
+++ /dev/null
@@ -1,149 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Versioning
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
One of Strolch's features that sets it apart from other frameworks, is that versioning is baked into
- Strolch's fabric. The feature is opt-in, as it is not required in all projects, but it only needs enabling,
- for all modifications to objects to be versioned, so that rollbacks can be done when needed.
-
-
The feature is enabled for each realm. In the StrolchConfiguration.xml
file enable it by adding
- the enableVersioning
propery per realm:
-
-<StrolchConfiguration>
- <env id="dev">
- ...
- <Component>
- <name>RealmHandler</name>
- <api>li.strolch.agent.api.RealmHandler</api>
- <impl>li.strolch.agent.impl.DefaultRealmHandler</impl>
- <depends>PrivilegeHandler</depends>
- <Properties>
- <realms>defaultRealm, otherRealm</realms>
- <enableVersioning>true</enableVersioning>
- <dataStoreMode>TRANSIENT</dataStoreMode>
- <dataStoreFile>StrolchModel.xml</dataStoreFile>
- <enableVersioning.otherRealm>true</enableVersioning.otherRealm>
- <dataStoreMode.otherRealm>TRANSIENT</dataStoreMode.otherRealm>
- <dataStoreFile.otherRealm>StrolchModel.xml</dataStoreFile.otherRealm>
- </Properties>
- </Component>
- </env>
- ...
-</StrolchConfiguration>
-
-
-
Once versioning is enabled, versioning is handled automatically. The API for versioning is implemented on the
- ElementMaps.
-
-
Example: Revert to previous version of a Resource:
-
-Resource res = tx.getResourceBy("TestType", "MyTestResource");
-ResourceMap resourceMap = tx.getResourceMap();
-Resource previousVersion = resourceMap.revertToVersion(tx, res);
-// or
-Resource previousVersion = resourceMap.revertToVersion(tx, "TestType", "MyTestResource", 1);
-
-
Example: Retrieve all versions of a Resource:
-
-List<Resource> versions = resourceMap.getVersionsFor(tx, "TestType", "MyTestResource");
-
-
-
Note: When reverting to a previous version, it is important to remember, that any references on an
- element to other elements will also be restored. As long as the relationship is to the same
- element, then this is not an issue, but should the relationship have changed, then it this must
- be handled and the user performing a revert be allowed to decided which element to reference in
- the reverted version.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/documentation.html b/li.strolch.website/www.strolch.li/documentation.html
deleted file mode 100644
index cbdc7ea93..000000000
--- a/li.strolch.website/www.strolch.li/documentation.html
+++ /dev/null
@@ -1,121 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Strolch's documentation has only just begun, but as more and more details of the implementation in Strolch
- are fixed, more documentation can be created and will be available here.
-
-
Currently we have the following topics of discussion:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/downloads.html b/li.strolch.website/www.strolch.li/downloads.html
deleted file mode 100644
index 95d0916a1..000000000
--- a/li.strolch.website/www.strolch.li/downloads.html
+++ /dev/null
@@ -1,114 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
Strolch: Downloads
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Strolch is on Maven central , but if the
- latest version is not there, then build it locally. A guide can be found on the
- development page.
-
-
Strolch is also built on Jenkins , so you can see if the latest
- version passes all tests.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.eot b/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.eot
deleted file mode 100644
index 4a4ca865d..000000000
Binary files a/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.eot and /dev/null differ
diff --git a/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.svg b/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.svg
deleted file mode 100644
index e3e2dc739..000000000
--- a/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.svg
+++ /dev/null
@@ -1,229 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.ttf b/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.ttf
deleted file mode 100644
index 67fa00bf8..000000000
Binary files a/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.ttf and /dev/null differ
diff --git a/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.woff b/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.woff
deleted file mode 100644
index 8c54182aa..000000000
Binary files a/li.strolch.website/www.strolch.li/fonts/glyphicons-halflings-regular.woff and /dev/null differ
diff --git a/li.strolch.website/www.strolch.li/google052dbec2d053a4e1.html b/li.strolch.website/www.strolch.li/google052dbec2d053a4e1.html
deleted file mode 100644
index c2d9222d5..000000000
--- a/li.strolch.website/www.strolch.li/google052dbec2d053a4e1.html
+++ /dev/null
@@ -1 +0,0 @@
-google-site-verification: google052dbec2d053a4e1.html
\ No newline at end of file
diff --git a/li.strolch.website/www.strolch.li/history.html b/li.strolch.website/www.strolch.li/history.html
deleted file mode 100644
index 25c134b5d..000000000
--- a/li.strolch.website/www.strolch.li/history.html
+++ /dev/null
@@ -1,265 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Strolch: Overview
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Overview
-
-
Strolch is an open source component based software agent written in Java and can be compared, in a light
- sense, with the Java EE stack: Strolch takes care of persistence, implements Services for use cases, Commands
- as re-usable algorithms and has a parameterized data model.
-
-
Strolch has an intrinsic understanding for mandates, which are called realms so that a single agent can be
- used to implement applications with multiple users/customers for instance in SaaS environments.
-
-
The parameterized data model consists of three top level objects, Resources, Orders and Activities. These
- objects can have any number of ParameterBags which in turn can have any number of Parameters on them. This
- allows for a very dynamic modelling of data structures including modification at run time. Multiple ready to
- use Parameter types are already implemented which handle the primitive types in Java including ListParameters
- for collections of these primitive types.
-
-
One of the main features of the Strolch agent, is that persistence is handled transparently and the user must
- not be worried about databases and the likes. Currently there are two implementations for persisting the
- Strolch model, a PostgreSQL and an XML file persistence. Currently both persistence layers persist the data
- by converting to XML and storing it into the database. The XML file persistence stores each object in its own
- file.
-
-
The agent itself has a small memory footprint and requires very few components to start. For the agent to be
- useful it needs additional functionality which is implemented in StrolchComponents. Each component is
- registered via its Java interface on the agent and is bound to the life cycle of the agent. When the agent is
- started, these components can be retrieved and used to perform any number of functionalities. This is the
- preferred way to extend the Strolch agent. There are a number of components already implemented, e.g. the
- ServiceHandler which executes Services in a controlled fashion and can validate authorized access to these
- services.
-
-
No software product is complete without a system for authentication and authorization. Strolch implements
- this by using the Privilege framework which has been written by Robert von Burg. The standard ServiceHandler
- detects the existence of the PrivilegeHandler and then validates that the user has authorization to perform
- the service. This framework is implemented as its own Strolch component, thus can be retrieved at any time
- during execution to perform fine grained and special authorization validation.
-
-
Motivation
-
-
A question often asked is why create Strolch. What are its benefits in contrast to using Java SE with an
- OR-Mapper like Hibernate, or using Java EE on JBoss or Glassfish? Especially since many of the features
- existing in those stacks needed to be re-created in Strolch.
-
-
The first answer to this question is that those systems are often overly complicated and bloated. Java SE
- with Hibernate certainly is a viable option when it comes to being light-weightier but Hibernate, even though
- it is supposed to, often fails to truly help remove the need to really understand an RDBMS. Often enough
- Hibernate will just get in the way of the most important part of development: writing the business code.
- Being an OR-Mapper which is supposed to implement all the nitty-gritty details of an RDBMS system, Hibernate,
- and JPA for that matter, still often has the developer go back to understanding these details.
-
-
Strolch tries a different approach to persistence. Instead of writing pojos/entities, Strolch's model has the
- concept that each element's attributes are part of a composition pattern: each attribute is its own object
- and thus can be dynamically changed at runtime, but also makes persistence of such an element generic.
- Instead of having fixed attributes for a concrete class, these parameters are stored in a map and are
- accessed through the parameter's ID.
-
-
Assigning an ID to an attribute for accessing of course brings its own downsides, i.e. the parameter might
- simply not be there, when being accessed. This is certainly an issue that the developer must handle, when
- implementing a project using Strolch, but allows the developer to not need to worry about persistence, as
- this is generically handled.
-
-
Since the persistence is generically handled, and Strolch stays lightweight on its requirements at runtime,
- the developer can quickly get down to what is important for business value: Writing the business logic and
- the presentation layer. Here too Strolch tries to help the developer by bringing in concepts which are easy
- to follow: Use cases are implemented as Services, and re-usable business logic is put into Commands.
-
-
There will be reasons against using Strolch, as there will be against using the Java EE stack, or an
- OR-Mapper or even the Java ecosystem for that fact. Important is to note, that the concepts behind Strolch
- are nothing new, but have been implemented in at least two previous proprietary products. Since those
- products are not accessible to the public, it was decided that a re-implementation might be of use to the
- programming community at large.
-
-
Currently there is at least one company using Strolch in a commercial project which helps drive Strolch's
- development and further motivates its existence.
-
-
Strolch is an open source project and licensed under the Apache License 2.0.
-
-
Technologoy
-
-
Strolch is written in Java and is programmed against the JDK 8. Strolch runs on any JRE 8 compliant
- environment. Strolch is tested on the Oracle JRE 8.
-
-
Dependencies
-
-
Strolch strives to use as few external dependencies as possible, so that the Strolch runtime is not bloated
- unnecessarily. The following list of Strolch dependencies is a summary and was created using mvn
- dependency:tree on the strolch_minimal project for release 1.6.47.
-
-
Logging
-
- org.slf4j:slf4j-api:jar:1.7.25
- ch.qos.logback:logback-classic:jar:1.2.3
- ch.qos.logback:logback-core:jar:1.2.3
-
-
-
Utils
-
- javax.mail:javax.mail-api:jar:1.6.0
- com.sun.mail:javax.mail:jar:1.6.0
- javax.activation:activation:jar:1.1
-
-
-
Testing
-
- junit:junit:jar:4.12
- org.hamcrest:hamcrest-core:jar:1.3
- org.hamcrest:hamcrest-library:jar:1.3
- org.mockito:mockito-core:jar:2.0.8-beta
- org.objenesis:objenesis:jar:2.1
-
-
-
Model
-
- com.google.code.gson:gson:jar:2.8.2
-
-
-
SOQL
-
- org.abego.treelayout:org.abego.treelayout.core:jar:1.0.3
- org.antlr:antlr-runtime:jar:3.5.2
- org.antlr:antlr4-runtime:jar:4.7
- org.antlr:antlr4:jar:4.7
- org.antlr:ST4:jar:4.0.8
- org.glassfish:javax.json:jar:1.0.4
- com.ibm.icu:icu4j:jar:58.2
-
-
-
PostgreSQL
-
- com.zaxxer:HikariCP:jar:2.7.1
- org.postgresql:postgresql:jar:42.1.4
-
-
-
REST
-
- javax.annotation:javax.annotation-api:jar:1.3.1
- javax.servlet:javax.servlet-api:jar:3.1.0
- javax.ws.rs:javax.ws.rs-api:jar:2.1
-
-
-
REST testing
-
- javax.validation:validation-api:jar:1.1.0.Final
- org.glassfish.grizzly:grizzly-framework:jar:2.3.28
- org.glassfish.grizzly:grizzly-http-server:jar:2.3.28
- org.glassfish.grizzly:grizzly-http-servlet:jar:2.3.28
- org.glassfish.grizzly:grizzly-http:jar:2.3.28
- org.glassfish.hk2:hk2-api:jar:2.5.0-b32
- org.glassfish.hk2:hk2-locator:jar:2.5.0-b32
- org.glassfish.hk2:hk2-utils:jar:2.5.0-b32
- org.glassfish.hk2:osgi-resource-locator:jar:1.0.1
- org.glassfish.hk2.external:aopalliance-repackaged:jar:2.5.0-b32
- org.glassfish.hk2.external:javax.inject:jar:2.5.0-b32
- org.glassfish.jersey.bundles.repackaged:jersey-guava:jar:2.25.1
- org.glassfish.jersey.containers:jersey-container-grizzly2-http:jar:2.25.1
- org.glassfish.jersey.containers:jersey-container-grizzly2-servlet:jar:2.25.1
- org.glassfish.jersey.containers:jersey-container-servlet-core:jar:2.25.1
- org.glassfish.jersey.containers:jersey-container-servlet:jar:2.25.1
- org.glassfish.jersey.core:jersey-client:jar:2.25.1
- org.glassfish.jersey.core:jersey-common:jar:2.25.1
- org.glassfish.jersey.core:jersey-server:jar:2.25.1
- org.glassfish.jersey.media:jersey-media-jaxb:jar:2.25.1
- org.glassfish.jersey.test-framework:jersey-test-framework-core:jar:2.25.1
- org.javassist:javassist:jar:3.20.0-GA
-
-
-
-
API
-
-
Check out the API page to see how to use Strolch.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/ico/favicon.ico b/li.strolch.website/www.strolch.li/ico/favicon.ico
deleted file mode 100644
index f576a4696..000000000
Binary files a/li.strolch.website/www.strolch.li/ico/favicon.ico and /dev/null differ
diff --git a/li.strolch.website/www.strolch.li/images/Strolch-Bird-View.svg b/li.strolch.website/www.strolch.li/images/Strolch-Bird-View.svg
deleted file mode 100644
index df7f1c50f..000000000
--- a/li.strolch.website/www.strolch.li/images/Strolch-Bird-View.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-
-Browser / Other Strolch Agent REST API (JSON / XML) JDBC (JSON / XML) Presentation Business Database External Systems
(Agents, ERP, etc.)
[Not supported by viewer] Any API, preferred REST with JSON
Any API, preferred REST with JSON System
\ No newline at end of file
diff --git a/li.strolch.website/www.strolch.li/images/Strolch-PLC-Architecture-Overview.png b/li.strolch.website/www.strolch.li/images/Strolch-PLC-Architecture-Overview.png
deleted file mode 100644
index 67592f64a..000000000
Binary files a/li.strolch.website/www.strolch.li/images/Strolch-PLC-Architecture-Overview.png and /dev/null differ
diff --git a/li.strolch.website/www.strolch.li/images/Strolch-PLC-Architecture.png b/li.strolch.website/www.strolch.li/images/Strolch-PLC-Architecture.png
deleted file mode 100644
index 8913eddb9..000000000
Binary files a/li.strolch.website/www.strolch.li/images/Strolch-PLC-Architecture.png and /dev/null differ
diff --git a/li.strolch.website/www.strolch.li/images/Strolch-Plc-Example.png b/li.strolch.website/www.strolch.li/images/Strolch-Plc-Example.png
deleted file mode 100644
index 9021db906..000000000
Binary files a/li.strolch.website/www.strolch.li/images/Strolch-Plc-Example.png and /dev/null differ
diff --git a/li.strolch.website/www.strolch.li/images/Strolch-Squirrel-View.svg b/li.strolch.website/www.strolch.li/images/Strolch-Squirrel-View.svg
deleted file mode 100644
index b07099282..000000000
--- a/li.strolch.website/www.strolch.li/images/Strolch-Squirrel-View.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-
-Strolch Agent Services Commands REST Endpoints DAOs ResourceMap ActivityMap OrderMap Realm n Component X Component Y Component Z Search API Privileges Transactions Observers Versioning Policies Audits
\ No newline at end of file
diff --git a/li.strolch.website/www.strolch.li/images/strolch-model-example.png b/li.strolch.website/www.strolch.li/images/strolch-model-example.png
deleted file mode 100644
index f88f79bc7..000000000
Binary files a/li.strolch.website/www.strolch.li/images/strolch-model-example.png and /dev/null differ
diff --git a/li.strolch.website/www.strolch.li/index.html b/li.strolch.website/www.strolch.li/index.html
deleted file mode 100644
index b27326690..000000000
--- a/li.strolch.website/www.strolch.li/index.html
+++ /dev/null
@@ -1,176 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Strolch: Overview
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Strolch in short
-
-
Strolch is framework for developing Software. It's main features are:
-
-
-
-
Strolch Intro
-
-
It is a different framework to Spring and other similar type of Java frameworks, as the model is defined as
- an abstract model, where you always have the same three types of objects: Resources, Orders and Activities.
- The fields are mapped as Parameter objects, of which the important primitives are available.
-
-
The nice part about this framework is, that you can be up and ready in a matter of minutes, and start
- building your project immediately in that you open your favourite XML editor and start modelling your
- data.
-
-
Once your data is defined, you write your business logic in the form of Services, Commands and Searches.
- There are many predefined services and commands to manipulate the object model, so that you write your own
- services when you need to enforce special business rules.
-
-
Through the use of Policy objects, you decouple algorithms from your object model, so that at runtime you can
- change the behaviour, or easily implement different behaviour depending on your use-case. For instance you
- might have a simple billing service which performs a few preparatory steps, and then calls the configured
- billing policy to execute the billing depending on the customer, the warehouse, etc.
-
-
And of course persistence is as simple as configuring the persistence handler, pointing to your RDBMS and
- then setting the mode to CACHED. For you as a developer there is no more thinking in terms of SQL etc., as
- this is completely hidden from the developer. There is even a simple file persistence layer if you are
- running IoT devices.
-
-
The runtime can be just about anything. Usually it is run inside an Apache Tomcat instance as a webapp, as a
- WEB UI has been required for all current Strolch projects. You could just as well use a main class. Accessing
- the Strolch Agent remotely is usually done through REST.
-
-
Strolch is being actively developed, and customers constantly give us reasons to improve and extend the
- framework. There is a Polymer Inspector component which makes it easy to see and manipulate the actual data.
- The new Search API makes it really easy to query your data.
-
-
Yes, Strolch is different, but the concept has come out of the planning and execution segment, and has been
- refined over the years until it has become what it is today.
-
-
API
-
-
Check out the API page to see how to use Strolch.
-
-
More to motivation etc.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/js/bootstrap.js b/li.strolch.website/www.strolch.li/js/bootstrap.js
deleted file mode 100644
index 8ae571b6d..000000000
--- a/li.strolch.website/www.strolch.li/js/bootstrap.js
+++ /dev/null
@@ -1,1951 +0,0 @@
-/*!
- * Bootstrap v3.1.1 (http://getbootstrap.com)
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */
-
-if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript requires jQuery') }
-
-/* ========================================================================
- * Bootstrap: transition.js v3.1.1
- * http://getbootstrap.com/javascript/#transitions
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
- // ============================================================
-
- function transitionEnd() {
- var el = document.createElement('bootstrap')
-
- var transEndEventNames = {
- 'WebkitTransition' : 'webkitTransitionEnd',
- 'MozTransition' : 'transitionend',
- 'OTransition' : 'oTransitionEnd otransitionend',
- 'transition' : 'transitionend'
- }
-
- for (var name in transEndEventNames) {
- if (el.style[name] !== undefined) {
- return { end: transEndEventNames[name] }
- }
- }
-
- return false // explicit for ie8 ( ._.)
- }
-
- // http://blog.alexmaccaw.com/css-transitions
- $.fn.emulateTransitionEnd = function (duration) {
- var called = false, $el = this
- $(this).one($.support.transition.end, function () { called = true })
- var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
- setTimeout(callback, duration)
- return this
- }
-
- $(function () {
- $.support.transition = transitionEnd()
- })
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: alert.js v3.1.1
- * http://getbootstrap.com/javascript/#alerts
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // ALERT CLASS DEFINITION
- // ======================
-
- var dismiss = '[data-dismiss="alert"]'
- var Alert = function (el) {
- $(el).on('click', dismiss, this.close)
- }
-
- Alert.prototype.close = function (e) {
- var $this = $(this)
- var selector = $this.attr('data-target')
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7
- }
-
- var $parent = $(selector)
-
- if (e) e.preventDefault()
-
- if (!$parent.length) {
- $parent = $this.hasClass('alert') ? $this : $this.parent()
- }
-
- $parent.trigger(e = $.Event('close.bs.alert'))
-
- if (e.isDefaultPrevented()) return
-
- $parent.removeClass('in')
-
- function removeElement() {
- $parent.trigger('closed.bs.alert').remove()
- }
-
- $.support.transition && $parent.hasClass('fade') ?
- $parent
- .one($.support.transition.end, removeElement)
- .emulateTransitionEnd(150) :
- removeElement()
- }
-
-
- // ALERT PLUGIN DEFINITION
- // =======================
-
- var old = $.fn.alert
-
- $.fn.alert = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.alert')
-
- if (!data) $this.data('bs.alert', (data = new Alert(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
-
- $.fn.alert.Constructor = Alert
-
-
- // ALERT NO CONFLICT
- // =================
-
- $.fn.alert.noConflict = function () {
- $.fn.alert = old
- return this
- }
-
-
- // ALERT DATA-API
- // ==============
-
- $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close)
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: button.js v3.1.1
- * http://getbootstrap.com/javascript/#buttons
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // BUTTON PUBLIC CLASS DEFINITION
- // ==============================
-
- var Button = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Button.DEFAULTS, options)
- this.isLoading = false
- }
-
- Button.DEFAULTS = {
- loadingText: 'loading...'
- }
-
- Button.prototype.setState = function (state) {
- var d = 'disabled'
- var $el = this.$element
- var val = $el.is('input') ? 'val' : 'html'
- var data = $el.data()
-
- state = state + 'Text'
-
- if (!data.resetText) $el.data('resetText', $el[val]())
-
- $el[val](data[state] || this.options[state])
-
- // push to event loop to allow forms to submit
- setTimeout($.proxy(function () {
- if (state == 'loadingText') {
- this.isLoading = true
- $el.addClass(d).attr(d, d)
- } else if (this.isLoading) {
- this.isLoading = false
- $el.removeClass(d).removeAttr(d)
- }
- }, this), 0)
- }
-
- Button.prototype.toggle = function () {
- var changed = true
- var $parent = this.$element.closest('[data-toggle="buttons"]')
-
- if ($parent.length) {
- var $input = this.$element.find('input')
- if ($input.prop('type') == 'radio') {
- if ($input.prop('checked') && this.$element.hasClass('active')) changed = false
- else $parent.find('.active').removeClass('active')
- }
- if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change')
- }
-
- if (changed) this.$element.toggleClass('active')
- }
-
-
- // BUTTON PLUGIN DEFINITION
- // ========================
-
- var old = $.fn.button
-
- $.fn.button = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.button')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.button', (data = new Button(this, options)))
-
- if (option == 'toggle') data.toggle()
- else if (option) data.setState(option)
- })
- }
-
- $.fn.button.Constructor = Button
-
-
- // BUTTON NO CONFLICT
- // ==================
-
- $.fn.button.noConflict = function () {
- $.fn.button = old
- return this
- }
-
-
- // BUTTON DATA-API
- // ===============
-
- $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {
- var $btn = $(e.target)
- if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
- $btn.button('toggle')
- e.preventDefault()
- })
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: carousel.js v3.1.1
- * http://getbootstrap.com/javascript/#carousel
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // CAROUSEL CLASS DEFINITION
- // =========================
-
- var Carousel = function (element, options) {
- this.$element = $(element)
- this.$indicators = this.$element.find('.carousel-indicators')
- this.options = options
- this.paused =
- this.sliding =
- this.interval =
- this.$active =
- this.$items = null
-
- this.options.pause == 'hover' && this.$element
- .on('mouseenter', $.proxy(this.pause, this))
- .on('mouseleave', $.proxy(this.cycle, this))
- }
-
- Carousel.DEFAULTS = {
- interval: 5000,
- pause: 'hover',
- wrap: true
- }
-
- Carousel.prototype.cycle = function (e) {
- e || (this.paused = false)
-
- this.interval && clearInterval(this.interval)
-
- this.options.interval
- && !this.paused
- && (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
-
- return this
- }
-
- Carousel.prototype.getActiveIndex = function () {
- this.$active = this.$element.find('.item.active')
- this.$items = this.$active.parent().children()
-
- return this.$items.index(this.$active)
- }
-
- Carousel.prototype.to = function (pos) {
- var that = this
- var activeIndex = this.getActiveIndex()
-
- if (pos > (this.$items.length - 1) || pos < 0) return
-
- if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) })
- if (activeIndex == pos) return this.pause().cycle()
-
- return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos]))
- }
-
- Carousel.prototype.pause = function (e) {
- e || (this.paused = true)
-
- if (this.$element.find('.next, .prev').length && $.support.transition) {
- this.$element.trigger($.support.transition.end)
- this.cycle(true)
- }
-
- this.interval = clearInterval(this.interval)
-
- return this
- }
-
- Carousel.prototype.next = function () {
- if (this.sliding) return
- return this.slide('next')
- }
-
- Carousel.prototype.prev = function () {
- if (this.sliding) return
- return this.slide('prev')
- }
-
- Carousel.prototype.slide = function (type, next) {
- var $active = this.$element.find('.item.active')
- var $next = next || $active[type]()
- var isCycling = this.interval
- var direction = type == 'next' ? 'left' : 'right'
- var fallback = type == 'next' ? 'first' : 'last'
- var that = this
-
- if (!$next.length) {
- if (!this.options.wrap) return
- $next = this.$element.find('.item')[fallback]()
- }
-
- if ($next.hasClass('active')) return this.sliding = false
-
- var e = $.Event('slide.bs.carousel', { relatedTarget: $next[0], direction: direction })
- this.$element.trigger(e)
- if (e.isDefaultPrevented()) return
-
- this.sliding = true
-
- isCycling && this.pause()
-
- if (this.$indicators.length) {
- this.$indicators.find('.active').removeClass('active')
- this.$element.one('slid.bs.carousel', function () {
- var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()])
- $nextIndicator && $nextIndicator.addClass('active')
- })
- }
-
- if ($.support.transition && this.$element.hasClass('slide')) {
- $next.addClass(type)
- $next[0].offsetWidth // force reflow
- $active.addClass(direction)
- $next.addClass(direction)
- $active
- .one($.support.transition.end, function () {
- $next.removeClass([type, direction].join(' ')).addClass('active')
- $active.removeClass(['active', direction].join(' '))
- that.sliding = false
- setTimeout(function () { that.$element.trigger('slid.bs.carousel') }, 0)
- })
- .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000)
- } else {
- $active.removeClass('active')
- $next.addClass('active')
- this.sliding = false
- this.$element.trigger('slid.bs.carousel')
- }
-
- isCycling && this.cycle()
-
- return this
- }
-
-
- // CAROUSEL PLUGIN DEFINITION
- // ==========================
-
- var old = $.fn.carousel
-
- $.fn.carousel = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.carousel')
- var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option)
- var action = typeof option == 'string' ? option : options.slide
-
- if (!data) $this.data('bs.carousel', (data = new Carousel(this, options)))
- if (typeof option == 'number') data.to(option)
- else if (action) data[action]()
- else if (options.interval) data.pause().cycle()
- })
- }
-
- $.fn.carousel.Constructor = Carousel
-
-
- // CAROUSEL NO CONFLICT
- // ====================
-
- $.fn.carousel.noConflict = function () {
- $.fn.carousel = old
- return this
- }
-
-
- // CAROUSEL DATA-API
- // =================
-
- $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) {
- var $this = $(this), href
- var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- var options = $.extend({}, $target.data(), $this.data())
- var slideIndex = $this.attr('data-slide-to')
- if (slideIndex) options.interval = false
-
- $target.carousel(options)
-
- if (slideIndex = $this.attr('data-slide-to')) {
- $target.data('bs.carousel').to(slideIndex)
- }
-
- e.preventDefault()
- })
-
- $(window).on('load', function () {
- $('[data-ride="carousel"]').each(function () {
- var $carousel = $(this)
- $carousel.carousel($carousel.data())
- })
- })
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: collapse.js v3.1.1
- * http://getbootstrap.com/javascript/#collapse
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // COLLAPSE PUBLIC CLASS DEFINITION
- // ================================
-
- var Collapse = function (element, options) {
- this.$element = $(element)
- this.options = $.extend({}, Collapse.DEFAULTS, options)
- this.transitioning = null
-
- if (this.options.parent) this.$parent = $(this.options.parent)
- if (this.options.toggle) this.toggle()
- }
-
- Collapse.DEFAULTS = {
- toggle: true
- }
-
- Collapse.prototype.dimension = function () {
- var hasWidth = this.$element.hasClass('width')
- return hasWidth ? 'width' : 'height'
- }
-
- Collapse.prototype.show = function () {
- if (this.transitioning || this.$element.hasClass('in')) return
-
- var startEvent = $.Event('show.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
-
- var actives = this.$parent && this.$parent.find('> .panel > .in')
-
- if (actives && actives.length) {
- var hasData = actives.data('bs.collapse')
- if (hasData && hasData.transitioning) return
- actives.collapse('hide')
- hasData || actives.data('bs.collapse', null)
- }
-
- var dimension = this.dimension()
-
- this.$element
- .removeClass('collapse')
- .addClass('collapsing')
- [dimension](0)
-
- this.transitioning = 1
-
- var complete = function () {
- this.$element
- .removeClass('collapsing')
- .addClass('collapse in')
- [dimension]('auto')
- this.transitioning = 0
- this.$element.trigger('shown.bs.collapse')
- }
-
- if (!$.support.transition) return complete.call(this)
-
- var scrollSize = $.camelCase(['scroll', dimension].join('-'))
-
- this.$element
- .one($.support.transition.end, $.proxy(complete, this))
- .emulateTransitionEnd(350)
- [dimension](this.$element[0][scrollSize])
- }
-
- Collapse.prototype.hide = function () {
- if (this.transitioning || !this.$element.hasClass('in')) return
-
- var startEvent = $.Event('hide.bs.collapse')
- this.$element.trigger(startEvent)
- if (startEvent.isDefaultPrevented()) return
-
- var dimension = this.dimension()
-
- this.$element
- [dimension](this.$element[dimension]())
- [0].offsetHeight
-
- this.$element
- .addClass('collapsing')
- .removeClass('collapse')
- .removeClass('in')
-
- this.transitioning = 1
-
- var complete = function () {
- this.transitioning = 0
- this.$element
- .trigger('hidden.bs.collapse')
- .removeClass('collapsing')
- .addClass('collapse')
- }
-
- if (!$.support.transition) return complete.call(this)
-
- this.$element
- [dimension](0)
- .one($.support.transition.end, $.proxy(complete, this))
- .emulateTransitionEnd(350)
- }
-
- Collapse.prototype.toggle = function () {
- this[this.$element.hasClass('in') ? 'hide' : 'show']()
- }
-
-
- // COLLAPSE PLUGIN DEFINITION
- // ==========================
-
- var old = $.fn.collapse
-
- $.fn.collapse = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.collapse')
- var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
-
- if (!data && options.toggle && option == 'show') option = !option
- if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.collapse.Constructor = Collapse
-
-
- // COLLAPSE NO CONFLICT
- // ====================
-
- $.fn.collapse.noConflict = function () {
- $.fn.collapse = old
- return this
- }
-
-
- // COLLAPSE DATA-API
- // =================
-
- $(document).on('click.bs.collapse.data-api', '[data-toggle=collapse]', function (e) {
- var $this = $(this), href
- var target = $this.attr('data-target')
- || e.preventDefault()
- || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
- var $target = $(target)
- var data = $target.data('bs.collapse')
- var option = data ? 'toggle' : $this.data()
- var parent = $this.attr('data-parent')
- var $parent = parent && $(parent)
-
- if (!data || !data.transitioning) {
- if ($parent) $parent.find('[data-toggle=collapse][data-parent="' + parent + '"]').not($this).addClass('collapsed')
- $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
- }
-
- $target.collapse(option)
- })
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: dropdown.js v3.1.1
- * http://getbootstrap.com/javascript/#dropdowns
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // DROPDOWN CLASS DEFINITION
- // =========================
-
- var backdrop = '.dropdown-backdrop'
- var toggle = '[data-toggle=dropdown]'
- var Dropdown = function (element) {
- $(element).on('click.bs.dropdown', this.toggle)
- }
-
- Dropdown.prototype.toggle = function (e) {
- var $this = $(this)
-
- if ($this.is('.disabled, :disabled')) return
-
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
-
- clearMenus()
-
- if (!isActive) {
- if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
- // if mobile we use a backdrop because click events don't delegate
- $('
').insertAfter($(this)).on('click', clearMenus)
- }
-
- var relatedTarget = { relatedTarget: this }
- $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget))
-
- if (e.isDefaultPrevented()) return
-
- $parent
- .toggleClass('open')
- .trigger('shown.bs.dropdown', relatedTarget)
-
- $this.focus()
- }
-
- return false
- }
-
- Dropdown.prototype.keydown = function (e) {
- if (!/(38|40|27)/.test(e.keyCode)) return
-
- var $this = $(this)
-
- e.preventDefault()
- e.stopPropagation()
-
- if ($this.is('.disabled, :disabled')) return
-
- var $parent = getParent($this)
- var isActive = $parent.hasClass('open')
-
- if (!isActive || (isActive && e.keyCode == 27)) {
- if (e.which == 27) $parent.find(toggle).focus()
- return $this.click()
- }
-
- var desc = ' li:not(.divider):visible a'
- var $items = $parent.find('[role=menu]' + desc + ', [role=listbox]' + desc)
-
- if (!$items.length) return
-
- var index = $items.index($items.filter(':focus'))
-
- if (e.keyCode == 38 && index > 0) index-- // up
- if (e.keyCode == 40 && index < $items.length - 1) index++ // down
- if (!~index) index = 0
-
- $items.eq(index).focus()
- }
-
- function clearMenus(e) {
- $(backdrop).remove()
- $(toggle).each(function () {
- var $parent = getParent($(this))
- var relatedTarget = { relatedTarget: this }
- if (!$parent.hasClass('open')) return
- $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
- if (e.isDefaultPrevented()) return
- $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
- })
- }
-
- function getParent($this) {
- var selector = $this.attr('data-target')
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- var $parent = selector && $(selector)
-
- return $parent && $parent.length ? $parent : $this.parent()
- }
-
-
- // DROPDOWN PLUGIN DEFINITION
- // ==========================
-
- var old = $.fn.dropdown
-
- $.fn.dropdown = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.dropdown')
-
- if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)))
- if (typeof option == 'string') data[option].call($this)
- })
- }
-
- $.fn.dropdown.Constructor = Dropdown
-
-
- // DROPDOWN NO CONFLICT
- // ====================
-
- $.fn.dropdown.noConflict = function () {
- $.fn.dropdown = old
- return this
- }
-
-
- // APPLY TO STANDARD DROPDOWN ELEMENTS
- // ===================================
-
- $(document)
- .on('click.bs.dropdown.data-api', clearMenus)
- .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() })
- .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
- .on('keydown.bs.dropdown.data-api', toggle + ', [role=menu], [role=listbox]', Dropdown.prototype.keydown)
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: modal.js v3.1.1
- * http://getbootstrap.com/javascript/#modals
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // MODAL CLASS DEFINITION
- // ======================
-
- var Modal = function (element, options) {
- this.options = options
- this.$element = $(element)
- this.$backdrop =
- this.isShown = null
-
- if (this.options.remote) {
- this.$element
- .find('.modal-content')
- .load(this.options.remote, $.proxy(function () {
- this.$element.trigger('loaded.bs.modal')
- }, this))
- }
- }
-
- Modal.DEFAULTS = {
- backdrop: true,
- keyboard: true,
- show: true
- }
-
- Modal.prototype.toggle = function (_relatedTarget) {
- return this[!this.isShown ? 'show' : 'hide'](_relatedTarget)
- }
-
- Modal.prototype.show = function (_relatedTarget) {
- var that = this
- var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget })
-
- this.$element.trigger(e)
-
- if (this.isShown || e.isDefaultPrevented()) return
-
- this.isShown = true
-
- this.escape()
-
- this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
-
- this.backdrop(function () {
- var transition = $.support.transition && that.$element.hasClass('fade')
-
- if (!that.$element.parent().length) {
- that.$element.appendTo(document.body) // don't move modals dom position
- }
-
- that.$element
- .show()
- .scrollTop(0)
-
- if (transition) {
- that.$element[0].offsetWidth // force reflow
- }
-
- that.$element
- .addClass('in')
- .attr('aria-hidden', false)
-
- that.enforceFocus()
-
- var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })
-
- transition ?
- that.$element.find('.modal-dialog') // wait for modal to slide in
- .one($.support.transition.end, function () {
- that.$element.focus().trigger(e)
- })
- .emulateTransitionEnd(300) :
- that.$element.focus().trigger(e)
- })
- }
-
- Modal.prototype.hide = function (e) {
- if (e) e.preventDefault()
-
- e = $.Event('hide.bs.modal')
-
- this.$element.trigger(e)
-
- if (!this.isShown || e.isDefaultPrevented()) return
-
- this.isShown = false
-
- this.escape()
-
- $(document).off('focusin.bs.modal')
-
- this.$element
- .removeClass('in')
- .attr('aria-hidden', true)
- .off('click.dismiss.bs.modal')
-
- $.support.transition && this.$element.hasClass('fade') ?
- this.$element
- .one($.support.transition.end, $.proxy(this.hideModal, this))
- .emulateTransitionEnd(300) :
- this.hideModal()
- }
-
- Modal.prototype.enforceFocus = function () {
- $(document)
- .off('focusin.bs.modal') // guard against infinite focus loop
- .on('focusin.bs.modal', $.proxy(function (e) {
- if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
- this.$element.focus()
- }
- }, this))
- }
-
- Modal.prototype.escape = function () {
- if (this.isShown && this.options.keyboard) {
- this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) {
- e.which == 27 && this.hide()
- }, this))
- } else if (!this.isShown) {
- this.$element.off('keyup.dismiss.bs.modal')
- }
- }
-
- Modal.prototype.hideModal = function () {
- var that = this
- this.$element.hide()
- this.backdrop(function () {
- that.removeBackdrop()
- that.$element.trigger('hidden.bs.modal')
- })
- }
-
- Modal.prototype.removeBackdrop = function () {
- this.$backdrop && this.$backdrop.remove()
- this.$backdrop = null
- }
-
- Modal.prototype.backdrop = function (callback) {
- var animate = this.$element.hasClass('fade') ? 'fade' : ''
-
- if (this.isShown && this.options.backdrop) {
- var doAnimate = $.support.transition && animate
-
- this.$backdrop = $('
')
- .appendTo(document.body)
-
- this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) {
- if (e.target !== e.currentTarget) return
- this.options.backdrop == 'static'
- ? this.$element[0].focus.call(this.$element[0])
- : this.hide.call(this)
- }, this))
-
- if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
-
- this.$backdrop.addClass('in')
-
- if (!callback) return
-
- doAnimate ?
- this.$backdrop
- .one($.support.transition.end, callback)
- .emulateTransitionEnd(150) :
- callback()
-
- } else if (!this.isShown && this.$backdrop) {
- this.$backdrop.removeClass('in')
-
- $.support.transition && this.$element.hasClass('fade') ?
- this.$backdrop
- .one($.support.transition.end, callback)
- .emulateTransitionEnd(150) :
- callback()
-
- } else if (callback) {
- callback()
- }
- }
-
-
- // MODAL PLUGIN DEFINITION
- // =======================
-
- var old = $.fn.modal
-
- $.fn.modal = function (option, _relatedTarget) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.modal')
- var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option)
-
- if (!data) $this.data('bs.modal', (data = new Modal(this, options)))
- if (typeof option == 'string') data[option](_relatedTarget)
- else if (options.show) data.show(_relatedTarget)
- })
- }
-
- $.fn.modal.Constructor = Modal
-
-
- // MODAL NO CONFLICT
- // =================
-
- $.fn.modal.noConflict = function () {
- $.fn.modal = old
- return this
- }
-
-
- // MODAL DATA-API
- // ==============
-
- $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) {
- var $this = $(this)
- var href = $this.attr('href')
- var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
- var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
-
- if ($this.is('a')) e.preventDefault()
-
- $target
- .modal(option, this)
- .one('hide', function () {
- $this.is(':visible') && $this.focus()
- })
- })
-
- $(document)
- .on('show.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
- .on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: tooltip.js v3.1.1
- * http://getbootstrap.com/javascript/#tooltip
- * Inspired by the original jQuery.tipsy by Jason Frame
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // TOOLTIP PUBLIC CLASS DEFINITION
- // ===============================
-
- var Tooltip = function (element, options) {
- this.type =
- this.options =
- this.enabled =
- this.timeout =
- this.hoverState =
- this.$element = null
-
- this.init('tooltip', element, options)
- }
-
- Tooltip.DEFAULTS = {
- animation: true,
- placement: 'top',
- selector: false,
- template: '',
- trigger: 'hover focus',
- title: '',
- delay: 0,
- html: false,
- container: false
- }
-
- Tooltip.prototype.init = function (type, element, options) {
- this.enabled = true
- this.type = type
- this.$element = $(element)
- this.options = this.getOptions(options)
-
- var triggers = this.options.trigger.split(' ')
-
- for (var i = triggers.length; i--;) {
- var trigger = triggers[i]
-
- if (trigger == 'click') {
- this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
- } else if (trigger != 'manual') {
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
-
- this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
- this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
- }
- }
-
- this.options.selector ?
- (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
- this.fixTitle()
- }
-
- Tooltip.prototype.getDefaults = function () {
- return Tooltip.DEFAULTS
- }
-
- Tooltip.prototype.getOptions = function (options) {
- options = $.extend({}, this.getDefaults(), this.$element.data(), options)
-
- if (options.delay && typeof options.delay == 'number') {
- options.delay = {
- show: options.delay,
- hide: options.delay
- }
- }
-
- return options
- }
-
- Tooltip.prototype.getDelegateOptions = function () {
- var options = {}
- var defaults = this.getDefaults()
-
- this._options && $.each(this._options, function (key, value) {
- if (defaults[key] != value) options[key] = value
- })
-
- return options
- }
-
- Tooltip.prototype.enter = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
-
- clearTimeout(self.timeout)
-
- self.hoverState = 'in'
-
- if (!self.options.delay || !self.options.delay.show) return self.show()
-
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'in') self.show()
- }, self.options.delay.show)
- }
-
- Tooltip.prototype.leave = function (obj) {
- var self = obj instanceof this.constructor ?
- obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
-
- clearTimeout(self.timeout)
-
- self.hoverState = 'out'
-
- if (!self.options.delay || !self.options.delay.hide) return self.hide()
-
- self.timeout = setTimeout(function () {
- if (self.hoverState == 'out') self.hide()
- }, self.options.delay.hide)
- }
-
- Tooltip.prototype.show = function () {
- var e = $.Event('show.bs.' + this.type)
-
- if (this.hasContent() && this.enabled) {
- this.$element.trigger(e)
-
- if (e.isDefaultPrevented()) return
- var that = this;
-
- var $tip = this.tip()
-
- this.setContent()
-
- if (this.options.animation) $tip.addClass('fade')
-
- var placement = typeof this.options.placement == 'function' ?
- this.options.placement.call(this, $tip[0], this.$element[0]) :
- this.options.placement
-
- var autoToken = /\s?auto?\s?/i
- var autoPlace = autoToken.test(placement)
- if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
-
- $tip
- .detach()
- .css({ top: 0, left: 0, display: 'block' })
- .addClass(placement)
-
- this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
-
- var pos = this.getPosition()
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
-
- if (autoPlace) {
- var $parent = this.$element.parent()
-
- var orgPlacement = placement
- var docScroll = document.documentElement.scrollTop || document.body.scrollTop
- var parentWidth = this.options.container == 'body' ? window.innerWidth : $parent.outerWidth()
- var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight()
- var parentLeft = this.options.container == 'body' ? 0 : $parent.offset().left
-
- placement = placement == 'bottom' && pos.top + pos.height + actualHeight - docScroll > parentHeight ? 'top' :
- placement == 'top' && pos.top - docScroll - actualHeight < 0 ? 'bottom' :
- placement == 'right' && pos.right + actualWidth > parentWidth ? 'left' :
- placement == 'left' && pos.left - actualWidth < parentLeft ? 'right' :
- placement
-
- $tip
- .removeClass(orgPlacement)
- .addClass(placement)
- }
-
- var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
-
- this.applyPlacement(calculatedOffset, placement)
- this.hoverState = null
-
- var complete = function() {
- that.$element.trigger('shown.bs.' + that.type)
- }
-
- $.support.transition && this.$tip.hasClass('fade') ?
- $tip
- .one($.support.transition.end, complete)
- .emulateTransitionEnd(150) :
- complete()
- }
- }
-
- Tooltip.prototype.applyPlacement = function (offset, placement) {
- var replace
- var $tip = this.tip()
- var width = $tip[0].offsetWidth
- var height = $tip[0].offsetHeight
-
- // manually read margins because getBoundingClientRect includes difference
- var marginTop = parseInt($tip.css('margin-top'), 10)
- var marginLeft = parseInt($tip.css('margin-left'), 10)
-
- // we must check for NaN for ie 8/9
- if (isNaN(marginTop)) marginTop = 0
- if (isNaN(marginLeft)) marginLeft = 0
-
- offset.top = offset.top + marginTop
- offset.left = offset.left + marginLeft
-
- // $.fn.offset doesn't round pixel values
- // so we use setOffset directly with our own function B-0
- $.offset.setOffset($tip[0], $.extend({
- using: function (props) {
- $tip.css({
- top: Math.round(props.top),
- left: Math.round(props.left)
- })
- }
- }, offset), 0)
-
- $tip.addClass('in')
-
- // check to see if placing tip in new offset caused the tip to resize itself
- var actualWidth = $tip[0].offsetWidth
- var actualHeight = $tip[0].offsetHeight
-
- if (placement == 'top' && actualHeight != height) {
- replace = true
- offset.top = offset.top + height - actualHeight
- }
-
- if (/bottom|top/.test(placement)) {
- var delta = 0
-
- if (offset.left < 0) {
- delta = offset.left * -2
- offset.left = 0
-
- $tip.offset(offset)
-
- actualWidth = $tip[0].offsetWidth
- actualHeight = $tip[0].offsetHeight
- }
-
- this.replaceArrow(delta - width + actualWidth, actualWidth, 'left')
- } else {
- this.replaceArrow(actualHeight - height, actualHeight, 'top')
- }
-
- if (replace) $tip.offset(offset)
- }
-
- Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
- this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
- }
-
- Tooltip.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
-
- $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
- $tip.removeClass('fade in top bottom left right')
- }
-
- Tooltip.prototype.hide = function () {
- var that = this
- var $tip = this.tip()
- var e = $.Event('hide.bs.' + this.type)
-
- function complete() {
- if (that.hoverState != 'in') $tip.detach()
- that.$element.trigger('hidden.bs.' + that.type)
- }
-
- this.$element.trigger(e)
-
- if (e.isDefaultPrevented()) return
-
- $tip.removeClass('in')
-
- $.support.transition && this.$tip.hasClass('fade') ?
- $tip
- .one($.support.transition.end, complete)
- .emulateTransitionEnd(150) :
- complete()
-
- this.hoverState = null
-
- return this
- }
-
- Tooltip.prototype.fixTitle = function () {
- var $e = this.$element
- if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
- $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
- }
- }
-
- Tooltip.prototype.hasContent = function () {
- return this.getTitle()
- }
-
- Tooltip.prototype.getPosition = function () {
- var el = this.$element[0]
- return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : {
- width: el.offsetWidth,
- height: el.offsetHeight
- }, this.$element.offset())
- }
-
- Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
- return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
- placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
- /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
- }
-
- Tooltip.prototype.getTitle = function () {
- var title
- var $e = this.$element
- var o = this.options
-
- title = $e.attr('data-original-title')
- || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
-
- return title
- }
-
- Tooltip.prototype.tip = function () {
- return this.$tip = this.$tip || $(this.options.template)
- }
-
- Tooltip.prototype.arrow = function () {
- return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
- }
-
- Tooltip.prototype.validate = function () {
- if (!this.$element[0].parentNode) {
- this.hide()
- this.$element = null
- this.options = null
- }
- }
-
- Tooltip.prototype.enable = function () {
- this.enabled = true
- }
-
- Tooltip.prototype.disable = function () {
- this.enabled = false
- }
-
- Tooltip.prototype.toggleEnabled = function () {
- this.enabled = !this.enabled
- }
-
- Tooltip.prototype.toggle = function (e) {
- var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
- self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
- }
-
- Tooltip.prototype.destroy = function () {
- clearTimeout(this.timeout)
- this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
- }
-
-
- // TOOLTIP PLUGIN DEFINITION
- // =========================
-
- var old = $.fn.tooltip
-
- $.fn.tooltip = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tooltip')
- var options = typeof option == 'object' && option
-
- if (!data && option == 'destroy') return
- if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.tooltip.Constructor = Tooltip
-
-
- // TOOLTIP NO CONFLICT
- // ===================
-
- $.fn.tooltip.noConflict = function () {
- $.fn.tooltip = old
- return this
- }
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: popover.js v3.1.1
- * http://getbootstrap.com/javascript/#popovers
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // POPOVER PUBLIC CLASS DEFINITION
- // ===============================
-
- var Popover = function (element, options) {
- this.init('popover', element, options)
- }
-
- if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js')
-
- Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, {
- placement: 'right',
- trigger: 'click',
- content: '',
- template: ''
- })
-
-
- // NOTE: POPOVER EXTENDS tooltip.js
- // ================================
-
- Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype)
-
- Popover.prototype.constructor = Popover
-
- Popover.prototype.getDefaults = function () {
- return Popover.DEFAULTS
- }
-
- Popover.prototype.setContent = function () {
- var $tip = this.tip()
- var title = this.getTitle()
- var content = this.getContent()
-
- $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
- $tip.find('.popover-content')[ // we use append for html objects to maintain js events
- this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text'
- ](content)
-
- $tip.removeClass('fade top bottom left right in')
-
- // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do
- // this manually by checking the contents.
- if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide()
- }
-
- Popover.prototype.hasContent = function () {
- return this.getTitle() || this.getContent()
- }
-
- Popover.prototype.getContent = function () {
- var $e = this.$element
- var o = this.options
-
- return $e.attr('data-content')
- || (typeof o.content == 'function' ?
- o.content.call($e[0]) :
- o.content)
- }
-
- Popover.prototype.arrow = function () {
- return this.$arrow = this.$arrow || this.tip().find('.arrow')
- }
-
- Popover.prototype.tip = function () {
- if (!this.$tip) this.$tip = $(this.options.template)
- return this.$tip
- }
-
-
- // POPOVER PLUGIN DEFINITION
- // =========================
-
- var old = $.fn.popover
-
- $.fn.popover = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.popover')
- var options = typeof option == 'object' && option
-
- if (!data && option == 'destroy') return
- if (!data) $this.data('bs.popover', (data = new Popover(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.popover.Constructor = Popover
-
-
- // POPOVER NO CONFLICT
- // ===================
-
- $.fn.popover.noConflict = function () {
- $.fn.popover = old
- return this
- }
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: scrollspy.js v3.1.1
- * http://getbootstrap.com/javascript/#scrollspy
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // SCROLLSPY CLASS DEFINITION
- // ==========================
-
- function ScrollSpy(element, options) {
- var href
- var process = $.proxy(this.process, this)
-
- this.$element = $(element).is('body') ? $(window) : $(element)
- this.$body = $('body')
- this.$scrollElement = this.$element.on('scroll.bs.scroll-spy.data-api', process)
- this.options = $.extend({}, ScrollSpy.DEFAULTS, options)
- this.selector = (this.options.target
- || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
- || '') + ' .nav li > a'
- this.offsets = $([])
- this.targets = $([])
- this.activeTarget = null
-
- this.refresh()
- this.process()
- }
-
- ScrollSpy.DEFAULTS = {
- offset: 10
- }
-
- ScrollSpy.prototype.refresh = function () {
- var offsetMethod = this.$element[0] == window ? 'offset' : 'position'
-
- this.offsets = $([])
- this.targets = $([])
-
- var self = this
- var $targets = this.$body
- .find(this.selector)
- .map(function () {
- var $el = $(this)
- var href = $el.data('target') || $el.attr('href')
- var $href = /^#./.test(href) && $(href)
-
- return ($href
- && $href.length
- && $href.is(':visible')
- && [[ $href[offsetMethod]().top + (!$.isWindow(self.$scrollElement.get(0)) && self.$scrollElement.scrollTop()), href ]]) || null
- })
- .sort(function (a, b) { return a[0] - b[0] })
- .each(function () {
- self.offsets.push(this[0])
- self.targets.push(this[1])
- })
- }
-
- ScrollSpy.prototype.process = function () {
- var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
- var scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
- var maxScroll = scrollHeight - this.$scrollElement.height()
- var offsets = this.offsets
- var targets = this.targets
- var activeTarget = this.activeTarget
- var i
-
- if (scrollTop >= maxScroll) {
- return activeTarget != (i = targets.last()[0]) && this.activate(i)
- }
-
- if (activeTarget && scrollTop <= offsets[0]) {
- return activeTarget != (i = targets[0]) && this.activate(i)
- }
-
- for (i = offsets.length; i--;) {
- activeTarget != targets[i]
- && scrollTop >= offsets[i]
- && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
- && this.activate( targets[i] )
- }
- }
-
- ScrollSpy.prototype.activate = function (target) {
- this.activeTarget = target
-
- $(this.selector)
- .parentsUntil(this.options.target, '.active')
- .removeClass('active')
-
- var selector = this.selector +
- '[data-target="' + target + '"],' +
- this.selector + '[href="' + target + '"]'
-
- var active = $(selector)
- .parents('li')
- .addClass('active')
-
- if (active.parent('.dropdown-menu').length) {
- active = active
- .closest('li.dropdown')
- .addClass('active')
- }
-
- active.trigger('activate.bs.scrollspy')
- }
-
-
- // SCROLLSPY PLUGIN DEFINITION
- // ===========================
-
- var old = $.fn.scrollspy
-
- $.fn.scrollspy = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.scrollspy')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.scrollspy.Constructor = ScrollSpy
-
-
- // SCROLLSPY NO CONFLICT
- // =====================
-
- $.fn.scrollspy.noConflict = function () {
- $.fn.scrollspy = old
- return this
- }
-
-
- // SCROLLSPY DATA-API
- // ==================
-
- $(window).on('load', function () {
- $('[data-spy="scroll"]').each(function () {
- var $spy = $(this)
- $spy.scrollspy($spy.data())
- })
- })
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: tab.js v3.1.1
- * http://getbootstrap.com/javascript/#tabs
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // TAB CLASS DEFINITION
- // ====================
-
- var Tab = function (element) {
- this.element = $(element)
- }
-
- Tab.prototype.show = function () {
- var $this = this.element
- var $ul = $this.closest('ul:not(.dropdown-menu)')
- var selector = $this.data('target')
-
- if (!selector) {
- selector = $this.attr('href')
- selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
- }
-
- if ($this.parent('li').hasClass('active')) return
-
- var previous = $ul.find('.active:last a')[0]
- var e = $.Event('show.bs.tab', {
- relatedTarget: previous
- })
-
- $this.trigger(e)
-
- if (e.isDefaultPrevented()) return
-
- var $target = $(selector)
-
- this.activate($this.parent('li'), $ul)
- this.activate($target, $target.parent(), function () {
- $this.trigger({
- type: 'shown.bs.tab',
- relatedTarget: previous
- })
- })
- }
-
- Tab.prototype.activate = function (element, container, callback) {
- var $active = container.find('> .active')
- var transition = callback
- && $.support.transition
- && $active.hasClass('fade')
-
- function next() {
- $active
- .removeClass('active')
- .find('> .dropdown-menu > .active')
- .removeClass('active')
-
- element.addClass('active')
-
- if (transition) {
- element[0].offsetWidth // reflow for transition
- element.addClass('in')
- } else {
- element.removeClass('fade')
- }
-
- if (element.parent('.dropdown-menu')) {
- element.closest('li.dropdown').addClass('active')
- }
-
- callback && callback()
- }
-
- transition ?
- $active
- .one($.support.transition.end, next)
- .emulateTransitionEnd(150) :
- next()
-
- $active.removeClass('in')
- }
-
-
- // TAB PLUGIN DEFINITION
- // =====================
-
- var old = $.fn.tab
-
- $.fn.tab = function ( option ) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.tab')
-
- if (!data) $this.data('bs.tab', (data = new Tab(this)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.tab.Constructor = Tab
-
-
- // TAB NO CONFLICT
- // ===============
-
- $.fn.tab.noConflict = function () {
- $.fn.tab = old
- return this
- }
-
-
- // TAB DATA-API
- // ============
-
- $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
- e.preventDefault()
- $(this).tab('show')
- })
-
-}(jQuery);
-
-/* ========================================================================
- * Bootstrap: affix.js v3.1.1
- * http://getbootstrap.com/javascript/#affix
- * ========================================================================
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- * ======================================================================== */
-
-
-+function ($) {
- 'use strict';
-
- // AFFIX CLASS DEFINITION
- // ======================
-
- var Affix = function (element, options) {
- this.options = $.extend({}, Affix.DEFAULTS, options)
- this.$window = $(window)
- .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this))
- .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this))
-
- this.$element = $(element)
- this.affixed =
- this.unpin =
- this.pinnedOffset = null
-
- this.checkPosition()
- }
-
- Affix.RESET = 'affix affix-top affix-bottom'
-
- Affix.DEFAULTS = {
- offset: 0
- }
-
- Affix.prototype.getPinnedOffset = function () {
- if (this.pinnedOffset) return this.pinnedOffset
- this.$element.removeClass(Affix.RESET).addClass('affix')
- var scrollTop = this.$window.scrollTop()
- var position = this.$element.offset()
- return (this.pinnedOffset = position.top - scrollTop)
- }
-
- Affix.prototype.checkPositionWithEventLoop = function () {
- setTimeout($.proxy(this.checkPosition, this), 1)
- }
-
- Affix.prototype.checkPosition = function () {
- if (!this.$element.is(':visible')) return
-
- var scrollHeight = $(document).height()
- var scrollTop = this.$window.scrollTop()
- var position = this.$element.offset()
- var offset = this.options.offset
- var offsetTop = offset.top
- var offsetBottom = offset.bottom
-
- if (this.affixed == 'top') position.top += scrollTop
-
- if (typeof offset != 'object') offsetBottom = offsetTop = offset
- if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element)
- if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element)
-
- var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false :
- offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' :
- offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false
-
- if (this.affixed === affix) return
- if (this.unpin) this.$element.css('top', '')
-
- var affixType = 'affix' + (affix ? '-' + affix : '')
- var e = $.Event(affixType + '.bs.affix')
-
- this.$element.trigger(e)
-
- if (e.isDefaultPrevented()) return
-
- this.affixed = affix
- this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null
-
- this.$element
- .removeClass(Affix.RESET)
- .addClass(affixType)
- .trigger($.Event(affixType.replace('affix', 'affixed')))
-
- if (affix == 'bottom') {
- this.$element.offset({ top: scrollHeight - offsetBottom - this.$element.height() })
- }
- }
-
-
- // AFFIX PLUGIN DEFINITION
- // =======================
-
- var old = $.fn.affix
-
- $.fn.affix = function (option) {
- return this.each(function () {
- var $this = $(this)
- var data = $this.data('bs.affix')
- var options = typeof option == 'object' && option
-
- if (!data) $this.data('bs.affix', (data = new Affix(this, options)))
- if (typeof option == 'string') data[option]()
- })
- }
-
- $.fn.affix.Constructor = Affix
-
-
- // AFFIX NO CONFLICT
- // =================
-
- $.fn.affix.noConflict = function () {
- $.fn.affix = old
- return this
- }
-
-
- // AFFIX DATA-API
- // ==============
-
- $(window).on('load', function () {
- $('[data-spy="affix"]').each(function () {
- var $spy = $(this)
- var data = $spy.data()
-
- data.offset = data.offset || {}
-
- if (data.offsetBottom) data.offset.bottom = data.offsetBottom
- if (data.offsetTop) data.offset.top = data.offsetTop
-
- $spy.affix(data)
- })
- })
-
-}(jQuery);
diff --git a/li.strolch.website/www.strolch.li/js/bootstrap.min.js b/li.strolch.website/www.strolch.li/js/bootstrap.min.js
deleted file mode 100644
index b04a0e82f..000000000
--- a/li.strolch.website/www.strolch.li/js/bootstrap.min.js
+++ /dev/null
@@ -1,6 +0,0 @@
-/*!
- * Bootstrap v3.1.1 (http://getbootstrap.com)
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */
-if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});return this.$element.trigger(j),j.isDefaultPrevented()?void 0:(this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this)};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);!e&&f.toggle&&"show"==c&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(b){a(d).remove(),a(e).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('
').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;f.toggleClass("open").trigger("shown.bs.dropdown",h),e.focus()}return!1}},f.prototype.keydown=function(b){if(/(38|40|27)/.test(b.keyCode)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var f=c(d),g=f.hasClass("open");if(!g||g&&27==b.keyCode)return 27==b.which&&f.find(e).focus(),d.click();var h=" li:not(.divider):visible a",i=f.find("[role=menu]"+h+", [role=listbox]"+h);if(i.length){var j=i.index(i.filter(":focus"));38==b.keyCode&&j>0&&j--,40==b.keyCode&&j ').appendTo(document.body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),d&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;d?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()):b&&b()};var c=a.fn.modal;a.fn.modal=function(c,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},b.DEFAULTS,e.data(),"object"==typeof c&&c);f||e.data("bs.modal",f=new b(this,g)),"string"==typeof c?f[c](d):g.show&&f.show(d)})},a.fn.modal.Constructor=b,a.fn.modal.noConflict=function(){return a.fn.modal=c,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d=c.attr("href"),e=a(c.attr("data-target")||d&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(d)&&d},e.data(),c.data());c.is("a")&&b.preventDefault(),e.modal(f,this).one("hide",function(){c.is(":visible")&&c.focus()})}),a(document).on("show.bs.modal",".modal",function(){a(document.body).addClass("modal-open")}).on("hidden.bs.modal",".modal",function(){a(document.body).removeClass("modal-open")})}(jQuery),+function(a){"use strict";var b=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};b.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},b.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},b.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},b.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show()},b.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},b.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){if(this.$element.trigger(b),b.isDefaultPrevented())return;var c=this,d=this.tip();this.setContent(),this.options.animation&&d.addClass("fade");var e="function"==typeof this.options.placement?this.options.placement.call(this,d[0],this.$element[0]):this.options.placement,f=/\s?auto?\s?/i,g=f.test(e);g&&(e=e.replace(f,"")||"top"),d.detach().css({top:0,left:0,display:"block"}).addClass(e),this.options.container?d.appendTo(this.options.container):d.insertAfter(this.$element);var h=this.getPosition(),i=d[0].offsetWidth,j=d[0].offsetHeight;if(g){var k=this.$element.parent(),l=e,m=document.documentElement.scrollTop||document.body.scrollTop,n="body"==this.options.container?window.innerWidth:k.outerWidth(),o="body"==this.options.container?window.innerHeight:k.outerHeight(),p="body"==this.options.container?0:k.offset().left;e="bottom"==e&&h.top+h.height+j-m>o?"top":"top"==e&&h.top-m-j<0?"bottom":"right"==e&&h.right+i>n?"left":"left"==e&&h.left-i
'}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(a(c).is("body")?window:c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);{var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})}},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(b.RESET).addClass("affix");var a=this.$window.scrollTop(),c=this.$element.offset();return this.pinnedOffset=c.top-a},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"top"==this.affixed&&(e.top+=d),"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(b.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:c-h-this.$element.height()}))}}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery);
\ No newline at end of file
diff --git a/li.strolch.website/www.strolch.li/plc.html b/li.strolch.website/www.strolch.li/plc.html
deleted file mode 100644
index e54f06d07..000000000
--- a/li.strolch.website/www.strolch.li/plc.html
+++ /dev/null
@@ -1,617 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Strolch: PLC
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Overview
-
-
Using Strolch as a PLC has certain advantages and disadvantages. The following is a list of advantages:
-
- Same programming model and language for server and PLC
- PLC has the same privilege handling as in Strolch
- Simulating down to the PLC level is easily possible for easier testing of server logic
-
-
-
Of course using the Java language as a PLC has its limitations, we have manage to use it for customers and
- are satisfied with the result. What follows is a description in how to set up your own Strolch based PLC.
-
-
Checkout the code at GitHub
-
-
Architecture
-
Overview
-
-
The Strolch PLC architecture sees the Strolch Agent as the server, managing logical devices, i.e. multiple
- sensors and actors together and thus deciding on further steps. With this architecture multiple PLCs can be
- combined together in one agent for flow control.
-
-
PLC Architecture
-
-
On the agent side the two main classes are the PlcGwServerHandler
and the
- PlcGwService
-
The PlcGwServerHandler
handles connections from remote PLCs over WebSockets and sends the
- requests to these PLCs. A PlcGwService
instance will be notified and can then decide on an
- action. In an execution model with Activities
, the PlcNotificationListener
- interface can be implemented, or the PlcExecutionPolicy
can be directly extended.
-
-
On the PLC side, the PlcGwClientHandler
is optional if no agent is required. The PlcHandler
- initializes the model and connections. The Plc
class is Strolch agnostic and manages the
- connections and notifies PlcListener
instances on changes coming from the underlying
- connections. The PlcService
implementations implement business logic, and can also be notified
- on updates from connections.
-
-
Example set up
-
This example setup describes the movement of containers over conveyors. The conveyors have motors which can
- be started and stopped by a GPIO output pin controlled on a Raspberry Pi and each conveyor has a light
- barrier to detect the occupancy of a container and the Raspberry Pi detects this on GPIO input pins.
-
Further at each conveyor location is a barcode reader to read the ID of a container.
-
The general idea is that the PLC notifies a Strolch agent of changes, and only turns conveyors on, when the
- agent gives the command. Thus the agent handles business logic and the PLC controls the I/Os.
-
-
-
-
New Project
-
- First create a new Strolch Web project using the Strolch Maven archetype
-
- Now add the following Maven dependencies:
-
-<properties>
- <strolch.version>1.6.0-SNAPSHOT</strolch.version>
- <strolch.plc.version>0.1.0-SNAPSHOT</strolch.plc.version>
-</properties>
-
-<dependencyManagement>
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>li.strolch.bom</artifactId>
- <type>pom</type>
- <version>${strolch.version}</version>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>strolch-plc-bom</artifactId>
- <type>pom</type>
- <version>${strolch.plc.version}</version>
- <scope>import</scope>
- </dependency>
-</dependencyManagement>
-
-<dependencies>
- <!-- PLC -->
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>strolch-plc-core</artifactId>
- </dependency>
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>strolch-plc-rest</artifactId>
- </dependency>
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>strolch-plc-gw-client</artifactId>
- </dependency>
-</dependencies>
-
-
-
- Add a bower dependency: "strolch-wc-plc": "strolch-li/strolch-wc-plc#^0.3.4"
to src/main/webapp/bower.json
-
- After adding the dependeny, run gulp
in the webapp directory. Gulp should have been
- installed through the instructions from the development page .
-
-
-
- Now we need to add the PLC web views to our new project. This is added in the src/main/webapp/app/src/c-app.html
- file. Add the following:
-
-
-<!-- HTML Imports -->
-<link rel="import" href="../bower_components/strolch-wc-plc/strolch-wc-plc-connections.html">
-<link rel="import" href="../bower_components/strolch-wc-plc/strolch-wc-plc-logical-devices.html">
-
-<!-- Change default-page to plcLogicalDevices -->
-<c-app-routing id="appRouting"
- login-page="login"
- default-page="plcLogicalDevices"
- auth-valid="[[authTokenValid]]"
- page="{{page}}"
- route-tail="{{routeTail}}"
- use-hash-as-path></c-app-routing>
-
-<!-- Add the new pages in the iron-pages element: -->
-<template is="dom-if" if="[[equal(page, 'plcConnections')]]" restamp>
- <strolch-wc-plc-connections id="plcConnections"
- base-path="../"
- base-rest-path="[[baseRestPath]]"
- route="{{subroute}}"></strolch-wc-plc-connections>
-</template>
-<template is="dom-if" if="[[equal(page, 'plcLogicalDevices')]]" restamp>
- <strolch-wc-plc-logical-devices id="plcLogicalDevices"
- base-path="../"
- base-rest-path="[[baseRestPath]]"
- base-ws-path="[[baseWsPath]]"
- route="{{subroute}}"></strolch-wc-plc-logical-devices>
-</template>
-
-// add a new property to the WebSocket path for observing changes on the PLC
-wsObserverPath: {
- type: String,
- value: function () {
- return CustomWeb.baseWsPath + "/plc/observer";
- }
-}
-
-
-
- Don't forget to add the PLC Rest classes to your ResourceConfig
-
-@ApplicationPath("rest")
-public class RestfulApplication extends ResourceConfig {
-
- public RestfulApplication() {
-
- ...
-
- // strolch plc services
- packages(PlcConnectionsResource.class.getPackage().getName());
-
- ...
- }
-}
-
-
-
-
- Now we need to configure the PLC's runtime by modifying runtime/StrolchConfiguration.xml
- and adding the following:
-
-<!--
- This component configures the PlcHandler by
- loading the PlcConnections, PlcAddresses and PlcTelegrams
--->
-<Component>
- <name>PlcHandler</name>
- <api>li.strolch.plc.core.PlcHandler</api>
- <impl>li.strolch.plc.core.DefaultPlcHandler</impl>
- <depends>RealmHandler</depends>
- <Properties>
- <!-- The component handling the low level connections -->
- <plcClass>li.strolch.plc.core.hw.DefaultPlc</plcClass>
- </Properties>
-</Component>
-
-<!--
- This component handles registrations of the PlcServices, i.e. your PLC business logic
--->
-<Component>
- <name>PlcServiceInitializer</name>
- <api>li.strolch.plc.core.PlcServiceInitializer</api>
- <impl>li.strolch.plc.example.CustomPlcServiceInitializer</impl>
- <depends>PlcHandler</depends>
- <Properties>
- </Properties>
-</Component>
-
-<!--
- This component notifies a Strolch agent of changes on the PLC
- only if you have a Strolch server with a configured
- li.strolch.plc.gw.server.PlcServerWebSocketEndpoint ready to accept connections
--->
-<Component>
- <name>PlcGwClientHandler</name>
- <api>li.strolch.plc.gw.client.PlcGwClientHandler</api>
- <impl>li.strolch.plc.gw.client.PlcGwClientHandler</impl>
- <depends>PlcHandler</depends>
- <depends>PlcServiceInitializer</depends>
- <Properties>
- <plcId>plc-01</plcId>
- <gwUsername>plc-01</gwUsername>
- <gwPassword>plc-01</gwPassword>
- <gwServerUrl>ws://localhost:8080/agent/websocket/strolch/plc</gwServerUrl>
- </Properties>
-</Component>
-
-
-
- Now we add the custom classes we just declared.
-
- PlcServiceInitializer
-
-import java.util.ArrayList;
-import java.util.List;
-
-import li.strolch.plc.example.services.*;
-import li.strolch.agent.api.ComponentContainer;
-import li.strolch.plc.core.PlcHandler;
-import li.strolch.plc.core.PlcService;
-import li.strolch.plc.core.PlcServiceInitializer;
-
-public class CustomPlcServiceInitializer extends PlcServiceInitializer {
-
- public CustomPlcServiceInitializer(ComponentContainer container, String componentName) {
- super(container, componentName);
- }
-
- @Override
- protected List<PlcService> getPlcServices(PlcHandler plcHandler) {
- ArrayList<PlcService> plcServices = new ArrayList<>();
-
- StartupPlcService startupPlcService = new StartupPlcService(plcHandler);
- ConveyorPlcService conveyorPlcService = new ConveyorPlcService(plcHandler);
-
- plcServices.add(conveyorPlcService);
- plcServices.add(startupPlcService);
-
- return plcServices;
- }
-}
-
- PlcPostInitializer
-
-import li.strolch.agent.api.ComponentContainer;
-import li.strolch.plc.core.PlcPostInitializer;
-
-public class CustomPostInitializer extends PlcPostInitializer {
- public CustomPostInitializer(ComponentContainer container, String componentName) {
- super(container, componentName);
- }
-
- // override the initialize(), start(), stop() and destroy() methods as needed
-}
-
-
-
- In the CustomPlcServiceInitializer
we added two PlcServices, for which the code is
- missing. The following are simple examples:
-
- StartupPlcService
-
-import li.strolch.persistence.api.StrolchTransaction;
-import li.strolch.plc.core.PlcHandler;
-import li.strolch.plc.core.PlcService;
-
-public class StartupPlcService extends PlcService {
-
- public static final String PLC = "PLC";
- public static final String STARTED = "Started";
- public static final String STOPPED = "Stopped";
-
- public StartupPlcService(PlcHandler plcHandler) {
- super(plcHandler);
- }
-
- @Override
- public void start(StrolchTransaction tx) {
- send(PLC, STARTED);
- super.start(tx);
- }
-
- @Override
- public void stop() {
- send(PLC, STOPPED);
- super.stop();
- }
-}
-
- ConveyorPlcService
-
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import li.strolch.plc.core.PlcHandler;
-import li.strolch.plc.core.PlcService;
-import li.strolch.plc.model.PlcAddress;
-
-public class ConveyorPlcService extends PlcService {
-
- public static final int BOX_TRANSFER_DURATION = 30;
-
- private static final String R_CONVEYOR_01 = "Conveyor01";
- private static final String A_START_BUTTON = "StartButton";
- private static final String T_MOTOR_ON = "MotorOn";
- private static final String T_MOTOR_OFF = "MotorOff";
- private static final String A_BOX_DETECTED = "BoxDetected";
-
- private boolean motorOn;
- private ScheduledFuture<?> motorStopTask;
-
- public ConveyorPlcService(PlcHandler plcHandler) {
- super(plcHandler);
- }
-
- @Override
- public void handleNotification(PlcAddress address, Object value) {
- String resource = address.resource;
- String action = address.action;
-
- if (!resource.equals("Conveyor01"))
- throw new IllegalStateException("Unexpected resource " + resource);
-
- boolean active = (boolean) value;
-
- if (action.equals(A_START_BUTTON)) {
-
- if (active) {
- logger.info("Start button pressed. Starting motors...");
- send(R_CONVEYOR_01, T_MOTOR_ON);
- this.motorOn = true;
- scheduleStopTask();
- }
-
- } else if (action.equals(A_BOX_DETECTED)) {
-
- if (active && this.motorOn) {
- logger.info("Container detected, refreshing stop task...");
- scheduleStopTask();
- }
-
- } else {
- logger.info("Unhandled notification " + address.toKeyAddress());
- }
- }
-
- private void scheduleStopTask() {
- if (this.motorStopTask != null)
- this.motorStopTask.cancel(false);
- this.motorStopTask = schedule(this::stopMotor, BOX_TRANSFER_DURATION, TimeUnit.SECONDS);
- }
-
- private void stopMotor() {
- send(R_CONVEYOR_01, T_MOTOR_OFF);
- }
-
- @Override
- public void register() {
- this.plcHandler.register(R_CONVEYOR_01, A_START_BUTTON, this);
- this.plcHandler.register(R_CONVEYOR_01, A_BOX_DETECTED, this);
- super.register();
- }
-
- @Override
- public void unregister() {
- this.plcHandler.unregister(R_CONVEYOR_01, A_START_BUTTON, this);
- this.plcHandler.unregister(R_CONVEYOR_01, A_BOX_DETECTED, this);
- super.unregister();
- }
-}
-
-
-
- Now the last part is to add the model, i.e. PlcConnections, PlcAddresses and PlcTelegrams. To have
- less configuration files and make it easier to reconfigure at runtime, this data is stored in normal
- Strolch Resources
- In this example we will use simple Raspberry Pi GPIOs. For convenience, and also when sharing I/O
- definitions with external partners, it is easier to use a CSV file to define the I/Os and then use
- the PlcAddressGenerator
to generate and validate the model.
- For this purpose in this example, we will use one conveyor with 2 inputs and 1 output. The CSV file
- should have the following content:
-
-Description,Type,SubType,Device,Pin,Resource,Action1,Action2,Connection,DeviceId
-Material Flow,Group,,,,,,,,MaterialFlow
-Conveyor 1,Input,Pin,,4,Conveyor,Occupied,,raspiBcmGpioInput
-Conveyor 1,Input,Pin,,17,Conveyor,BoxDetected,,raspiBcmGpioInput
-Conveyor 1,Output,Pin,,18,Conveyor,MotorOn,MotorOff,raspiBcmGpioOutput
-
- The CSV headers are as follows:
-
- Description → a simple description for this PlcAddress
- Type →
-
- Group → Must be the first line and generates a PlcLogicalDevice, all succeeding
- lines are grouped to this device. Add additional to group further devices
-
- Input → defines a boolean input
- Output → defines a boolean output
- Virtual → defines a virtual address which has no corresponding hardware connection.
- Used for internal communication.
-
- DataLogicScanner → defines an address to read barcodes from a DataLogic Scanner.
- The actions must be left empty as the keys Barcode (address), On and Off (telegrams)
- will be generated.
-
-
-
- SubType →
-
- For Input and Output types →
-
- DevPin, DevPin0 → Generates the address as <Connection>.<Device>.<Pin>
.
- DevPin0 decrements the Device and Pin values by one.
-
- Pin → Generates the address as <Connection>.<Pin>
.
-
-
-
- For Virtual types →
-
-
-
-
- Device → Device number
- Pin → The pin number on the device
- Resource → The resource ID with which to notify the agent
- Action1 → The action ID
- Action2 → The second action ID if required
- Connection → The ID of the PlcConnection with which this I/O is attached
- DeviceId → For type Group: Set the ID of this PlcLogicalDevice being generated
-
-
- When you use this file as input for the PlcAddressGenerator
, then it will generate
- PlcLogicalDevice, PlcAddress and PlcTelegram elements:
-
-<Resource Id="D_MaterialFlow" Name="MaterialFlow" Type="PlcLogicalDevice">
- <ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
- <Parameter Id="description" Name="Description" Type="String" Value="Material Flow"/>
- <Parameter Id="group" Name="Group" Type="String" Value="01 Material Flow"/>
- <Parameter Id="index" Name="Index" Type="Integer" Value="10"/>
- </ParameterBag>
- <ParameterBag Id="relations" Name="Relations" Type="Relations">
- <Parameter Id="addresses" Name="Addresses" Type="StringList" Interpretation="Resource-Ref" Uom="PlcAddress" Value="A_Conveyor-Occupied, A_Conveyor-BoxDetected, A_Conveyor-MotorOn"/>
- <Parameter Id="telegrams" Name="Telegrams" Type="StringList" Interpretation="Resource-Ref" Uom="PlcTelegram" Value="T_Conveyor-MotorOn, T_Conveyor-MotorOff"/>
- </ParameterBag>
-</Resource>
-
-<Resource Id="A_Conveyor-Occupied" Name="Conveyor - Occupied" Type="PlcAddress">
- <ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
- <Parameter Id="description" Name="Description" Type="String" Index="5" Value="Conveyor 1"/>
- <Parameter Id="address" Name="HW Address" Type="String" Interpretation="PlcConnection" Index="10" Value="raspiBcmGpioInput.4"/>
- <Parameter Id="resource" Name="Resource ID for PlcAddress" Type="String" Index="20" Value="Conveyor"/>
- <Parameter Id="action" Name="Action ID for PlcAddress" Type="String" Index="30" Value="Occupied"/>
- <Parameter Id="index" Name="Index" Type="Integer" Index="40" Value="10"/>
- <Parameter Id="value" Name="Value" Type="Boolean" Index="100" Value="false"/>
- </ParameterBag>
-</Resource>
-
-<Resource Id="T_Conveyor-MotorOn" Name="Conveyor - MotorOn" Type="PlcTelegram">
- <ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
- <Parameter Id="description" Name="Description" Type="String" Index="5" Value="Conveyor 1"/>
- <Parameter Id="address" Name="HW Address" Type="String" Interpretation="PlcConnection" Index="10" Value="raspiBcmGpioOutput.18"/>
- <Parameter Id="resource" Name="Resource ID for PlcAddress" Type="String" Index="20" Value="Conveyor"/>
- <Parameter Id="action" Name="Action ID for PlcAddress" Type="String" Index="30" Value="MotorOn"/>
- <Parameter Id="index" Name="Index" Type="Integer" Index="40" Value="10"/>
- <Parameter Id="value" Name="Value" Type="Boolean" Index="100" Value="true"/>
- </ParameterBag>
-</Resource>
-
- The PlcLogicalDevice references the PlcAddress and PlcTelegram objects, and is then used in the UI
- for grouping.
- The PlcAddress is used to store the current value and defines the keys with which the agent will be
- notified
- The PlcTelegram is used to store default values to send, for specific keys. E.g. The action
- On
would send true, and Off
would send false. This is semantics, and is
- defined in each project depending on the hardware.
-
-
-
- Copy the file plc-state.xml
- to your runtime and reference it by use of a <IncludeFile file="plc-state.xml" />
- element. Modify the PlcId to be the same as the one you defined in the
- StrolchConfiguration.xml
.
-
-
- Now that we have a model, the PlcConnections are to be defined. In the previous example we used a
- Raspberry Pi's GPIOs. This needs to be defined as a PlcConnection:
-
-<Resource Id="raspiBcmGpioOutput" Name="Raspi BCM GPIO Output" Type="PlcConnection">
- <ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
- <Parameter Id="className" Name="Connection Class" Type="String" Value="li.strolch.plc.core.hw.gpio.RaspiBcmGpioOutputConnection"/>
- <Parameter Id="state" Name="Connection State" Type="String" Interpretation="Enumeration" Uom="ConnectionState" Value="Disconnected"/>
- <Parameter Id="stateMsg" Name="Connection State Msg" Type="String" Interpretation="Enumeration" Uom="ConnectionState"
- Value=""/>
- <Parameter Id="inverted" Name="Inverted" Type="Boolean" Value="false"/>
- <Parameter Id="bcmOutputPins" Name="BCM Output Pins" Type="IntegerList" Value="27"/>
- </ParameterBag>
-</Resource>
-<Resource Id="raspiBcmGpioInput" Name="Raspi BCM GPIO Input" Type="PlcConnection">
- <ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
- <Parameter Id="className" Name="Connection Class" Type="String" Value="li.strolch.plc.core.hw.gpio.RaspiBcmGpioInputConnection"/>
- <Parameter Id="state" Name="Connection State" Type="String" Interpretation="Enumeration" Uom="ConnectionState" Value="Disconnected"/>
- <Parameter Id="stateMsg" Name="Connection State Msg" Type="String" Interpretation="Enumeration" Uom="ConnectionState"
- Value=""/>
- <Parameter Id="inverted" Name="Inverted" Type="Boolean" Value="true"/>
- <Parameter Id="bcmInputPins" Name="BCM Input Pins" Type="IntegerList" Value="4"/>
- </ParameterBag>
-</Resource>
- See
- strolch-plc-example-connections.xml
- for further examples.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/tutorial-configuration.html b/li.strolch.website/www.strolch.li/tutorial-configuration.html
deleted file mode 100644
index 583de548c..000000000
--- a/li.strolch.website/www.strolch.li/tutorial-configuration.html
+++ /dev/null
@@ -1,893 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Strolch: Tutorial
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Previous: Start Next:
- Model
-
-
-
Let's start by creating a new Apache Maven project. We'll need a POM with the proper dependencies. We expect
- you to be familiar with Apache Maven, so we'll just show you a working POM file:
-
-
pom.xml
-
-<?xml version="1.0"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>li.strolch</groupId>
- <artifactId>strolch-bookshop</artifactId>
- <version>0.1.0-SNAPSHOT</version>
- <packaging>war</packaging>
-
- <name>strolch-bookshop</name>
- <description>Bookshop built on Strolch</description>
- <inceptionYear>2017</inceptionYear>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
- <buildTimestamp>${maven.build.timestamp}</buildTimestamp>
-
- <jdk.version>1.8</jdk.version>
-
- <jersey.version>2.25.1</jersey.version>
- <slf4j.version>1.7.25</slf4j.version>
- <logback.version>1.2.3</logback.version>
- <petitparser.version>2.1.0</petitparser.version>
- <hikaricp.version>2.7.1</hikaricp.version>
- <postgresql.version>42.1.4</postgresql.version>
- <gson.version>2.8.2</gson.version>
- <annotation.version>1.3.1</annotation.version>
- <javaxmail.version>1.6.0</javaxmail.version>
- <serverlet.version>3.1.0</serverlet.version>
- <jaxrs.api.version>2.1</jaxrs.api.version>
-
- <junit.version>4.12</junit.version>
- <hamcrest.version>1.3</hamcrest.version>
- <mockito.version>2.0.8-beta</mockito.version>
-
- <maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
- <maven-source-plugin.version>3.0.1</maven-source-plugin.version>
- <maven-jar-plugin.version>3.0.2</maven-jar-plugin.version>
- <maven-war-plugin.version>3.1.0</maven-war-plugin.version>
-
- <strolch.version>1.6.0-SNAPSHOT</strolch.version>
-
- <warFinalName>bookshop</warFinalName>
- <m2eclipse.wtp.contextRoot>${warFinalName}</m2eclipse.wtp.contextRoot>
- </properties>
-
- <dependencies>
-
- <!-- base -->
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>${slf4j.version}</version>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>${logback.version}</version>
- <scope>runtime</scope>
- </dependency>
-
- <!-- strolch -->
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>li.strolch.utils</artifactId>
- <version>${strolch.version}</version>
- </dependency>
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>li.strolch.privilege</artifactId>
- <version>${strolch.version}</version>
- </dependency>
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>li.strolch.model</artifactId>
- <version>${strolch.version}</version>
- </dependency>
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>li.strolch.agent</artifactId>
- <version>${strolch.version}</version>
- </dependency>
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>li.strolch.rest</artifactId>
- <version>${strolch.version}</version>
- </dependency>
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>li.strolch.service</artifactId>
- <version>${strolch.version}</version>
- </dependency>
- <dependency>
- <groupId>li.strolch</groupId>
- <artifactId>li.strolch.testbase</artifactId>
- <version>${strolch.version}</version>
- <scope>test</scope>
- </dependency>
-
- <!-- utils -->
- <dependency>
- <groupId>com.google.code.gson</groupId>
- <artifactId>gson</artifactId>
- <version>${gson.version}</version>
- </dependency>
-
- <!-- web -->
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-api</artifactId>
- <version>${serverlet.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>javax.ws.rs</groupId>
- <artifactId>javax.ws.rs-api</artifactId>
- <version>${jaxrs.api.version}</version>
- </dependency>
- <dependency>
- <groupId>org.glassfish.jersey.core</groupId>
- <artifactId>jersey-common</artifactId>
- <version>${jersey.version}</version>
- </dependency>
- <dependency>
- <groupId>org.glassfish.jersey.core</groupId>
- <artifactId>jersey-server</artifactId>
- <version>${jersey.version}</version>
- </dependency>
- <dependency>
- <groupId>org.glassfish.jersey.containers</groupId>
- <artifactId>jersey-container-servlet</artifactId>
- <version>${jersey.version}</version>
- </dependency>
-
- <!-- testing -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>${junit.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-core</artifactId>
- <version>${hamcrest.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-library</artifactId>
- <version>${hamcrest.version}</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <resources>
- <!-- filter properties files, and copy the rest -->
- <resource>
- <directory>src/main/resources</directory>
- <filtering>true</filtering>
- <includes>
- <include>**/*.properties</include>
- </includes>
- </resource>
- <resource>
- <directory>src/main/resources</directory>
- <filtering>false</filtering>
- <excludes>
- <exclude>**/*.properties</exclude>
- </excludes>
- </resource>
- </resources>
-
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>${maven-compiler-plugin.version}</version>
- <configuration>
- <source>${jdk.version}</source>
- <target>${jdk.version}</target>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-war-plugin</artifactId>
- <version>${maven-war-plugin.version}</version>
- <configuration>
- <failOnMissingWebXml>false</failOnMissingWebXml>
- <warName>${warFinalName}</warName>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
- <profiles>
- <!-- active when building on eitch's machines -->
- <profile>
- <id>m2e.eitchpc</id>
- <activation>
- <property>
- <name>user.name</name>
- <value>eitch</value>
- </property>
- <os>
- <family>unix</family>
- </os>
- </activation>
- <properties>
- <strolch.env>dev.eitchpc</strolch.env>
- </properties>
- </profile>
- </profiles>
-</project>
-
-
-
Now we need the rest of the directory structure:
-
-
-../strolch-bookshop/
- - src/main/java/
- - li/strolch/bookshop/
- - <!-- java classes -->
- - src/main/resources/
- - ENV.properties
- - appVersion.properties
- - logback.xml
- - src/main/webapp/WEB-INF/
- - StrolchBootstrap.xml
- - runtime
- - config/
- - PrivilegeConfig.xml
- - PrivilegeRoles.xml
- - PrivilegeUsers.xml
- - StrolchConfiguration.xml
- - StrolchPolicies.xml
- - data/
- - StrolchModel.xsd
- - defaultModel.xml
- - templates.xml
- - temp/
-
-
-
A few notes to the resource files:
-
- The ENV.properties
file is filtered by maven and the environment to load is written in it
- using the environment variable strolch.env.
-
- The appVersion.properties
file is also filtered by maven and allows to reflect on the
- version of this app at runtime.
-
- The logback.xml
file configures logging using SLF4j and Logback.
-
-
-
The StrolchBootstrap.xml
file is used to configure Strolch's environment and root directory. For
- a webapp it can be annoying to store Strolch's configuration inside the webapp, which is why we can define an
- absolute path where the configuration is kept. In the following example we keep it in the root of the
- sources:
-
-<?xml version="1.0" encoding="UTF-8"?>
-<StrolchBootstrap>
- <env id="dev.eitchpc" default="true">
- <root>/home/eitch/src/git/strolch-bookshop/runtime</root>
- <environment>dev</environment>
- </env>
-</StrolchBootstrap>
-
-
-
Here we define two environments, but the both redefine the environment to dev. This is because we want this
- app to start on two different machines with different user home directories. See the profiles in the POM as
- to how these environments are activated using a environment property strolch.env.
-
-
In this next step we'll create Strolch's configuration at the location we defined in the StrolchBootstrap.xml
- file. Strolch's configuration contains of three directories: config, data and temp. config contains static
- files which usually aren't changed, data contains model files in XML format and temp is used at runtime for
- any temporary files, e.g. storing active sessions.
-
-
The configuration as well as the model has been described on Strolch's documentation web page, we'll just
- provide you with the files for the bookshop:
-
-
PrivilegeConfig.xml
-
-<?xml version="1.0" encoding="UTF-8"?>
-<Privilege>
- <Container>
- <Parameters>
- <!-- parameters for the container itself -->
- <Parameter name="secretKey" value="45f251ce-d51f-4624-990a-8dcd5b181f0e"/>
- <Parameter name="secretSalt" value="4770a32d-1512-4891-9a63-362504932500"/>
- <Parameter name="persistSessions" value="true"/>
- <Parameter name="autoPersistOnUserChangesData" value="false"/>
- <Parameter name="privilegeConflictResolution" value="MERGE"/>
- </Parameters>
- <EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
- <Parameters>
- <Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512"/>
- <Parameter name="hashIterations" value="10000"/>
- <Parameter name="hashKeyLength" value="256"/>
- </Parameters>
- </EncryptionHandler>
- <PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler">
- <Parameters>
- <Parameter name="usersXmlFile" value="PrivilegeUsers.xml"/>
- <Parameter name="rolesXmlFile" value="PrivilegeRoles.xml"/>
- </Parameters>
- </PersistenceHandler>
- <UserChallengeHandler class="li.strolch.privilege.handler.MailUserChallengeHandler">
- </UserChallengeHandler>
- </Container>
- <Policies>
- <Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege"/>
- <Policy name="RoleAccessPrivilege" class="li.strolch.privilege.policy.RoleAccessPrivilege"/>
- <Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege"/>
- <Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
- </Policies>
-</Privilege>
-
-
-
PrivilegeRoles.xml
-
-<?xml version="1.0" encoding="UTF-8"?>
-<Roles>
- <Role name="User">
- <Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
- </Privilege>
-
- <Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
- <AllAllowed>true</AllAllowed>
- <Allow>li.strolch.bookshop.search.BookSearch</Allow>
- </Privilege>
-
- <Privilege name="GetResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="GetOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="GetActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="AddResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="AddOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="AddActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="UpdateResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="UpdateOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="UpdateActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="RemoveResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="RemoveOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="RemoveActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- </Role>
- <Role name="UserPrivileges">
- <Privilege name="PrivilegeSetUserLocale" policy="UserAccessPrivilege" />
- <Privilege name="PrivilegeSetUserPassword" policy="UserAccessPrivilege" />
- </Role>
-
- <!--
- Internal
- -->
- <Role name="StrolchAdmin">
- <Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
-
- <Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
-
- <Privilege name="GetResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="GetOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="GetActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="AddResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="AddOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="AddActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="UpdateResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="UpdateOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="UpdateActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="RemoveResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="RemoveOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="RemoveActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
-
- <Privilege name="PrivilegeAddUser" policy="UserAccessPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="PrivilegeSetUserPassword" policy="UserAccessPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- </Role>
-
- <Role name="agent">
- <Privilege name="li.strolch.privilege.handler.SystemAction" policy="DefaultPrivilege">
- <Allow>li.strolch.runtime.privilege.StrolchSystemAction</Allow>
- <Allow>li.strolch.runtime.privilege.StrolchSystemActionWithResult</Allow>
- <Allow>li.strolch.persistence.postgresql.PostgreSqlSchemaInitializer</Allow>
- </Privilege>
-
- <Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
-
- <Privilege name="li.strolch.model.query.StrolchQuery" policy="DefaultPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
-
- <Privilege name="GetResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="GetOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="GetActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="AddResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="AddOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="AddActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="UpdateResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="UpdateOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="UpdateActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="RemoveResource" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="RemoveOrder" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="RemoveActivity" policy="ModelPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
-
- <Privilege name="PrivilegeAction" policy="DefaultPrivilege">
- <Allow>Persist</Allow>
- <Allow>PersistSessions</Allow>
- <Allow>GetCertificates</Allow>
- </Privilege>
- <Privilege name="PrivilegeAddUser" policy="UserAccessPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="PrivilegeModifyUser" policy="UserAccessPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- <Privilege name="PrivilegeGetUser" policy="UserAccessPrivilege">
- <AllAllowed>true</AllAllowed>
- </Privilege>
- </Role>
-
-</Roles>
-
-
-
PrivilegeUsers.xml
-
-<?xml version="1.0" encoding="UTF-8"?>
-<Users>
- <User userId="U10" username="jill" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918">
- <Firstname>Jill</Firstname>
- <Lastname>Someone</Lastname>
- <State>ENABLED</State>
- <Locale>en-GB</Locale>
- <Roles>
- <Role>User</Role>
- <Role>UserPrivileges</Role>
- </Roles>
- <Properties>
- <Property name="email" value="eitch+jill@eitchnet.ch" />
- </Properties>
- </User>
-
- <User userId="U01" username="admin" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918">
- <Firstname>Jill</Firstname>
- <Lastname>Someone</Lastname>
- <State>ENABLED</State>
- <Locale>en-GB</Locale>
- <Roles>
- <Role>StrolchAdmin</Role>
- <Role>UserPrivileges</Role>
- </Roles>
- <Properties>
- <Property name="email" value="eitch+admin@eitchnet.ch" />
- </Properties>
- </User>
-
- <!--
- Internal
- -->
- <User userId="S01" username="agent">
- <State>SYSTEM</State>
- <Roles>
- <Role>agent</Role>
- </Roles>
- </User>
-
-</Users>
-
-
-
StrolchConfiguration.xml
-
-<?xml version="1.0" encoding="UTF-8"?>
-<StrolchConfiguration>
- <env id="global">
- <Runtime>
- <applicationName>Bookshop</applicationName>
- <Properties>
- <locale>en</locale>
- <verbose>true</verbose>
- </Properties>
- </Runtime>
-
- <Component>
- <name>PrivilegeHandler</name>
- <api>li.strolch.runtime.privilege.PrivilegeHandler</api>
- <impl>li.strolch.runtime.privilege.DefaultStrolchPrivilegeHandler</impl>
- <Properties>
- <privilegeConfigFile>PrivilegeConfig.xml</privilegeConfigFile>
- </Properties>
- </Component>
-
- <Component>
- <name>RealmHandler</name>
- <api>li.strolch.agent.api.RealmHandler</api>
- <impl>li.strolch.agent.impl.DefaultRealmHandler</impl>
- <depends>PrivilegeHandler</depends>
- <Properties>
- <realms>defaultRealm</realms>
-
- <dataStoreMode>TRANSIENT</dataStoreMode>
- <dataStoreFile>defaultModel.xml</dataStoreFile>
- <enableObserverUpdates>true</enableObserverUpdates>
- </Properties>
- </Component>
-
- <Component>
- <name>ServiceHandler</name>
- <api>li.strolch.service.api.ServiceHandler</api>
- <impl>li.strolch.service.api.DefaultServiceHandler</impl>
- <depends>RealmHandler</depends>
- <depends>PrivilegeHandler</depends>
- <Properties>
- <verbose>true</verbose>
- </Properties>
- </Component>
-
- <Component>
- <name>PolicyHandler</name>
- <api>li.strolch.policy.PolicyHandler</api>
- <impl>li.strolch.policy.DefaultPolicyHandler</impl>
- <Properties>
- <readPolicyFile>true</readPolicyFile>
- </Properties>
- </Component>
-
- <Component>
- <name>ExecutionHandler</name>
- <api>li.strolch.execution.ExecutionHandler</api>
- <impl>li.strolch.execution.EventBasedExecutionHandler</impl>
- <depends>RealmHandler</depends>
- <depends>PrivilegeHandler</depends>
- </Component>
-
- <Component>
- <name>RestfulHandler</name>
- <api>li.strolch.rest.RestfulStrolchComponent</api>
- <impl>li.strolch.rest.RestfulStrolchComponent</impl>
- <depends>SessionHandler</depends>
- <Properties>
- <secureCookie>false</secureCookie>
- <restLogging>false</restLogging>
- <restLoggingEntity>false</restLoggingEntity>
- <restTracing>ALL</restTracing>
- </Properties>
- </Component>
- <Component>
- <name>SessionHandler</name>
- <api>li.strolch.rest.StrolchSessionHandler</api>
- <impl>li.strolch.rest.DefaultStrolchSessionHandler</impl>
- <depends>PrivilegeHandler</depends>
- <Properties>
- <session.ttl.minutes>30</session.ttl.minutes>
- <session.reload>true</session.reload>
- </Properties>
- </Component>
-
- <Component>
- <name>MailHandler</name>
- <api>li.strolch.handler.mail.MailHandler</api>
- <impl>li.strolch.handler.mail.SmtpMailHandler</impl>
- <Properties>
- <fromAddr>relayer@eitchnet.ch</fromAddr>
- <fromName>Susi</fromName>
- <overrideRecipients>IPSC Test <eitch@eitchnet.ch></overrideRecipients>
- <recipientWhitelist>eitch@eitchnet.ch</recipientWhitelist>
- <username>test</username>
- <password>test</password>
- <auth>true</auth>
- <startTls>true</startTls>
- <host>smtp.gmail.com</host>
- <port>587</port>
- </Properties>
- </Component>
-
- </env>
-
- <env id="dev">
- <!-- overrides go here -->
- </env>
-
-</StrolchConfiguration>
-
-
-
StrolchPolicies.xml
-
-<StrolchPolicies>
- <PolicyType Type="ExecutionPolicy" Api="li.strolch.execution.policy.ExecutionPolicy">
- <Policy Key="DurationExecution" Class="li.strolch.execution.policy.DurationExecution" />
- <Policy Key="ReservationExection" Class="li.strolch.execution.policy.ReservationExection" />
- </PolicyType>
- <PolicyType Type="ConfirmationPolicy" Api="li.strolch.execution.policy.ConfirmationPolicy">
- <Policy Key="DefaultConfirmation" Class="li.strolch.execution.policy.ConfirmationPolicy" />
- </PolicyType>
- <PolicyType Type="ActivityArchivalPolicy" Api="li.strolch.execution.policy.ActivityArchivalPolicy">
- <Policy Key="DefaultActivityArchival" Class="li.strolch.execution.policy.ActivityArchivalPolicy" />
- </PolicyType>
-</StrolchPolicies>
-
-
-
A few notes on the configuration:
-
- Note how there are three users. Jill is a user with currently no privileges as it's role definition is
- empty. Admin can do everything, and the agent user is a system user which can also do everything.
-
- There is one realm defined in the RealmHandler
component which references the defaultModel.xml
- file in the data directory. This file then includes the currently still empty templates.xml
- file.
-
- We have defined a global environment, but are using the dev environment. The dev environment includes
- the definitions in the global environment.
-
- In PrivilegeConfig.xml
we have enabled persistence of sessions, so you will be needing the
- unlimited JCE libraries for your JVM and when you restart the server, you don't need to log back in, if
- your session is still alive.
-
- In PrivilegeRoles.xml
there seems to be a lot of boilerplate. One thing about a highly
- configurable system is that sometimes the configuration is bigger. In this case we have opted to have
- the configuration shown and not use default values which you don't see, so that privilege acces is
- clearly seen.
-
-
-
-
Your project is now ready to be imported into your favourite IDE. We have used both IntelliJ and Eclipse so
- this is up to you.
-
- Now that we have a configuration, it is time to have Strolch started when the WAR is deployed and started. In
- your IDE create a new class as follows:
-
-
StartupListener.java
-
-package li.strolch.bookshop.web;
-
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-import javax.servlet.annotation.WebListener;
-import java.io.InputStream;
-
-import li.strolch.agent.api.StrolchAgent;
-import li.strolch.agent.api.StrolchBootstrapper;
-import li.strolch.utils.helper.StringHelper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@WebListener
-public class StartupListener implements ServletContextListener {
-
- private static final Logger logger = LoggerFactory.getLogger(StartupListener.class);
- private static final String APP_NAME = "Bookshop";
-
- private StrolchAgent agent;
-
- @Override
- public void contextInitialized(ServletContextEvent sce) {
-
- logger.info("Starting " + APP_NAME + "...");
- long start = System.currentTimeMillis();
- try {
- String boostrapFileName = "/WEB-INF/" + StrolchBootstrapper.FILE_BOOTSTRAP;
- InputStream bootstrapFile = sce.getServletContext().getResourceAsStream(boostrapFileName);
- StrolchBootstrapper bootstrapper = new StrolchBootstrapper(StartupListener.class);
- this.agent = bootstrapper.setupByBoostrapFile(StartupListener.class, bootstrapFile);
- this.agent.initialize();
- this.agent.start();
- } catch (Throwable e) {
- logger.error("Failed to start " + APP_NAME + " due to: " + e.getMessage(), e);
- throw e;
- }
-
- long took = System.currentTimeMillis() - start;
- logger.info("Started " + APP_NAME + " in " + (StringHelper.formatMillisecondsDuration(took)));
- }
-
- @Override
- public void contextDestroyed(ServletContextEvent sce) {
- if (this.agent != null) {
- logger.info("Destroying " + APP_NAME + "...");
- try {
- this.agent.stop();
- this.agent.destroy();
- } catch (Throwable e) {
- logger.error("Failed to stop " + APP_NAME + " due to: " + e.getMessage(), e);
- throw e;
- }
- }
- logger.info("Destroyed " + APP_NAME);
- }
-}
-
-
-
Now configure your IDE to start the web project, and then once it has started, you should see the following
- in the logs:
-
-Bookshop:dev All 8 Strolch Components started. Took 44ms. Strolch is now ready to be used. Have fun =))
-
-
-
This log tells us the name of the app as defined in the StrolchConfiguration.xml file as well as which
- environment was loaded. Further we can see that 8 components were configured and started.
-
-
This concludes the initial setup of a new Strolch project. We can now go ahead and start building the
- business logic.
-
-
-
-
Previous: Start Next:
- Model
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/tutorial-crud-book.html b/li.strolch.website/www.strolch.li/tutorial-crud-book.html
deleted file mode 100644
index 6fe35149b..000000000
--- a/li.strolch.website/www.strolch.li/tutorial-crud-book.html
+++ /dev/null
@@ -1,623 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Strolch: Tutorial CRUD Book
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Previous: Model
-
-
Preparation
-
-
Since Books are central to the bookshop, we'll first create the CRUD
- REST API for them. The API will be as follows:
-
-
-GET ../rest/books?query=,offset=,limit=
-GET ../rest/books/{id}
-POST ../rest/books
-PUT ../rest/books/{id}
-DELETE ../rest/books/{id}
-
-
-
Thus corresponding with querying, getting, creating, updating and removing of books. So let's go ahead and
- add these REST APIs to our project.
-
-
Our project is using JAX-RS 2.0 as the API and Jersey 2.x as the implementation, thus first we need to
- configure JAX-RS. Thus create the following class:
-
-@ApplicationPath("rest")
-public class RestfulApplication extends ResourceConfig {
-
- public RestfulApplication() {
-
- // add strolch resources
- register(AuthenticationService.class);
- register(ModelQuery.class);
- register(Inspector.class);
-
- // add project resources by package name
- packages(BooksResource.class.getPackage().getName());
-
- // filters
- register(AuthenticationRequestFilter.class, Priorities.AUTHENTICATION);
- register(AccessControlResponseFilter.class);
- register(AuthenticationResponseFilter.class);
- register(HttpCacheResponseFilter.class);
-
- // log exceptions and return them as plain text to the caller
- register(StrolchRestfulExceptionMapper.class);
-
- // the JSON generated is in UTF-8
- register(CharsetResponseFilter.class);
-
- RestfulStrolchComponent restfulComponent = RestfulStrolchComponent.getInstance();
- if (restfulComponent.isRestLogging()) {
- register(new LoggingFeature(java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME),
- Level.SEVERE, LoggingFeature.Verbosity.PAYLOAD_ANY, Integer.MAX_VALUE));
-
- property(ServerProperties.TRACING, "ALL");
- property(ServerProperties.TRACING_THRESHOLD, "TRACE");
- }
- }
-}
-
-
As we add new resources they will be automatically since we register the entire package.
-
-
Now add the books resource class:
-
-@Path("books")
-public class BooksResource {
-
-}
-
-
-
Search
-
-
The first service we'll add is to query, or search for the existing books. The API defines three parameters,
- with which the result can be controlled. The method can be defined as follows:
-
-
-@GET
-@Produces(MediaType.APPLICATION_JSON)
-public Response query(@Context HttpServletRequest request, @QueryParam("query") String queryS,
- @QueryParam("offset") String offsetS, @QueryParam("limit") String limitS) {
-
- // TODO
-}
-
-
-
To fill this method we need a few things. First let's define a constants class where we keep String constants
- which we used in the model file:
-
-public class BookShopConstants {
-
- public static final String TYPE_BOOK = "Book";
-
-}
-
-
-
As this tutorial progresses, more and more constants will be added here. This class helps with two issues:
- Through the constants we can easily reason over where certain fields, and types are used and of course String
- literals in code are a rather bad thing.
-
-
In Strolch there are multiple way to access objects. The old way was using Queries, the new search API is
- much more fluent and easier to read and write. The search API, as well as the deprecated query API allows us
- to implement privilege validation and thus one should create corresponding classes for each type of search.
- Book entities are Resources, thus we will be creating a ResourceSearch
. The search is for
- Resources of type Book thus the resulting search looks as follows:
-
-public class BooksSearch<U> extends ResourceSearch<U> {
- public BookSearch() {
- types(TYPE_BOOK);
- }
-
- public BookSearch stringQuery(String value) {
- if (isEmpty(value))
- return this;
-
- // split by spaces
- value = value.trim();
- String[] values = value.split(" ");
-
- // add where clauses for id, name and description
- where(id().containsIgnoreCase(values) //
- .or(name().containsIgnoreCase(values)) //
- .or(param(BAG_PARAMETERS, PARAM_DESCRIPTION).containsIgnoreCase(values)));
-
- return this;
- }
-}
-
-
-
Note how we added a special method stringQuery(String)
- this method defines where a search
- string entered by the user will be used to match a book. In this case for ID
, name
- and the description
parameter.
-
-
So that our users can call this query, we must give them this as a privilege. This is done by adding the full
- class name to the PrivilegeRoles.xml
file as follows:
-
-
-...
- <Role name="User">
- <Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
- <Allow>internal</Allow>
- <Allow>li.strolch.bookshop.search.BookSearch</Allow>
- </Privilege>
- </Role>
-...
-
-
-
Note: The internal
allow value is a special privilege which is used internally when a
- service or something performs internal queries. This means that a service can perform a query
- for object to which the user might not have access, but without which the service could not be
- completed. We will use this in a later stage.
-
-
Now we have all parts we need to implement the query method. The method will include opening a transaction,
- instantiating the search, executing the search, and returning the result:
-
-@Path("books")
-public class BooksResource {
-
- @GET
- @Produces(MediaType.APPLICATION_JSON)
- public Response query(@Context HttpServletRequest request, @QueryParam("query") String queryS,
- @QueryParam("offset") String offsetS, @QueryParam("limit") String limitS) {
-
- // this is an authenticated method call, thus we can get the certificate from the request:
- Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE);
-
- int offset = StringHelper.isNotEmpty(offsetS) ? Integer.valueOf(offsetS) : 0;
- int limit = StringHelper.isNotEmpty(limitS) ? Integer.valueOf(limitS) : 0;
-
- // open the TX with the certificate, using this class as context
- Paging<Resource> paging;
- try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, getClass())) {
-
- // perform a book search
- paging = new BookSearch() //
- .stringQuery(queryS) //
- .search(tx) //
- .orderByName(false) //
- .toPaging(offset, limit);
- }
-
- ResourceVisitor<JsonObject> visitor = new StrolchRootElementToJsonVisitor().flat().asResourceVisitor();
- return ResponseUtil.toResponse(paging, e -> e.accept(visitor));
- }
-}
-
-
-
Note: We automatically transform the Resource objects to JSON using the StrolchElementToJsonVisitor
.
- By calling the method .flat()
we have a more compact JSON format. Paging is handled
- by a util class.
-
-
The helper class ResponseUtil
takes care of creating the JsonObject and the proper page. As a
- rule we use the format where we return two fields: msg
is a dash if all is ok, otherwise an
- error message will be present. Data is always in the data
field. This is just a personal taste,
- and can be changed to one's own taste.
-
-
Get
-
- We have all we need now to implement the GET method:
-
-
-@GET
-@Path("{id}")
-@Produces(MediaType.APPLICATION_JSON)
-public Response get(@Context HttpServletRequest request, @PathParam("id") String id) {
-
- // this is an authenticated method call, thus we can get the certificate from the request:
- Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE);
-
- // open the TX with the certificate, using this class as context
- try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, getClass())) {
-
- // get the book
- Resource book = tx.getResourceBy(BookShopConstants.TYPE_BOOK, id);
- if (book == null)
- return ResponseUtil.toResponse(Status.NOT_FOUND, "Book " + id + " does not exist!");
-
- // transform to JSON
- JsonObject bookJ = book.accept(new StrolchRootElementToJsonVisitor().flat());
-
- // return
- return ResponseUtil.toResponse(StrolchRestfulConstants.DATA, bookJ);
- }
-}
-
-
-
Note how we simply retrieve the book as a Resource from the TX. This is a good moment to familiarize yourself
- with the API of the StrolchTransaction
. There are methods to retrieve elements, and also perform
- queries. We will use more of these methods later.
-
-
Further it can be noted that a simple retrieval isn't validated against the user's privileges, the user is
- authenticated, which is enough for the moment.
-
-
Create
-
- To create a new book we need to implement a
Service
. This service will be called
CreateBookService
.
- A Service always has a
ServiceArgument
and a
ServiceResult
. Our service will use the
-
JsonServiceArgument
and the
JsonServiceResult
. The implementation of the POST method
- is as follows:
-
-
-@POST
-@Produces(MediaType.APPLICATION_JSON)
-public Response create(@Context HttpServletRequest request, String data) {
-
- // this is an authenticated method call, thus we can get the certificate from the request:
- Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE);
-
- // parse data to JSON
- JsonObject jsonData = JsonParser.parseString(data).getAsJsonObject();
-
- // instantiate the service with the argument
- CreateBookService svc = new CreateBookService();
- JsonServiceArgument arg = svc.getArgumentInstance();
- arg.jsonElement = jsonData;
-
- // perform the service
- ServiceHandler serviceHandler = RestfulStrolchComponent.getInstance().getServiceHandler();
- JsonServiceResult result = serviceHandler.doService(cert, svc, arg);
-
- // return depending on the result state
- if (result.isOk())
- return ResponseUtil.toResponse(StrolchRestfulConstants.DATA, result.getResult());
- return ResponseUtil.toResponse(result);
-}
-
-
-
Note: We return the created object again as JSON in its own data field.
-
- The service is implemented as follows:
-
-public class CreateBookService extends AbstractService<JsonServiceArgument, JsonServiceResult> {
-
- @Override
- protected JsonServiceResult getResultInstance() {
- return new JsonServiceResult();
- }
-
- @Override
- public JsonServiceArgument getArgumentInstance() {
- return new JsonServiceArgument();
- }
-
- @Override
- protected JsonServiceResult internalDoService(JsonServiceArgument arg) throws Exception {
-
- // open a new transaction, using the realm from the argument, or the certificate
- Resource book;
- try (StrolchTransaction tx = openArgOrUserTx(arg)) {
-
- // get a new book "instance" from the template
- book = tx.getResourceTemplate(BookShopConstants.TYPE_BOOK);
-
- // map all values from the JSON object into the new book element
- book.accept(new FromFlatJsonVisitor(arg.jsonElement.getAsJsonObject()).ignoreBag(BAG_RELATIONS));
-
- // save changes
- tx.add(book);
-
- // notify the TX that it should commit on close
- tx.commitOnClose();
- }
-
- // map the return value to JSON
- JsonObject result = book.accept(new StrolchElementToJsonVisitor().flat());
-
- // and return the result
- return new JsonServiceResult(result);
- }
-}
-
-
-
-
Note: For the authenticated user to be able to perform this service, we must add it to their
- privileges:
-
-...
- <Role name="User">
- ...
- <Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
- <Allow>li.strolch.bookshop.service.CreateBookService</Allow>
- </Privilege>
- ...
- </Role>
-...
-
-
-
Update
-
-
Updating of a book is basically the same as the creation, we just use PUT, verify that the book exists and
- give the user the privilege.
-
-
PUT Method:
-
-@PUT
-@Path("{id}")
-@Produces(MediaType.APPLICATION_JSON)
-public Response update(@Context HttpServletRequest request, @PathParam("id") String id, String data) {
-
- // this is an authenticated method call, thus we can get the certificate from the request:
- Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE);
-
- // parse data to JSON
- JsonObject jsonData = JsonParser.parseString(data).getAsJsonObject();
-
- // instantiate the service with the argument
- UpdateBookService svc = new UpdateBookService();
- JsonServiceArgument arg = svc.getArgumentInstance();
- arg.objectId = id;
- arg.jsonElement = jsonData;
-
- // perform the service
- ServiceHandler serviceHandler = RestfulStrolchComponent.getInstance().getServiceHandler();
- JsonServiceResult result = serviceHandler.doService(cert, svc, arg);
-
- // return depending on the result state
- if (result.isOk())
- return ResponseUtil.toResponse(StrolchRestfulConstants.DATA, result.getResult());
- return ResponseUtil.toResponse(result);
-}
-
-
-
Update Service:
-
-public class UpdateBookService extends AbstractService<JsonServiceArgument, JsonServiceResult> {
-
- @Override
- protected JsonServiceResult getResultInstance() {
- return new JsonServiceResult();
- }
-
- @Override
- public JsonServiceArgument getArgumentInstance() {
- return new JsonServiceArgument();
- }
-
- @Override
- protected JsonServiceResult internalDoService(JsonServiceArgument arg) throws Exception {
-
- // verify same book
- DBC.PRE.assertEquals("ObjectId and given Id must be same!", arg.objectId,
- arg.jsonElement.getAsJsonObject().get(Json.ID).getAsString());
-
- // open a new transaction, using the realm from the argument, or the certificate
- Resource book;
- try (StrolchTransaction tx = openArgOrUserTx(arg)) {
-
- // get the existing book
- book = tx.getResourceBy(BookShopConstants.TYPE_BOOK, arg.objectId, true);
-
- // map all values from the JSON object into the new book element
- book.accept(new FromFlatJsonVisitor(arg.jsonElement.getAsJsonObject()).ignoreBag(BAG_RELATIONS));
-
- // save changes
- tx.update(book);
-
- // notify the TX that it should commit on close
- tx.commitOnClose();
- }
-
- // map the return value to JSON
- JsonObject result = book.accept(new StrolchElementToJsonVisitor().flat());
-
- // and return the result
- return new JsonServiceResult(result);
- }
-}
-
-
-
Privilege:
-
-...
- <Role name="User">
- ...
- <Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
- ...
- <Allow>li.strolch.bookshop.service.UpdateBookService</Allow>
- ...
- </Privilege>
- ...
- </Role>
-...
-
-
-
-
Remove
-
-
To remove a book, we need a DELETE method, a remove service and the associated privilege.
-
-
-
DELETE Method:
-
-@DELETE
-@Path("{id}")
-@Produces(MediaType.APPLICATION_JSON)
-public Response update(@Context HttpServletRequest request, @PathParam("id") String id) {
-
- // this is an authenticated method call, thus we can get the certificate from the request:
- Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE);
-
- // instantiate the service with the argument
- RemoveBookService svc = new RemoveBookService();
- StringServiceArgument arg = svc.getArgumentInstance();
- arg.value = id;
-
- // perform the service
- ServiceHandler serviceHandler = RestfulStrolchComponent.getInstance().getServiceHandler();
- ServiceResult result = serviceHandler.doService(cert, svc, arg);
-
- // return depending on the result state
- return ResponseUtil.toResponse(result);
-}
-
-
-
Remove Service:
-
-public class RemoveBookService extends AbstractService<StringServiceArgument, ServiceResult> {
-
- @Override
- protected ServiceResult getResultInstance() {
- return new ServiceResult();
- }
-
- @Override
- public StringServiceArgument getArgumentInstance() {
- return new StringServiceArgument();
- }
-
- @Override
- protected ServiceResult internalDoService(StringServiceArgument arg) throws Exception {
-
- // open a new transaction, using the realm from the argument, or the certificate
- try (StrolchTransaction tx = openArgOrUserTx(arg)) {
-
- // get the existing book
- Resource book = tx.getResourceBy(BookShopConstants.TYPE_BOOK, arg.value, true);
-
- // save changes
- tx.remove(book);
-
- // notify the TX that it should commit on close
- tx.commitOnClose();
- }
-
- // and return the result
- return ServiceResult.success();
- }
-}
-
-
-
Privilege:
-
-...
- <Role name="User">
- ...
- <Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
- ...
- <Allow>li.strolch.bookshop.service.RemoveBookService</Allow>
- ...
- </Privilege>
- ...
- </Role>
-...
-
-
-
Notes:
-
One should now see a pattern emerge:
-
- The REST API delegates to the Services, or Searches, with the exception of the retrieval of a single
- object by id.
-
- Services should do initial validation of the input. Not much validation was done here, but more could be
- done.
-
- Commands are reusable objects to perform recurring work.
- Searches and Services are privileged actions for which a user must have the privilege to perform the
- action.
-
-
-
-
The book services are quite simple, but as more requirements arise, it should be easy to implement them in
- the service layer. Thus should a service be required to be performed by an integration layer, then they can
- simply call the services, since the input is defined and validation is done there (i.e. NOT in the REST
- API).
-
-
This concludes the CRUD of books.
-
-
Previous: Model
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/tutorial-model.html b/li.strolch.website/www.strolch.li/tutorial-model.html
deleted file mode 100644
index db63661c4..000000000
--- a/li.strolch.website/www.strolch.li/tutorial-model.html
+++ /dev/null
@@ -1,276 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Strolch: Tutorial Model
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Previous:
- Configuration Next: CRUD
- Books
-
-
-
Looking back at our functionality, we can list the following entities that need to be modelled (We'll go into
- detail further down):
-
- Book → books can be orderd
- UserCart → we want to store the cart of the user
- Account → we need to know where to send the orders
- PurchaseOrder → we need to know what was ordered and keep track of its state
- FromStock → we want to use activities to implement the process of an order
-
-
-
In Strolch we model entities by defining the element as a template. Thus in the templates.xml
- file we can add the templates with the following content:
-
-
Book
-
-<Resource Id="Book" Name="Book Template" Type="Template">
- <ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
- <Parameter Id="description" Name="Description" Type="String" Value="" />
- <Parameter Id="quantity" Name="Quantity in Stock" Type="Integer" Value="0" />
- </ParameterBag>
-</Resource>
-
-
-
Account
-
-<Resource Id="Account" Name="Account Template" Type="Template">
- <ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
- <Parameter Id="user" Name="User" Type="String" Value="" />
- <Parameter Id="firstName" Name="First Name" Type="String" Value="" />
- <Parameter Id="lastName" Name="Last Name" Type="String" Value="" />
- <Parameter Id="email" Name="E-Mail" Type="String" Value="" />
- </ParameterBag>
- <ParameterBag Name="Address" Id="address" Type="Address">
- <Parameter Id="phone" Name="Telephone Number" Type="String" Value="" />
- <Parameter Id="street" Name="Street" Type="String" Value="" />
- <Parameter Id="city" Name="City" Type="String" Value="" />
- <Parameter Id="zip" Name="Postal Code" Type="String" Value="" />
- <Parameter Id="country" Name="Country" Type="String" Value="" />
- </ParameterBag>
-</Resource>
-
-
-
UserCart
-
-<Resource Id="UserCart" Name="UserCart Template" Type="Template">
- <ParameterBag Id="books" Name="Books" Type="Book">
- <!-- Parameter Id="bookId" Name="Book reference" Type="Float" Value="0" / -->
- </ParameterBag>
- <ParameterBag Id="relations" Name="Relations" Type="Parameters">
- <Parameter Id="account" Name="Account" Type="String" Interpretation="Resource-Ref" Uom="Account" Value="" />
- </ParameterBag>
-</Resource>
-
-
-
PurchaseOrder
-
-<Order Id="PurchaseOrder" Name="PurchaseOrder Template" Type="Template" State="Created">
- <ParameterBag Id="books" Name="Books" Type="Book">
- <!-- Parameter Id="bookId" Name="Book reference" Type="Float" Value="0" / -->
- </ParameterBag>
- <ParameterBag Id="relations" Name="Relations" Type="Parameters">
- <Parameter Id="account" Name="Account" Type="String" Interpretation="Resource-Ref" Uom="Account" Value="" />
- </ParameterBag>
-</Order>
-
-
-
FromStock
-
-<Activity Id="FromStock" Name="From Stock Template" Type="FromStock" TimeOrdering="Series">
- <ParameterBag Name="objectives" Id="Objectives" Type="Objectives">
- <Parameter Name="Duration" Id="duration" Value="PT1MS" Type="Duration" />
- </ParameterBag>
-
- <Action Id="validate" Name="Validation of order" Type="Use" ResourceType="Validation" ResourceId="validation" />
-
- <!-- for each book we do a consume, i.e. reduce the stock quantity -->
- <Action Id="Consume" Name="Consume Template for book" Type="Template">
- <ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
- <Parameter Id="quantity" Name="Quantity" Type="Float" Value="0" />
- </ParameterBag>
- </Action>
-
- <Action Id="package" Name="Packaging of PurchaseOrder" Type="Use" ResourceType="Packaging" ResourceId="packaging" />
- <Action Id="send" Name="Sending of package" Type="Use" ResourceType="Sending" ResourceId="sending" />
-
-</Activity>
-
-
-
Let's explain a few things:
-
- The Book
entity is a Resource
object and only contains the description and the
- current quantity in stock.
-
- The Account
entity is a Resource and contains the address and further details of the user,
- and with the user
parameter the username is defined, thus referencing the real user.
-
- The UserCart
entity is a Resource and has a reference to the account Resource. Note how the
- reference is done using a StringParameter, where Interpretation, UOM and the value is set in a specific
- manner.
-
- The UserCart
entity is a Resource and references books using a special
- ParameterBag
with the type set to Book
, the actual type of the book entity.
- Each Parameter is of type Float
and the ID of the parameter is the ID of the book, and the
- value is the quantity that the user would like to purchase. There will only be one cart per
- user/account.
-
- The PurchaseOrder
entity is an Order
object, and is basically a copy of the
- UserCart entity. This is the confirmed purchase order for the contents of a cart, and can then be used
- for reports on how much of which book was sold.
-
- The FromStock
entity is an Activity
object and defines the process we will go
- through when delivering a purchase to a user. Note how the activity has a ParameterBag
- objectives
with a duration
parameter. This defines globally for this activity
- how long each Action
should execute. This can be overridden in each Action and can help to
- plan how much effort goes into the delivering of each PurchaseOrder.
-
- Further note how the activity has three special actions (validate
, package
and
- send
) on which a ResourceType
and ResourceId
are defined. Actions
- are always performed on a Resource, as the referenced Resource defines the behaviour of the action
- through defined Policy
objects.
-
- For each book which will be purchased, an Action will be created of type Consume
. In the
- template this is defined by a template Action with the id Consume
and will later be changed
- accordingly.
-
-
-
-
Since we are referencing resources from actions in the activity, we need to add these as well, but not as
- templates. They can be added to the defaultModel.xml
file:
-
-<Resource Id="validation" Name="Validation Resource" Type="Validation">
- <Policies>
- <Policy Type="ExecutionPolicy" Value="key:ValidationExecution" />
- <Policy Type="ConfirmationPolicy" Value="key:DefaultConfirmation" />
- </Policies>
-</Resource>
-
-<Resource Id="packaging" Name="Packaging Resource" Type="Packaging">
- <Policies>
- <Policy Type="ExecutionPolicy" Value="key:PackagingExecution" />
- <Policy Type="ConfirmationPolicy" Value="key:DefaultConfirmation" />
- </Policies>
-</Resource>
-
-<Resource Id="sending" Name="Sending Resource" Type="Sending">
- <Policies>
- <Policy Type="ExecutionPolicy" Value="key:SendingExecution" />
- <Policy Type="ConfirmationPolicy" Value="key:DefaultConfirmation" />
- </Policies>
-</Resource>
-
-
-
What should now be noted by these three new Resources is that they have Policy definitions:
-
- ExecutionPolicy
→ defines how an action on this resource is executed by
- referencing an ExecutionPolicy implementation.
-
- ConfirmationPolicy
→ defines behaviour to be performed on every state change of
- an action being performed on this resource by referencing an
- ConfirmationPolicy implementation.
-
-
-
Currently these resources reference policies which don't exist. We will resolve this issue later, when we
- implement the execution of the activity.
-
-
This concludes the model definition. In the next step we'll start creating services and commands for our
- model.
-
-
Previous:
- Configuration Next: CRUD
- Books
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/tutorial.html b/li.strolch.website/www.strolch.li/tutorial.html
deleted file mode 100644
index 62e2cf184..000000000
--- a/li.strolch.website/www.strolch.li/tutorial.html
+++ /dev/null
@@ -1,122 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Strolch: Tutorial
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
In this tutorial we will build a book store using Strolch. This book store will be without a UI, but we will
- do everything using REST APIs, which should make it easy to add a UI later using whatever framework suits one
- most.
-
-
The book store will have the following features:
-
- The store owner can add, update and remove books
- The store owner can edit the stock quantity
- Users can view a list of books
- Users can add books to a virtual cart
- Users can create and verify an account using an e-mail address
- Users can submit an order for the books in their cart
- The store owner can see the orders by state (pending, preparing, sent)
- The store owner can update the state of an order (preparing, sent)
- Notify the user when the order is sent
-
-
-
Navigation:
-
-
-
The code to the book can be downloaded from GitHub
- and will be updated as this tutorial is updated.
-
-
Next: Configuration
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/li.strolch.website/www.strolch.li/xsd/StrolchModel-1.6.xsd b/li.strolch.website/www.strolch.li/xsd/StrolchModel-1.6.xsd
deleted file mode 100644
index 0375feb57..000000000
--- a/li.strolch.website/www.strolch.li/xsd/StrolchModel-1.6.xsd
+++ /dev/null
@@ -1,201 +0,0 @@
-
-
-
-
- This is Version 1.6.x of the StrolchModel XSD.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file