.\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\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 color: @list-group-link-hover-color;\n background-color: @list-group-hover-bg;\n }\n}\n\n.list-group-item {\n // Disabled state\n &.disabled,\n &.disabled:hover,\n &.disabled:focus {\n background-color: @list-group-disabled-bg;\n color: @list-group-disabled-color;\n cursor: @cursor-disabled;\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-disabled-text-color;\n }\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 .list-group-item-heading > small,\n .list-group-item-heading > .small {\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","// List Groups\n\n.list-group-item-variant(@state; @background; @color) {\n .list-group-item-@{state} {\n color: @color;\n background-color: @background;\n\n a& {\n color: @color;\n\n .list-group-item-heading {\n color: inherit;\n }\n\n &:hover,\n &:focus {\n color: @color;\n background-color: darken(@background, 5%);\n }\n &.active,\n &.active:hover,\n &.active:focus {\n color: #fff;\n background-color: @color;\n border-color: @color;\n }\n }\n }\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: @panel-heading-padding;\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: @panel-footer-padding;\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 > .panel-collapse > .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.list-group + .panel-footer {\n border-top-width: 0;\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 > .panel-collapse > .table {\n margin-bottom: 0;\n\n caption {\n padding-left: @panel-body-padding;\n padding-right: @panel-body-padding;\n }\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 border-top-left-radius: (@panel-border-radius - 1);\n border-top-right-radius: (@panel-border-radius - 1);\n\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 border-bottom-left-radius: (@panel-border-radius - 1);\n border-bottom-right-radius: (@panel-border-radius - 1);\n\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 > .table + .panel-body,\n > .table-responsive + .panel-body {\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\n + .panel {\n margin-top: 5px;\n }\n }\n\n .panel-heading {\n border-bottom: 0;\n\n + .panel-collapse > .panel-body,\n + .panel-collapse > .list-group {\n border-top: 1px solid @panel-inner-border;\n }\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","// Panels\n\n.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {\n border-color: @border;\n\n & > .panel-heading {\n color: @heading-text-color;\n background-color: @heading-bg-color;\n border-color: @heading-border;\n\n + .panel-collapse > .panel-body {\n border-top-color: @border;\n }\n .badge {\n color: @heading-bg-color;\n background-color: @heading-text-color;\n }\n }\n & > .panel-footer {\n + .panel-collapse > .panel-body {\n border-bottom-color: @border;\n }\n }\n}\n","// Embeds responsive\n//\n// Credit: Nicolas Gallagher and SUIT CSS.\n\n.embed-responsive {\n position: relative;\n display: block;\n height: 0;\n padding: 0;\n overflow: hidden;\n\n .embed-responsive-item,\n iframe,\n embed,\n object,\n video {\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n height: 100%;\n width: 100%;\n border: 0;\n }\n\n // Modifier class for 16:9 aspect ratio\n &.embed-responsive-16by9 {\n padding-bottom: 56.25%;\n }\n\n // Modifier class for 4:3 aspect ratio\n &.embed-responsive-4by3 {\n padding-bottom: 75%;\n }\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: hidden;\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.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\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: 0;\n}\n\n// Modal background\n.modal-backdrop {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\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 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// Measure scrollbar width for padding body during modal show/hide\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\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 // Reset font and text propertes given new insertion method\n font-family: @font-family-base;\n font-size: @font-size-small;\n font-weight: normal;\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// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1\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 right: @tooltip-arrow-width;\n margin-bottom: -@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 left: @tooltip-arrow-width;\n margin-bottom: -@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 right: @tooltip-arrow-width;\n margin-top: -@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 left: @tooltip-arrow-width;\n margin-top: -@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 // Reset font and text propertes given new insertion method\n font-family: @font-family-base;\n font-size: @font-size-base;\n font-weight: normal;\n line-height: @line-height-base;\n text-align: left;\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 background-color: @popover-title-bg;\n border-bottom: 1px solid darken(@popover-title-bg, 5%);\n border-radius: (@border-radius-large - 1) (@border-radius-large - 1) 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// 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 // WebKit CSS3 transforms for supported devices\n @media all and (transform-3d), (-webkit-transform-3d) {\n transition: transform .6s ease-in-out;\n backface-visibility: hidden;\n perspective: 1000;\n\n &.next,\n &.active.right {\n transform: translate3d(100%, 0, 0);\n left: 0;\n }\n &.prev,\n &.active.left {\n transform: translate3d(-100%, 0, 0);\n left: 0;\n }\n &.next.left,\n &.prev.right,\n &.active {\n transform: translate3d(0, 0, 0);\n left: 0;\n }\n }\n }\n\n > .active,\n > .next,\n > .prev {\n display: block;\n }\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: 0;\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 margin-left: -10px;\n }\n .icon-next,\n .glyphicon-chevron-right {\n right: 50%;\n margin-right: -10px;\n }\n .icon-prev,\n .icon-next {\n width: 20px;\n height: 20px;\n margin-top: -10px;\n font-family: serif;\n }\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 font-size: 30px;\n }\n .glyphicon-chevron-left,\n .icon-prev {\n margin-left: -15px;\n }\n .glyphicon-chevron-right,\n .icon-next {\n margin-right: -15px;\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","// Clearfix\n//\n// For modern browsers\n// 1. The space content is one way to avoid an Opera bug when the\n// contenteditable attribute is included anywhere else in the document.\n// Otherwise it causes space to appear at the top and bottom of elements\n// that are clearfixed.\n// 2. The use of `table` rather than `block` is only necessary if using\n// `:before` to contain the top-margins of child elements.\n//\n// Source: http://nicolasgallagher.com/micro-clearfix-hack/\n\n.clearfix() {\n &:before,\n &:after {\n content: \" \"; // 1\n display: table; // 2\n }\n &:after {\n clear: both;\n }\n}\n","// Center-align a block level element\n\n.center-block() {\n display: block;\n margin-left: auto;\n margin-right: auto;\n}\n","// CSS image replacement\n//\n// Heads up! v3 launched with with only `.hide-text()`, but per our pattern for\n// mixins being reused as classes with the same name, this doesn't hold up. As\n// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.\n//\n// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757\n\n// Deprecated as of v3.0.1 (will be removed in v4)\n.hide-text() {\n font: ~\"0/0\" a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n// New mixin to use as of v3.0.1\n.text-hide() {\n .hide-text();\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/#support-ie10-width\n// Source: http://timkadlec.com/2013/01/windows-phone-8-and-device-width/\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// Note: Deprecated .visible-xs, .visible-sm, .visible-md, and .visible-lg as of v3.2.0\n.visible-xs,\n.visible-sm,\n.visible-md,\n.visible-lg {\n .responsive-invisibility();\n}\n\n.visible-xs-block,\n.visible-xs-inline,\n.visible-xs-inline-block,\n.visible-sm-block,\n.visible-sm-inline,\n.visible-sm-inline-block,\n.visible-md-block,\n.visible-md-inline,\n.visible-md-inline-block,\n.visible-lg-block,\n.visible-lg-inline,\n.visible-lg-inline-block {\n display: none !important;\n}\n\n.visible-xs {\n @media (max-width: @screen-xs-max) {\n .responsive-visibility();\n }\n}\n.visible-xs-block {\n @media (max-width: @screen-xs-max) {\n display: block !important;\n }\n}\n.visible-xs-inline {\n @media (max-width: @screen-xs-max) {\n display: inline !important;\n }\n}\n.visible-xs-inline-block {\n @media (max-width: @screen-xs-max) {\n display: inline-block !important;\n }\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-sm-block {\n @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n display: block !important;\n }\n}\n.visible-sm-inline {\n @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n display: inline !important;\n }\n}\n.visible-sm-inline-block {\n @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) {\n display: inline-block !important;\n }\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-md-block {\n @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n display: block !important;\n }\n}\n.visible-md-inline {\n @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n display: inline !important;\n }\n}\n.visible-md-inline-block {\n @media (min-width: @screen-md-min) and (max-width: @screen-md-max) {\n display: inline-block !important;\n }\n}\n\n.visible-lg {\n @media (min-width: @screen-lg-min) {\n .responsive-visibility();\n }\n}\n.visible-lg-block {\n @media (min-width: @screen-lg-min) {\n display: block !important;\n }\n}\n.visible-lg-inline {\n @media (min-width: @screen-lg-min) {\n display: inline !important;\n }\n}\n.visible-lg-inline-block {\n @media (min-width: @screen-lg-min) {\n display: inline-block !important;\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// Note: Deprecated .visible-print as of v3.2.0\n.visible-print {\n .responsive-invisibility();\n\n @media print {\n .responsive-visibility();\n }\n}\n.visible-print-block {\n display: none !important;\n\n @media print {\n display: block !important;\n }\n}\n.visible-print-inline {\n display: none !important;\n\n @media print {\n display: inline !important;\n }\n}\n.visible-print-inline-block {\n display: none !important;\n\n @media print {\n display: inline-block !important;\n }\n}\n\n.hidden-print {\n @media print {\n .responsive-invisibility();\n }\n}\n","// Responsive utilities\n\n//\n// More easily include all the states for responsive-utilities.less.\n.responsive-visibility() {\n display: block !important;\n table& { display: table; }\n tr& { display: table-row !important; }\n th&,\n td& { display: table-cell !important; }\n}\n\n.responsive-invisibility() {\n display: none !important;\n}\n"]}
\ No newline at end of file
diff --git a/www2/css/bootstrap.min.css b/www2/css/bootstrap.min.css
deleted file mode 100644
index b6fe4e0f..00000000
--- a/www2/css/bootstrap.min.css
+++ /dev/null
@@ -1,5 +0,0 @@
-/*!
- * Bootstrap v3.3.1 (http://getbootstrap.com)
- * Copyright 2011-2014 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:before,:after{color:#000!important;text-shadow:none!important;background:transparent!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]: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}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.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,.glyphicon-eur: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"}*{-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:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.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{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}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:#777}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:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover{color:#286090}.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:#337ab7}a.bg-primary:hover{background-color:#286090}.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;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}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;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote 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:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse 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'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-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-right:15px;padding-left: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:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-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:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-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:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-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:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>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){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>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:hover>.active,.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:hover>.success,.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:hover>.info,.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:hover>.warning,.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:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>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{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=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,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px}input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],input[type=radio].disabled,input[type=checkbox].disabled,fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm,.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm,select.form-group-sm .form-control{height:30px;line-height:30px}textarea.input-sm,textarea.form-group-sm .form-control,select[multiple].input-sm,select[multiple].form-group-sm .form-control{height:auto}.input-lg,.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg,select.form-group-lg .form-control{height:46px;line-height:46px}textarea.input-lg,textarea.form-group-lg .form-control,select[multiple].input-lg,select[multiple].form-group-lg .form-control{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.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;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn:focus,.btn:active:focus,.btn.active:focus,.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,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default.focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;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.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:#337ab7;border-color:#2e6da4}.btn-primary:hover,.btn-primary:focus,.btn-primary.focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.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.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:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success.focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;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.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.focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;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.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.focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;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.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.focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;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.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{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#23527c;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:#777;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%}.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;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none;visibility:hidden}.collapse.in{display:block;visibility:visible}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px 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;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.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+.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-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=radio],[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.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-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn: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-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn: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{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a: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:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a: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;visibility:hidden}.tab-content>.active{display:block;visibility:visible}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important;visibility:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.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;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>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-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.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;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand: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{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a: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-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a: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{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a: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:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#23527c;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;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a: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:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#286090}.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;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;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}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px 15px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding:48px 0}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#c7ddef}.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-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-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,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr: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,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{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{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:absolute;top:0;right:0;left:0;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-weight:400;line-height:1.4;visibility:visible;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;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{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:1.42857143;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;perspective:1000}.carousel-inner>.item.next,.carousel-inner>.item.active.right{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.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%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-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%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd: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{display:table;content:" "}.clearfix:after,.dl-horizontal dd: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-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{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 (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file
diff --git a/www2/css/carousel.css b/www2/css/carousel.css
deleted file mode 100644
index efbbcae3..00000000
--- a/www2/css/carousel.css
+++ /dev/null
@@ -1,144 +0,0 @@
-/* GLOBAL STYLES
--------------------------------------------------- */
-/* Padding below the footer and lighter body text */
-
-body {
- padding-bottom: 40px;
- color: #5a5a5a;
-}
-
-
-/* CUSTOMIZE THE NAVBAR
--------------------------------------------------- */
-
-/* Special class on .container surrounding .navbar, used for positioning it into place. */
-.navbar-wrapper {
- position: absolute;
- top: 0;
- right: 0;
- left: 0;
- z-index: 20;
-}
-
-/* Flip around the padding for proper display in narrow viewports */
-.navbar-wrapper > .container {
- padding-right: 0;
- padding-left: 0;
-}
-.navbar-wrapper .navbar {
- padding-right: 15px;
- padding-left: 15px;
-}
-.navbar-wrapper .navbar .container {
- width: auto;
-}
-
-
-/* CUSTOMIZE THE CAROUSEL
--------------------------------------------------- */
-
-/* Carousel base class */
-.carousel {
- height: 500px;
- margin-bottom: 60px;
-}
-/* Since positioning the image, we need to help out the caption */
-.carousel-caption {
- z-index: 10;
- text-shadow:
- -0px -0px 3px #000,
- -2px -2px 0 #000,
- 2px -2px 0 #000,
- -2px 2px 0 #000,
- 2px 2px 0 #000;
-}
-
-/* Declare heights because of positioning of img element */
-.carousel .item {
- height: 500px;
- background-color: #064880;
- }
-.carousel .item.dark {
- background-color: #06132c;
- }
-.carousel-inner > .item > img {
- position: absolute;
- top: 0;
- left: 0;
- min-width: 100%;
- height: 500px;
-}
-
-
-/* MARKETING CONTENT
--------------------------------------------------- */
-
-/* Center align the text within the three columns below the carousel */
-.marketing .col-lg-3 {
- margin-bottom: 20px;
- text-align: center;
-}
-.marketing h2 {
- font-weight: normal;
-}
-.marketing .col-lg-3 p {
- margin-right: 10px;
- margin-left: 10px;
-}
-
-
-/* Featurettes
-------------------------- */
-
-.featurette-divider {
- margin: 80px 0; /* Space out the Bootstrap
more */
- border-top: 1px solid #d2d2d2;
-}
-
-/* Thin out the marketing headings */
-.featurette-heading {
- font-weight: 300;
- line-height: 1;
- letter-spacing: -1px;
-}
-
-
-/* RESPONSIVE CSS
--------------------------------------------------- */
-
-@media (min-width: 768px) {
- /* Navbar positioning foo */
- .navbar-wrapper {
- margin-top: 20px;
- }
- .navbar-wrapper .container {
- padding-right: 15px;
- padding-left: 15px;
- }
- .navbar-wrapper .navbar {
- padding-right: 0;
- padding-left: 0;
- }
-
- /* The navbar becomes detached from the top, so we round the corners */
- .navbar-wrapper .navbar {
- border-radius: 4px;
- }
-
- /* Bump up size of carousel content */
- .carousel-caption p {
- margin-bottom: 20px;
- font-size: 21px;
- line-height: 1.4;
- }
-
- .featurette-heading {
- font-size: 50px;
- }
-}
-
-@media (min-width: 992px) {
- .featurette-heading {
- margin-top: 120px;
- }
-}
\ No newline at end of file
diff --git a/www2/css/prettify.css b/www2/css/prettify.css
deleted file mode 100644
index 3c7acd2e..00000000
--- a/www2/css/prettify.css
+++ /dev/null
@@ -1,87 +0,0 @@
-.com {
- color: gray;
-}
-
-.lit {
- color: red;
-}
-
-.pun {
- color: gray;
-}
-
-.pln {
- color: #333333;
-}
-
-pre.prettyprint {
- border: 1px solid lightgray;
- background-color: #fcfcfc;
- padding: 5px;
-
- font-size: 10pt;
- line-height: 1.5em;
- font-family: monospace;
-}
-
-ol.linenums {
- margin-top:0;
- margin-bottom:0;
-}
-
-li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8 {
- list-style:none;
-}
-
-li.L1,li.L3,li.L5,li.L7,li.L9 {
- background:#eee;
-}
-
-.str,.atv {
- color: green;
-}
-
-.kwd,.tag {
- color:#2B7CE9;
-}
-
-.typ,.atn,.dec {
- color: darkorange;
-}
-
-@media print {
- .com {
- color:#600;
- font-style:italic;
- }
-
- .typ {
- color:#404;
- font-weight:700;
- }
-
- .lit {
- color:#044;
- }
-
- .pun {
- color:#440;
- }
-
- .pln {
- color:#000;
- }
-
- .atn {
- color:#404;
- }
-
- .str,.atv {
- color:#060;
- }
-
- .kwd,.tag {
- color:#006;
- font-weight:700;
- }
-}
\ No newline at end of file
diff --git a/www2/dist/img/network/acceptDeleteIcon.png b/www2/dist/img/network/acceptDeleteIcon.png
deleted file mode 100644
index 02a06285..00000000
Binary files a/www2/dist/img/network/acceptDeleteIcon.png and /dev/null differ
diff --git a/www2/dist/img/network/addNodeIcon.png b/www2/dist/img/network/addNodeIcon.png
deleted file mode 100644
index 6fa30613..00000000
Binary files a/www2/dist/img/network/addNodeIcon.png and /dev/null differ
diff --git a/www2/dist/img/network/backIcon.png b/www2/dist/img/network/backIcon.png
deleted file mode 100644
index e2f99126..00000000
Binary files a/www2/dist/img/network/backIcon.png and /dev/null differ
diff --git a/www2/dist/img/network/connectIcon.png b/www2/dist/img/network/connectIcon.png
deleted file mode 100644
index 4164da1f..00000000
Binary files a/www2/dist/img/network/connectIcon.png and /dev/null differ
diff --git a/www2/dist/img/network/cross.png b/www2/dist/img/network/cross.png
deleted file mode 100644
index 9cbd189a..00000000
Binary files a/www2/dist/img/network/cross.png and /dev/null differ
diff --git a/www2/dist/img/network/cross2.png b/www2/dist/img/network/cross2.png
deleted file mode 100644
index 9fc4b95c..00000000
Binary files a/www2/dist/img/network/cross2.png and /dev/null differ
diff --git a/www2/dist/img/network/deleteIcon.png b/www2/dist/img/network/deleteIcon.png
deleted file mode 100644
index 54025647..00000000
Binary files a/www2/dist/img/network/deleteIcon.png and /dev/null differ
diff --git a/www2/dist/img/network/downArrow.png b/www2/dist/img/network/downArrow.png
deleted file mode 100644
index e77d5e6d..00000000
Binary files a/www2/dist/img/network/downArrow.png and /dev/null differ
diff --git a/www2/dist/img/network/editIcon.png b/www2/dist/img/network/editIcon.png
deleted file mode 100644
index 494d0f00..00000000
Binary files a/www2/dist/img/network/editIcon.png and /dev/null differ
diff --git a/www2/dist/img/network/leftArrow.png b/www2/dist/img/network/leftArrow.png
deleted file mode 100644
index 3823536e..00000000
Binary files a/www2/dist/img/network/leftArrow.png and /dev/null differ
diff --git a/www2/dist/img/network/minus.png b/www2/dist/img/network/minus.png
deleted file mode 100644
index 30698076..00000000
Binary files a/www2/dist/img/network/minus.png and /dev/null differ
diff --git a/www2/dist/img/network/plus.png b/www2/dist/img/network/plus.png
deleted file mode 100644
index f7ab2a33..00000000
Binary files a/www2/dist/img/network/plus.png and /dev/null differ
diff --git a/www2/dist/img/network/rightArrow.png b/www2/dist/img/network/rightArrow.png
deleted file mode 100644
index c3a209d8..00000000
Binary files a/www2/dist/img/network/rightArrow.png and /dev/null differ
diff --git a/www2/dist/img/network/upArrow.png b/www2/dist/img/network/upArrow.png
deleted file mode 100644
index 8aedced7..00000000
Binary files a/www2/dist/img/network/upArrow.png and /dev/null differ
diff --git a/www2/dist/img/network/zoomExtends.png b/www2/dist/img/network/zoomExtends.png
deleted file mode 100644
index 74595c63..00000000
Binary files a/www2/dist/img/network/zoomExtends.png and /dev/null differ
diff --git a/www2/dist/img/timeline/delete.png b/www2/dist/img/timeline/delete.png
deleted file mode 100644
index d54d0e06..00000000
Binary files a/www2/dist/img/timeline/delete.png and /dev/null differ
diff --git a/www2/dist/vis.css b/www2/dist/vis.css
deleted file mode 100644
index 529a17fb..00000000
--- a/www2/dist/vis.css
+++ /dev/null
@@ -1,795 +0,0 @@
-.vis .overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
-
- /* Must be displayed above for example selected Timeline items */
- z-index: 10;
-}
-
-.vis-active {
- box-shadow: 0 0 10px #86d5f8;
-}
-
-/* override some bootstrap styles screwing up the timelines css */
-
-.vis [class*="span"] {
- min-height: 0;
- width: auto;
-}
-
-.vis.timeline {
-}
-
-
-.vis.timeline.root {
- position: relative;
- border: 1px solid #bfbfbf;
-
- overflow: hidden;
- padding: 0;
- margin: 0;
-
- box-sizing: border-box;
-}
-
-.vis.timeline .vispanel {
- position: absolute;
-
- padding: 0;
- margin: 0;
-
- box-sizing: border-box;
-}
-
-.vis.timeline .vispanel.center,
-.vis.timeline .vispanel.left,
-.vis.timeline .vispanel.right,
-.vis.timeline .vispanel.top,
-.vis.timeline .vispanel.bottom {
- border: 1px #bfbfbf;
-}
-
-.vis.timeline .vispanel.center,
-.vis.timeline .vispanel.left,
-.vis.timeline .vispanel.right {
- border-top-style: solid;
- border-bottom-style: solid;
- overflow: hidden;
-}
-
-.vis.timeline .vispanel.center,
-.vis.timeline .vispanel.top,
-.vis.timeline .vispanel.bottom {
- border-left-style: solid;
- border-right-style: solid;
-}
-
-.vis.timeline .background {
- overflow: hidden;
-}
-
-.vis.timeline .vispanel > .content {
- position: relative;
-}
-
-.vis.timeline .vispanel .shadow {
- position: absolute;
- width: 100%;
- height: 1px;
- box-shadow: 0 0 10px rgba(0,0,0,0.8);
- /* TODO: find a nice way to ensure shadows are drawn on top of items
- z-index: 1;
- */
-}
-
-.vis.timeline .vispanel .shadow.top {
- top: -1px;
- left: 0;
-}
-
-.vis.timeline .vispanel .shadow.bottom {
- bottom: -1px;
- left: 0;
-}
-
-.vis.timeline .labelset {
- position: relative;
-
- overflow: hidden;
-
- box-sizing: border-box;
-}
-
-.vis.timeline .labelset .vlabel {
- position: relative;
- left: 0;
- top: 0;
- width: 100%;
- color: #4d4d4d;
-
- box-sizing: border-box;
-}
-
-.vis.timeline .labelset .vlabel {
- border-bottom: 1px solid #bfbfbf;
-}
-
-.vis.timeline .labelset .vlabel:last-child {
- border-bottom: none;
-}
-
-.vis.timeline .labelset .vlabel .inner {
- display: inline-block;
- padding: 5px;
-}
-
-.vis.timeline .labelset .vlabel .inner.hidden {
- padding: 0;
-}
-
-
-.vis.timeline .itemset {
- position: relative;
- padding: 0;
- margin: 0;
-
- box-sizing: border-box;
-}
-
-.vis.timeline .itemset .background,
-.vis.timeline .itemset .foreground {
- position: absolute;
- width: 100%;
- height: 100%;
- overflow: visible;
-}
-
-.vis.timeline .axis {
- position: absolute;
- width: 100%;
- height: 0;
- left: 0;
- z-index: 1;
-}
-
-.vis.timeline .foreground .group {
- position: relative;
- box-sizing: border-box;
- border-bottom: 1px solid #bfbfbf;
-}
-
-.vis.timeline .foreground .group:last-child {
- border-bottom: none;
-}
-
-
-.vis.timeline .item {
- position: absolute;
- color: #1A1A1A;
- border-color: #97B0F8;
- border-width: 1px;
- background-color: #D5DDF6;
- display: inline-block;
- padding: 5px;
-}
-
-.vis.timeline .item.selected {
- border-color: #FFC200;
- background-color: #FFF785;
-
- /* z-index must be higher than the z-index of custom time bar and current time bar */
- z-index: 2;
-}
-
-.vis.timeline .editable .item.selected {
- cursor: move;
-}
-
-.vis.timeline .item.point.selected {
- background-color: #FFF785;
-}
-
-.vis.timeline .item.box {
- text-align: center;
- border-style: solid;
- border-radius: 2px;
-}
-
-.vis.timeline .item.point {
- background: none;
-}
-
-.vis.timeline .item.dot {
- position: absolute;
- padding: 0;
- border-width: 4px;
- border-style: solid;
- border-radius: 4px;
-}
-
-.vis.timeline .item.range {
- border-style: solid;
- border-radius: 2px;
- box-sizing: border-box;
-}
-
-.vis.timeline .item.background {
- overflow: hidden;
- border: none;
- background-color: rgba(213, 221, 246, 0.4);
- box-sizing: border-box;
- padding: 0;
- margin: 0;
-}
-
-.vis.timeline .item.range .content {
- position: relative;
- display: inline-block;
- max-width: 100%;
- overflow: hidden;
-}
-
-.vis.timeline .item.background .content {
- position: absolute;
- display: inline-block;
- overflow: hidden;
- max-width: 100%;
- margin: 5px;
-}
-
-.vis.timeline .item.line {
- padding: 0;
- position: absolute;
- width: 0;
- border-left-width: 1px;
- border-left-style: solid;
-}
-
-.vis.timeline .item .content {
- white-space: nowrap;
- overflow: hidden;
-}
-
-.vis.timeline .item .delete {
- background: url('img/timeline/delete.png') no-repeat top center;
- position: absolute;
- width: 24px;
- height: 24px;
- top: 0;
- right: -24px;
- cursor: pointer;
-}
-
-.vis.timeline .item.range .drag-left {
- position: absolute;
- width: 24px;
- height: 100%;
- top: 0;
- left: -4px;
-
- cursor: w-resize;
-}
-
-.vis.timeline .item.range .drag-right {
- position: absolute;
- width: 24px;
- height: 100%;
- top: 0;
- right: -4px;
-
- cursor: e-resize;
-}
-
-.vis.timeline .timeaxis {
- position: relative;
- overflow: hidden;
-}
-
-.vis.timeline .timeaxis.foreground {
- top: 0;
- left: 0;
- width: 100%;
-}
-
-.vis.timeline .timeaxis.background {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
-}
-
-.vis.timeline .timeaxis .text {
- position: absolute;
- color: #4d4d4d;
- padding: 3px;
- white-space: nowrap;
-}
-
-.vis.timeline .timeaxis .text.measure {
- position: absolute;
- padding-left: 0;
- padding-right: 0;
- margin-left: 0;
- margin-right: 0;
- visibility: hidden;
-}
-
-.vis.timeline .timeaxis .grid.vertical {
- position: absolute;
- width: 0;
- border-right: 1px solid;
-}
-
-.vis.timeline .timeaxis .grid.minor {
- border-color: #e5e5e5;
-}
-
-.vis.timeline .timeaxis .grid.major {
- border-color: #bfbfbf;
-}
-
-.vis.timeline .currenttime {
- background-color: #FF7F6E;
- width: 2px;
- z-index: 1;
-}
-.vis.timeline .customtime {
- background-color: #6E94FF;
- width: 2px;
- cursor: move;
- z-index: 1;
-}
-.vis.timeline.root {
- /*
- -webkit-transition: height .4s ease-in-out;
- transition: height .4s ease-in-out;
- */
-}
-
-.vis.timeline .vispanel {
- /*
- -webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
- transition: height .4s ease-in-out, top .4s ease-in-out;
- */
-}
-
-.vis.timeline .axis {
- /*
- -webkit-transition: top .4s ease-in-out;
- transition: top .4s ease-in-out;
- */
-}
-
-/* TODO: get animation working nicely
-
-.vis.timeline .item {
- -webkit-transition: top .4s ease-in-out;
- transition: top .4s ease-in-out;
-}
-
-.vis.timeline .item.line {
- -webkit-transition: height .4s ease-in-out, top .4s ease-in-out;
- transition: height .4s ease-in-out, top .4s ease-in-out;
-}
-/**/
-
-.vis.timeline .vispanel.background.horizontal .grid.horizontal {
- position: absolute;
- width: 100%;
- height: 0;
- border-bottom: 1px solid;
-}
-
-.vis.timeline .vispanel.background.horizontal .grid.minor {
- border-color: #e5e5e5;
-}
-
-.vis.timeline .vispanel.background.horizontal .grid.major {
- border-color: #bfbfbf;
-}
-
-
-.vis.timeline .dataaxis .yAxis.major {
- width: 100%;
- position: absolute;
- color: #4d4d4d;
- white-space: nowrap;
-}
-
-.vis.timeline .dataaxis .yAxis.major.measure{
- padding: 0px 0px 0px 0px;
- margin: 0px 0px 0px 0px;
- border: 0px;
- visibility: hidden;
- width: auto;
-}
-
-
-.vis.timeline .dataaxis .yAxis.minor{
- position: absolute;
- width: 100%;
- color: #bebebe;
- white-space: nowrap;
-}
-
-.vis.timeline .dataaxis .yAxis.minor.measure{
- padding: 0px 0px 0px 0px;
- margin: 0px 0px 0px 0px;
- border: 0px;
- visibility: hidden;
- width: auto;
-}
-
-.vis.timeline .dataaxis .yAxis.title{
- position: absolute;
- color: #4d4d4d;
- white-space: nowrap;
- bottom: 20px;
- text-align: center;
-}
-
-.vis.timeline .dataaxis .yAxis.title.measure{
- padding: 0px 0px 0px 0px;
- margin: 0px 0px 0px 0px;
- visibility: hidden;
- width: auto;
-}
-
-.vis.timeline .dataaxis .yAxis.title.left {
- bottom: 0px;
- -webkit-transform-origin: left top;
- -moz-transform-origin: left top;
- -ms-transform-origin: left top;
- -o-transform-origin: left top;
- transform-origin: left bottom;
- -webkit-transform: rotate(-90deg);
- -moz-transform: rotate(-90deg);
- -ms-transform: rotate(-90deg);
- -o-transform: rotate(-90deg);
- transform: rotate(-90deg);
-}
-
-.vis.timeline .dataaxis .yAxis.title.right {
- bottom: 0px;
- -webkit-transform-origin: right bottom;
- -moz-transform-origin: right bottom;
- -ms-transform-origin: right bottom;
- -o-transform-origin: right bottom;
- transform-origin: right bottom;
- -webkit-transform: rotate(90deg);
- -moz-transform: rotate(90deg);
- -ms-transform: rotate(90deg);
- -o-transform: rotate(90deg);
- transform: rotate(90deg);
-}
-
-.vis.timeline .legend {
- background-color: rgba(247, 252, 255, 0.65);
- padding: 5px;
- border-color: #b3b3b3;
- border-style:solid;
- border-width: 1px;
- box-shadow: 2px 2px 10px rgba(154, 154, 154, 0.55);
-}
-
-.vis.timeline .legendText {
- /*font-size: 10px;*/
- white-space: nowrap;
- display: inline-block
-}
-.vis.timeline .graphGroup0 {
- fill:#4f81bd;
- fill-opacity:0;
- stroke-width:2px;
- stroke: #4f81bd;
-}
-
-.vis.timeline .graphGroup1 {
- fill:#f79646;
- fill-opacity:0;
- stroke-width:2px;
- stroke: #f79646;
-}
-
-.vis.timeline .graphGroup2 {
- fill: #8c51cf;
- fill-opacity:0;
- stroke-width:2px;
- stroke: #8c51cf;
-}
-
-.vis.timeline .graphGroup3 {
- fill: #75c841;
- fill-opacity:0;
- stroke-width:2px;
- stroke: #75c841;
-}
-
-.vis.timeline .graphGroup4 {
- fill: #ff0100;
- fill-opacity:0;
- stroke-width:2px;
- stroke: #ff0100;
-}
-
-.vis.timeline .graphGroup5 {
- fill: #37d8e6;
- fill-opacity:0;
- stroke-width:2px;
- stroke: #37d8e6;
-}
-
-.vis.timeline .graphGroup6 {
- fill: #042662;
- fill-opacity:0;
- stroke-width:2px;
- stroke: #042662;
-}
-
-.vis.timeline .graphGroup7 {
- fill:#00ff26;
- fill-opacity:0;
- stroke-width:2px;
- stroke: #00ff26;
-}
-
-.vis.timeline .graphGroup8 {
- fill:#ff00ff;
- fill-opacity:0;
- stroke-width:2px;
- stroke: #ff00ff;
-}
-
-.vis.timeline .graphGroup9 {
- fill: #8f3938;
- fill-opacity:0;
- stroke-width:2px;
- stroke: #8f3938;
-}
-
-.vis.timeline .fill {
- fill-opacity:0.1;
- stroke: none;
-}
-
-
-.vis.timeline .bar {
- fill-opacity:0.5;
- stroke-width:1px;
-}
-
-.vis.timeline .point {
- stroke-width:2px;
- fill-opacity:1.0;
-}
-
-
-.vis.timeline .legendBackground {
- stroke-width:1px;
- fill-opacity:0.9;
- fill: #ffffff;
- stroke: #c2c2c2;
-}
-
-
-.vis.timeline .outline {
- stroke-width:1px;
- fill-opacity:1;
- fill: #ffffff;
- stroke: #e5e5e5;
-}
-
-.vis.timeline .iconFill {
- fill-opacity:0.3;
- stroke: none;
-}
-
-
-
-div.network-manipulationDiv {
- border-width: 0;
- border-bottom: 1px;
- border-style:solid;
- border-color: #d6d9d8;
- background: #ffffff; /* Old browsers */
- background: -moz-linear-gradient(top, #ffffff 0%, #fcfcfc 48%, #fafafa 50%, #fcfcfc 100%); /* FF3.6+ */
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(48%,#fcfcfc), color-stop(50%,#fafafa), color-stop(100%,#fcfcfc)); /* Chrome,Safari4+ */
- background: -webkit-linear-gradient(top, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* Chrome10+,Safari5.1+ */
- background: -o-linear-gradient(top, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* Opera 11.10+ */
- background: -ms-linear-gradient(top, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* IE10+ */
- background: linear-gradient(to bottom, #ffffff 0%,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%); /* W3C */
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc',GradientType=0 ); /* IE6-9 */
-
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 30px;
-}
-
-div.network-manipulation-editMode {
- position:absolute;
- left: 0;
- top: 0;
- height: 30px;
- margin-top:20px;
-}
-
-div.network-manipulation-closeDiv {
- position:absolute;
- right: 0;
- top: 0;
- width: 30px;
- height: 30px;
-
- background-position: 20px 3px;
- background-repeat: no-repeat;
- background-image: url("img/network/cross.png");
- cursor: pointer;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-
-div.network-manipulation-closeDiv:hover {
- opacity: 0.6;
-}
-
-span.network-manipulationUI {
- font-family: verdana;
- font-size: 12px;
- -moz-border-radius: 15px;
- border-radius: 15px;
- display:inline-block;
- background-position: 0px 0px;
- background-repeat:no-repeat;
- height:24px;
- margin: -14px 0px 0px 10px;
- vertical-align:middle;
- cursor: pointer;
- padding: 0px 8px 0px 8px;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-
-span.network-manipulationUI:hover {
- box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.20);
-}
-
-span.network-manipulationUI:active {
- box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.50);
-}
-
-span.network-manipulationUI.back {
- background-image: url("img/network/backIcon.png");
-}
-
-span.network-manipulationUI.none:hover {
- box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.0);
- cursor: default;
-}
-span.network-manipulationUI.none:active {
- box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.0);
-}
-span.network-manipulationUI.none {
- padding: 0;
-}
-span.network-manipulationUI.notification{
- margin: 2px;
- font-weight: bold;
-}
-
-span.network-manipulationUI.add {
- background-image: url("img/network/addNodeIcon.png");
-}
-
-span.network-manipulationUI.edit {
- background-image: url("img/network/editIcon.png");
-}
-
-span.network-manipulationUI.edit.editmode {
- background-color: #fcfcfc;
- border-style:solid;
- border-width:1px;
- border-color: #cccccc;
-}
-
-span.network-manipulationUI.connect {
- background-image: url("img/network/connectIcon.png");
-}
-
-span.network-manipulationUI.delete {
- background-image: url("img/network/deleteIcon.png");
-}
-/* top right bottom left */
-span.network-manipulationLabel {
- margin: 0px 0px 0px 23px;
- line-height: 25px;
-}
-div.network-seperatorLine {
- display:inline-block;
- width:1px;
- height:20px;
- background-color: #bdbdbd;
- margin: 5px 7px 0px 15px;
-}
-
-div.network-navigation_wrapper {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
-}
-div.network-navigation {
- width:34px;
- height:34px;
- -moz-border-radius: 17px;
- border-radius: 17px;
- position:absolute;
- display:inline-block;
- background-position: 2px 2px;
- background-repeat:no-repeat;
- cursor: pointer;
- -webkit-touch-callout: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
-}
-
-div.network-navigation:hover {
- box-shadow: 0px 0px 3px 3px rgba(56, 207, 21, 0.30);
-}
-
-div.network-navigation:active {
- box-shadow: 0px 0px 1px 3px rgba(56, 207, 21, 0.95);
-}
-
-div.network-navigation.up {
- background-image: url("img/network/upArrow.png");
- bottom:50px;
- left:55px;
-}
-div.network-navigation.down {
- background-image: url("img/network/downArrow.png");
- bottom:10px;
- left:55px;
-}
-div.network-navigation.left {
- background-image: url("img/network/leftArrow.png");
- bottom:10px;
- left:15px;
-}
-div.network-navigation.right {
- background-image: url("img/network/rightArrow.png");
- bottom:10px;
- left:95px;
-}
-div.network-navigation.zoomIn {
- background-image: url("img/network/plus.png");
- bottom:10px;
- right:15px;
-}
-div.network-navigation.zoomOut {
- background-image: url("img/network/minus.png");
- bottom:10px;
- right:55px;
-}
-div.network-navigation.zoomExtends {
- background-image: url("img/network/zoomExtends.png");
- bottom:50px;
- right:15px;
-}
\ No newline at end of file
diff --git a/www2/dist/vis.js b/www2/dist/vis.js
deleted file mode 100644
index 516d408f..00000000
--- a/www2/dist/vis.js
+++ /dev/null
@@ -1,34028 +0,0 @@
-/**
- * vis.js
- * https://github.com/almende/vis
- *
- * A dynamic, browser-based visualization library.
- *
- * @version 3.7.2-SNAPSHOT
- * @date 2014-12-24
- *
- * @license
- * Copyright (C) 2011-2014 Almende B.V, http://almende.com
- *
- * Vis.js is dual licensed under both
- *
- * * The Apache 2.0 License
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * and
- *
- * * The MIT License
- * http://opensource.org/licenses/MIT
- *
- * Vis.js may be distributed under either license.
- */
-
-"use strict";
-
-(function webpackUniversalModuleDefinition(root, factory) {
- if(typeof exports === 'object' && typeof module === 'object')
- module.exports = factory();
- else if(typeof define === 'function' && define.amd)
- define(factory);
- else if(typeof exports === 'object')
- exports["vis"] = factory();
- else
- root["vis"] = factory();
-})(this, function() {
-return /******/ (function(modules) { // webpackBootstrap
-/******/ // The module cache
-/******/ var installedModules = {};
-/******/
-/******/ // The require function
-/******/ function __webpack_require__(moduleId) {
-/******/
-/******/ // Check if module is in cache
-/******/ if(installedModules[moduleId])
-/******/ return installedModules[moduleId].exports;
-/******/
-/******/ // Create a new module (and put it into the cache)
-/******/ var module = installedModules[moduleId] = {
-/******/ exports: {},
-/******/ id: moduleId,
-/******/ loaded: false
-/******/ };
-/******/
-/******/ // Execute the module function
-/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
-/******/
-/******/ // Flag the module as loaded
-/******/ module.loaded = true;
-/******/
-/******/ // Return the exports of the module
-/******/ return module.exports;
-/******/ }
-/******/
-/******/
-/******/ // expose the modules object (__webpack_modules__)
-/******/ __webpack_require__.m = modules;
-/******/
-/******/ // expose the module cache
-/******/ __webpack_require__.c = installedModules;
-/******/
-/******/ // __webpack_public_path__
-/******/ __webpack_require__.p = "";
-/******/
-/******/ // Load entry module and return exports
-/******/ return __webpack_require__(0);
-/******/ })
-/************************************************************************/
-/******/ ([
-/* 0 */
-/***/ function(module, exports, __webpack_require__) {
-
- // utils
- exports.util = __webpack_require__(1);
- exports.DOMutil = __webpack_require__(6);
-
- // data
- exports.DataSet = __webpack_require__(7);
- exports.DataView = __webpack_require__(9);
- exports.Queue = __webpack_require__(8);
-
- // Graph3d
- exports.Graph3d = __webpack_require__(10);
- exports.graph3d = {
- Camera: __webpack_require__(14),
- Filter: __webpack_require__(15),
- Point2d: __webpack_require__(13),
- Point3d: __webpack_require__(12),
- Slider: __webpack_require__(16),
- StepNumber: __webpack_require__(17)
- };
-
- // Timeline
- exports.Timeline = __webpack_require__(18);
- exports.Graph2d = __webpack_require__(42);
- exports.timeline = {
- DateUtil: __webpack_require__(24),
- DataStep: __webpack_require__(45),
- Range: __webpack_require__(21),
- stack: __webpack_require__(28),
- TimeStep: __webpack_require__(38),
-
- components: {
- items: {
- Item: __webpack_require__(30),
- BackgroundItem: __webpack_require__(34),
- BoxItem: __webpack_require__(32),
- PointItem: __webpack_require__(33),
- RangeItem: __webpack_require__(29)
- },
-
- Component: __webpack_require__(23),
- CurrentTime: __webpack_require__(39),
- CustomTime: __webpack_require__(41),
- DataAxis: __webpack_require__(44),
- GraphGroup: __webpack_require__(46),
- Group: __webpack_require__(27),
- BackgroundGroup: __webpack_require__(31),
- ItemSet: __webpack_require__(26),
- Legend: __webpack_require__(50),
- LineGraph: __webpack_require__(43),
- TimeAxis: __webpack_require__(37)
- }
- };
-
- // Network
- exports.Network = __webpack_require__(51);
- exports.network = {
- Edge: __webpack_require__(52),
- Groups: __webpack_require__(54),
- Images: __webpack_require__(55),
- Node: __webpack_require__(53),
- Popup: __webpack_require__(56),
- dotparser: __webpack_require__(57),
- gephiParser: __webpack_require__(58)
- };
-
- // Deprecated since v3.0.0
- exports.Graph = function () {
- throw new Error('Graph is renamed to Network. Please create a graph as new vis.Network(...)');
- };
-
- // bundled external libraries
- exports.moment = __webpack_require__(2);
- exports.hammer = __webpack_require__(19);
-
-
-/***/ },
-/* 1 */
-/***/ function(module, exports, __webpack_require__) {
-
- // utility functions
-
- // first check if moment.js is already loaded in the browser window, if so,
- // use this instance. Else, load via commonjs.
- var moment = __webpack_require__(2);
-
- /**
- * Test whether given object is a number
- * @param {*} object
- * @return {Boolean} isNumber
- */
- exports.isNumber = function(object) {
- return (object instanceof Number || typeof object == 'number');
- };
-
- /**
- * Test whether given object is a string
- * @param {*} object
- * @return {Boolean} isString
- */
- exports.isString = function(object) {
- return (object instanceof String || typeof object == 'string');
- };
-
- /**
- * Test whether given object is a Date, or a String containing a Date
- * @param {Date | String} object
- * @return {Boolean} isDate
- */
- exports.isDate = function(object) {
- if (object instanceof Date) {
- return true;
- }
- else if (exports.isString(object)) {
- // test whether this string contains a date
- var match = ASPDateRegex.exec(object);
- if (match) {
- return true;
- }
- else if (!isNaN(Date.parse(object))) {
- return true;
- }
- }
-
- return false;
- };
-
- /**
- * Test whether given object is an instance of google.visualization.DataTable
- * @param {*} object
- * @return {Boolean} isDataTable
- */
- exports.isDataTable = function(object) {
- return (typeof (google) !== 'undefined') &&
- (google.visualization) &&
- (google.visualization.DataTable) &&
- (object instanceof google.visualization.DataTable);
- };
-
- /**
- * Create a semi UUID
- * source: http://stackoverflow.com/a/105074/1262753
- * @return {String} uuid
- */
- exports.randomUUID = function() {
- var S4 = function () {
- return Math.floor(
- Math.random() * 0x10000 /* 65536 */
- ).toString(16);
- };
-
- return (
- S4() + S4() + '-' +
- S4() + '-' +
- S4() + '-' +
- S4() + '-' +
- S4() + S4() + S4()
- );
- };
-
- /**
- * Extend object a with the properties of object b or a series of objects
- * Only properties with defined values are copied
- * @param {Object} a
- * @param {... Object} b
- * @return {Object} a
- */
- exports.extend = function (a, b) {
- for (var i = 1, len = arguments.length; i < len; i++) {
- var other = arguments[i];
- for (var prop in other) {
- if (other.hasOwnProperty(prop)) {
- a[prop] = other[prop];
- }
- }
- }
-
- return a;
- };
-
- /**
- * Extend object a with selected properties of object b or a series of objects
- * Only properties with defined values are copied
- * @param {Array.
} props
- * @param {Object} a
- * @param {... Object} b
- * @return {Object} a
- */
- exports.selectiveExtend = function (props, a, b) {
- if (!Array.isArray(props)) {
- throw new Error('Array with property names expected as first argument');
- }
-
- for (var i = 2; i < arguments.length; i++) {
- var other = arguments[i];
-
- for (var p = 0; p < props.length; p++) {
- var prop = props[p];
- if (other.hasOwnProperty(prop)) {
- a[prop] = other[prop];
- }
- }
- }
- return a;
- };
-
- /**
- * Extend object a with selected properties of object b or a series of objects
- * Only properties with defined values are copied
- * @param {Array.} props
- * @param {Object} a
- * @param {... Object} b
- * @return {Object} a
- */
- exports.selectiveDeepExtend = function (props, a, b) {
- // TODO: add support for Arrays to deepExtend
- if (Array.isArray(b)) {
- throw new TypeError('Arrays are not supported by deepExtend');
- }
- for (var i = 2; i < arguments.length; i++) {
- var other = arguments[i];
- for (var p = 0; p < props.length; p++) {
- var prop = props[p];
- if (other.hasOwnProperty(prop)) {
- if (b[prop] && b[prop].constructor === Object) {
- if (a[prop] === undefined) {
- a[prop] = {};
- }
- if (a[prop].constructor === Object) {
- exports.deepExtend(a[prop], b[prop]);
- }
- else {
- a[prop] = b[prop];
- }
- } else if (Array.isArray(b[prop])) {
- throw new TypeError('Arrays are not supported by deepExtend');
- } else {
- a[prop] = b[prop];
- }
-
- }
- }
- }
- return a;
- };
-
- /**
- * Extend object a with selected properties of object b or a series of objects
- * Only properties with defined values are copied
- * @param {Array.} props
- * @param {Object} a
- * @param {... Object} b
- * @return {Object} a
- */
- exports.selectiveNotDeepExtend = function (props, a, b) {
- // TODO: add support for Arrays to deepExtend
- if (Array.isArray(b)) {
- throw new TypeError('Arrays are not supported by deepExtend');
- }
- for (var prop in b) {
- if (b.hasOwnProperty(prop)) {
- if (props.indexOf(prop) == -1) {
- if (b[prop] && b[prop].constructor === Object) {
- if (a[prop] === undefined) {
- a[prop] = {};
- }
- if (a[prop].constructor === Object) {
- exports.deepExtend(a[prop], b[prop]);
- }
- else {
- a[prop] = b[prop];
- }
- } else if (Array.isArray(b[prop])) {
- throw new TypeError('Arrays are not supported by deepExtend');
- } else {
- a[prop] = b[prop];
- }
- }
- }
- }
- return a;
- };
-
- /**
- * Deep extend an object a with the properties of object b
- * @param {Object} a
- * @param {Object} b
- * @returns {Object}
- */
- exports.deepExtend = function(a, b) {
- // TODO: add support for Arrays to deepExtend
- if (Array.isArray(b)) {
- throw new TypeError('Arrays are not supported by deepExtend');
- }
-
- for (var prop in b) {
- if (b.hasOwnProperty(prop)) {
- if (b[prop] && b[prop].constructor === Object) {
- if (a[prop] === undefined) {
- a[prop] = {};
- }
- if (a[prop].constructor === Object) {
- exports.deepExtend(a[prop], b[prop]);
- }
- else {
- a[prop] = b[prop];
- }
- } else if (Array.isArray(b[prop])) {
- throw new TypeError('Arrays are not supported by deepExtend');
- } else {
- a[prop] = b[prop];
- }
- }
- }
- return a;
- };
-
- /**
- * Test whether all elements in two arrays are equal.
- * @param {Array} a
- * @param {Array} b
- * @return {boolean} Returns true if both arrays have the same length and same
- * elements.
- */
- exports.equalArray = function (a, b) {
- if (a.length != b.length) return false;
-
- for (var i = 0, len = a.length; i < len; i++) {
- if (a[i] != b[i]) return false;
- }
-
- return true;
- };
-
- /**
- * Convert an object to another type
- * @param {Boolean | Number | String | Date | Moment | Null | undefined} object
- * @param {String | undefined} type Name of the type. Available types:
- * 'Boolean', 'Number', 'String',
- * 'Date', 'Moment', ISODate', 'ASPDate'.
- * @return {*} object
- * @throws Error
- */
- exports.convert = function(object, type) {
- var match;
-
- if (object === undefined) {
- return undefined;
- }
- if (object === null) {
- return null;
- }
-
- if (!type) {
- return object;
- }
- if (!(typeof type === 'string') && !(type instanceof String)) {
- throw new Error('Type must be a string');
- }
-
- //noinspection FallthroughInSwitchStatementJS
- switch (type) {
- case 'boolean':
- case 'Boolean':
- return Boolean(object);
-
- case 'number':
- case 'Number':
- return Number(object.valueOf());
-
- case 'string':
- case 'String':
- return String(object);
-
- case 'Date':
- if (exports.isNumber(object)) {
- return new Date(object);
- }
- if (object instanceof Date) {
- return new Date(object.valueOf());
- }
- else if (moment.isMoment(object)) {
- return new Date(object.valueOf());
- }
- if (exports.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return new Date(Number(match[1])); // parse number
- }
- else {
- return moment(object).toDate(); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + exports.getType(object) +
- ' to type Date');
- }
-
- case 'Moment':
- if (exports.isNumber(object)) {
- return moment(object);
- }
- if (object instanceof Date) {
- return moment(object.valueOf());
- }
- else if (moment.isMoment(object)) {
- return moment(object);
- }
- if (exports.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return moment(Number(match[1])); // parse number
- }
- else {
- return moment(object); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + exports.getType(object) +
- ' to type Date');
- }
-
- case 'ISODate':
- if (exports.isNumber(object)) {
- return new Date(object);
- }
- else if (object instanceof Date) {
- return object.toISOString();
- }
- else if (moment.isMoment(object)) {
- return object.toDate().toISOString();
- }
- else if (exports.isString(object)) {
- match = ASPDateRegex.exec(object);
- if (match) {
- // object is an ASP date
- return new Date(Number(match[1])).toISOString(); // parse number
- }
- else {
- return new Date(object).toISOString(); // parse string
- }
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + exports.getType(object) +
- ' to type ISODate');
- }
-
- case 'ASPDate':
- if (exports.isNumber(object)) {
- return '/Date(' + object + ')/';
- }
- else if (object instanceof Date) {
- return '/Date(' + object.valueOf() + ')/';
- }
- else if (exports.isString(object)) {
- match = ASPDateRegex.exec(object);
- var value;
- if (match) {
- // object is an ASP date
- value = new Date(Number(match[1])).valueOf(); // parse number
- }
- else {
- value = new Date(object).valueOf(); // parse string
- }
- return '/Date(' + value + ')/';
- }
- else {
- throw new Error(
- 'Cannot convert object of type ' + exports.getType(object) +
- ' to type ASPDate');
- }
-
- default:
- throw new Error('Unknown type "' + type + '"');
- }
- };
-
- // parse ASP.Net Date pattern,
- // for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
- // code from http://momentjs.com/
- var ASPDateRegex = /^\/?Date\((\-?\d+)/i;
-
- /**
- * Get the type of an object, for example exports.getType([]) returns 'Array'
- * @param {*} object
- * @return {String} type
- */
- exports.getType = function(object) {
- var type = typeof object;
-
- if (type == 'object') {
- if (object == null) {
- return 'null';
- }
- if (object instanceof Boolean) {
- return 'Boolean';
- }
- if (object instanceof Number) {
- return 'Number';
- }
- if (object instanceof String) {
- return 'String';
- }
- if (Array.isArray(object)) {
- return 'Array';
- }
- if (object instanceof Date) {
- return 'Date';
- }
- return 'Object';
- }
- else if (type == 'number') {
- return 'Number';
- }
- else if (type == 'boolean') {
- return 'Boolean';
- }
- else if (type == 'string') {
- return 'String';
- }
-
- return type;
- };
-
- /**
- * Retrieve the absolute left value of a DOM element
- * @param {Element} elem A dom element, for example a div
- * @return {number} left The absolute left position of this element
- * in the browser page.
- */
- exports.getAbsoluteLeft = function(elem) {
- return elem.getBoundingClientRect().left + window.pageXOffset;
- };
-
- /**
- * Retrieve the absolute top value of a DOM element
- * @param {Element} elem A dom element, for example a div
- * @return {number} top The absolute top position of this element
- * in the browser page.
- */
- exports.getAbsoluteTop = function(elem) {
- return elem.getBoundingClientRect().top + window.pageYOffset;
- };
-
- /**
- * add a className to the given elements style
- * @param {Element} elem
- * @param {String} className
- */
- exports.addClassName = function(elem, className) {
- var classes = elem.className.split(' ');
- if (classes.indexOf(className) == -1) {
- classes.push(className); // add the class to the array
- elem.className = classes.join(' ');
- }
- };
-
- /**
- * add a className to the given elements style
- * @param {Element} elem
- * @param {String} className
- */
- exports.removeClassName = function(elem, className) {
- var classes = elem.className.split(' ');
- var index = classes.indexOf(className);
- if (index != -1) {
- classes.splice(index, 1); // remove the class from the array
- elem.className = classes.join(' ');
- }
- };
-
- /**
- * For each method for both arrays and objects.
- * In case of an array, the built-in Array.forEach() is applied.
- * In case of an Object, the method loops over all properties of the object.
- * @param {Object | Array} object An Object or Array
- * @param {function} callback Callback method, called for each item in
- * the object or array with three parameters:
- * callback(value, index, object)
- */
- exports.forEach = function(object, callback) {
- var i,
- len;
- if (Array.isArray(object)) {
- // array
- for (i = 0, len = object.length; i < len; i++) {
- callback(object[i], i, object);
- }
- }
- else {
- // object
- for (i in object) {
- if (object.hasOwnProperty(i)) {
- callback(object[i], i, object);
- }
- }
- }
- };
-
- /**
- * Convert an object into an array: all objects properties are put into the
- * array. The resulting array is unordered.
- * @param {Object} object
- * @param {Array} array
- */
- exports.toArray = function(object) {
- var array = [];
-
- for (var prop in object) {
- if (object.hasOwnProperty(prop)) array.push(object[prop]);
- }
-
- return array;
- }
-
- /**
- * Update a property in an object
- * @param {Object} object
- * @param {String} key
- * @param {*} value
- * @return {Boolean} changed
- */
- exports.updateProperty = function(object, key, value) {
- if (object[key] !== value) {
- object[key] = value;
- return true;
- }
- else {
- return false;
- }
- };
-
- /**
- * Add and event listener. Works for all browsers
- * @param {Element} element An html element
- * @param {string} action The action, for example "click",
- * without the prefix "on"
- * @param {function} listener The callback function to be executed
- * @param {boolean} [useCapture]
- */
- exports.addEventListener = function(element, action, listener, useCapture) {
- if (element.addEventListener) {
- if (useCapture === undefined)
- useCapture = false;
-
- if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
- action = "DOMMouseScroll"; // For Firefox
- }
-
- element.addEventListener(action, listener, useCapture);
- } else {
- element.attachEvent("on" + action, listener); // IE browsers
- }
- };
-
- /**
- * Remove an event listener from an element
- * @param {Element} element An html dom element
- * @param {string} action The name of the event, for example "mousedown"
- * @param {function} listener The listener function
- * @param {boolean} [useCapture]
- */
- exports.removeEventListener = function(element, action, listener, useCapture) {
- if (element.removeEventListener) {
- // non-IE browsers
- if (useCapture === undefined)
- useCapture = false;
-
- if (action === "mousewheel" && navigator.userAgent.indexOf("Firefox") >= 0) {
- action = "DOMMouseScroll"; // For Firefox
- }
-
- element.removeEventListener(action, listener, useCapture);
- } else {
- // IE browsers
- element.detachEvent("on" + action, listener);
- }
- };
-
- /**
- * Cancels the event if it is cancelable, without stopping further propagation of the event.
- */
- exports.preventDefault = function (event) {
- if (!event)
- event = window.event;
-
- if (event.preventDefault) {
- event.preventDefault(); // non-IE browsers
- }
- else {
- event.returnValue = false; // IE browsers
- }
- };
-
- /**
- * Get HTML element which is the target of the event
- * @param {Event} event
- * @return {Element} target element
- */
- exports.getTarget = function(event) {
- // code from http://www.quirksmode.org/js/events_properties.html
- if (!event) {
- event = window.event;
- }
-
- var target;
-
- if (event.target) {
- target = event.target;
- }
- else if (event.srcElement) {
- target = event.srcElement;
- }
-
- if (target.nodeType != undefined && target.nodeType == 3) {
- // defeat Safari bug
- target = target.parentNode;
- }
-
- return target;
- };
-
- exports.option = {};
-
- /**
- * Convert a value into a boolean
- * @param {Boolean | function | undefined} value
- * @param {Boolean} [defaultValue]
- * @returns {Boolean} bool
- */
- exports.option.asBoolean = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (value != null) {
- return (value != false);
- }
-
- return defaultValue || null;
- };
-
- /**
- * Convert a value into a number
- * @param {Boolean | function | undefined} value
- * @param {Number} [defaultValue]
- * @returns {Number} number
- */
- exports.option.asNumber = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (value != null) {
- return Number(value) || defaultValue || null;
- }
-
- return defaultValue || null;
- };
-
- /**
- * Convert a value into a string
- * @param {String | function | undefined} value
- * @param {String} [defaultValue]
- * @returns {String} str
- */
- exports.option.asString = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (value != null) {
- return String(value);
- }
-
- return defaultValue || null;
- };
-
- /**
- * Convert a size or location into a string with pixels or a percentage
- * @param {String | Number | function | undefined} value
- * @param {String} [defaultValue]
- * @returns {String} size
- */
- exports.option.asSize = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- if (exports.isString(value)) {
- return value;
- }
- else if (exports.isNumber(value)) {
- return value + 'px';
- }
- else {
- return defaultValue || null;
- }
- };
-
- /**
- * Convert a value into a DOM element
- * @param {HTMLElement | function | undefined} value
- * @param {HTMLElement} [defaultValue]
- * @returns {HTMLElement | null} dom
- */
- exports.option.asElement = function (value, defaultValue) {
- if (typeof value == 'function') {
- value = value();
- }
-
- return value || defaultValue || null;
- };
-
-
-
- exports.GiveDec = function(Hex) {
- var Value;
-
- if (Hex == "A")
- Value = 10;
- else if (Hex == "B")
- Value = 11;
- else if (Hex == "C")
- Value = 12;
- else if (Hex == "D")
- Value = 13;
- else if (Hex == "E")
- Value = 14;
- else if (Hex == "F")
- Value = 15;
- else
- Value = eval(Hex);
-
- return Value;
- };
-
- exports.GiveHex = function(Dec) {
- var Value;
-
- if(Dec == 10)
- Value = "A";
- else if (Dec == 11)
- Value = "B";
- else if (Dec == 12)
- Value = "C";
- else if (Dec == 13)
- Value = "D";
- else if (Dec == 14)
- Value = "E";
- else if (Dec == 15)
- Value = "F";
- else
- Value = "" + Dec;
-
- return Value;
- };
-
- /**
- * Parse a color property into an object with border, background, and
- * highlight colors
- * @param {Object | String} color
- * @return {Object} colorObject
- */
- exports.parseColor = function(color) {
- var c;
- if (exports.isString(color)) {
- if (exports.isValidRGB(color)) {
- var rgb = color.substr(4).substr(0,color.length-5).split(',');
- color = exports.RGBToHex(rgb[0],rgb[1],rgb[2]);
- }
- if (exports.isValidHex(color)) {
- var hsv = exports.hexToHSV(color);
- var lighterColorHSV = {h:hsv.h,s:hsv.s * 0.45,v:Math.min(1,hsv.v * 1.05)};
- var darkerColorHSV = {h:hsv.h,s:Math.min(1,hsv.v * 1.25),v:hsv.v*0.6};
- var darkerColorHex = exports.HSVToHex(darkerColorHSV.h ,darkerColorHSV.h ,darkerColorHSV.v);
- var lighterColorHex = exports.HSVToHex(lighterColorHSV.h,lighterColorHSV.s,lighterColorHSV.v);
-
- c = {
- background: color,
- border:darkerColorHex,
- highlight: {
- background:lighterColorHex,
- border:darkerColorHex
- },
- hover: {
- background:lighterColorHex,
- border:darkerColorHex
- }
- };
- }
- else {
- c = {
- background:color,
- border:color,
- highlight: {
- background:color,
- border:color
- },
- hover: {
- background:color,
- border:color
- }
- };
- }
- }
- else {
- c = {};
- c.background = color.background || 'white';
- c.border = color.border || c.background;
-
- if (exports.isString(color.highlight)) {
- c.highlight = {
- border: color.highlight,
- background: color.highlight
- }
- }
- else {
- c.highlight = {};
- c.highlight.background = color.highlight && color.highlight.background || c.background;
- c.highlight.border = color.highlight && color.highlight.border || c.border;
- }
-
- if (exports.isString(color.hover)) {
- c.hover = {
- border: color.hover,
- background: color.hover
- }
- }
- else {
- c.hover = {};
- c.hover.background = color.hover && color.hover.background || c.background;
- c.hover.border = color.hover && color.hover.border || c.border;
- }
- }
-
- return c;
- };
-
- /**
- * http://www.yellowpipe.com/yis/tools/hex-to-rgb/color-converter.php
- *
- * @param {String} hex
- * @returns {{r: *, g: *, b: *}}
- */
- exports.hexToRGB = function(hex) {
- hex = hex.replace("#","").toUpperCase();
-
- var a = exports.GiveDec(hex.substring(0, 1));
- var b = exports.GiveDec(hex.substring(1, 2));
- var c = exports.GiveDec(hex.substring(2, 3));
- var d = exports.GiveDec(hex.substring(3, 4));
- var e = exports.GiveDec(hex.substring(4, 5));
- var f = exports.GiveDec(hex.substring(5, 6));
-
- var r = (a * 16) + b;
- var g = (c * 16) + d;
- var b = (e * 16) + f;
-
- return {r:r,g:g,b:b};
- };
-
- exports.RGBToHex = function(red,green,blue) {
- var a = exports.GiveHex(Math.floor(red / 16));
- var b = exports.GiveHex(red % 16);
- var c = exports.GiveHex(Math.floor(green / 16));
- var d = exports.GiveHex(green % 16);
- var e = exports.GiveHex(Math.floor(blue / 16));
- var f = exports.GiveHex(blue % 16);
-
- var hex = a + b + c + d + e + f;
- return "#" + hex;
- };
-
-
- /**
- * http://www.javascripter.net/faq/rgb2hsv.htm
- *
- * @param red
- * @param green
- * @param blue
- * @returns {*}
- * @constructor
- */
- exports.RGBToHSV = function(red,green,blue) {
- red=red/255; green=green/255; blue=blue/255;
- var minRGB = Math.min(red,Math.min(green,blue));
- var maxRGB = Math.max(red,Math.max(green,blue));
-
- // Black-gray-white
- if (minRGB == maxRGB) {
- return {h:0,s:0,v:minRGB};
- }
-
- // Colors other than black-gray-white:
- var d = (red==minRGB) ? green-blue : ((blue==minRGB) ? red-green : blue-red);
- var h = (red==minRGB) ? 3 : ((blue==minRGB) ? 1 : 5);
- var hue = 60*(h - d/(maxRGB - minRGB))/360;
- var saturation = (maxRGB - minRGB)/maxRGB;
- var value = maxRGB;
- return {h:hue,s:saturation,v:value};
- };
-
- var cssUtil = {
- // split a string with css styles into an object with key/values
- split: function (cssText) {
- var styles = {};
-
- cssText.split(';').forEach(function (style) {
- if (style.trim() != '') {
- var parts = style.split(':');
- var key = parts[0].trim();
- var value = parts[1].trim();
- styles[key] = value;
- }
- });
-
- return styles;
- },
-
- // build a css text string from an object with key/values
- join: function (styles) {
- return Object.keys(styles)
- .map(function (key) {
- return key + ': ' + styles[key];
- })
- .join('; ');
- }
- };
-
- /**
- * Append a string with css styles to an element
- * @param {Element} element
- * @param {String} cssText
- */
- exports.addCssText = function (element, cssText) {
- var currentStyles = cssUtil.split(element.style.cssText);
- var newStyles = cssUtil.split(cssText);
- var styles = exports.extend(currentStyles, newStyles);
-
- element.style.cssText = cssUtil.join(styles);
- };
-
- /**
- * Remove a string with css styles from an element
- * @param {Element} element
- * @param {String} cssText
- */
- exports.removeCssText = function (element, cssText) {
- var styles = cssUtil.split(element.style.cssText);
- var removeStyles = cssUtil.split(cssText);
-
- for (var key in removeStyles) {
- if (removeStyles.hasOwnProperty(key)) {
- delete styles[key];
- }
- }
-
- element.style.cssText = cssUtil.join(styles);
- };
-
- /**
- * https://gist.github.com/mjijackson/5311256
- * @param h
- * @param s
- * @param v
- * @returns {{r: number, g: number, b: number}}
- * @constructor
- */
- exports.HSVToRGB = function(h, s, v) {
- var r, g, b;
-
- var i = Math.floor(h * 6);
- var f = h * 6 - i;
- var p = v * (1 - s);
- var q = v * (1 - f * s);
- var t = v * (1 - (1 - f) * s);
-
- switch (i % 6) {
- case 0: r = v, g = t, b = p; break;
- case 1: r = q, g = v, b = p; break;
- case 2: r = p, g = v, b = t; break;
- case 3: r = p, g = q, b = v; break;
- case 4: r = t, g = p, b = v; break;
- case 5: r = v, g = p, b = q; break;
- }
-
- return {r:Math.floor(r * 255), g:Math.floor(g * 255), b:Math.floor(b * 255) };
- };
-
- exports.HSVToHex = function(h, s, v) {
- var rgb = exports.HSVToRGB(h, s, v);
- return exports.RGBToHex(rgb.r, rgb.g, rgb.b);
- };
-
- exports.hexToHSV = function(hex) {
- var rgb = exports.hexToRGB(hex);
- return exports.RGBToHSV(rgb.r, rgb.g, rgb.b);
- };
-
- exports.isValidHex = function(hex) {
- var isOk = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(hex);
- return isOk;
- };
-
- exports.isValidRGB = function(rgb) {
- rgb = rgb.replace(" ","");
- var isOk = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/i.test(rgb);
- return isOk;
- }
-
- /**
- * This recursively redirects the prototype of JSON objects to the referenceObject
- * This is used for default options.
- *
- * @param referenceObject
- * @returns {*}
- */
- exports.selectiveBridgeObject = function(fields, referenceObject) {
- if (typeof referenceObject == "object") {
- var objectTo = Object.create(referenceObject);
- for (var i = 0; i < fields.length; i++) {
- if (referenceObject.hasOwnProperty(fields[i])) {
- if (typeof referenceObject[fields[i]] == "object") {
- objectTo[fields[i]] = exports.bridgeObject(referenceObject[fields[i]]);
- }
- }
- }
- return objectTo;
- }
- else {
- return null;
- }
- };
-
- /**
- * This recursively redirects the prototype of JSON objects to the referenceObject
- * This is used for default options.
- *
- * @param referenceObject
- * @returns {*}
- */
- exports.bridgeObject = function(referenceObject) {
- if (typeof referenceObject == "object") {
- var objectTo = Object.create(referenceObject);
- for (var i in referenceObject) {
- if (referenceObject.hasOwnProperty(i)) {
- if (typeof referenceObject[i] == "object") {
- objectTo[i] = exports.bridgeObject(referenceObject[i]);
- }
- }
- }
- return objectTo;
- }
- else {
- return null;
- }
- };
-
-
- /**
- * this is used to set the options of subobjects in the options object. A requirement of these subobjects
- * is that they have an 'enabled' element which is optional for the user but mandatory for the program.
- *
- * @param [object] mergeTarget | this is either this.options or the options used for the groups.
- * @param [object] options | options
- * @param [String] option | this is the option key in the options argument
- * @private
- */
- exports.mergeOptions = function (mergeTarget, options, option) {
- if (options[option] !== undefined) {
- if (typeof options[option] == 'boolean') {
- mergeTarget[option].enabled = options[option];
- }
- else {
- mergeTarget[option].enabled = true;
- for (var prop in options[option]) {
- if (options[option].hasOwnProperty(prop)) {
- mergeTarget[option][prop] = options[option][prop];
- }
- }
- }
- }
- }
-
-
- /**
- * This function does a binary search for a visible item in a sorted list. If we find a visible item, the code that uses
- * this function will then iterate in both directions over this sorted list to find all visible items.
- *
- * @param {Item[]} orderedItems | Items ordered by start
- * @param {function} searchFunction | -1 is lower, 0 is found, 1 is higher
- * @param {String} field
- * @param {String} field2
- * @returns {number}
- * @private
- */
- exports.binarySearchCustom = function(orderedItems, searchFunction, field, field2) {
- var maxIterations = 10000;
- var iteration = 0;
- var low = 0;
- var high = orderedItems.length - 1;
-
- while (low <= high && iteration < maxIterations) {
- var middle = Math.floor((low + high) / 2);
-
- var item = orderedItems[middle];
- var value = (field2 === undefined) ? item[field] : item[field][field2];
-
- var searchResult = searchFunction(value);
- if (searchResult == 0) { // jihaa, found a visible item!
- return middle;
- }
- else if (searchResult == -1) { // it is too small --> increase low
- low = middle + 1;
- }
- else { // it is too big --> decrease high
- high = middle - 1;
- }
-
- iteration++;
- }
-
- return -1;
- };
-
- /**
- * This function does a binary search for a specific value in a sorted array. If it does not exist but is in between of
- * two values, we return either the one before or the one after, depending on user input
- * If it is found, we return the index, else -1.
- *
- * @param {Array} orderedItems
- * @param {{start: number, end: number}} target
- * @param {String} field
- * @param {String} sidePreference 'before' or 'after'
- * @returns {number}
- * @private
- */
- exports.binarySearchValue = function(orderedItems, target, field, sidePreference) {
- var maxIterations = 10000;
- var iteration = 0;
- var low = 0;
- var high = orderedItems.length - 1;
- var prevValue, value, nextValue, middle;
-
- while (low <= high && iteration < maxIterations) {
- // get a new guess
- middle = Math.floor(0.5*(high+low));
- prevValue = orderedItems[Math.max(0,middle - 1)][field];
- value = orderedItems[middle][field];
- nextValue = orderedItems[Math.min(orderedItems.length-1,middle + 1)][field];
-
- if (value == target) { // we found the target
- return middle;
- }
- else if (prevValue < target && value > target) { // target is in between of the previous and the current
- return sidePreference == 'before' ? Math.max(0,middle - 1) : middle;
- }
- else if (value < target && nextValue > target) { // target is in between of the current and the next
- return sidePreference == 'before' ? middle : Math.min(orderedItems.length-1,middle + 1);
- }
- else { // didnt find the target, we need to change our boundaries.
- if (value < target) { // it is too small --> increase low
- low = middle + 1;
- }
- else { // it is too big --> decrease high
- high = middle - 1;
- }
- }
- iteration++;
- }
-
- // didnt find anything. Return -1.
- return -1;
- };
-
- /**
- * Quadratic ease-in-out
- * http://gizma.com/easing/
- * @param {number} t Current time
- * @param {number} start Start value
- * @param {number} end End value
- * @param {number} duration Duration
- * @returns {number} Value corresponding with current time
- */
- exports.easeInOutQuad = function (t, start, end, duration) {
- var change = end - start;
- t /= duration/2;
- if (t < 1) return change/2*t*t + start;
- t--;
- return -change/2 * (t*(t-2) - 1) + start;
- };
-
-
-
- /*
- * Easing Functions - inspired from http://gizma.com/easing/
- * only considering the t value for the range [0, 1] => [0, 1]
- * https://gist.github.com/gre/1650294
- */
- exports.easingFunctions = {
- // no easing, no acceleration
- linear: function (t) {
- return t
- },
- // accelerating from zero velocity
- easeInQuad: function (t) {
- return t * t
- },
- // decelerating to zero velocity
- easeOutQuad: function (t) {
- return t * (2 - t)
- },
- // acceleration until halfway, then deceleration
- easeInOutQuad: function (t) {
- return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t
- },
- // accelerating from zero velocity
- easeInCubic: function (t) {
- return t * t * t
- },
- // decelerating to zero velocity
- easeOutCubic: function (t) {
- return (--t) * t * t + 1
- },
- // acceleration until halfway, then deceleration
- easeInOutCubic: function (t) {
- return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
- },
- // accelerating from zero velocity
- easeInQuart: function (t) {
- return t * t * t * t
- },
- // decelerating to zero velocity
- easeOutQuart: function (t) {
- return 1 - (--t) * t * t * t
- },
- // acceleration until halfway, then deceleration
- easeInOutQuart: function (t) {
- return t < .5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t
- },
- // accelerating from zero velocity
- easeInQuint: function (t) {
- return t * t * t * t * t
- },
- // decelerating to zero velocity
- easeOutQuint: function (t) {
- return 1 + (--t) * t * t * t * t
- },
- // acceleration until halfway, then deceleration
- easeInOutQuint: function (t) {
- return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t
- }
- };
-
-/***/ },
-/* 2 */
-/***/ function(module, exports, __webpack_require__) {
-
- // first check if moment.js is already loaded in the browser window, if so,
- // use this instance. Else, load via commonjs.
- module.exports = (typeof window !== 'undefined') && window['moment'] || __webpack_require__(3);
-
-
-/***/ },
-/* 3 */
-/***/ function(module, exports, __webpack_require__) {
-
- var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(global, module) {//! moment.js
- //! version : 2.8.4
- //! authors : Tim Wood, Iskren Chernev, Moment.js contributors
- //! license : MIT
- //! momentjs.com
-
- (function (undefined) {
- /************************************
- Constants
- ************************************/
-
- var moment,
- VERSION = '2.8.4',
- // the global-scope this is NOT the global object in Node.js
- globalScope = typeof global !== 'undefined' ? global : this,
- oldGlobalMoment,
- round = Math.round,
- hasOwnProperty = Object.prototype.hasOwnProperty,
- i,
-
- YEAR = 0,
- MONTH = 1,
- DATE = 2,
- HOUR = 3,
- MINUTE = 4,
- SECOND = 5,
- MILLISECOND = 6,
-
- // internal storage for locale config files
- locales = {},
-
- // extra moment internal properties (plugins register props here)
- momentProperties = [],
-
- // check for nodeJS
- hasModule = (typeof module !== 'undefined' && module && module.exports),
-
- // ASP.NET json date format regex
- aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
- aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,
-
- // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
- // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
- isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,
-
- // format tokens
- formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g,
- localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,
-
- // parsing token regexes
- parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
- parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
- parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999
- parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999
- parseTokenDigits = /\d+/, // nonzero number of digits
- parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic.
- parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z
- parseTokenT = /T/i, // T (ISO separator)
- parseTokenOffsetMs = /[\+\-]?\d+/, // 1234567890123
- parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123
-
- //strict parsing regexes
- parseTokenOneDigit = /\d/, // 0 - 9
- parseTokenTwoDigits = /\d\d/, // 00 - 99
- parseTokenThreeDigits = /\d{3}/, // 000 - 999
- parseTokenFourDigits = /\d{4}/, // 0000 - 9999
- parseTokenSixDigits = /[+-]?\d{6}/, // -999,999 - 999,999
- parseTokenSignedNumber = /[+-]?\d+/, // -inf - inf
-
- // iso 8601 regex
- // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
- isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,
-
- isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
-
- isoDates = [
- ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/],
- ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/],
- ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/],
- ['GGGG-[W]WW', /\d{4}-W\d{2}/],
- ['YYYY-DDD', /\d{4}-\d{3}/]
- ],
-
- // iso time formats and regexes
- isoTimes = [
- ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/],
- ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/],
- ['HH:mm', /(T| )\d\d:\d\d/],
- ['HH', /(T| )\d\d/]
- ],
-
- // timezone chunker '+10:00' > ['10', '00'] or '-1530' > ['-15', '30']
- parseTimezoneChunker = /([\+\-]|\d\d)/gi,
-
- // getter and setter names
- proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
- unitMillisecondFactors = {
- 'Milliseconds' : 1,
- 'Seconds' : 1e3,
- 'Minutes' : 6e4,
- 'Hours' : 36e5,
- 'Days' : 864e5,
- 'Months' : 2592e6,
- 'Years' : 31536e6
- },
-
- unitAliases = {
- ms : 'millisecond',
- s : 'second',
- m : 'minute',
- h : 'hour',
- d : 'day',
- D : 'date',
- w : 'week',
- W : 'isoWeek',
- M : 'month',
- Q : 'quarter',
- y : 'year',
- DDD : 'dayOfYear',
- e : 'weekday',
- E : 'isoWeekday',
- gg: 'weekYear',
- GG: 'isoWeekYear'
- },
-
- camelFunctions = {
- dayofyear : 'dayOfYear',
- isoweekday : 'isoWeekday',
- isoweek : 'isoWeek',
- weekyear : 'weekYear',
- isoweekyear : 'isoWeekYear'
- },
-
- // format function strings
- formatFunctions = {},
-
- // default relative time thresholds
- relativeTimeThresholds = {
- s: 45, // seconds to minute
- m: 45, // minutes to hour
- h: 22, // hours to day
- d: 26, // days to month
- M: 11 // months to year
- },
-
- // tokens to ordinalize and pad
- ordinalizeTokens = 'DDD w W M D d'.split(' '),
- paddedTokens = 'M D H h m s w W'.split(' '),
-
- formatTokenFunctions = {
- M : function () {
- return this.month() + 1;
- },
- MMM : function (format) {
- return this.localeData().monthsShort(this, format);
- },
- MMMM : function (format) {
- return this.localeData().months(this, format);
- },
- D : function () {
- return this.date();
- },
- DDD : function () {
- return this.dayOfYear();
- },
- d : function () {
- return this.day();
- },
- dd : function (format) {
- return this.localeData().weekdaysMin(this, format);
- },
- ddd : function (format) {
- return this.localeData().weekdaysShort(this, format);
- },
- dddd : function (format) {
- return this.localeData().weekdays(this, format);
- },
- w : function () {
- return this.week();
- },
- W : function () {
- return this.isoWeek();
- },
- YY : function () {
- return leftZeroFill(this.year() % 100, 2);
- },
- YYYY : function () {
- return leftZeroFill(this.year(), 4);
- },
- YYYYY : function () {
- return leftZeroFill(this.year(), 5);
- },
- YYYYYY : function () {
- var y = this.year(), sign = y >= 0 ? '+' : '-';
- return sign + leftZeroFill(Math.abs(y), 6);
- },
- gg : function () {
- return leftZeroFill(this.weekYear() % 100, 2);
- },
- gggg : function () {
- return leftZeroFill(this.weekYear(), 4);
- },
- ggggg : function () {
- return leftZeroFill(this.weekYear(), 5);
- },
- GG : function () {
- return leftZeroFill(this.isoWeekYear() % 100, 2);
- },
- GGGG : function () {
- return leftZeroFill(this.isoWeekYear(), 4);
- },
- GGGGG : function () {
- return leftZeroFill(this.isoWeekYear(), 5);
- },
- e : function () {
- return this.weekday();
- },
- E : function () {
- return this.isoWeekday();
- },
- a : function () {
- return this.localeData().meridiem(this.hours(), this.minutes(), true);
- },
- A : function () {
- return this.localeData().meridiem(this.hours(), this.minutes(), false);
- },
- H : function () {
- return this.hours();
- },
- h : function () {
- return this.hours() % 12 || 12;
- },
- m : function () {
- return this.minutes();
- },
- s : function () {
- return this.seconds();
- },
- S : function () {
- return toInt(this.milliseconds() / 100);
- },
- SS : function () {
- return leftZeroFill(toInt(this.milliseconds() / 10), 2);
- },
- SSS : function () {
- return leftZeroFill(this.milliseconds(), 3);
- },
- SSSS : function () {
- return leftZeroFill(this.milliseconds(), 3);
- },
- Z : function () {
- var a = -this.zone(),
- b = '+';
- if (a < 0) {
- a = -a;
- b = '-';
- }
- return b + leftZeroFill(toInt(a / 60), 2) + ':' + leftZeroFill(toInt(a) % 60, 2);
- },
- ZZ : function () {
- var a = -this.zone(),
- b = '+';
- if (a < 0) {
- a = -a;
- b = '-';
- }
- return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2);
- },
- z : function () {
- return this.zoneAbbr();
- },
- zz : function () {
- return this.zoneName();
- },
- x : function () {
- return this.valueOf();
- },
- X : function () {
- return this.unix();
- },
- Q : function () {
- return this.quarter();
- }
- },
-
- deprecations = {},
-
- lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin'];
-
- // Pick the first defined of two or three arguments. dfl comes from
- // default.
- function dfl(a, b, c) {
- switch (arguments.length) {
- case 2: return a != null ? a : b;
- case 3: return a != null ? a : b != null ? b : c;
- default: throw new Error('Implement me');
- }
- }
-
- function hasOwnProp(a, b) {
- return hasOwnProperty.call(a, b);
- }
-
- function defaultParsingFlags() {
- // We need to deep clone this object, and es5 standard is not very
- // helpful.
- return {
- empty : false,
- unusedTokens : [],
- unusedInput : [],
- overflow : -2,
- charsLeftOver : 0,
- nullInput : false,
- invalidMonth : null,
- invalidFormat : false,
- userInvalidated : false,
- iso: false
- };
- }
-
- function printMsg(msg) {
- if (moment.suppressDeprecationWarnings === false &&
- typeof console !== 'undefined' && console.warn) {
- console.warn('Deprecation warning: ' + msg);
- }
- }
-
- function deprecate(msg, fn) {
- var firstTime = true;
- return extend(function () {
- if (firstTime) {
- printMsg(msg);
- firstTime = false;
- }
- return fn.apply(this, arguments);
- }, fn);
- }
-
- function deprecateSimple(name, msg) {
- if (!deprecations[name]) {
- printMsg(msg);
- deprecations[name] = true;
- }
- }
-
- function padToken(func, count) {
- return function (a) {
- return leftZeroFill(func.call(this, a), count);
- };
- }
- function ordinalizeToken(func, period) {
- return function (a) {
- return this.localeData().ordinal(func.call(this, a), period);
- };
- }
-
- while (ordinalizeTokens.length) {
- i = ordinalizeTokens.pop();
- formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i);
- }
- while (paddedTokens.length) {
- i = paddedTokens.pop();
- formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2);
- }
- formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3);
-
-
- /************************************
- Constructors
- ************************************/
-
- function Locale() {
- }
-
- // Moment prototype object
- function Moment(config, skipOverflow) {
- if (skipOverflow !== false) {
- checkOverflow(config);
- }
- copyConfig(this, config);
- this._d = new Date(+config._d);
- }
-
- // Duration Constructor
- function Duration(duration) {
- var normalizedInput = normalizeObjectUnits(duration),
- years = normalizedInput.year || 0,
- quarters = normalizedInput.quarter || 0,
- months = normalizedInput.month || 0,
- weeks = normalizedInput.week || 0,
- days = normalizedInput.day || 0,
- hours = normalizedInput.hour || 0,
- minutes = normalizedInput.minute || 0,
- seconds = normalizedInput.second || 0,
- milliseconds = normalizedInput.millisecond || 0;
-
- // representation for dateAddRemove
- this._milliseconds = +milliseconds +
- seconds * 1e3 + // 1000
- minutes * 6e4 + // 1000 * 60
- hours * 36e5; // 1000 * 60 * 60
- // Because of dateAddRemove treats 24 hours as different from a
- // day when working around DST, we need to store them separately
- this._days = +days +
- weeks * 7;
- // It is impossible translate months into days without knowing
- // which months you are are talking about, so we have to store
- // it separately.
- this._months = +months +
- quarters * 3 +
- years * 12;
-
- this._data = {};
-
- this._locale = moment.localeData();
-
- this._bubble();
- }
-
- /************************************
- Helpers
- ************************************/
-
-
- function extend(a, b) {
- for (var i in b) {
- if (hasOwnProp(b, i)) {
- a[i] = b[i];
- }
- }
-
- if (hasOwnProp(b, 'toString')) {
- a.toString = b.toString;
- }
-
- if (hasOwnProp(b, 'valueOf')) {
- a.valueOf = b.valueOf;
- }
-
- return a;
- }
-
- function copyConfig(to, from) {
- var i, prop, val;
-
- if (typeof from._isAMomentObject !== 'undefined') {
- to._isAMomentObject = from._isAMomentObject;
- }
- if (typeof from._i !== 'undefined') {
- to._i = from._i;
- }
- if (typeof from._f !== 'undefined') {
- to._f = from._f;
- }
- if (typeof from._l !== 'undefined') {
- to._l = from._l;
- }
- if (typeof from._strict !== 'undefined') {
- to._strict = from._strict;
- }
- if (typeof from._tzm !== 'undefined') {
- to._tzm = from._tzm;
- }
- if (typeof from._isUTC !== 'undefined') {
- to._isUTC = from._isUTC;
- }
- if (typeof from._offset !== 'undefined') {
- to._offset = from._offset;
- }
- if (typeof from._pf !== 'undefined') {
- to._pf = from._pf;
- }
- if (typeof from._locale !== 'undefined') {
- to._locale = from._locale;
- }
-
- if (momentProperties.length > 0) {
- for (i in momentProperties) {
- prop = momentProperties[i];
- val = from[prop];
- if (typeof val !== 'undefined') {
- to[prop] = val;
- }
- }
- }
-
- return to;
- }
-
- function absRound(number) {
- if (number < 0) {
- return Math.ceil(number);
- } else {
- return Math.floor(number);
- }
- }
-
- // left zero fill a number
- // see http://jsperf.com/left-zero-filling for performance comparison
- function leftZeroFill(number, targetLength, forceSign) {
- var output = '' + Math.abs(number),
- sign = number >= 0;
-
- while (output.length < targetLength) {
- output = '0' + output;
- }
- return (sign ? (forceSign ? '+' : '') : '-') + output;
- }
-
- function positiveMomentsDifference(base, other) {
- var res = {milliseconds: 0, months: 0};
-
- res.months = other.month() - base.month() +
- (other.year() - base.year()) * 12;
- if (base.clone().add(res.months, 'M').isAfter(other)) {
- --res.months;
- }
-
- res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
-
- return res;
- }
-
- function momentsDifference(base, other) {
- var res;
- other = makeAs(other, base);
- if (base.isBefore(other)) {
- res = positiveMomentsDifference(base, other);
- } else {
- res = positiveMomentsDifference(other, base);
- res.milliseconds = -res.milliseconds;
- res.months = -res.months;
- }
-
- return res;
- }
-
- // TODO: remove 'name' arg after deprecation is removed
- function createAdder(direction, name) {
- return function (val, period) {
- var dur, tmp;
- //invert the arguments, but complain about it
- if (period !== null && !isNaN(+period)) {
- deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).');
- tmp = val; val = period; period = tmp;
- }
-
- val = typeof val === 'string' ? +val : val;
- dur = moment.duration(val, period);
- addOrSubtractDurationFromMoment(this, dur, direction);
- return this;
- };
- }
-
- function addOrSubtractDurationFromMoment(mom, duration, isAdding, updateOffset) {
- var milliseconds = duration._milliseconds,
- days = duration._days,
- months = duration._months;
- updateOffset = updateOffset == null ? true : updateOffset;
-
- if (milliseconds) {
- mom._d.setTime(+mom._d + milliseconds * isAdding);
- }
- if (days) {
- rawSetter(mom, 'Date', rawGetter(mom, 'Date') + days * isAdding);
- }
- if (months) {
- rawMonthSetter(mom, rawGetter(mom, 'Month') + months * isAdding);
- }
- if (updateOffset) {
- moment.updateOffset(mom, days || months);
- }
- }
-
- // check if is an array
- function isArray(input) {
- return Object.prototype.toString.call(input) === '[object Array]';
- }
-
- function isDate(input) {
- return Object.prototype.toString.call(input) === '[object Date]' ||
- input instanceof Date;
- }
-
- // compare two arrays, return the number of differences
- function compareArrays(array1, array2, dontConvert) {
- var len = Math.min(array1.length, array2.length),
- lengthDiff = Math.abs(array1.length - array2.length),
- diffs = 0,
- i;
- for (i = 0; i < len; i++) {
- if ((dontConvert && array1[i] !== array2[i]) ||
- (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
- diffs++;
- }
- }
- return diffs + lengthDiff;
- }
-
- function normalizeUnits(units) {
- if (units) {
- var lowered = units.toLowerCase().replace(/(.)s$/, '$1');
- units = unitAliases[units] || camelFunctions[lowered] || lowered;
- }
- return units;
- }
-
- function normalizeObjectUnits(inputObject) {
- var normalizedInput = {},
- normalizedProp,
- prop;
-
- for (prop in inputObject) {
- if (hasOwnProp(inputObject, prop)) {
- normalizedProp = normalizeUnits(prop);
- if (normalizedProp) {
- normalizedInput[normalizedProp] = inputObject[prop];
- }
- }
- }
-
- return normalizedInput;
- }
-
- function makeList(field) {
- var count, setter;
-
- if (field.indexOf('week') === 0) {
- count = 7;
- setter = 'day';
- }
- else if (field.indexOf('month') === 0) {
- count = 12;
- setter = 'month';
- }
- else {
- return;
- }
-
- moment[field] = function (format, index) {
- var i, getter,
- method = moment._locale[field],
- results = [];
-
- if (typeof format === 'number') {
- index = format;
- format = undefined;
- }
-
- getter = function (i) {
- var m = moment().utc().set(setter, i);
- return method.call(moment._locale, m, format || '');
- };
-
- if (index != null) {
- return getter(index);
- }
- else {
- for (i = 0; i < count; i++) {
- results.push(getter(i));
- }
- return results;
- }
- };
- }
-
- function toInt(argumentForCoercion) {
- var coercedNumber = +argumentForCoercion,
- value = 0;
-
- if (coercedNumber !== 0 && isFinite(coercedNumber)) {
- if (coercedNumber >= 0) {
- value = Math.floor(coercedNumber);
- } else {
- value = Math.ceil(coercedNumber);
- }
- }
-
- return value;
- }
-
- function daysInMonth(year, month) {
- return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
- }
-
- function weeksInYear(year, dow, doy) {
- return weekOfYear(moment([year, 11, 31 + dow - doy]), dow, doy).week;
- }
-
- function daysInYear(year) {
- return isLeapYear(year) ? 366 : 365;
- }
-
- function isLeapYear(year) {
- return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
- }
-
- function checkOverflow(m) {
- var overflow;
- if (m._a && m._pf.overflow === -2) {
- overflow =
- m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH :
- m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE :
- m._a[HOUR] < 0 || m._a[HOUR] > 24 ||
- (m._a[HOUR] === 24 && (m._a[MINUTE] !== 0 ||
- m._a[SECOND] !== 0 ||
- m._a[MILLISECOND] !== 0)) ? HOUR :
- m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE :
- m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND :
- m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND :
- -1;
-
- if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
- overflow = DATE;
- }
-
- m._pf.overflow = overflow;
- }
- }
-
- function isValid(m) {
- if (m._isValid == null) {
- m._isValid = !isNaN(m._d.getTime()) &&
- m._pf.overflow < 0 &&
- !m._pf.empty &&
- !m._pf.invalidMonth &&
- !m._pf.nullInput &&
- !m._pf.invalidFormat &&
- !m._pf.userInvalidated;
-
- if (m._strict) {
- m._isValid = m._isValid &&
- m._pf.charsLeftOver === 0 &&
- m._pf.unusedTokens.length === 0 &&
- m._pf.bigHour === undefined;
- }
- }
- return m._isValid;
- }
-
- function normalizeLocale(key) {
- return key ? key.toLowerCase().replace('_', '-') : key;
- }
-
- // pick the locale from the array
- // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
- // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
- function chooseLocale(names) {
- var i = 0, j, next, locale, split;
-
- while (i < names.length) {
- split = normalizeLocale(names[i]).split('-');
- j = split.length;
- next = normalizeLocale(names[i + 1]);
- next = next ? next.split('-') : null;
- while (j > 0) {
- locale = loadLocale(split.slice(0, j).join('-'));
- if (locale) {
- return locale;
- }
- if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
- //the next array item is better than a shallower substring of this one
- break;
- }
- j--;
- }
- i++;
- }
- return null;
- }
-
- function loadLocale(name) {
- var oldLocale = null;
- if (!locales[name] && hasModule) {
- try {
- oldLocale = moment.locale();
- !(function webpackMissingModule() { var e = new Error("Cannot find module \"./locale\""); e.code = 'MODULE_NOT_FOUND'; throw e; }());
- // because defineLocale currently also sets the global locale, we want to undo that for lazy loaded locales
- moment.locale(oldLocale);
- } catch (e) { }
- }
- return locales[name];
- }
-
- // Return a moment from input, that is local/utc/zone equivalent to model.
- function makeAs(input, model) {
- var res, diff;
- if (model._isUTC) {
- res = model.clone();
- diff = (moment.isMoment(input) || isDate(input) ?
- +input : +moment(input)) - (+res);
- // Use low-level api, because this fn is low-level api.
- res._d.setTime(+res._d + diff);
- moment.updateOffset(res, false);
- return res;
- } else {
- return moment(input).local();
- }
- }
-
- /************************************
- Locale
- ************************************/
-
-
- extend(Locale.prototype, {
-
- set : function (config) {
- var prop, i;
- for (i in config) {
- prop = config[i];
- if (typeof prop === 'function') {
- this[i] = prop;
- } else {
- this['_' + i] = prop;
- }
- }
- // Lenient ordinal parsing accepts just a number in addition to
- // number + (possibly) stuff coming from _ordinalParseLenient.
- this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + /\d{1,2}/.source);
- },
-
- _months : 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'),
- months : function (m) {
- return this._months[m.month()];
- },
-
- _monthsShort : 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'),
- monthsShort : function (m) {
- return this._monthsShort[m.month()];
- },
-
- monthsParse : function (monthName, format, strict) {
- var i, mom, regex;
-
- if (!this._monthsParse) {
- this._monthsParse = [];
- this._longMonthsParse = [];
- this._shortMonthsParse = [];
- }
-
- for (i = 0; i < 12; i++) {
- // make the regex if we don't have it already
- mom = moment.utc([2000, i]);
- if (strict && !this._longMonthsParse[i]) {
- this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
- this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
- }
- if (!strict && !this._monthsParse[i]) {
- regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
- this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
- }
- // test the regex
- if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
- return i;
- } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
- return i;
- } else if (!strict && this._monthsParse[i].test(monthName)) {
- return i;
- }
- }
- },
-
- _weekdays : 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'),
- weekdays : function (m) {
- return this._weekdays[m.day()];
- },
-
- _weekdaysShort : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
- weekdaysShort : function (m) {
- return this._weekdaysShort[m.day()];
- },
-
- _weekdaysMin : 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'),
- weekdaysMin : function (m) {
- return this._weekdaysMin[m.day()];
- },
-
- weekdaysParse : function (weekdayName) {
- var i, mom, regex;
-
- if (!this._weekdaysParse) {
- this._weekdaysParse = [];
- }
-
- for (i = 0; i < 7; i++) {
- // make the regex if we don't have it already
- if (!this._weekdaysParse[i]) {
- mom = moment([2000, 1]).day(i);
- regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
- this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
- }
- // test the regex
- if (this._weekdaysParse[i].test(weekdayName)) {
- return i;
- }
- }
- },
-
- _longDateFormat : {
- LTS : 'h:mm:ss A',
- LT : 'h:mm A',
- L : 'MM/DD/YYYY',
- LL : 'MMMM D, YYYY',
- LLL : 'MMMM D, YYYY LT',
- LLLL : 'dddd, MMMM D, YYYY LT'
- },
- longDateFormat : function (key) {
- var output = this._longDateFormat[key];
- if (!output && this._longDateFormat[key.toUpperCase()]) {
- output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) {
- return val.slice(1);
- });
- this._longDateFormat[key] = output;
- }
- return output;
- },
-
- isPM : function (input) {
- // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
- // Using charAt should be more compatible.
- return ((input + '').toLowerCase().charAt(0) === 'p');
- },
-
- _meridiemParse : /[ap]\.?m?\.?/i,
- meridiem : function (hours, minutes, isLower) {
- if (hours > 11) {
- return isLower ? 'pm' : 'PM';
- } else {
- return isLower ? 'am' : 'AM';
- }
- },
-
- _calendar : {
- sameDay : '[Today at] LT',
- nextDay : '[Tomorrow at] LT',
- nextWeek : 'dddd [at] LT',
- lastDay : '[Yesterday at] LT',
- lastWeek : '[Last] dddd [at] LT',
- sameElse : 'L'
- },
- calendar : function (key, mom, now) {
- var output = this._calendar[key];
- return typeof output === 'function' ? output.apply(mom, [now]) : output;
- },
-
- _relativeTime : {
- future : 'in %s',
- past : '%s ago',
- s : 'a few seconds',
- m : 'a minute',
- mm : '%d minutes',
- h : 'an hour',
- hh : '%d hours',
- d : 'a day',
- dd : '%d days',
- M : 'a month',
- MM : '%d months',
- y : 'a year',
- yy : '%d years'
- },
-
- relativeTime : function (number, withoutSuffix, string, isFuture) {
- var output = this._relativeTime[string];
- return (typeof output === 'function') ?
- output(number, withoutSuffix, string, isFuture) :
- output.replace(/%d/i, number);
- },
-
- pastFuture : function (diff, output) {
- var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
- return typeof format === 'function' ? format(output) : format.replace(/%s/i, output);
- },
-
- ordinal : function (number) {
- return this._ordinal.replace('%d', number);
- },
- _ordinal : '%d',
- _ordinalParse : /\d{1,2}/,
-
- preparse : function (string) {
- return string;
- },
-
- postformat : function (string) {
- return string;
- },
-
- week : function (mom) {
- return weekOfYear(mom, this._week.dow, this._week.doy).week;
- },
-
- _week : {
- dow : 0, // Sunday is the first day of the week.
- doy : 6 // The week that contains Jan 1st is the first week of the year.
- },
-
- _invalidDate: 'Invalid date',
- invalidDate: function () {
- return this._invalidDate;
- }
- });
-
- /************************************
- Formatting
- ************************************/
-
-
- function removeFormattingTokens(input) {
- if (input.match(/\[[\s\S]/)) {
- return input.replace(/^\[|\]$/g, '');
- }
- return input.replace(/\\/g, '');
- }
-
- function makeFormatFunction(format) {
- var array = format.match(formattingTokens), i, length;
-
- for (i = 0, length = array.length; i < length; i++) {
- if (formatTokenFunctions[array[i]]) {
- array[i] = formatTokenFunctions[array[i]];
- } else {
- array[i] = removeFormattingTokens(array[i]);
- }
- }
-
- return function (mom) {
- var output = '';
- for (i = 0; i < length; i++) {
- output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
- }
- return output;
- };
- }
-
- // format date using native date object
- function formatMoment(m, format) {
- if (!m.isValid()) {
- return m.localeData().invalidDate();
- }
-
- format = expandFormat(format, m.localeData());
-
- if (!formatFunctions[format]) {
- formatFunctions[format] = makeFormatFunction(format);
- }
-
- return formatFunctions[format](m);
- }
-
- function expandFormat(format, locale) {
- var i = 5;
-
- function replaceLongDateFormatTokens(input) {
- return locale.longDateFormat(input) || input;
- }
-
- localFormattingTokens.lastIndex = 0;
- while (i >= 0 && localFormattingTokens.test(format)) {
- format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
- localFormattingTokens.lastIndex = 0;
- i -= 1;
- }
-
- return format;
- }
-
-
- /************************************
- Parsing
- ************************************/
-
-
- // get the regex to find the next token
- function getParseRegexForToken(token, config) {
- var a, strict = config._strict;
- switch (token) {
- case 'Q':
- return parseTokenOneDigit;
- case 'DDDD':
- return parseTokenThreeDigits;
- case 'YYYY':
- case 'GGGG':
- case 'gggg':
- return strict ? parseTokenFourDigits : parseTokenOneToFourDigits;
- case 'Y':
- case 'G':
- case 'g':
- return parseTokenSignedNumber;
- case 'YYYYYY':
- case 'YYYYY':
- case 'GGGGG':
- case 'ggggg':
- return strict ? parseTokenSixDigits : parseTokenOneToSixDigits;
- case 'S':
- if (strict) {
- return parseTokenOneDigit;
- }
- /* falls through */
- case 'SS':
- if (strict) {
- return parseTokenTwoDigits;
- }
- /* falls through */
- case 'SSS':
- if (strict) {
- return parseTokenThreeDigits;
- }
- /* falls through */
- case 'DDD':
- return parseTokenOneToThreeDigits;
- case 'MMM':
- case 'MMMM':
- case 'dd':
- case 'ddd':
- case 'dddd':
- return parseTokenWord;
- case 'a':
- case 'A':
- return config._locale._meridiemParse;
- case 'x':
- return parseTokenOffsetMs;
- case 'X':
- return parseTokenTimestampMs;
- case 'Z':
- case 'ZZ':
- return parseTokenTimezone;
- case 'T':
- return parseTokenT;
- case 'SSSS':
- return parseTokenDigits;
- case 'MM':
- case 'DD':
- case 'YY':
- case 'GG':
- case 'gg':
- case 'HH':
- case 'hh':
- case 'mm':
- case 'ss':
- case 'ww':
- case 'WW':
- return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits;
- case 'M':
- case 'D':
- case 'd':
- case 'H':
- case 'h':
- case 'm':
- case 's':
- case 'w':
- case 'W':
- case 'e':
- case 'E':
- return parseTokenOneOrTwoDigits;
- case 'Do':
- return strict ? config._locale._ordinalParse : config._locale._ordinalParseLenient;
- default :
- a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), 'i'));
- return a;
- }
- }
-
- function timezoneMinutesFromString(string) {
- string = string || '';
- var possibleTzMatches = (string.match(parseTokenTimezone) || []),
- tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [],
- parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0],
- minutes = +(parts[1] * 60) + toInt(parts[2]);
-
- return parts[0] === '+' ? -minutes : minutes;
- }
-
- // function to convert string input to date
- function addTimeToArrayFromToken(token, input, config) {
- var a, datePartArray = config._a;
-
- switch (token) {
- // QUARTER
- case 'Q':
- if (input != null) {
- datePartArray[MONTH] = (toInt(input) - 1) * 3;
- }
- break;
- // MONTH
- case 'M' : // fall through to MM
- case 'MM' :
- if (input != null) {
- datePartArray[MONTH] = toInt(input) - 1;
- }
- break;
- case 'MMM' : // fall through to MMMM
- case 'MMMM' :
- a = config._locale.monthsParse(input, token, config._strict);
- // if we didn't find a month name, mark the date as invalid.
- if (a != null) {
- datePartArray[MONTH] = a;
- } else {
- config._pf.invalidMonth = input;
- }
- break;
- // DAY OF MONTH
- case 'D' : // fall through to DD
- case 'DD' :
- if (input != null) {
- datePartArray[DATE] = toInt(input);
- }
- break;
- case 'Do' :
- if (input != null) {
- datePartArray[DATE] = toInt(parseInt(
- input.match(/\d{1,2}/)[0], 10));
- }
- break;
- // DAY OF YEAR
- case 'DDD' : // fall through to DDDD
- case 'DDDD' :
- if (input != null) {
- config._dayOfYear = toInt(input);
- }
-
- break;
- // YEAR
- case 'YY' :
- datePartArray[YEAR] = moment.parseTwoDigitYear(input);
- break;
- case 'YYYY' :
- case 'YYYYY' :
- case 'YYYYYY' :
- datePartArray[YEAR] = toInt(input);
- break;
- // AM / PM
- case 'a' : // fall through to A
- case 'A' :
- config._isPm = config._locale.isPM(input);
- break;
- // HOUR
- case 'h' : // fall through to hh
- case 'hh' :
- config._pf.bigHour = true;
- /* falls through */
- case 'H' : // fall through to HH
- case 'HH' :
- datePartArray[HOUR] = toInt(input);
- break;
- // MINUTE
- case 'm' : // fall through to mm
- case 'mm' :
- datePartArray[MINUTE] = toInt(input);
- break;
- // SECOND
- case 's' : // fall through to ss
- case 'ss' :
- datePartArray[SECOND] = toInt(input);
- break;
- // MILLISECOND
- case 'S' :
- case 'SS' :
- case 'SSS' :
- case 'SSSS' :
- datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000);
- break;
- // UNIX OFFSET (MILLISECONDS)
- case 'x':
- config._d = new Date(toInt(input));
- break;
- // UNIX TIMESTAMP WITH MS
- case 'X':
- config._d = new Date(parseFloat(input) * 1000);
- break;
- // TIMEZONE
- case 'Z' : // fall through to ZZ
- case 'ZZ' :
- config._useUTC = true;
- config._tzm = timezoneMinutesFromString(input);
- break;
- // WEEKDAY - human
- case 'dd':
- case 'ddd':
- case 'dddd':
- a = config._locale.weekdaysParse(input);
- // if we didn't get a weekday name, mark the date as invalid
- if (a != null) {
- config._w = config._w || {};
- config._w['d'] = a;
- } else {
- config._pf.invalidWeekday = input;
- }
- break;
- // WEEK, WEEK DAY - numeric
- case 'w':
- case 'ww':
- case 'W':
- case 'WW':
- case 'd':
- case 'e':
- case 'E':
- token = token.substr(0, 1);
- /* falls through */
- case 'gggg':
- case 'GGGG':
- case 'GGGGG':
- token = token.substr(0, 2);
- if (input) {
- config._w = config._w || {};
- config._w[token] = toInt(input);
- }
- break;
- case 'gg':
- case 'GG':
- config._w = config._w || {};
- config._w[token] = moment.parseTwoDigitYear(input);
- }
- }
-
- function dayOfYearFromWeekInfo(config) {
- var w, weekYear, week, weekday, dow, doy, temp;
-
- w = config._w;
- if (w.GG != null || w.W != null || w.E != null) {
- dow = 1;
- doy = 4;
-
- // TODO: We need to take the current isoWeekYear, but that depends on
- // how we interpret now (local, utc, fixed offset). So create
- // a now version of current config (take local/utc/offset flags, and
- // create now).
- weekYear = dfl(w.GG, config._a[YEAR], weekOfYear(moment(), 1, 4).year);
- week = dfl(w.W, 1);
- weekday = dfl(w.E, 1);
- } else {
- dow = config._locale._week.dow;
- doy = config._locale._week.doy;
-
- weekYear = dfl(w.gg, config._a[YEAR], weekOfYear(moment(), dow, doy).year);
- week = dfl(w.w, 1);
-
- if (w.d != null) {
- // weekday -- low day numbers are considered next week
- weekday = w.d;
- if (weekday < dow) {
- ++week;
- }
- } else if (w.e != null) {
- // local weekday -- counting starts from begining of week
- weekday = w.e + dow;
- } else {
- // default to begining of week
- weekday = dow;
- }
- }
- temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow);
-
- config._a[YEAR] = temp.year;
- config._dayOfYear = temp.dayOfYear;
- }
-
- // convert an array to a date.
- // the array should mirror the parameters below
- // note: all values past the year are optional and will default to the lowest possible value.
- // [year, month, day , hour, minute, second, millisecond]
- function dateFromConfig(config) {
- var i, date, input = [], currentDate, yearToUse;
-
- if (config._d) {
- return;
- }
-
- currentDate = currentDateArray(config);
-
- //compute day of the year from weeks and weekdays
- if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
- dayOfYearFromWeekInfo(config);
- }
-
- //if the day of the year is set, figure out what it is
- if (config._dayOfYear) {
- yearToUse = dfl(config._a[YEAR], currentDate[YEAR]);
-
- if (config._dayOfYear > daysInYear(yearToUse)) {
- config._pf._overflowDayOfYear = true;
- }
-
- date = makeUTCDate(yearToUse, 0, config._dayOfYear);
- config._a[MONTH] = date.getUTCMonth();
- config._a[DATE] = date.getUTCDate();
- }
-
- // Default to current date.
- // * if no year, month, day of month are given, default to today
- // * if day of month is given, default month and year
- // * if month is given, default only year
- // * if year is given, don't default anything
- for (i = 0; i < 3 && config._a[i] == null; ++i) {
- config._a[i] = input[i] = currentDate[i];
- }
-
- // Zero out whatever was not defaulted, including time
- for (; i < 7; i++) {
- config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
- }
-
- // Check for 24:00:00.000
- if (config._a[HOUR] === 24 &&
- config._a[MINUTE] === 0 &&
- config._a[SECOND] === 0 &&
- config._a[MILLISECOND] === 0) {
- config._nextDay = true;
- config._a[HOUR] = 0;
- }
-
- config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input);
- // Apply timezone offset from input. The actual zone can be changed
- // with parseZone.
- if (config._tzm != null) {
- config._d.setUTCMinutes(config._d.getUTCMinutes() + config._tzm);
- }
-
- if (config._nextDay) {
- config._a[HOUR] = 24;
- }
- }
-
- function dateFromObject(config) {
- var normalizedInput;
-
- if (config._d) {
- return;
- }
-
- normalizedInput = normalizeObjectUnits(config._i);
- config._a = [
- normalizedInput.year,
- normalizedInput.month,
- normalizedInput.day || normalizedInput.date,
- normalizedInput.hour,
- normalizedInput.minute,
- normalizedInput.second,
- normalizedInput.millisecond
- ];
-
- dateFromConfig(config);
- }
-
- function currentDateArray(config) {
- var now = new Date();
- if (config._useUTC) {
- return [
- now.getUTCFullYear(),
- now.getUTCMonth(),
- now.getUTCDate()
- ];
- } else {
- return [now.getFullYear(), now.getMonth(), now.getDate()];
- }
- }
-
- // date from string and format string
- function makeDateFromStringAndFormat(config) {
- if (config._f === moment.ISO_8601) {
- parseISO(config);
- return;
- }
-
- config._a = [];
- config._pf.empty = true;
-
- // This array is used to make a Date, either with `new Date` or `Date.UTC`
- var string = '' + config._i,
- i, parsedInput, tokens, token, skipped,
- stringLength = string.length,
- totalParsedInputLength = 0;
-
- tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
-
- for (i = 0; i < tokens.length; i++) {
- token = tokens[i];
- parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
- if (parsedInput) {
- skipped = string.substr(0, string.indexOf(parsedInput));
- if (skipped.length > 0) {
- config._pf.unusedInput.push(skipped);
- }
- string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
- totalParsedInputLength += parsedInput.length;
- }
- // don't parse if it's not a known token
- if (formatTokenFunctions[token]) {
- if (parsedInput) {
- config._pf.empty = false;
- }
- else {
- config._pf.unusedTokens.push(token);
- }
- addTimeToArrayFromToken(token, parsedInput, config);
- }
- else if (config._strict && !parsedInput) {
- config._pf.unusedTokens.push(token);
- }
- }
-
- // add remaining unparsed input length to the string
- config._pf.charsLeftOver = stringLength - totalParsedInputLength;
- if (string.length > 0) {
- config._pf.unusedInput.push(string);
- }
-
- // clear _12h flag if hour is <= 12
- if (config._pf.bigHour === true && config._a[HOUR] <= 12) {
- config._pf.bigHour = undefined;
- }
- // handle am pm
- if (config._isPm && config._a[HOUR] < 12) {
- config._a[HOUR] += 12;
- }
- // if is 12 am, change hours to 0
- if (config._isPm === false && config._a[HOUR] === 12) {
- config._a[HOUR] = 0;
- }
- dateFromConfig(config);
- checkOverflow(config);
- }
-
- function unescapeFormat(s) {
- return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
- return p1 || p2 || p3 || p4;
- });
- }
-
- // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
- function regexpEscape(s) {
- return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
- }
-
- // date from string and array of format strings
- function makeDateFromStringAndArray(config) {
- var tempConfig,
- bestMoment,
-
- scoreToBeat,
- i,
- currentScore;
-
- if (config._f.length === 0) {
- config._pf.invalidFormat = true;
- config._d = new Date(NaN);
- return;
- }
-
- for (i = 0; i < config._f.length; i++) {
- currentScore = 0;
- tempConfig = copyConfig({}, config);
- if (config._useUTC != null) {
- tempConfig._useUTC = config._useUTC;
- }
- tempConfig._pf = defaultParsingFlags();
- tempConfig._f = config._f[i];
- makeDateFromStringAndFormat(tempConfig);
-
- if (!isValid(tempConfig)) {
- continue;
- }
-
- // if there is any input that was not parsed add a penalty for that format
- currentScore += tempConfig._pf.charsLeftOver;
-
- //or tokens
- currentScore += tempConfig._pf.unusedTokens.length * 10;
-
- tempConfig._pf.score = currentScore;
-
- if (scoreToBeat == null || currentScore < scoreToBeat) {
- scoreToBeat = currentScore;
- bestMoment = tempConfig;
- }
- }
-
- extend(config, bestMoment || tempConfig);
- }
-
- // date from iso format
- function parseISO(config) {
- var i, l,
- string = config._i,
- match = isoRegex.exec(string);
-
- if (match) {
- config._pf.iso = true;
- for (i = 0, l = isoDates.length; i < l; i++) {
- if (isoDates[i][1].exec(string)) {
- // match[5] should be 'T' or undefined
- config._f = isoDates[i][0] + (match[6] || ' ');
- break;
- }
- }
- for (i = 0, l = isoTimes.length; i < l; i++) {
- if (isoTimes[i][1].exec(string)) {
- config._f += isoTimes[i][0];
- break;
- }
- }
- if (string.match(parseTokenTimezone)) {
- config._f += 'Z';
- }
- makeDateFromStringAndFormat(config);
- } else {
- config._isValid = false;
- }
- }
-
- // date from iso format or fallback
- function makeDateFromString(config) {
- parseISO(config);
- if (config._isValid === false) {
- delete config._isValid;
- moment.createFromInputFallback(config);
- }
- }
-
- function map(arr, fn) {
- var res = [], i;
- for (i = 0; i < arr.length; ++i) {
- res.push(fn(arr[i], i));
- }
- return res;
- }
-
- function makeDateFromInput(config) {
- var input = config._i, matched;
- if (input === undefined) {
- config._d = new Date();
- } else if (isDate(input)) {
- config._d = new Date(+input);
- } else if ((matched = aspNetJsonRegex.exec(input)) !== null) {
- config._d = new Date(+matched[1]);
- } else if (typeof input === 'string') {
- makeDateFromString(config);
- } else if (isArray(input)) {
- config._a = map(input.slice(0), function (obj) {
- return parseInt(obj, 10);
- });
- dateFromConfig(config);
- } else if (typeof(input) === 'object') {
- dateFromObject(config);
- } else if (typeof(input) === 'number') {
- // from milliseconds
- config._d = new Date(input);
- } else {
- moment.createFromInputFallback(config);
- }
- }
-
- function makeDate(y, m, d, h, M, s, ms) {
- //can't just apply() to create a date:
- //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
- var date = new Date(y, m, d, h, M, s, ms);
-
- //the date constructor doesn't accept years < 1970
- if (y < 1970) {
- date.setFullYear(y);
- }
- return date;
- }
-
- function makeUTCDate(y) {
- var date = new Date(Date.UTC.apply(null, arguments));
- if (y < 1970) {
- date.setUTCFullYear(y);
- }
- return date;
- }
-
- function parseWeekday(input, locale) {
- if (typeof input === 'string') {
- if (!isNaN(input)) {
- input = parseInt(input, 10);
- }
- else {
- input = locale.weekdaysParse(input);
- if (typeof input !== 'number') {
- return null;
- }
- }
- }
- return input;
- }
-
- /************************************
- Relative Time
- ************************************/
-
-
- // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
- function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
- return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
- }
-
- function relativeTime(posNegDuration, withoutSuffix, locale) {
- var duration = moment.duration(posNegDuration).abs(),
- seconds = round(duration.as('s')),
- minutes = round(duration.as('m')),
- hours = round(duration.as('h')),
- days = round(duration.as('d')),
- months = round(duration.as('M')),
- years = round(duration.as('y')),
-
- args = seconds < relativeTimeThresholds.s && ['s', seconds] ||
- minutes === 1 && ['m'] ||
- minutes < relativeTimeThresholds.m && ['mm', minutes] ||
- hours === 1 && ['h'] ||
- hours < relativeTimeThresholds.h && ['hh', hours] ||
- days === 1 && ['d'] ||
- days < relativeTimeThresholds.d && ['dd', days] ||
- months === 1 && ['M'] ||
- months < relativeTimeThresholds.M && ['MM', months] ||
- years === 1 && ['y'] || ['yy', years];
-
- args[2] = withoutSuffix;
- args[3] = +posNegDuration > 0;
- args[4] = locale;
- return substituteTimeAgo.apply({}, args);
- }
-
-
- /************************************
- Week of Year
- ************************************/
-
-
- // firstDayOfWeek 0 = sun, 6 = sat
- // the day of the week that starts the week
- // (usually sunday or monday)
- // firstDayOfWeekOfYear 0 = sun, 6 = sat
- // the first week is the week that contains the first
- // of this day of the week
- // (eg. ISO weeks use thursday (4))
- function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) {
- var end = firstDayOfWeekOfYear - firstDayOfWeek,
- daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(),
- adjustedMoment;
-
-
- if (daysToDayOfWeek > end) {
- daysToDayOfWeek -= 7;
- }
-
- if (daysToDayOfWeek < end - 7) {
- daysToDayOfWeek += 7;
- }
-
- adjustedMoment = moment(mom).add(daysToDayOfWeek, 'd');
- return {
- week: Math.ceil(adjustedMoment.dayOfYear() / 7),
- year: adjustedMoment.year()
- };
- }
-
- //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
- function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) {
- var d = makeUTCDate(year, 0, 1).getUTCDay(), daysToAdd, dayOfYear;
-
- d = d === 0 ? 7 : d;
- weekday = weekday != null ? weekday : firstDayOfWeek;
- daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0);
- dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1;
-
- return {
- year: dayOfYear > 0 ? year : year - 1,
- dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear
- };
- }
-
- /************************************
- Top Level Functions
- ************************************/
-
- function makeMoment(config) {
- var input = config._i,
- format = config._f,
- res;
-
- config._locale = config._locale || moment.localeData(config._l);
-
- if (input === null || (format === undefined && input === '')) {
- return moment.invalid({nullInput: true});
- }
-
- if (typeof input === 'string') {
- config._i = input = config._locale.preparse(input);
- }
-
- if (moment.isMoment(input)) {
- return new Moment(input, true);
- } else if (format) {
- if (isArray(format)) {
- makeDateFromStringAndArray(config);
- } else {
- makeDateFromStringAndFormat(config);
- }
- } else {
- makeDateFromInput(config);
- }
-
- res = new Moment(config);
- if (res._nextDay) {
- // Adding is smart enough around DST
- res.add(1, 'd');
- res._nextDay = undefined;
- }
-
- return res;
- }
-
- moment = function (input, format, locale, strict) {
- var c;
-
- if (typeof(locale) === 'boolean') {
- strict = locale;
- locale = undefined;
- }
- // object construction must be done this way.
- // https://github.com/moment/moment/issues/1423
- c = {};
- c._isAMomentObject = true;
- c._i = input;
- c._f = format;
- c._l = locale;
- c._strict = strict;
- c._isUTC = false;
- c._pf = defaultParsingFlags();
-
- return makeMoment(c);
- };
-
- moment.suppressDeprecationWarnings = false;
-
- moment.createFromInputFallback = deprecate(
- 'moment construction falls back to js Date. This is ' +
- 'discouraged and will be removed in upcoming major ' +
- 'release. Please refer to ' +
- 'https://github.com/moment/moment/issues/1407 for more info.',
- function (config) {
- config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
- }
- );
-
- // Pick a moment m from moments so that m[fn](other) is true for all
- // other. This relies on the function fn to be transitive.
- //
- // moments should either be an array of moment objects or an array, whose
- // first element is an array of moment objects.
- function pickBy(fn, moments) {
- var res, i;
- if (moments.length === 1 && isArray(moments[0])) {
- moments = moments[0];
- }
- if (!moments.length) {
- return moment();
- }
- res = moments[0];
- for (i = 1; i < moments.length; ++i) {
- if (moments[i][fn](res)) {
- res = moments[i];
- }
- }
- return res;
- }
-
- moment.min = function () {
- var args = [].slice.call(arguments, 0);
-
- return pickBy('isBefore', args);
- };
-
- moment.max = function () {
- var args = [].slice.call(arguments, 0);
-
- return pickBy('isAfter', args);
- };
-
- // creating with utc
- moment.utc = function (input, format, locale, strict) {
- var c;
-
- if (typeof(locale) === 'boolean') {
- strict = locale;
- locale = undefined;
- }
- // object construction must be done this way.
- // https://github.com/moment/moment/issues/1423
- c = {};
- c._isAMomentObject = true;
- c._useUTC = true;
- c._isUTC = true;
- c._l = locale;
- c._i = input;
- c._f = format;
- c._strict = strict;
- c._pf = defaultParsingFlags();
-
- return makeMoment(c).utc();
- };
-
- // creating with unix timestamp (in seconds)
- moment.unix = function (input) {
- return moment(input * 1000);
- };
-
- // duration
- moment.duration = function (input, key) {
- var duration = input,
- // matching against regexp is expensive, do it on demand
- match = null,
- sign,
- ret,
- parseIso,
- diffRes;
-
- if (moment.isDuration(input)) {
- duration = {
- ms: input._milliseconds,
- d: input._days,
- M: input._months
- };
- } else if (typeof input === 'number') {
- duration = {};
- if (key) {
- duration[key] = input;
- } else {
- duration.milliseconds = input;
- }
- } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) {
- sign = (match[1] === '-') ? -1 : 1;
- duration = {
- y: 0,
- d: toInt(match[DATE]) * sign,
- h: toInt(match[HOUR]) * sign,
- m: toInt(match[MINUTE]) * sign,
- s: toInt(match[SECOND]) * sign,
- ms: toInt(match[MILLISECOND]) * sign
- };
- } else if (!!(match = isoDurationRegex.exec(input))) {
- sign = (match[1] === '-') ? -1 : 1;
- parseIso = function (inp) {
- // We'd normally use ~~inp for this, but unfortunately it also
- // converts floats to ints.
- // inp may be undefined, so careful calling replace on it.
- var res = inp && parseFloat(inp.replace(',', '.'));
- // apply sign while we're at it
- return (isNaN(res) ? 0 : res) * sign;
- };
- duration = {
- y: parseIso(match[2]),
- M: parseIso(match[3]),
- d: parseIso(match[4]),
- h: parseIso(match[5]),
- m: parseIso(match[6]),
- s: parseIso(match[7]),
- w: parseIso(match[8])
- };
- } else if (typeof duration === 'object' &&
- ('from' in duration || 'to' in duration)) {
- diffRes = momentsDifference(moment(duration.from), moment(duration.to));
-
- duration = {};
- duration.ms = diffRes.milliseconds;
- duration.M = diffRes.months;
- }
-
- ret = new Duration(duration);
-
- if (moment.isDuration(input) && hasOwnProp(input, '_locale')) {
- ret._locale = input._locale;
- }
-
- return ret;
- };
-
- // version number
- moment.version = VERSION;
-
- // default format
- moment.defaultFormat = isoFormat;
-
- // constant that refers to the ISO standard
- moment.ISO_8601 = function () {};
-
- // Plugins that add properties should also add the key here (null value),
- // so we can properly clone ourselves.
- moment.momentProperties = momentProperties;
-
- // This function will be called whenever a moment is mutated.
- // It is intended to keep the offset in sync with the timezone.
- moment.updateOffset = function () {};
-
- // This function allows you to set a threshold for relative time strings
- moment.relativeTimeThreshold = function (threshold, limit) {
- if (relativeTimeThresholds[threshold] === undefined) {
- return false;
- }
- if (limit === undefined) {
- return relativeTimeThresholds[threshold];
- }
- relativeTimeThresholds[threshold] = limit;
- return true;
- };
-
- moment.lang = deprecate(
- 'moment.lang is deprecated. Use moment.locale instead.',
- function (key, value) {
- return moment.locale(key, value);
- }
- );
-
- // This function will load locale and then set the global locale. If
- // no arguments are passed in, it will simply return the current global
- // locale key.
- moment.locale = function (key, values) {
- var data;
- if (key) {
- if (typeof(values) !== 'undefined') {
- data = moment.defineLocale(key, values);
- }
- else {
- data = moment.localeData(key);
- }
-
- if (data) {
- moment.duration._locale = moment._locale = data;
- }
- }
-
- return moment._locale._abbr;
- };
-
- moment.defineLocale = function (name, values) {
- if (values !== null) {
- values.abbr = name;
- if (!locales[name]) {
- locales[name] = new Locale();
- }
- locales[name].set(values);
-
- // backwards compat for now: also set the locale
- moment.locale(name);
-
- return locales[name];
- } else {
- // useful for testing
- delete locales[name];
- return null;
- }
- };
-
- moment.langData = deprecate(
- 'moment.langData is deprecated. Use moment.localeData instead.',
- function (key) {
- return moment.localeData(key);
- }
- );
-
- // returns locale data
- moment.localeData = function (key) {
- var locale;
-
- if (key && key._locale && key._locale._abbr) {
- key = key._locale._abbr;
- }
-
- if (!key) {
- return moment._locale;
- }
-
- if (!isArray(key)) {
- //short-circuit everything else
- locale = loadLocale(key);
- if (locale) {
- return locale;
- }
- key = [key];
- }
-
- return chooseLocale(key);
- };
-
- // compare moment object
- moment.isMoment = function (obj) {
- return obj instanceof Moment ||
- (obj != null && hasOwnProp(obj, '_isAMomentObject'));
- };
-
- // for typechecking Duration objects
- moment.isDuration = function (obj) {
- return obj instanceof Duration;
- };
-
- for (i = lists.length - 1; i >= 0; --i) {
- makeList(lists[i]);
- }
-
- moment.normalizeUnits = function (units) {
- return normalizeUnits(units);
- };
-
- moment.invalid = function (flags) {
- var m = moment.utc(NaN);
- if (flags != null) {
- extend(m._pf, flags);
- }
- else {
- m._pf.userInvalidated = true;
- }
-
- return m;
- };
-
- moment.parseZone = function () {
- return moment.apply(null, arguments).parseZone();
- };
-
- moment.parseTwoDigitYear = function (input) {
- return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
- };
-
- /************************************
- Moment Prototype
- ************************************/
-
-
- extend(moment.fn = Moment.prototype, {
-
- clone : function () {
- return moment(this);
- },
-
- valueOf : function () {
- return +this._d + ((this._offset || 0) * 60000);
- },
-
- unix : function () {
- return Math.floor(+this / 1000);
- },
-
- toString : function () {
- return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
- },
-
- toDate : function () {
- return this._offset ? new Date(+this) : this._d;
- },
-
- toISOString : function () {
- var m = moment(this).utc();
- if (0 < m.year() && m.year() <= 9999) {
- if ('function' === typeof Date.prototype.toISOString) {
- // native implementation is ~50x faster, use it when we can
- return this.toDate().toISOString();
- } else {
- return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
- }
- } else {
- return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
- }
- },
-
- toArray : function () {
- var m = this;
- return [
- m.year(),
- m.month(),
- m.date(),
- m.hours(),
- m.minutes(),
- m.seconds(),
- m.milliseconds()
- ];
- },
-
- isValid : function () {
- return isValid(this);
- },
-
- isDSTShifted : function () {
- if (this._a) {
- return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0;
- }
-
- return false;
- },
-
- parsingFlags : function () {
- return extend({}, this._pf);
- },
-
- invalidAt: function () {
- return this._pf.overflow;
- },
-
- utc : function (keepLocalTime) {
- return this.zone(0, keepLocalTime);
- },
-
- local : function (keepLocalTime) {
- if (this._isUTC) {
- this.zone(0, keepLocalTime);
- this._isUTC = false;
-
- if (keepLocalTime) {
- this.add(this._dateTzOffset(), 'm');
- }
- }
- return this;
- },
-
- format : function (inputString) {
- var output = formatMoment(this, inputString || moment.defaultFormat);
- return this.localeData().postformat(output);
- },
-
- add : createAdder(1, 'add'),
-
- subtract : createAdder(-1, 'subtract'),
-
- diff : function (input, units, asFloat) {
- var that = makeAs(input, this),
- zoneDiff = (this.zone() - that.zone()) * 6e4,
- diff, output, daysAdjust;
-
- units = normalizeUnits(units);
-
- if (units === 'year' || units === 'month') {
- // average number of days in the months in the given dates
- diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2
- // difference in months
- output = ((this.year() - that.year()) * 12) + (this.month() - that.month());
- // adjust by taking difference in days, average number of days
- // and dst in the given months.
- daysAdjust = (this - moment(this).startOf('month')) -
- (that - moment(that).startOf('month'));
- // same as above but with zones, to negate all dst
- daysAdjust -= ((this.zone() - moment(this).startOf('month').zone()) -
- (that.zone() - moment(that).startOf('month').zone())) * 6e4;
- output += daysAdjust / diff;
- if (units === 'year') {
- output = output / 12;
- }
- } else {
- diff = (this - that);
- output = units === 'second' ? diff / 1e3 : // 1000
- units === 'minute' ? diff / 6e4 : // 1000 * 60
- units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60
- units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
- units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
- diff;
- }
- return asFloat ? output : absRound(output);
- },
-
- from : function (time, withoutSuffix) {
- return moment.duration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
- },
-
- fromNow : function (withoutSuffix) {
- return this.from(moment(), withoutSuffix);
- },
-
- calendar : function (time) {
- // We want to compare the start of today, vs this.
- // Getting start-of-today depends on whether we're zone'd or not.
- var now = time || moment(),
- sod = makeAs(now, this).startOf('day'),
- diff = this.diff(sod, 'days', true),
- format = diff < -6 ? 'sameElse' :
- diff < -1 ? 'lastWeek' :
- diff < 0 ? 'lastDay' :
- diff < 1 ? 'sameDay' :
- diff < 2 ? 'nextDay' :
- diff < 7 ? 'nextWeek' : 'sameElse';
- return this.format(this.localeData().calendar(format, this, moment(now)));
- },
-
- isLeapYear : function () {
- return isLeapYear(this.year());
- },
-
- isDST : function () {
- return (this.zone() < this.clone().month(0).zone() ||
- this.zone() < this.clone().month(5).zone());
- },
-
- day : function (input) {
- var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
- if (input != null) {
- input = parseWeekday(input, this.localeData());
- return this.add(input - day, 'd');
- } else {
- return day;
- }
- },
-
- month : makeAccessor('Month', true),
-
- startOf : function (units) {
- units = normalizeUnits(units);
- // the following switch intentionally omits break keywords
- // to utilize falling through the cases.
- switch (units) {
- case 'year':
- this.month(0);
- /* falls through */
- case 'quarter':
- case 'month':
- this.date(1);
- /* falls through */
- case 'week':
- case 'isoWeek':
- case 'day':
- this.hours(0);
- /* falls through */
- case 'hour':
- this.minutes(0);
- /* falls through */
- case 'minute':
- this.seconds(0);
- /* falls through */
- case 'second':
- this.milliseconds(0);
- /* falls through */
- }
-
- // weeks are a special case
- if (units === 'week') {
- this.weekday(0);
- } else if (units === 'isoWeek') {
- this.isoWeekday(1);
- }
-
- // quarters are also special
- if (units === 'quarter') {
- this.month(Math.floor(this.month() / 3) * 3);
- }
-
- return this;
- },
-
- endOf: function (units) {
- units = normalizeUnits(units);
- if (units === undefined || units === 'millisecond') {
- return this;
- }
- return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
- },
-
- isAfter: function (input, units) {
- var inputMs;
- units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
- if (units === 'millisecond') {
- input = moment.isMoment(input) ? input : moment(input);
- return +this > +input;
- } else {
- inputMs = moment.isMoment(input) ? +input : +moment(input);
- return inputMs < +this.clone().startOf(units);
- }
- },
-
- isBefore: function (input, units) {
- var inputMs;
- units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond');
- if (units === 'millisecond') {
- input = moment.isMoment(input) ? input : moment(input);
- return +this < +input;
- } else {
- inputMs = moment.isMoment(input) ? +input : +moment(input);
- return +this.clone().endOf(units) < inputMs;
- }
- },
-
- isSame: function (input, units) {
- var inputMs;
- units = normalizeUnits(units || 'millisecond');
- if (units === 'millisecond') {
- input = moment.isMoment(input) ? input : moment(input);
- return +this === +input;
- } else {
- inputMs = +moment(input);
- return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units));
- }
- },
-
- min: deprecate(
- 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548',
- function (other) {
- other = moment.apply(null, arguments);
- return other < this ? this : other;
- }
- ),
-
- max: deprecate(
- 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548',
- function (other) {
- other = moment.apply(null, arguments);
- return other > this ? this : other;
- }
- ),
-
- // keepLocalTime = true means only change the timezone, without
- // affecting the local hour. So 5:31:26 +0300 --[zone(2, true)]-->
- // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist int zone
- // +0200, so we adjust the time as needed, to be valid.
- //
- // Keeping the time actually adds/subtracts (one hour)
- // from the actual represented time. That is why we call updateOffset
- // a second time. In case it wants us to change the offset again
- // _changeInProgress == true case, then we have to adjust, because
- // there is no such time in the given timezone.
- zone : function (input, keepLocalTime) {
- var offset = this._offset || 0,
- localAdjust;
- if (input != null) {
- if (typeof input === 'string') {
- input = timezoneMinutesFromString(input);
- }
- if (Math.abs(input) < 16) {
- input = input * 60;
- }
- if (!this._isUTC && keepLocalTime) {
- localAdjust = this._dateTzOffset();
- }
- this._offset = input;
- this._isUTC = true;
- if (localAdjust != null) {
- this.subtract(localAdjust, 'm');
- }
- if (offset !== input) {
- if (!keepLocalTime || this._changeInProgress) {
- addOrSubtractDurationFromMoment(this,
- moment.duration(offset - input, 'm'), 1, false);
- } else if (!this._changeInProgress) {
- this._changeInProgress = true;
- moment.updateOffset(this, true);
- this._changeInProgress = null;
- }
- }
- } else {
- return this._isUTC ? offset : this._dateTzOffset();
- }
- return this;
- },
-
- zoneAbbr : function () {
- return this._isUTC ? 'UTC' : '';
- },
-
- zoneName : function () {
- return this._isUTC ? 'Coordinated Universal Time' : '';
- },
-
- parseZone : function () {
- if (this._tzm) {
- this.zone(this._tzm);
- } else if (typeof this._i === 'string') {
- this.zone(this._i);
- }
- return this;
- },
-
- hasAlignedHourOffset : function (input) {
- if (!input) {
- input = 0;
- }
- else {
- input = moment(input).zone();
- }
-
- return (this.zone() - input) % 60 === 0;
- },
-
- daysInMonth : function () {
- return daysInMonth(this.year(), this.month());
- },
-
- dayOfYear : function (input) {
- var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1;
- return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
- },
-
- quarter : function (input) {
- return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
- },
-
- weekYear : function (input) {
- var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year;
- return input == null ? year : this.add((input - year), 'y');
- },
-
- isoWeekYear : function (input) {
- var year = weekOfYear(this, 1, 4).year;
- return input == null ? year : this.add((input - year), 'y');
- },
-
- week : function (input) {
- var week = this.localeData().week(this);
- return input == null ? week : this.add((input - week) * 7, 'd');
- },
-
- isoWeek : function (input) {
- var week = weekOfYear(this, 1, 4).week;
- return input == null ? week : this.add((input - week) * 7, 'd');
- },
-
- weekday : function (input) {
- var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
- return input == null ? weekday : this.add(input - weekday, 'd');
- },
-
- isoWeekday : function (input) {
- // behaves the same as moment#day except
- // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
- // as a setter, sunday should belong to the previous week.
- return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7);
- },
-
- isoWeeksInYear : function () {
- return weeksInYear(this.year(), 1, 4);
- },
-
- weeksInYear : function () {
- var weekInfo = this.localeData()._week;
- return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
- },
-
- get : function (units) {
- units = normalizeUnits(units);
- return this[units]();
- },
-
- set : function (units, value) {
- units = normalizeUnits(units);
- if (typeof this[units] === 'function') {
- this[units](value);
- }
- return this;
- },
-
- // If passed a locale key, it will set the locale for this
- // instance. Otherwise, it will return the locale configuration
- // variables for this instance.
- locale : function (key) {
- var newLocaleData;
-
- if (key === undefined) {
- return this._locale._abbr;
- } else {
- newLocaleData = moment.localeData(key);
- if (newLocaleData != null) {
- this._locale = newLocaleData;
- }
- return this;
- }
- },
-
- lang : deprecate(
- 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
- function (key) {
- if (key === undefined) {
- return this.localeData();
- } else {
- return this.locale(key);
- }
- }
- ),
-
- localeData : function () {
- return this._locale;
- },
-
- _dateTzOffset : function () {
- // On Firefox.24 Date#getTimezoneOffset returns a floating point.
- // https://github.com/moment/moment/pull/1871
- return Math.round(this._d.getTimezoneOffset() / 15) * 15;
- }
- });
-
- function rawMonthSetter(mom, value) {
- var dayOfMonth;
-
- // TODO: Move this out of here!
- if (typeof value === 'string') {
- value = mom.localeData().monthsParse(value);
- // TODO: Another silent failure?
- if (typeof value !== 'number') {
- return mom;
- }
- }
-
- dayOfMonth = Math.min(mom.date(),
- daysInMonth(mom.year(), value));
- mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
- return mom;
- }
-
- function rawGetter(mom, unit) {
- return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]();
- }
-
- function rawSetter(mom, unit, value) {
- if (unit === 'Month') {
- return rawMonthSetter(mom, value);
- } else {
- return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
- }
- }
-
- function makeAccessor(unit, keepTime) {
- return function (value) {
- if (value != null) {
- rawSetter(this, unit, value);
- moment.updateOffset(this, keepTime);
- return this;
- } else {
- return rawGetter(this, unit);
- }
- };
- }
-
- moment.fn.millisecond = moment.fn.milliseconds = makeAccessor('Milliseconds', false);
- moment.fn.second = moment.fn.seconds = makeAccessor('Seconds', false);
- moment.fn.minute = moment.fn.minutes = makeAccessor('Minutes', false);
- // Setting the hour should keep the time, because the user explicitly
- // specified which hour he wants. So trying to maintain the same hour (in
- // a new timezone) makes sense. Adding/subtracting hours does not follow
- // this rule.
- moment.fn.hour = moment.fn.hours = makeAccessor('Hours', true);
- // moment.fn.month is defined separately
- moment.fn.date = makeAccessor('Date', true);
- moment.fn.dates = deprecate('dates accessor is deprecated. Use date instead.', makeAccessor('Date', true));
- moment.fn.year = makeAccessor('FullYear', true);
- moment.fn.years = deprecate('years accessor is deprecated. Use year instead.', makeAccessor('FullYear', true));
-
- // add plural methods
- moment.fn.days = moment.fn.day;
- moment.fn.months = moment.fn.month;
- moment.fn.weeks = moment.fn.week;
- moment.fn.isoWeeks = moment.fn.isoWeek;
- moment.fn.quarters = moment.fn.quarter;
-
- // add aliased format methods
- moment.fn.toJSON = moment.fn.toISOString;
-
- /************************************
- Duration Prototype
- ************************************/
-
-
- function daysToYears (days) {
- // 400 years have 146097 days (taking into account leap year rules)
- return days * 400 / 146097;
- }
-
- function yearsToDays (years) {
- // years * 365 + absRound(years / 4) -
- // absRound(years / 100) + absRound(years / 400);
- return years * 146097 / 400;
- }
-
- extend(moment.duration.fn = Duration.prototype, {
-
- _bubble : function () {
- var milliseconds = this._milliseconds,
- days = this._days,
- months = this._months,
- data = this._data,
- seconds, minutes, hours, years = 0;
-
- // The following code bubbles up values, see the tests for
- // examples of what that means.
- data.milliseconds = milliseconds % 1000;
-
- seconds = absRound(milliseconds / 1000);
- data.seconds = seconds % 60;
-
- minutes = absRound(seconds / 60);
- data.minutes = minutes % 60;
-
- hours = absRound(minutes / 60);
- data.hours = hours % 24;
-
- days += absRound(hours / 24);
-
- // Accurately convert days to years, assume start from year 0.
- years = absRound(daysToYears(days));
- days -= absRound(yearsToDays(years));
-
- // 30 days to a month
- // TODO (iskren): Use anchor date (like 1st Jan) to compute this.
- months += absRound(days / 30);
- days %= 30;
-
- // 12 months -> 1 year
- years += absRound(months / 12);
- months %= 12;
-
- data.days = days;
- data.months = months;
- data.years = years;
- },
-
- abs : function () {
- this._milliseconds = Math.abs(this._milliseconds);
- this._days = Math.abs(this._days);
- this._months = Math.abs(this._months);
-
- this._data.milliseconds = Math.abs(this._data.milliseconds);
- this._data.seconds = Math.abs(this._data.seconds);
- this._data.minutes = Math.abs(this._data.minutes);
- this._data.hours = Math.abs(this._data.hours);
- this._data.months = Math.abs(this._data.months);
- this._data.years = Math.abs(this._data.years);
-
- return this;
- },
-
- weeks : function () {
- return absRound(this.days() / 7);
- },
-
- valueOf : function () {
- return this._milliseconds +
- this._days * 864e5 +
- (this._months % 12) * 2592e6 +
- toInt(this._months / 12) * 31536e6;
- },
-
- humanize : function (withSuffix) {
- var output = relativeTime(this, !withSuffix, this.localeData());
-
- if (withSuffix) {
- output = this.localeData().pastFuture(+this, output);
- }
-
- return this.localeData().postformat(output);
- },
-
- add : function (input, val) {
- // supports only 2.0-style add(1, 's') or add(moment)
- var dur = moment.duration(input, val);
-
- this._milliseconds += dur._milliseconds;
- this._days += dur._days;
- this._months += dur._months;
-
- this._bubble();
-
- return this;
- },
-
- subtract : function (input, val) {
- var dur = moment.duration(input, val);
-
- this._milliseconds -= dur._milliseconds;
- this._days -= dur._days;
- this._months -= dur._months;
-
- this._bubble();
-
- return this;
- },
-
- get : function (units) {
- units = normalizeUnits(units);
- return this[units.toLowerCase() + 's']();
- },
-
- as : function (units) {
- var days, months;
- units = normalizeUnits(units);
-
- if (units === 'month' || units === 'year') {
- days = this._days + this._milliseconds / 864e5;
- months = this._months + daysToYears(days) * 12;
- return units === 'month' ? months : months / 12;
- } else {
- // handle milliseconds separately because of floating point math errors (issue #1867)
- days = this._days + Math.round(yearsToDays(this._months / 12));
- switch (units) {
- case 'week': return days / 7 + this._milliseconds / 6048e5;
- case 'day': return days + this._milliseconds / 864e5;
- case 'hour': return days * 24 + this._milliseconds / 36e5;
- case 'minute': return days * 24 * 60 + this._milliseconds / 6e4;
- case 'second': return days * 24 * 60 * 60 + this._milliseconds / 1000;
- // Math.floor prevents floating point math errors here
- case 'millisecond': return Math.floor(days * 24 * 60 * 60 * 1000) + this._milliseconds;
- default: throw new Error('Unknown unit ' + units);
- }
- }
- },
-
- lang : moment.fn.lang,
- locale : moment.fn.locale,
-
- toIsoString : deprecate(
- 'toIsoString() is deprecated. Please use toISOString() instead ' +
- '(notice the capitals)',
- function () {
- return this.toISOString();
- }
- ),
-
- toISOString : function () {
- // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
- var years = Math.abs(this.years()),
- months = Math.abs(this.months()),
- days = Math.abs(this.days()),
- hours = Math.abs(this.hours()),
- minutes = Math.abs(this.minutes()),
- seconds = Math.abs(this.seconds() + this.milliseconds() / 1000);
-
- if (!this.asSeconds()) {
- // this is the same as C#'s (Noda) and python (isodate)...
- // but not other JS (goog.date)
- return 'P0D';
- }
-
- return (this.asSeconds() < 0 ? '-' : '') +
- 'P' +
- (years ? years + 'Y' : '') +
- (months ? months + 'M' : '') +
- (days ? days + 'D' : '') +
- ((hours || minutes || seconds) ? 'T' : '') +
- (hours ? hours + 'H' : '') +
- (minutes ? minutes + 'M' : '') +
- (seconds ? seconds + 'S' : '');
- },
-
- localeData : function () {
- return this._locale;
- }
- });
-
- moment.duration.fn.toString = moment.duration.fn.toISOString;
-
- function makeDurationGetter(name) {
- moment.duration.fn[name] = function () {
- return this._data[name];
- };
- }
-
- for (i in unitMillisecondFactors) {
- if (hasOwnProp(unitMillisecondFactors, i)) {
- makeDurationGetter(i.toLowerCase());
- }
- }
-
- moment.duration.fn.asMilliseconds = function () {
- return this.as('ms');
- };
- moment.duration.fn.asSeconds = function () {
- return this.as('s');
- };
- moment.duration.fn.asMinutes = function () {
- return this.as('m');
- };
- moment.duration.fn.asHours = function () {
- return this.as('h');
- };
- moment.duration.fn.asDays = function () {
- return this.as('d');
- };
- moment.duration.fn.asWeeks = function () {
- return this.as('weeks');
- };
- moment.duration.fn.asMonths = function () {
- return this.as('M');
- };
- moment.duration.fn.asYears = function () {
- return this.as('y');
- };
-
- /************************************
- Default Locale
- ************************************/
-
-
- // Set default locale, other locale will inherit from English.
- moment.locale('en', {
- ordinalParse: /\d{1,2}(th|st|nd|rd)/,
- ordinal : function (number) {
- var b = number % 10,
- output = (toInt(number % 100 / 10) === 1) ? 'th' :
- (b === 1) ? 'st' :
- (b === 2) ? 'nd' :
- (b === 3) ? 'rd' : 'th';
- return number + output;
- }
- });
-
- /* EMBED_LOCALES */
-
- /************************************
- Exposing Moment
- ************************************/
-
- function makeGlobal(shouldDeprecate) {
- /*global ender:false */
- if (typeof ender !== 'undefined') {
- return;
- }
- oldGlobalMoment = globalScope.moment;
- if (shouldDeprecate) {
- globalScope.moment = deprecate(
- 'Accessing Moment through the global scope is ' +
- 'deprecated, and will be removed in an upcoming ' +
- 'release.',
- moment);
- } else {
- globalScope.moment = moment;
- }
- }
-
- // CommonJS module is defined
- if (hasModule) {
- module.exports = moment;
- } else if (true) {
- !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require, exports, module) {
- if (module.config && module.config() && module.config().noGlobal === true) {
- // release the global variable
- globalScope.moment = oldGlobalMoment;
- }
-
- return moment;
- }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
- makeGlobal(true);
- } else {
- makeGlobal();
- }
- }).call(this);
-
- /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(5)(module)))
-
-/***/ },
-/* 4 */
-/***/ function(module, exports, __webpack_require__) {
-
- function webpackContext(req) {
- throw new Error("Cannot find module '" + req + "'.");
- }
- webpackContext.keys = function() { return []; };
- webpackContext.resolve = webpackContext;
- module.exports = webpackContext;
- webpackContext.id = 4;
-
-
-/***/ },
-/* 5 */
-/***/ function(module, exports, __webpack_require__) {
-
- module.exports = function(module) {
- if(!module.webpackPolyfill) {
- module.deprecate = function() {};
- module.paths = [];
- // module.parent = undefined by default
- module.children = [];
- module.webpackPolyfill = 1;
- }
- return module;
- }
-
-
-/***/ },
-/* 6 */
-/***/ function(module, exports, __webpack_require__) {
-
- // DOM utility methods
-
- /**
- * this prepares the JSON container for allocating SVG elements
- * @param JSONcontainer
- * @private
- */
- exports.prepareElements = function(JSONcontainer) {
- // cleanup the redundant svgElements;
- for (var elementType in JSONcontainer) {
- if (JSONcontainer.hasOwnProperty(elementType)) {
- JSONcontainer[elementType].redundant = JSONcontainer[elementType].used;
- JSONcontainer[elementType].used = [];
- }
- }
- };
-
- /**
- * this cleans up all the unused SVG elements. By asking for the parentNode, we only need to supply the JSON container from
- * which to remove the redundant elements.
- *
- * @param JSONcontainer
- * @private
- */
- exports.cleanupElements = function(JSONcontainer) {
- // cleanup the redundant svgElements;
- for (var elementType in JSONcontainer) {
- if (JSONcontainer.hasOwnProperty(elementType)) {
- if (JSONcontainer[elementType].redundant) {
- for (var i = 0; i < JSONcontainer[elementType].redundant.length; i++) {
- JSONcontainer[elementType].redundant[i].parentNode.removeChild(JSONcontainer[elementType].redundant[i]);
- }
- JSONcontainer[elementType].redundant = [];
- }
- }
- }
- };
-
- /**
- * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer
- * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this.
- *
- * @param elementType
- * @param JSONcontainer
- * @param svgContainer
- * @returns {*}
- * @private
- */
- exports.getSVGElement = function (elementType, JSONcontainer, svgContainer) {
- var element;
- // allocate SVG element, if it doesnt yet exist, create one.
- if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before
- // check if there is an redundant element
- if (JSONcontainer[elementType].redundant.length > 0) {
- element = JSONcontainer[elementType].redundant[0];
- JSONcontainer[elementType].redundant.shift();
- }
- else {
- // create a new element and add it to the SVG
- element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
- svgContainer.appendChild(element);
- }
- }
- else {
- // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
- element = document.createElementNS('http://www.w3.org/2000/svg', elementType);
- JSONcontainer[elementType] = {used: [], redundant: []};
- svgContainer.appendChild(element);
- }
- JSONcontainer[elementType].used.push(element);
- return element;
- };
-
-
- /**
- * Allocate or generate an SVG element if needed. Store a reference to it in the JSON container and draw it in the svgContainer
- * the JSON container and the SVG container have to be supplied so other svg containers (like the legend) can use this.
- *
- * @param elementType
- * @param JSONcontainer
- * @param DOMContainer
- * @returns {*}
- * @private
- */
- exports.getDOMElement = function (elementType, JSONcontainer, DOMContainer, insertBefore) {
- var element;
- // allocate DOM element, if it doesnt yet exist, create one.
- if (JSONcontainer.hasOwnProperty(elementType)) { // this element has been created before
- // check if there is an redundant element
- if (JSONcontainer[elementType].redundant.length > 0) {
- element = JSONcontainer[elementType].redundant[0];
- JSONcontainer[elementType].redundant.shift();
- }
- else {
- // create a new element and add it to the SVG
- element = document.createElement(elementType);
- if (insertBefore !== undefined) {
- DOMContainer.insertBefore(element, insertBefore);
- }
- else {
- DOMContainer.appendChild(element);
- }
- }
- }
- else {
- // create a new element and add it to the SVG, also create a new object in the svgElements to keep track of it.
- element = document.createElement(elementType);
- JSONcontainer[elementType] = {used: [], redundant: []};
- if (insertBefore !== undefined) {
- DOMContainer.insertBefore(element, insertBefore);
- }
- else {
- DOMContainer.appendChild(element);
- }
- }
- JSONcontainer[elementType].used.push(element);
- return element;
- };
-
-
-
-
- /**
- * draw a point object. this is a seperate function because it can also be called by the legend.
- * The reason the JSONcontainer and the target SVG svgContainer have to be supplied is so the legend can use these functions
- * as well.
- *
- * @param x
- * @param y
- * @param group
- * @param JSONcontainer
- * @param svgContainer
- * @returns {*}
- */
- exports.drawPoint = function(x, y, group, JSONcontainer, svgContainer) {
- var point;
- if (group.options.drawPoints.style == 'circle') {
- point = exports.getSVGElement('circle',JSONcontainer,svgContainer);
- point.setAttributeNS(null, "cx", x);
- point.setAttributeNS(null, "cy", y);
- point.setAttributeNS(null, "r", 0.5 * group.options.drawPoints.size);
- }
- else {
- point = exports.getSVGElement('rect',JSONcontainer,svgContainer);
- point.setAttributeNS(null, "x", x - 0.5*group.options.drawPoints.size);
- point.setAttributeNS(null, "y", y - 0.5*group.options.drawPoints.size);
- point.setAttributeNS(null, "width", group.options.drawPoints.size);
- point.setAttributeNS(null, "height", group.options.drawPoints.size);
- }
-
- if(group.options.drawPoints.styles !== undefined) {
- point.setAttributeNS(null, "style", group.group.options.drawPoints.styles);
- }
- point.setAttributeNS(null, "class", group.className + " point");
- return point;
- };
-
- /**
- * draw a bar SVG element centered on the X coordinate
- *
- * @param x
- * @param y
- * @param className
- */
- exports.drawBar = function (x, y, width, height, className, JSONcontainer, svgContainer) {
- if (height != 0) {
- if (height < 0) {
- height *= -1;
- y -= height;
- }
- var rect = exports.getSVGElement('rect',JSONcontainer, svgContainer);
- rect.setAttributeNS(null, "x", x - 0.5 * width);
- rect.setAttributeNS(null, "y", y);
- rect.setAttributeNS(null, "width", width);
- rect.setAttributeNS(null, "height", height);
- rect.setAttributeNS(null, "class", className);
- }
- };
-
-/***/ },
-/* 7 */
-/***/ function(module, exports, __webpack_require__) {
-
- var util = __webpack_require__(1);
- var Queue = __webpack_require__(8);
-
- /**
- * DataSet
- *
- * Usage:
- * var dataSet = new DataSet({
- * fieldId: '_id',
- * type: {
- * // ...
- * }
- * });
- *
- * dataSet.add(item);
- * dataSet.add(data);
- * dataSet.update(item);
- * dataSet.update(data);
- * dataSet.remove(id);
- * dataSet.remove(ids);
- * var data = dataSet.get();
- * var data = dataSet.get(id);
- * var data = dataSet.get(ids);
- * var data = dataSet.get(ids, options, data);
- * dataSet.clear();
- *
- * A data set can:
- * - add/remove/update data
- * - gives triggers upon changes in the data
- * - can import/export data in various data formats
- *
- * @param {Array | DataTable} [data] Optional array with initial data
- * @param {Object} [options] Available options:
- * {String} fieldId Field name of the id in the
- * items, 'id' by default.
- * {Object.} [type]
- * {String[]} [fields] field names to be returned
- * {function} [filter] filter items
- * {String | function} [order] Order the items by
- * a field name or custom sort function.
- * {Array | DataTable} [data] If provided, items will be appended to this
- * array or table. Required in case of Google
- * DataTable.
- *
- * @throws Error
- */
- DataSet.prototype.get = function (args) {
- var me = this;
-
- // parse the arguments
- var id, ids, options, data;
- var firstType = util.getType(arguments[0]);
- if (firstType == 'String' || firstType == 'Number') {
- // get(id [, options] [, data])
- id = arguments[0];
- options = arguments[1];
- data = arguments[2];
- }
- else if (firstType == 'Array') {
- // get(ids [, options] [, data])
- ids = arguments[0];
- options = arguments[1];
- data = arguments[2];
- }
- else {
- // get([, options] [, data])
- options = arguments[0];
- data = arguments[1];
- }
-
- // determine the return type
- var returnType;
- if (options && options.returnType) {
- var allowedValues = ["DataTable", "Array", "Object"];
- returnType = allowedValues.indexOf(options.returnType) == -1 ? "Array" : options.returnType;
-
- if (data && (returnType != util.getType(data))) {
- throw new Error('Type of parameter "data" (' + util.getType(data) + ') ' +
- 'does not correspond with specified options.type (' + options.type + ')');
- }
- if (returnType == 'DataTable' && !util.isDataTable(data)) {
- throw new Error('Parameter "data" must be a DataTable ' +
- 'when options.type is "DataTable"');
- }
- }
- else if (data) {
- returnType = (util.getType(data) == 'DataTable') ? 'DataTable' : 'Array';
- }
- else {
- returnType = 'Array';
- }
-
- // build options
- var type = options && options.type || this._options.type;
- var filter = options && options.filter;
- var items = [], item, itemId, i, len;
-
- // convert items
- if (id != undefined) {
- // return a single item
- item = me._getItem(id, type);
- if (filter && !filter(item)) {
- item = null;
- }
- }
- else if (ids != undefined) {
- // return a subset of items
- for (i = 0, len = ids.length; i < len; i++) {
- item = me._getItem(ids[i], type);
- if (!filter || filter(item)) {
- items.push(item);
- }
- }
- }
- else {
- // return all items
- for (itemId in this._data) {
- if (this._data.hasOwnProperty(itemId)) {
- item = me._getItem(itemId, type);
- if (!filter || filter(item)) {
- items.push(item);
- }
- }
- }
- }
-
- // order the results
- if (options && options.order && id == undefined) {
- this._sort(items, options.order);
- }
-
- // filter fields of the items
- if (options && options.fields) {
- var fields = options.fields;
- if (id != undefined) {
- item = this._filterFields(item, fields);
- }
- else {
- for (i = 0, len = items.length; i < len; i++) {
- items[i] = this._filterFields(items[i], fields);
- }
- }
- }
-
- // return the results
- if (returnType == 'DataTable') {
- var columns = this._getColumnNames(data);
- if (id != undefined) {
- // append a single item to the data table
- me._appendRow(data, columns, item);
- }
- else {
- // copy the items to the provided data table
- for (i = 0; i < items.length; i++) {
- me._appendRow(data, columns, items[i]);
- }
- }
- return data;
- }
- else if (returnType == "Object") {
- var result = {};
- for (i = 0; i < items.length; i++) {
- result[items[i].id] = items[i];
- }
- return result;
- }
- else {
- // return an array
- if (id != undefined) {
- // a single item
- return item;
- }
- else {
- // multiple items
- if (data) {
- // copy the items to the provided array
- for (i = 0, len = items.length; i < len; i++) {
- data.push(items[i]);
- }
- return data;
- }
- else {
- // just return our array
- return items;
- }
- }
- }
- };
-
- /**
- * Get ids of all items or from a filtered set of items.
- * @param {Object} [options] An Object with options. Available options:
- * {function} [filter] filter items
- * {String | function} [order] Order the items by
- * a field name or custom sort function.
- * @return {Array} ids
- */
- DataSet.prototype.getIds = function (options) {
- var data = this._data,
- filter = options && options.filter,
- order = options && options.order,
- type = options && options.type || this._options.type,
- i,
- len,
- id,
- item,
- items,
- ids = [];
-
- if (filter) {
- // get filtered items
- if (order) {
- // create ordered list
- items = [];
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, type);
- if (filter(item)) {
- items.push(item);
- }
- }
- }
-
- this._sort(items, order);
-
- for (i = 0, len = items.length; i < len; i++) {
- ids[i] = items[i][this._fieldId];
- }
- }
- else {
- // create unordered list
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, type);
- if (filter(item)) {
- ids.push(item[this._fieldId]);
- }
- }
- }
- }
- }
- else {
- // get all items
- if (order) {
- // create an ordered list
- items = [];
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- items.push(data[id]);
- }
- }
-
- this._sort(items, order);
-
- for (i = 0, len = items.length; i < len; i++) {
- ids[i] = items[i][this._fieldId];
- }
- }
- else {
- // create unordered list
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = data[id];
- ids.push(item[this._fieldId]);
- }
- }
- }
- }
-
- return ids;
- };
-
- /**
- * Returns the DataSet itself. Is overwritten for example by the DataView,
- * which returns the DataSet it is connected to instead.
- */
- DataSet.prototype.getDataSet = function () {
- return this;
- };
-
- /**
- * Execute a callback function for every item in the dataset.
- * @param {function} callback
- * @param {Object} [options] Available options:
- * {Object.} [type]
- * {String[]} [fields] filter fields
- * {function} [filter] filter items
- * {String | function} [order] Order the items by
- * a field name or custom sort function.
- */
- DataSet.prototype.forEach = function (callback, options) {
- var filter = options && options.filter,
- type = options && options.type || this._options.type,
- data = this._data,
- item,
- id;
-
- if (options && options.order) {
- // execute forEach on ordered list
- var items = this.get(options);
-
- for (var i = 0, len = items.length; i < len; i++) {
- item = items[i];
- id = item[this._fieldId];
- callback(item, id);
- }
- }
- else {
- // unordered
- for (id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, type);
- if (!filter || filter(item)) {
- callback(item, id);
- }
- }
- }
- }
- };
-
- /**
- * Map every item in the dataset.
- * @param {function} callback
- * @param {Object} [options] Available options:
- * {Object.} [type]
- * {String[]} [fields] filter fields
- * {function} [filter] filter items
- * {String | function} [order] Order the items by
- * a field name or custom sort function.
- * @return {Object[]} mappedItems
- */
- DataSet.prototype.map = function (callback, options) {
- var filter = options && options.filter,
- type = options && options.type || this._options.type,
- mappedItems = [],
- data = this._data,
- item;
-
- // convert and filter items
- for (var id in data) {
- if (data.hasOwnProperty(id)) {
- item = this._getItem(id, type);
- if (!filter || filter(item)) {
- mappedItems.push(callback(item, id));
- }
- }
- }
-
- // order items
- if (options && options.order) {
- this._sort(mappedItems, options.order);
- }
-
- return mappedItems;
- };
-
- /**
- * Filter the fields of an item
- * @param {Object} item
- * @param {String[]} fields Field names
- * @return {Object} filteredItem
- * @private
- */
- DataSet.prototype._filterFields = function (item, fields) {
- var filteredItem = {};
-
- for (var field in item) {
- if (item.hasOwnProperty(field) && (fields.indexOf(field) != -1)) {
- filteredItem[field] = item[field];
- }
- }
-
- return filteredItem;
- };
-
- /**
- * Sort the provided array with items
- * @param {Object[]} items
- * @param {String | function} order A field name or custom sort function.
- * @private
- */
- DataSet.prototype._sort = function (items, order) {
- if (util.isString(order)) {
- // order by provided field name
- var name = order; // field name
- items.sort(function (a, b) {
- var av = a[name];
- var bv = b[name];
- return (av > bv) ? 1 : ((av < bv) ? -1 : 0);
- });
- }
- else if (typeof order === 'function') {
- // order by sort function
- items.sort(order);
- }
- // TODO: extend order by an Object {field:String, direction:String}
- // where direction can be 'asc' or 'desc'
- else {
- throw new TypeError('Order must be a function or a string');
- }
- };
-
- /**
- * Remove an object by pointer or by id
- * @param {String | Number | Object | Array} id Object or id, or an array with
- * objects or ids to be removed
- * @param {String} [senderId] Optional sender id
- * @return {Array} removedIds
- */
- DataSet.prototype.remove = function (id, senderId) {
- var removedIds = [],
- i, len, removedId;
-
- if (Array.isArray(id)) {
- for (i = 0, len = id.length; i < len; i++) {
- removedId = this._remove(id[i]);
- if (removedId != null) {
- removedIds.push(removedId);
- }
- }
- }
- else {
- removedId = this._remove(id);
- if (removedId != null) {
- removedIds.push(removedId);
- }
- }
-
- if (removedIds.length) {
- this._trigger('remove', {items: removedIds}, senderId);
- }
-
- return removedIds;
- };
-
- /**
- * Remove an item by its id
- * @param {Number | String | Object} id id or item
- * @returns {Number | String | null} id
- * @private
- */
- DataSet.prototype._remove = function (id) {
- if (util.isNumber(id) || util.isString(id)) {
- if (this._data[id]) {
- delete this._data[id];
- return id;
- }
- }
- else if (id instanceof Object) {
- var itemId = id[this._fieldId];
- if (itemId && this._data[itemId]) {
- delete this._data[itemId];
- return itemId;
- }
- }
- return null;
- };
-
- /**
- * Clear the data
- * @param {String} [senderId] Optional sender id
- * @return {Array} removedIds The ids of all removed items
- */
- DataSet.prototype.clear = function (senderId) {
- var ids = Object.keys(this._data);
-
- this._data = {};
-
- this._trigger('remove', {items: ids}, senderId);
-
- return ids;
- };
-
- /**
- * Find the item with maximum value of a specified field
- * @param {String} field
- * @return {Object | null} item Item containing max value, or null if no items
- */
- DataSet.prototype.max = function (field) {
- var data = this._data,
- max = null,
- maxField = null;
-
- for (var id in data) {
- if (data.hasOwnProperty(id)) {
- var item = data[id];
- var itemField = item[field];
- if (itemField != null && (!max || itemField > maxField)) {
- max = item;
- maxField = itemField;
- }
- }
- }
-
- return max;
- };
-
- /**
- * Find the item with minimum value of a specified field
- * @param {String} field
- * @return {Object | null} item Item containing max value, or null if no items
- */
- DataSet.prototype.min = function (field) {
- var data = this._data,
- min = null,
- minField = null;
-
- for (var id in data) {
- if (data.hasOwnProperty(id)) {
- var item = data[id];
- var itemField = item[field];
- if (itemField != null && (!min || itemField < minField)) {
- min = item;
- minField = itemField;
- }
- }
- }
-
- return min;
- };
-
- /**
- * Find all distinct values of a specified field
- * @param {String} field
- * @return {Array} values Array containing all distinct values. If data items
- * do not contain the specified field are ignored.
- * The returned array is unordered.
- */
- DataSet.prototype.distinct = function (field) {
- var data = this._data;
- var values = [];
- var fieldType = this._options.type && this._options.type[field] || null;
- var count = 0;
- var i;
-
- for (var prop in data) {
- if (data.hasOwnProperty(prop)) {
- var item = data[prop];
- var value = item[field];
- var exists = false;
- for (i = 0; i < count; i++) {
- if (values[i] == value) {
- exists = true;
- break;
- }
- }
- if (!exists && (value !== undefined)) {
- values[count] = value;
- count++;
- }
- }
- }
-
- if (fieldType) {
- for (i = 0; i < values.length; i++) {
- values[i] = util.convert(values[i], fieldType);
- }
- }
-
- return values;
- };
-
- /**
- * Add a single item. Will fail when an item with the same id already exists.
- * @param {Object} item
- * @return {String} id
- * @private
- */
- DataSet.prototype._addItem = function (item) {
- var id = item[this._fieldId];
-
- if (id != undefined) {
- // check whether this id is already taken
- if (this._data[id]) {
- // item already exists
- throw new Error('Cannot add item: item with id ' + id + ' already exists');
- }
- }
- else {
- // generate an id
- id = util.randomUUID();
- item[this._fieldId] = id;
- }
-
- var d = {};
- for (var field in item) {
- if (item.hasOwnProperty(field)) {
- var fieldType = this._type[field]; // type may be undefined
- d[field] = util.convert(item[field], fieldType);
- }
- }
- this._data[id] = d;
-
- return id;
- };
-
- /**
- * Get an item. Fields can be converted to a specific type
- * @param {String} id
- * @param {Object.} [types] field types to convert
- * @return {Object | null} item
- * @private
- */
- DataSet.prototype._getItem = function (id, types) {
- var field, value;
-
- // get the item from the dataset
- var raw = this._data[id];
- if (!raw) {
- return null;
- }
-
- // convert the items field types
- var converted = {};
- if (types) {
- for (field in raw) {
- if (raw.hasOwnProperty(field)) {
- value = raw[field];
- converted[field] = util.convert(value, types[field]);
- }
- }
- }
- else {
- // no field types specified, no converting needed
- for (field in raw) {
- if (raw.hasOwnProperty(field)) {
- value = raw[field];
- converted[field] = value;
- }
- }
- }
- return converted;
- };
-
- /**
- * Update a single item: merge with existing item.
- * Will fail when the item has no id, or when there does not exist an item
- * with the same id.
- * @param {Object} item
- * @return {String} id
- * @private
- */
- DataSet.prototype._updateItem = function (item) {
- var id = item[this._fieldId];
- if (id == undefined) {
- throw new Error('Cannot update item: item has no id (item: ' + JSON.stringify(item) + ')');
- }
- var d = this._data[id];
- if (!d) {
- // item doesn't exist
- throw new Error('Cannot update item: no item with id ' + id + ' found');
- }
-
- // merge with current item
- for (var field in item) {
- if (item.hasOwnProperty(field)) {
- var fieldType = this._type[field]; // type may be undefined
- d[field] = util.convert(item[field], fieldType);
- }
- }
-
- return id;
- };
-
- /**
- * Get an array with the column names of a Google DataTable
- * @param {DataTable} dataTable
- * @return {String[]} columnNames
- * @private
- */
- DataSet.prototype._getColumnNames = function (dataTable) {
- var columns = [];
- for (var col = 0, cols = dataTable.getNumberOfColumns(); col < cols; col++) {
- columns[col] = dataTable.getColumnId(col) || dataTable.getColumnLabel(col);
- }
- return columns;
- };
-
- /**
- * Append an item as a row to the dataTable
- * @param dataTable
- * @param columns
- * @param item
- * @private
- */
- DataSet.prototype._appendRow = function (dataTable, columns, item) {
- var row = dataTable.addRow();
-
- for (var col = 0, cols = columns.length; col < cols; col++) {
- var field = columns[col];
- dataTable.setValue(row, col, item[field]);
- }
- };
-
- module.exports = DataSet;
-
-
-/***/ },
-/* 8 */
-/***/ function(module, exports, __webpack_require__) {
-
- /**
- * A queue
- * @param {Object} options
- * Available options:
- * - delay: number When provided, the queue will be flushed
- * automatically after an inactivity of this delay
- * in milliseconds.
- * Default value is null.
- * - max: number When the queue exceeds the given maximum number
- * of entries, the queue is flushed automatically.
- * Default value of max is Infinity.
- * @constructor
- */
- function Queue(options) {
- // options
- this.delay = null;
- this.max = Infinity;
-
- // properties
- this._queue = [];
- this._timeout = null;
- this._extended = null;
-
- this.setOptions(options);
- }
-
- /**
- * Update the configuration of the queue
- * @param {Object} options
- * Available options:
- * - delay: number When provided, the queue will be flushed
- * automatically after an inactivity of this delay
- * in milliseconds.
- * Default value is null.
- * - max: number When the queue exceeds the given maximum number
- * of entries, the queue is flushed automatically.
- * Default value of max is Infinity.
- * @param options
- */
- Queue.prototype.setOptions = function (options) {
- if (options && typeof options.delay !== 'undefined') {
- this.delay = options.delay;
- }
- if (options && typeof options.max !== 'undefined') {
- this.max = options.max;
- }
-
- this._flushIfNeeded();
- };
-
- /**
- * Extend an object with queuing functionality.
- * The object will be extended with a function flush, and the methods provided
- * in options.replace will be replaced with queued ones.
- * @param {Object} object
- * @param {Object} options
- * Available options:
- * - replace: Array.
- * A list with method names of the methods
- * on the object to be replaced with queued ones.
- * - delay: number When provided, the queue will be flushed
- * automatically after an inactivity of this delay
- * in milliseconds.
- * Default value is null.
- * - max: number When the queue exceeds the given maximum number
- * of entries, the queue is flushed automatically.
- * Default value of max is Infinity.
- * @return {Queue} Returns the created queue
- */
- Queue.extend = function (object, options) {
- var queue = new Queue(options);
-
- if (object.flush !== undefined) {
- throw new Error('Target object already has a property flush');
- }
- object.flush = function () {
- queue.flush();
- };
-
- var methods = [{
- name: 'flush',
- original: undefined
- }];
-
- if (options && options.replace) {
- for (var i = 0; i < options.replace.length; i++) {
- var name = options.replace[i];
- methods.push({
- name: name,
- original: object[name]
- });
- queue.replace(object, name);
- }
- }
-
- queue._extended = {
- object: object,
- methods: methods
- };
-
- return queue;
- };
-
- /**
- * Destroy the queue. The queue will first flush all queued actions, and in
- * case it has extended an object, will restore the original object.
- */
- Queue.prototype.destroy = function () {
- this.flush();
-
- if (this._extended) {
- var object = this._extended.object;
- var methods = this._extended.methods;
- for (var i = 0; i < methods.length; i++) {
- var method = methods[i];
- if (method.original) {
- object[method.name] = method.original;
- }
- else {
- delete object[method.name];
- }
- }
- this._extended = null;
- }
- };
-
- /**
- * Replace a method on an object with a queued version
- * @param {Object} object Object having the method
- * @param {string} method The method name
- */
- Queue.prototype.replace = function(object, method) {
- var me = this;
- var original = object[method];
- if (!original) {
- throw new Error('Method ' + method + ' undefined');
- }
-
- object[method] = function () {
- // create an Array with the arguments
- var args = [];
- for (var i = 0; i < arguments.length; i++) {
- args[i] = arguments[i];
- }
-
- // add this call to the queue
- me.queue({
- args: args,
- fn: original,
- context: this
- });
- };
- };
-
- /**
- * Queue a call
- * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry
- */
- Queue.prototype.queue = function(entry) {
- if (typeof entry === 'function') {
- this._queue.push({fn: entry});
- }
- else {
- this._queue.push(entry);
- }
-
- this._flushIfNeeded();
- };
-
- /**
- * Check whether the queue needs to be flushed
- * @private
- */
- Queue.prototype._flushIfNeeded = function () {
- // flush when the maximum is exceeded.
- if (this._queue.length > this.max) {
- this.flush();
- }
-
- // flush after a period of inactivity when a delay is configured
- clearTimeout(this._timeout);
- if (this.queue.length > 0 && typeof this.delay === 'number') {
- var me = this;
- this._timeout = setTimeout(function () {
- me.flush();
- }, this.delay);
- }
- };
-
- /**
- * Flush all queued calls
- */
- Queue.prototype.flush = function () {
- while (this._queue.length > 0) {
- var entry = this._queue.shift();
- entry.fn.apply(entry.context || entry.fn, entry.args || []);
- }
- };
-
- module.exports = Queue;
-
-
-/***/ },
-/* 9 */
-/***/ function(module, exports, __webpack_require__) {
-
- var util = __webpack_require__(1);
- var DataSet = __webpack_require__(7);
-
- /**
- * DataView
- *
- * a dataview offers a filtered view on a dataset or an other dataview.
- *
- * @param {DataSet | DataView} data
- * @param {Object} [options] Available options: see method get
- *
- * @constructor DataView
- */
- function DataView (data, options) {
- this._data = null;
- this._ids = {}; // ids of the items currently in memory (just contains a boolean true)
- this._options = options || {};
- this._fieldId = 'id'; // name of the field containing id
- this._subscribers = {}; // event subscribers
-
- var me = this;
- this.listener = function () {
- me._onEvent.apply(me, arguments);
- };
-
- this.setData(data);
- }
-
- // TODO: implement a function .config() to dynamically update things like configured filter
- // and trigger changes accordingly
-
- /**
- * Set a data source for the view
- * @param {DataSet | DataView} data
- */
- DataView.prototype.setData = function (data) {
- var ids, i, len;
-
- if (this._data) {
- // unsubscribe from current dataset
- if (this._data.unsubscribe) {
- this._data.unsubscribe('*', this.listener);
- }
-
- // trigger a remove of all items in memory
- ids = [];
- for (var id in this._ids) {
- if (this._ids.hasOwnProperty(id)) {
- ids.push(id);
- }
- }
- this._ids = {};
- this._trigger('remove', {items: ids});
- }
-
- this._data = data;
-
- if (this._data) {
- // update fieldId
- this._fieldId = this._options.fieldId ||
- (this._data && this._data.options && this._data.options.fieldId) ||
- 'id';
-
- // trigger an add of all added items
- ids = this._data.getIds({filter: this._options && this._options.filter});
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- this._ids[id] = true;
- }
- this._trigger('add', {items: ids});
-
- // subscribe to new dataset
- if (this._data.on) {
- this._data.on('*', this.listener);
- }
- }
- };
-
- /**
- * Get data from the data view
- *
- * Usage:
- *
- * get()
- * get(options: Object)
- * get(options: Object, data: Array | DataTable)
- *
- * get(id: Number)
- * get(id: Number, options: Object)
- * get(id: Number, options: Object, data: Array | DataTable)
- *
- * get(ids: Number[])
- * get(ids: Number[], options: Object)
- * get(ids: Number[], options: Object, data: Array | DataTable)
- *
- * Where:
- *
- * {Number | String} id The id of an item
- * {Number[] | String{}} ids An array with ids of items
- * {Object} options An Object with options. Available options:
- * {String} [type] Type of data to be returned. Can
- * be 'DataTable' or 'Array' (default)
- * {Object.} [convert]
- * {String[]} [fields] field names to be returned
- * {function} [filter] filter items
- * {String | function} [order] Order the items by
- * a field name or custom sort function.
- * {Array | DataTable} [data] If provided, items will be appended to this
- * array or table. Required in case of Google
- * DataTable.
- * @param args
- */
- DataView.prototype.get = function (args) {
- var me = this;
-
- // parse the arguments
- var ids, options, data;
- var firstType = util.getType(arguments[0]);
- if (firstType == 'String' || firstType == 'Number' || firstType == 'Array') {
- // get(id(s) [, options] [, data])
- ids = arguments[0]; // can be a single id or an array with ids
- options = arguments[1];
- data = arguments[2];
- }
- else {
- // get([, options] [, data])
- options = arguments[0];
- data = arguments[1];
- }
-
- // extend the options with the default options and provided options
- var viewOptions = util.extend({}, this._options, options);
-
- // create a combined filter method when needed
- if (this._options.filter && options && options.filter) {
- viewOptions.filter = function (item) {
- return me._options.filter(item) && options.filter(item);
- }
- }
-
- // build up the call to the linked data set
- var getArguments = [];
- if (ids != undefined) {
- getArguments.push(ids);
- }
- getArguments.push(viewOptions);
- getArguments.push(data);
-
- return this._data && this._data.get.apply(this._data, getArguments);
- };
-
- /**
- * Get ids of all items or from a filtered set of items.
- * @param {Object} [options] An Object with options. Available options:
- * {function} [filter] filter items
- * {String | function} [order] Order the items by
- * a field name or custom sort function.
- * @return {Array} ids
- */
- DataView.prototype.getIds = function (options) {
- var ids;
-
- if (this._data) {
- var defaultFilter = this._options.filter;
- var filter;
-
- if (options && options.filter) {
- if (defaultFilter) {
- filter = function (item) {
- return defaultFilter(item) && options.filter(item);
- }
- }
- else {
- filter = options.filter;
- }
- }
- else {
- filter = defaultFilter;
- }
-
- ids = this._data.getIds({
- filter: filter,
- order: options && options.order
- });
- }
- else {
- ids = [];
- }
-
- return ids;
- };
-
- /**
- * Get the DataSet to which this DataView is connected. In case there is a chain
- * of multiple DataViews, the root DataSet of this chain is returned.
- * @return {DataSet} dataSet
- */
- DataView.prototype.getDataSet = function () {
- var dataSet = this;
- while (dataSet instanceof DataView) {
- dataSet = dataSet._data;
- }
- return dataSet || null;
- };
-
- /**
- * Event listener. Will propagate all events from the connected data set to
- * the subscribers of the DataView, but will filter the items and only trigger
- * when there are changes in the filtered data set.
- * @param {String} event
- * @param {Object | null} params
- * @param {String} senderId
- * @private
- */
- DataView.prototype._onEvent = function (event, params, senderId) {
- var i, len, id, item,
- ids = params && params.items,
- data = this._data,
- added = [],
- updated = [],
- removed = [];
-
- if (ids && data) {
- switch (event) {
- case 'add':
- // filter the ids of the added items
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- item = this.get(id);
- if (item) {
- this._ids[id] = true;
- added.push(id);
- }
- }
-
- break;
-
- case 'update':
- // determine the event from the views viewpoint: an updated
- // item can be added, updated, or removed from this view.
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- item = this.get(id);
-
- if (item) {
- if (this._ids[id]) {
- updated.push(id);
- }
- else {
- this._ids[id] = true;
- added.push(id);
- }
- }
- else {
- if (this._ids[id]) {
- delete this._ids[id];
- removed.push(id);
- }
- else {
- // nothing interesting for me :-(
- }
- }
- }
-
- break;
-
- case 'remove':
- // filter the ids of the removed items
- for (i = 0, len = ids.length; i < len; i++) {
- id = ids[i];
- if (this._ids[id]) {
- delete this._ids[id];
- removed.push(id);
- }
- }
-
- break;
- }
-
- if (added.length) {
- this._trigger('add', {items: added}, senderId);
- }
- if (updated.length) {
- this._trigger('update', {items: updated}, senderId);
- }
- if (removed.length) {
- this._trigger('remove', {items: removed}, senderId);
- }
- }
- };
-
- // copy subscription functionality from DataSet
- DataView.prototype.on = DataSet.prototype.on;
- DataView.prototype.off = DataSet.prototype.off;
- DataView.prototype._trigger = DataSet.prototype._trigger;
-
- // TODO: make these functions deprecated (replaced with `on` and `off` since version 0.5)
- DataView.prototype.subscribe = DataView.prototype.on;
- DataView.prototype.unsubscribe = DataView.prototype.off;
-
- module.exports = DataView;
-
-/***/ },
-/* 10 */
-/***/ function(module, exports, __webpack_require__) {
-
- var Emitter = __webpack_require__(11);
- var DataSet = __webpack_require__(7);
- var DataView = __webpack_require__(9);
- var util = __webpack_require__(1);
- var Point3d = __webpack_require__(12);
- var Point2d = __webpack_require__(13);
- var Camera = __webpack_require__(14);
- var Filter = __webpack_require__(15);
- var Slider = __webpack_require__(16);
- var StepNumber = __webpack_require__(17);
-
- /**
- * @constructor Graph3d
- * Graph3d displays data in 3d.
- *
- * Graph3d is developed in javascript as a Google Visualization Chart.
- *
- * @param {Element} container The DOM element in which the Graph3d will
- * be created. Normally a div element.
- * @param {DataSet | DataView | Array} [data]
- * @param {Object} [options]
- */
- function Graph3d(container, data, options) {
- if (!(this instanceof Graph3d)) {
- throw new SyntaxError('Constructor must be called with the new operator');
- }
-
- // create variables and set default values
- this.containerElement = container;
- this.width = '400px';
- this.height = '400px';
- this.margin = 10; // px
- this.defaultXCenter = '55%';
- this.defaultYCenter = '50%';
-
- this.xLabel = 'x';
- this.yLabel = 'y';
- this.zLabel = 'z';
-
- var passValueFn = function(v) { return v; };
- this.xValueLabel = passValueFn;
- this.yValueLabel = passValueFn;
- this.zValueLabel = passValueFn;
-
- this.filterLabel = 'time';
- this.legendLabel = 'value';
-
- this.style = Graph3d.STYLE.DOT;
- this.showPerspective = true;
- this.showGrid = true;
- this.keepAspectRatio = true;
- this.showShadow = false;
- this.showGrayBottom = false; // TODO: this does not work correctly
- this.showTooltip = false;
- this.verticalRatio = 0.5; // 0.1 to 1.0, where 1.0 results in a 'cube'
-
- this.animationInterval = 1000; // milliseconds
- this.animationPreload = false;
-
- this.camera = new Camera();
- this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window?
-
- this.dataTable = null; // The original data table
- this.dataPoints = null; // The table with point objects
-
- // the column indexes
- this.colX = undefined;
- this.colY = undefined;
- this.colZ = undefined;
- this.colValue = undefined;
- this.colFilter = undefined;
-
- this.xMin = 0;
- this.xStep = undefined; // auto by default
- this.xMax = 1;
- this.yMin = 0;
- this.yStep = undefined; // auto by default
- this.yMax = 1;
- this.zMin = 0;
- this.zStep = undefined; // auto by default
- this.zMax = 1;
- this.valueMin = 0;
- this.valueMax = 1;
- this.xBarWidth = 1;
- this.yBarWidth = 1;
- // TODO: customize axis range
-
- // constants
- this.colorAxis = '#4D4D4D';
- this.colorGrid = '#D3D3D3';
- this.colorDot = '#7DC1FF';
- this.colorDotBorder = '#3267D2';
-
- // create a frame and canvas
- this.create();
-
- // apply options (also when undefined)
- this.setOptions(options);
-
- // apply data
- if (data) {
- this.setData(data);
- }
- }
-
- // Extend Graph3d with an Emitter mixin
- Emitter(Graph3d.prototype);
-
- /**
- * Calculate the scaling values, dependent on the range in x, y, and z direction
- */
- Graph3d.prototype._setScale = function() {
- this.scale = new Point3d(1 / (this.xMax - this.xMin),
- 1 / (this.yMax - this.yMin),
- 1 / (this.zMax - this.zMin));
-
- // keep aspect ration between x and y scale if desired
- if (this.keepAspectRatio) {
- if (this.scale.x < this.scale.y) {
- //noinspection JSSuspiciousNameCombination
- this.scale.y = this.scale.x;
- }
- else {
- //noinspection JSSuspiciousNameCombination
- this.scale.x = this.scale.y;
- }
- }
-
- // scale the vertical axis
- this.scale.z *= this.verticalRatio;
- // TODO: can this be automated? verticalRatio?
-
- // determine scale for (optional) value
- this.scale.value = 1 / (this.valueMax - this.valueMin);
-
- // position the camera arm
- var xCenter = (this.xMax + this.xMin) / 2 * this.scale.x;
- var yCenter = (this.yMax + this.yMin) / 2 * this.scale.y;
- var zCenter = (this.zMax + this.zMin) / 2 * this.scale.z;
- this.camera.setArmLocation(xCenter, yCenter, zCenter);
- };
-
-
- /**
- * Convert a 3D location to a 2D location on screen
- * http://en.wikipedia.org/wiki/3D_projection
- * @param {Point3d} point3d A 3D point with parameters x, y, z
- * @return {Point2d} point2d A 2D point with parameters x, y
- */
- Graph3d.prototype._convert3Dto2D = function(point3d) {
- var translation = this._convertPointToTranslation(point3d);
- return this._convertTranslationToScreen(translation);
- };
-
- /**
- * Convert a 3D location its translation seen from the camera
- * http://en.wikipedia.org/wiki/3D_projection
- * @param {Point3d} point3d A 3D point with parameters x, y, z
- * @return {Point3d} translation A 3D point with parameters x, y, z This is
- * the translation of the point, seen from the
- * camera
- */
- Graph3d.prototype._convertPointToTranslation = function(point3d) {
- var ax = point3d.x * this.scale.x,
- ay = point3d.y * this.scale.y,
- az = point3d.z * this.scale.z,
-
- cx = this.camera.getCameraLocation().x,
- cy = this.camera.getCameraLocation().y,
- cz = this.camera.getCameraLocation().z,
-
- // calculate angles
- sinTx = Math.sin(this.camera.getCameraRotation().x),
- cosTx = Math.cos(this.camera.getCameraRotation().x),
- sinTy = Math.sin(this.camera.getCameraRotation().y),
- cosTy = Math.cos(this.camera.getCameraRotation().y),
- sinTz = Math.sin(this.camera.getCameraRotation().z),
- cosTz = Math.cos(this.camera.getCameraRotation().z),
-
- // calculate translation
- dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz),
- dy = sinTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) + cosTx * (cosTz * (ay - cy) - sinTz * (ax-cx)),
- dz = cosTx * (cosTy * (az - cz) + sinTy * (sinTz * (ay - cy) + cosTz * (ax - cx))) - sinTx * (cosTz * (ay - cy) - sinTz * (ax-cx));
-
- return new Point3d(dx, dy, dz);
- };
-
- /**
- * Convert a translation point to a point on the screen
- * @param {Point3d} translation A 3D point with parameters x, y, z This is
- * the translation of the point, seen from the
- * camera
- * @return {Point2d} point2d A 2D point with parameters x, y
- */
- Graph3d.prototype._convertTranslationToScreen = function(translation) {
- var ex = this.eye.x,
- ey = this.eye.y,
- ez = this.eye.z,
- dx = translation.x,
- dy = translation.y,
- dz = translation.z;
-
- // calculate position on screen from translation
- var bx;
- var by;
- if (this.showPerspective) {
- bx = (dx - ex) * (ez / dz);
- by = (dy - ey) * (ez / dz);
- }
- else {
- bx = dx * -(ez / this.camera.getArmLength());
- by = dy * -(ez / this.camera.getArmLength());
- }
-
- // shift and scale the point to the center of the screen
- // use the width of the graph to scale both horizontally and vertically.
- return new Point2d(
- this.xcenter + bx * this.frame.canvas.clientWidth,
- this.ycenter - by * this.frame.canvas.clientWidth);
- };
-
- /**
- * Set the background styling for the graph
- * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor
- */
- Graph3d.prototype._setBackgroundColor = function(backgroundColor) {
- var fill = 'white';
- var stroke = 'gray';
- var strokeWidth = 1;
-
- if (typeof(backgroundColor) === 'string') {
- fill = backgroundColor;
- stroke = 'none';
- strokeWidth = 0;
- }
- else if (typeof(backgroundColor) === 'object') {
- if (backgroundColor.fill !== undefined) fill = backgroundColor.fill;
- if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke;
- if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth;
- }
- else if (backgroundColor === undefined) {
- // use use defaults
- }
- else {
- throw 'Unsupported type of backgroundColor';
- }
-
- this.frame.style.backgroundColor = fill;
- this.frame.style.borderColor = stroke;
- this.frame.style.borderWidth = strokeWidth + 'px';
- this.frame.style.borderStyle = 'solid';
- };
-
-
- /// enumerate the available styles
- Graph3d.STYLE = {
- BAR: 0,
- BARCOLOR: 1,
- BARSIZE: 2,
- DOT : 3,
- DOTLINE : 4,
- DOTCOLOR: 5,
- DOTSIZE: 6,
- GRID : 7,
- LINE: 8,
- SURFACE : 9
- };
-
- /**
- * Retrieve the style index from given styleName
- * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line'
- * @return {Number} styleNumber Enumeration value representing the style, or -1
- * when not found
- */
- Graph3d.prototype._getStyleNumber = function(styleName) {
- switch (styleName) {
- case 'dot': return Graph3d.STYLE.DOT;
- case 'dot-line': return Graph3d.STYLE.DOTLINE;
- case 'dot-color': return Graph3d.STYLE.DOTCOLOR;
- case 'dot-size': return Graph3d.STYLE.DOTSIZE;
- case 'line': return Graph3d.STYLE.LINE;
- case 'grid': return Graph3d.STYLE.GRID;
- case 'surface': return Graph3d.STYLE.SURFACE;
- case 'bar': return Graph3d.STYLE.BAR;
- case 'bar-color': return Graph3d.STYLE.BARCOLOR;
- case 'bar-size': return Graph3d.STYLE.BARSIZE;
- }
-
- return -1;
- };
-
- /**
- * Determine the indexes of the data columns, based on the given style and data
- * @param {DataSet} data
- * @param {Number} style
- */
- Graph3d.prototype._determineColumnIndexes = function(data, style) {
- if (this.style === Graph3d.STYLE.DOT ||
- this.style === Graph3d.STYLE.DOTLINE ||
- this.style === Graph3d.STYLE.LINE ||
- this.style === Graph3d.STYLE.GRID ||
- this.style === Graph3d.STYLE.SURFACE ||
- this.style === Graph3d.STYLE.BAR) {
- // 3 columns expected, and optionally a 4th with filter values
- this.colX = 0;
- this.colY = 1;
- this.colZ = 2;
- this.colValue = undefined;
-
- if (data.getNumberOfColumns() > 3) {
- this.colFilter = 3;
- }
- }
- else if (this.style === Graph3d.STYLE.DOTCOLOR ||
- this.style === Graph3d.STYLE.DOTSIZE ||
- this.style === Graph3d.STYLE.BARCOLOR ||
- this.style === Graph3d.STYLE.BARSIZE) {
- // 4 columns expected, and optionally a 5th with filter values
- this.colX = 0;
- this.colY = 1;
- this.colZ = 2;
- this.colValue = 3;
-
- if (data.getNumberOfColumns() > 4) {
- this.colFilter = 4;
- }
- }
- else {
- throw 'Unknown style "' + this.style + '"';
- }
- };
-
- Graph3d.prototype.getNumberOfRows = function(data) {
- return data.length;
- }
-
-
- Graph3d.prototype.getNumberOfColumns = function(data) {
- var counter = 0;
- for (var column in data[0]) {
- if (data[0].hasOwnProperty(column)) {
- counter++;
- }
- }
- return counter;
- }
-
-
- Graph3d.prototype.getDistinctValues = function(data, column) {
- var distinctValues = [];
- for (var i = 0; i < data.length; i++) {
- if (distinctValues.indexOf(data[i][column]) == -1) {
- distinctValues.push(data[i][column]);
- }
- }
- return distinctValues;
- }
-
-
- Graph3d.prototype.getColumnRange = function(data,column) {
- var minMax = {min:data[0][column],max:data[0][column]};
- for (var i = 0; i < data.length; i++) {
- if (minMax.min > data[i][column]) { minMax.min = data[i][column]; }
- if (minMax.max < data[i][column]) { minMax.max = data[i][column]; }
- }
- return minMax;
- };
-
- /**
- * Initialize the data from the data table. Calculate minimum and maximum values
- * and column index values
- * @param {Array | DataSet | DataView} rawData The data containing the items for the Graph.
- * @param {Number} style Style Number
- */
- Graph3d.prototype._dataInitialize = function (rawData, style) {
- var me = this;
-
- // unsubscribe from the dataTable
- if (this.dataSet) {
- this.dataSet.off('*', this._onChange);
- }
-
- if (rawData === undefined)
- return;
-
- if (Array.isArray(rawData)) {
- rawData = new DataSet(rawData);
- }
-
- var data;
- if (rawData instanceof DataSet || rawData instanceof DataView) {
- data = rawData.get();
- }
- else {
- throw new Error('Array, DataSet, or DataView expected');
- }
-
- if (data.length == 0)
- return;
-
- this.dataSet = rawData;
- this.dataTable = data;
-
- // subscribe to changes in the dataset
- this._onChange = function () {
- me.setData(me.dataSet);
- };
- this.dataSet.on('*', this._onChange);
-
- // _determineColumnIndexes
- // getNumberOfRows (points)
- // getNumberOfColumns (x,y,z,v,t,t1,t2...)
- // getDistinctValues (unique values?)
- // getColumnRange
-
- // determine the location of x,y,z,value,filter columns
- this.colX = 'x';
- this.colY = 'y';
- this.colZ = 'z';
- this.colValue = 'style';
- this.colFilter = 'filter';
-
-
-
- // check if a filter column is provided
- if (data[0].hasOwnProperty('filter')) {
- if (this.dataFilter === undefined) {
- this.dataFilter = new Filter(rawData, this.colFilter, this);
- this.dataFilter.setOnLoadCallback(function() {me.redraw();});
- }
- }
-
-
- var withBars = this.style == Graph3d.STYLE.BAR ||
- this.style == Graph3d.STYLE.BARCOLOR ||
- this.style == Graph3d.STYLE.BARSIZE;
-
- // determine barWidth from data
- if (withBars) {
- if (this.defaultXBarWidth !== undefined) {
- this.xBarWidth = this.defaultXBarWidth;
- }
- else {
- var dataX = this.getDistinctValues(data,this.colX);
- this.xBarWidth = (dataX[1] - dataX[0]) || 1;
- }
-
- if (this.defaultYBarWidth !== undefined) {
- this.yBarWidth = this.defaultYBarWidth;
- }
- else {
- var dataY = this.getDistinctValues(data,this.colY);
- this.yBarWidth = (dataY[1] - dataY[0]) || 1;
- }
- }
-
- // calculate minimums and maximums
- var xRange = this.getColumnRange(data,this.colX);
- if (withBars) {
- xRange.min -= this.xBarWidth / 2;
- xRange.max += this.xBarWidth / 2;
- }
- this.xMin = (this.defaultXMin !== undefined) ? this.defaultXMin : xRange.min;
- this.xMax = (this.defaultXMax !== undefined) ? this.defaultXMax : xRange.max;
- if (this.xMax <= this.xMin) this.xMax = this.xMin + 1;
- this.xStep = (this.defaultXStep !== undefined) ? this.defaultXStep : (this.xMax-this.xMin)/5;
-
- var yRange = this.getColumnRange(data,this.colY);
- if (withBars) {
- yRange.min -= this.yBarWidth / 2;
- yRange.max += this.yBarWidth / 2;
- }
- this.yMin = (this.defaultYMin !== undefined) ? this.defaultYMin : yRange.min;
- this.yMax = (this.defaultYMax !== undefined) ? this.defaultYMax : yRange.max;
- if (this.yMax <= this.yMin) this.yMax = this.yMin + 1;
- this.yStep = (this.defaultYStep !== undefined) ? this.defaultYStep : (this.yMax-this.yMin)/5;
-
- var zRange = this.getColumnRange(data,this.colZ);
- this.zMin = (this.defaultZMin !== undefined) ? this.defaultZMin : zRange.min;
- this.zMax = (this.defaultZMax !== undefined) ? this.defaultZMax : zRange.max;
- if (this.zMax <= this.zMin) this.zMax = this.zMin + 1;
- this.zStep = (this.defaultZStep !== undefined) ? this.defaultZStep : (this.zMax-this.zMin)/5;
-
- if (this.colValue !== undefined) {
- var valueRange = this.getColumnRange(data,this.colValue);
- this.valueMin = (this.defaultValueMin !== undefined) ? this.defaultValueMin : valueRange.min;
- this.valueMax = (this.defaultValueMax !== undefined) ? this.defaultValueMax : valueRange.max;
- if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1;
- }
-
- // set the scale dependent on the ranges.
- this._setScale();
- };
-
-
-
- /**
- * Filter the data based on the current filter
- * @param {Array} data
- * @return {Array} dataPoints Array with point objects which can be drawn on screen
- */
- Graph3d.prototype._getDataPoints = function (data) {
- // TODO: store the created matrix dataPoints in the filters instead of reloading each time
- var x, y, i, z, obj, point;
-
- var dataPoints = [];
-
- if (this.style === Graph3d.STYLE.GRID ||
- this.style === Graph3d.STYLE.SURFACE) {
- // copy all values from the google data table to a matrix
- // the provided values are supposed to form a grid of (x,y) positions
-
- // create two lists with all present x and y values
- var dataX = [];
- var dataY = [];
- for (i = 0; i < this.getNumberOfRows(data); i++) {
- x = data[i][this.colX] || 0;
- y = data[i][this.colY] || 0;
-
- if (dataX.indexOf(x) === -1) {
- dataX.push(x);
- }
- if (dataY.indexOf(y) === -1) {
- dataY.push(y);
- }
- }
-
- var sortNumber = function (a, b) {
- return a - b;
- };
- dataX.sort(sortNumber);
- dataY.sort(sortNumber);
-
- // create a grid, a 2d matrix, with all values.
- var dataMatrix = []; // temporary data matrix
- for (i = 0; i < data.length; i++) {
- x = data[i][this.colX] || 0;
- y = data[i][this.colY] || 0;
- z = data[i][this.colZ] || 0;
-
- var xIndex = dataX.indexOf(x); // TODO: implement Array().indexOf() for Internet Explorer
- var yIndex = dataY.indexOf(y);
-
- if (dataMatrix[xIndex] === undefined) {
- dataMatrix[xIndex] = [];
- }
-
- var point3d = new Point3d();
- point3d.x = x;
- point3d.y = y;
- point3d.z = z;
-
- obj = {};
- obj.point = point3d;
- obj.trans = undefined;
- obj.screen = undefined;
- obj.bottom = new Point3d(x, y, this.zMin);
-
- dataMatrix[xIndex][yIndex] = obj;
-
- dataPoints.push(obj);
- }
-
- // fill in the pointers to the neighbors.
- for (x = 0; x < dataMatrix.length; x++) {
- for (y = 0; y < dataMatrix[x].length; y++) {
- if (dataMatrix[x][y]) {
- dataMatrix[x][y].pointRight = (x < dataMatrix.length-1) ? dataMatrix[x+1][y] : undefined;
- dataMatrix[x][y].pointTop = (y < dataMatrix[x].length-1) ? dataMatrix[x][y+1] : undefined;
- dataMatrix[x][y].pointCross =
- (x < dataMatrix.length-1 && y < dataMatrix[x].length-1) ?
- dataMatrix[x+1][y+1] :
- undefined;
- }
- }
- }
- }
- else { // 'dot', 'dot-line', etc.
- // copy all values from the google data table to a list with Point3d objects
- for (i = 0; i < data.length; i++) {
- point = new Point3d();
- point.x = data[i][this.colX] || 0;
- point.y = data[i][this.colY] || 0;
- point.z = data[i][this.colZ] || 0;
-
- if (this.colValue !== undefined) {
- point.value = data[i][this.colValue] || 0;
- }
-
- obj = {};
- obj.point = point;
- obj.bottom = new Point3d(point.x, point.y, this.zMin);
- obj.trans = undefined;
- obj.screen = undefined;
-
- dataPoints.push(obj);
- }
- }
-
- return dataPoints;
- };
-
- /**
- * Create the main frame for the Graph3d.
- * This function is executed once when a Graph3d object is created. The frame
- * contains a canvas, and this canvas contains all objects like the axis and
- * nodes.
- */
- Graph3d.prototype.create = function () {
- // remove all elements from the container element.
- while (this.containerElement.hasChildNodes()) {
- this.containerElement.removeChild(this.containerElement.firstChild);
- }
-
- this.frame = document.createElement('div');
- this.frame.style.position = 'relative';
- this.frame.style.overflow = 'hidden';
-
- // create the graph canvas (HTML canvas element)
- this.frame.canvas = document.createElement( 'canvas' );
- this.frame.canvas.style.position = 'relative';
- this.frame.appendChild(this.frame.canvas);
- //if (!this.frame.canvas.getContext) {
- {
- var noCanvas = document.createElement( 'DIV' );
- noCanvas.style.color = 'red';
- noCanvas.style.fontWeight = 'bold' ;
- noCanvas.style.padding = '10px';
- noCanvas.innerHTML = 'Error: your browser does not support HTML canvas';
- this.frame.canvas.appendChild(noCanvas);
- }
-
- this.frame.filter = document.createElement( 'div' );
- this.frame.filter.style.position = 'absolute';
- this.frame.filter.style.bottom = '0px';
- this.frame.filter.style.left = '0px';
- this.frame.filter.style.width = '100%';
- this.frame.appendChild(this.frame.filter);
-
- // add event listeners to handle moving and zooming the contents
- var me = this;
- var onmousedown = function (event) {me._onMouseDown(event);};
- var ontouchstart = function (event) {me._onTouchStart(event);};
- var onmousewheel = function (event) {me._onWheel(event);};
- var ontooltip = function (event) {me._onTooltip(event);};
- // TODO: these events are never cleaned up... can give a 'memory leakage'
-
- util.addEventListener(this.frame.canvas, 'keydown', onkeydown);
- util.addEventListener(this.frame.canvas, 'mousedown', onmousedown);
- util.addEventListener(this.frame.canvas, 'touchstart', ontouchstart);
- util.addEventListener(this.frame.canvas, 'mousewheel', onmousewheel);
- util.addEventListener(this.frame.canvas, 'mousemove', ontooltip);
-
- // add the new graph to the container element
- this.containerElement.appendChild(this.frame);
- };
-
-
- /**
- * Set a new size for the graph
- * @param {string} width Width in pixels or percentage (for example '800px'
- * or '50%')
- * @param {string} height Height in pixels or percentage (for example '400px'
- * or '30%')
- */
- Graph3d.prototype.setSize = function(width, height) {
- this.frame.style.width = width;
- this.frame.style.height = height;
-
- this._resizeCanvas();
- };
-
- /**
- * Resize the canvas to the current size of the frame
- */
- Graph3d.prototype._resizeCanvas = function() {
- this.frame.canvas.style.width = '100%';
- this.frame.canvas.style.height = '100%';
-
- this.frame.canvas.width = this.frame.canvas.clientWidth;
- this.frame.canvas.height = this.frame.canvas.clientHeight;
-
- // adjust with for margin
- this.frame.filter.style.width = (this.frame.canvas.clientWidth - 2 * 10) + 'px';
- };
-
- /**
- * Start animation
- */
- Graph3d.prototype.animationStart = function() {
- if (!this.frame.filter || !this.frame.filter.slider)
- throw 'No animation available';
-
- this.frame.filter.slider.play();
- };
-
-
- /**
- * Stop animation
- */
- Graph3d.prototype.animationStop = function() {
- if (!this.frame.filter || !this.frame.filter.slider) return;
-
- this.frame.filter.slider.stop();
- };
-
-
- /**
- * Resize the center position based on the current values in this.defaultXCenter
- * and this.defaultYCenter (which are strings with a percentage or a value
- * in pixels). The center positions are the variables this.xCenter
- * and this.yCenter
- */
- Graph3d.prototype._resizeCenter = function() {
- // calculate the horizontal center position
- if (this.defaultXCenter.charAt(this.defaultXCenter.length-1) === '%') {
- this.xcenter =
- parseFloat(this.defaultXCenter) / 100 *
- this.frame.canvas.clientWidth;
- }
- else {
- this.xcenter = parseFloat(this.defaultXCenter); // supposed to be in px
- }
-
- // calculate the vertical center position
- if (this.defaultYCenter.charAt(this.defaultYCenter.length-1) === '%') {
- this.ycenter =
- parseFloat(this.defaultYCenter) / 100 *
- (this.frame.canvas.clientHeight - this.frame.filter.clientHeight);
- }
- else {
- this.ycenter = parseFloat(this.defaultYCenter); // supposed to be in px
- }
- };
-
- /**
- * Set the rotation and distance of the camera
- * @param {Object} pos An object with the camera position. The object
- * contains three parameters:
- * - horizontal {Number}
- * The horizontal rotation, between 0 and 2*PI.
- * Optional, can be left undefined.
- * - vertical {Number}
- * The vertical rotation, between 0 and 0.5*PI
- * if vertical=0.5*PI, the graph is shown from the
- * top. Optional, can be left undefined.
- * - distance {Number}
- * The (normalized) distance of the camera to the
- * center of the graph, a value between 0.71 and 5.0.
- * Optional, can be left undefined.
- */
- Graph3d.prototype.setCameraPosition = function(pos) {
- if (pos === undefined) {
- return;
- }
-
- if (pos.horizontal !== undefined && pos.vertical !== undefined) {
- this.camera.setArmRotation(pos.horizontal, pos.vertical);
- }
-
- if (pos.distance !== undefined) {
- this.camera.setArmLength(pos.distance);
- }
-
- this.redraw();
- };
-
-
- /**
- * Retrieve the current camera rotation
- * @return {object} An object with parameters horizontal, vertical, and
- * distance
- */
- Graph3d.prototype.getCameraPosition = function() {
- var pos = this.camera.getArmRotation();
- pos.distance = this.camera.getArmLength();
- return pos;
- };
-
- /**
- * Load data into the 3D Graph
- */
- Graph3d.prototype._readData = function(data) {
- // read the data
- this._dataInitialize(data, this.style);
-
-
- if (this.dataFilter) {
- // apply filtering
- this.dataPoints = this.dataFilter._getDataPoints();
- }
- else {
- // no filtering. load all data
- this.dataPoints = this._getDataPoints(this.dataTable);
- }
-
- // draw the filter
- this._redrawFilter();
- };
-
- /**
- * Replace the dataset of the Graph3d
- * @param {Array | DataSet | DataView} data
- */
- Graph3d.prototype.setData = function (data) {
- this._readData(data);
- this.redraw();
-
- // start animation when option is true
- if (this.animationAutoStart && this.dataFilter) {
- this.animationStart();
- }
- };
-
- /**
- * Update the options. Options will be merged with current options
- * @param {Object} options
- */
- Graph3d.prototype.setOptions = function (options) {
- var cameraPosition = undefined;
-
- this.animationStop();
-
- if (options !== undefined) {
- // retrieve parameter values
- if (options.width !== undefined) this.width = options.width;
- if (options.height !== undefined) this.height = options.height;
-
- if (options.xCenter !== undefined) this.defaultXCenter = options.xCenter;
- if (options.yCenter !== undefined) this.defaultYCenter = options.yCenter;
-
- if (options.filterLabel !== undefined) this.filterLabel = options.filterLabel;
- if (options.legendLabel !== undefined) this.legendLabel = options.legendLabel;
- if (options.xLabel !== undefined) this.xLabel = options.xLabel;
- if (options.yLabel !== undefined) this.yLabel = options.yLabel;
- if (options.zLabel !== undefined) this.zLabel = options.zLabel;
-
- if (options.xValueLabel !== undefined) this.xValueLabel = options.xValueLabel;
- if (options.yValueLabel !== undefined) this.yValueLabel = options.yValueLabel;
- if (options.zValueLabel !== undefined) this.zValueLabel = options.zValueLabel;
-
- if (options.style !== undefined) {
- var styleNumber = this._getStyleNumber(options.style);
- if (styleNumber !== -1) {
- this.style = styleNumber;
- }
- }
- if (options.showGrid !== undefined) this.showGrid = options.showGrid;
- if (options.showPerspective !== undefined) this.showPerspective = options.showPerspective;
- if (options.showShadow !== undefined) this.showShadow = options.showShadow;
- if (options.tooltip !== undefined) this.showTooltip = options.tooltip;
- if (options.showAnimationControls !== undefined) this.showAnimationControls = options.showAnimationControls;
- if (options.keepAspectRatio !== undefined) this.keepAspectRatio = options.keepAspectRatio;
- if (options.verticalRatio !== undefined) this.verticalRatio = options.verticalRatio;
-
- if (options.animationInterval !== undefined) this.animationInterval = options.animationInterval;
- if (options.animationPreload !== undefined) this.animationPreload = options.animationPreload;
- if (options.animationAutoStart !== undefined)this.animationAutoStart = options.animationAutoStart;
-
- if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth;
- if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth;
-
- if (options.xMin !== undefined) this.defaultXMin = options.xMin;
- if (options.xStep !== undefined) this.defaultXStep = options.xStep;
- if (options.xMax !== undefined) this.defaultXMax = options.xMax;
- if (options.yMin !== undefined) this.defaultYMin = options.yMin;
- if (options.yStep !== undefined) this.defaultYStep = options.yStep;
- if (options.yMax !== undefined) this.defaultYMax = options.yMax;
- if (options.zMin !== undefined) this.defaultZMin = options.zMin;
- if (options.zStep !== undefined) this.defaultZStep = options.zStep;
- if (options.zMax !== undefined) this.defaultZMax = options.zMax;
- if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin;
- if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax;
-
- if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition;
-
- if (cameraPosition !== undefined) {
- this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical);
- this.camera.setArmLength(cameraPosition.distance);
- }
- else {
- this.camera.setArmRotation(1.0, 0.5);
- this.camera.setArmLength(1.7);
- }
- }
-
- this._setBackgroundColor(options && options.backgroundColor);
-
- this.setSize(this.width, this.height);
-
- // re-load the data
- if (this.dataTable) {
- this.setData(this.dataTable);
- }
-
- // start animation when option is true
- if (this.animationAutoStart && this.dataFilter) {
- this.animationStart();
- }
- };
-
- /**
- * Redraw the Graph.
- */
- Graph3d.prototype.redraw = function() {
- if (this.dataPoints === undefined) {
- throw 'Error: graph data not initialized';
- }
-
- this._resizeCanvas();
- this._resizeCenter();
- this._redrawSlider();
- this._redrawClear();
- this._redrawAxis();
-
- if (this.style === Graph3d.STYLE.GRID ||
- this.style === Graph3d.STYLE.SURFACE) {
- this._redrawDataGrid();
- }
- else if (this.style === Graph3d.STYLE.LINE) {
- this._redrawDataLine();
- }
- else if (this.style === Graph3d.STYLE.BAR ||
- this.style === Graph3d.STYLE.BARCOLOR ||
- this.style === Graph3d.STYLE.BARSIZE) {
- this._redrawDataBar();
- }
- else {
- // style is DOT, DOTLINE, DOTCOLOR, DOTSIZE
- this._redrawDataDot();
- }
-
- this._redrawInfo();
- this._redrawLegend();
- };
-
- /**
- * Clear the canvas before redrawing
- */
- Graph3d.prototype._redrawClear = function() {
- var canvas = this.frame.canvas;
- var ctx = canvas.getContext('2d');
-
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- };
-
-
- /**
- * Redraw the legend showing the colors
- */
- Graph3d.prototype._redrawLegend = function() {
- var y;
-
- if (this.style === Graph3d.STYLE.DOTCOLOR ||
- this.style === Graph3d.STYLE.DOTSIZE) {
-
- var dotSize = this.frame.clientWidth * 0.02;
-
- var widthMin, widthMax;
- if (this.style === Graph3d.STYLE.DOTSIZE) {
- widthMin = dotSize / 2; // px
- widthMax = dotSize / 2 + dotSize * 2; // Todo: put this in one function
- }
- else {
- widthMin = 20; // px
- widthMax = 20; // px
- }
-
- var height = Math.max(this.frame.clientHeight * 0.25, 100);
- var top = this.margin;
- var right = this.frame.clientWidth - this.margin;
- var left = right - widthMax;
- var bottom = top + height;
- }
-
- var canvas = this.frame.canvas;
- var ctx = canvas.getContext('2d');
- ctx.lineWidth = 1;
- ctx.font = '14px arial'; // TODO: put in options
-
- if (this.style === Graph3d.STYLE.DOTCOLOR) {
- // draw the color bar
- var ymin = 0;
- var ymax = height; // Todo: make height customizable
- for (y = ymin; y < ymax; y++) {
- var f = (y - ymin) / (ymax - ymin);
-
- //var width = (dotSize / 2 + (1-f) * dotSize * 2); // Todo: put this in one function
- var hue = f * 240;
- var color = this._hsv2rgb(hue, 1, 1);
-
- ctx.strokeStyle = color;
- ctx.beginPath();
- ctx.moveTo(left, top + y);
- ctx.lineTo(right, top + y);
- ctx.stroke();
- }
-
- ctx.strokeStyle = this.colorAxis;
- ctx.strokeRect(left, top, widthMax, height);
- }
-
- if (this.style === Graph3d.STYLE.DOTSIZE) {
- // draw border around color bar
- ctx.strokeStyle = this.colorAxis;
- ctx.fillStyle = this.colorDot;
- ctx.beginPath();
- ctx.moveTo(left, top);
- ctx.lineTo(right, top);
- ctx.lineTo(right - widthMax + widthMin, bottom);
- ctx.lineTo(left, bottom);
- ctx.closePath();
- ctx.fill();
- ctx.stroke();
- }
-
- if (this.style === Graph3d.STYLE.DOTCOLOR ||
- this.style === Graph3d.STYLE.DOTSIZE) {
- // print values along the color bar
- var gridLineLen = 5; // px
- var step = new StepNumber(this.valueMin, this.valueMax, (this.valueMax-this.valueMin)/5, true);
- step.start();
- if (step.getCurrent() < this.valueMin) {
- step.next();
- }
- while (!step.end()) {
- y = bottom - (step.getCurrent() - this.valueMin) / (this.valueMax - this.valueMin) * height;
-
- ctx.beginPath();
- ctx.moveTo(left - gridLineLen, y);
- ctx.lineTo(left, y);
- ctx.stroke();
-
- ctx.textAlign = 'right';
- ctx.textBaseline = 'middle';
- ctx.fillStyle = this.colorAxis;
- ctx.fillText(step.getCurrent(), left - 2 * gridLineLen, y);
-
- step.next();
- }
-
- ctx.textAlign = 'right';
- ctx.textBaseline = 'top';
- var label = this.legendLabel;
- ctx.fillText(label, right, bottom + this.margin);
- }
- };
-
- /**
- * Redraw the filter
- */
- Graph3d.prototype._redrawFilter = function() {
- this.frame.filter.innerHTML = '';
-
- if (this.dataFilter) {
- var options = {
- 'visible': this.showAnimationControls
- };
- var slider = new Slider(this.frame.filter, options);
- this.frame.filter.slider = slider;
-
- // TODO: css here is not nice here...
- this.frame.filter.style.padding = '10px';
- //this.frame.filter.style.backgroundColor = '#EFEFEF';
-
- slider.setValues(this.dataFilter.values);
- slider.setPlayInterval(this.animationInterval);
-
- // create an event handler
- var me = this;
- var onchange = function () {
- var index = slider.getIndex();
-
- me.dataFilter.selectValue(index);
- me.dataPoints = me.dataFilter._getDataPoints();
-
- me.redraw();
- };
- slider.setOnChangeCallback(onchange);
- }
- else {
- this.frame.filter.slider = undefined;
- }
- };
-
- /**
- * Redraw the slider
- */
- Graph3d.prototype._redrawSlider = function() {
- if ( this.frame.filter.slider !== undefined) {
- this.frame.filter.slider.redraw();
- }
- };
-
-
- /**
- * Redraw common information
- */
- Graph3d.prototype._redrawInfo = function() {
- if (this.dataFilter) {
- var canvas = this.frame.canvas;
- var ctx = canvas.getContext('2d');
-
- ctx.font = '14px arial'; // TODO: put in options
- ctx.lineStyle = 'gray';
- ctx.fillStyle = 'gray';
- ctx.textAlign = 'left';
- ctx.textBaseline = 'top';
-
- var x = this.margin;
- var y = this.margin;
- ctx.fillText(this.dataFilter.getLabel() + ': ' + this.dataFilter.getSelectedValue(), x, y);
- }
- };
-
-
- /**
- * Redraw the axis
- */
- Graph3d.prototype._redrawAxis = function() {
- var canvas = this.frame.canvas,
- ctx = canvas.getContext('2d'),
- from, to, step, prettyStep,
- text, xText, yText, zText,
- offset, xOffset, yOffset,
- xMin2d, xMax2d;
-
- // TODO: get the actual rendered style of the containerElement
- //ctx.font = this.containerElement.style.font;
- ctx.font = 24 / this.camera.getArmLength() + 'px arial';
-
- // calculate the length for the short grid lines
- var gridLenX = 0.025 / this.scale.x;
- var gridLenY = 0.025 / this.scale.y;
- var textMargin = 5 / this.camera.getArmLength(); // px
- var armAngle = this.camera.getArmRotation().horizontal;
-
- // draw x-grid lines
- ctx.lineWidth = 1;
- prettyStep = (this.defaultXStep === undefined);
- step = new StepNumber(this.xMin, this.xMax, this.xStep, prettyStep);
- step.start();
- if (step.getCurrent() < this.xMin) {
- step.next();
- }
- while (!step.end()) {
- var x = step.getCurrent();
-
- if (this.showGrid) {
- from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin));
- to = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin));
- ctx.strokeStyle = this.colorGrid;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(to.x, to.y);
- ctx.stroke();
- }
- else {
- from = this._convert3Dto2D(new Point3d(x, this.yMin, this.zMin));
- to = this._convert3Dto2D(new Point3d(x, this.yMin+gridLenX, this.zMin));
- ctx.strokeStyle = this.colorAxis;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(to.x, to.y);
- ctx.stroke();
-
- from = this._convert3Dto2D(new Point3d(x, this.yMax, this.zMin));
- to = this._convert3Dto2D(new Point3d(x, this.yMax-gridLenX, this.zMin));
- ctx.strokeStyle = this.colorAxis;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(to.x, to.y);
- ctx.stroke();
- }
-
- yText = (Math.cos(armAngle) > 0) ? this.yMin : this.yMax;
- text = this._convert3Dto2D(new Point3d(x, yText, this.zMin));
- if (Math.cos(armAngle * 2) > 0) {
- ctx.textAlign = 'center';
- ctx.textBaseline = 'top';
- text.y += textMargin;
- }
- else if (Math.sin(armAngle * 2) < 0){
- ctx.textAlign = 'right';
- ctx.textBaseline = 'middle';
- }
- else {
- ctx.textAlign = 'left';
- ctx.textBaseline = 'middle';
- }
- ctx.fillStyle = this.colorAxis;
- ctx.fillText(' ' + this.xValueLabel(step.getCurrent()) + ' ', text.x, text.y);
-
- step.next();
- }
-
- // draw y-grid lines
- ctx.lineWidth = 1;
- prettyStep = (this.defaultYStep === undefined);
- step = new StepNumber(this.yMin, this.yMax, this.yStep, prettyStep);
- step.start();
- if (step.getCurrent() < this.yMin) {
- step.next();
- }
- while (!step.end()) {
- if (this.showGrid) {
- from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin));
- to = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin));
- ctx.strokeStyle = this.colorGrid;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(to.x, to.y);
- ctx.stroke();
- }
- else {
- from = this._convert3Dto2D(new Point3d(this.xMin, step.getCurrent(), this.zMin));
- to = this._convert3Dto2D(new Point3d(this.xMin+gridLenY, step.getCurrent(), this.zMin));
- ctx.strokeStyle = this.colorAxis;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(to.x, to.y);
- ctx.stroke();
-
- from = this._convert3Dto2D(new Point3d(this.xMax, step.getCurrent(), this.zMin));
- to = this._convert3Dto2D(new Point3d(this.xMax-gridLenY, step.getCurrent(), this.zMin));
- ctx.strokeStyle = this.colorAxis;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(to.x, to.y);
- ctx.stroke();
- }
-
- xText = (Math.sin(armAngle ) > 0) ? this.xMin : this.xMax;
- text = this._convert3Dto2D(new Point3d(xText, step.getCurrent(), this.zMin));
- if (Math.cos(armAngle * 2) < 0) {
- ctx.textAlign = 'center';
- ctx.textBaseline = 'top';
- text.y += textMargin;
- }
- else if (Math.sin(armAngle * 2) > 0){
- ctx.textAlign = 'right';
- ctx.textBaseline = 'middle';
- }
- else {
- ctx.textAlign = 'left';
- ctx.textBaseline = 'middle';
- }
- ctx.fillStyle = this.colorAxis;
- ctx.fillText(' ' + this.yValueLabel(step.getCurrent()) + ' ', text.x, text.y);
-
- step.next();
- }
-
- // draw z-grid lines and axis
- ctx.lineWidth = 1;
- prettyStep = (this.defaultZStep === undefined);
- step = new StepNumber(this.zMin, this.zMax, this.zStep, prettyStep);
- step.start();
- if (step.getCurrent() < this.zMin) {
- step.next();
- }
- xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax;
- yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax;
- while (!step.end()) {
- // TODO: make z-grid lines really 3d?
- from = this._convert3Dto2D(new Point3d(xText, yText, step.getCurrent()));
- ctx.strokeStyle = this.colorAxis;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(from.x - textMargin, from.y);
- ctx.stroke();
-
- ctx.textAlign = 'right';
- ctx.textBaseline = 'middle';
- ctx.fillStyle = this.colorAxis;
- ctx.fillText(this.zValueLabel(step.getCurrent()) + ' ', from.x - 5, from.y);
-
- step.next();
- }
- ctx.lineWidth = 1;
- from = this._convert3Dto2D(new Point3d(xText, yText, this.zMin));
- to = this._convert3Dto2D(new Point3d(xText, yText, this.zMax));
- ctx.strokeStyle = this.colorAxis;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(to.x, to.y);
- ctx.stroke();
-
- // draw x-axis
- ctx.lineWidth = 1;
- // line at yMin
- xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin));
- xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin));
- ctx.strokeStyle = this.colorAxis;
- ctx.beginPath();
- ctx.moveTo(xMin2d.x, xMin2d.y);
- ctx.lineTo(xMax2d.x, xMax2d.y);
- ctx.stroke();
- // line at ymax
- xMin2d = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin));
- xMax2d = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin));
- ctx.strokeStyle = this.colorAxis;
- ctx.beginPath();
- ctx.moveTo(xMin2d.x, xMin2d.y);
- ctx.lineTo(xMax2d.x, xMax2d.y);
- ctx.stroke();
-
- // draw y-axis
- ctx.lineWidth = 1;
- // line at xMin
- from = this._convert3Dto2D(new Point3d(this.xMin, this.yMin, this.zMin));
- to = this._convert3Dto2D(new Point3d(this.xMin, this.yMax, this.zMin));
- ctx.strokeStyle = this.colorAxis;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(to.x, to.y);
- ctx.stroke();
- // line at xMax
- from = this._convert3Dto2D(new Point3d(this.xMax, this.yMin, this.zMin));
- to = this._convert3Dto2D(new Point3d(this.xMax, this.yMax, this.zMin));
- ctx.strokeStyle = this.colorAxis;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(to.x, to.y);
- ctx.stroke();
-
- // draw x-label
- var xLabel = this.xLabel;
- if (xLabel.length > 0) {
- yOffset = 0.1 / this.scale.y;
- xText = (this.xMin + this.xMax) / 2;
- yText = (Math.cos(armAngle) > 0) ? this.yMin - yOffset: this.yMax + yOffset;
- text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin));
- if (Math.cos(armAngle * 2) > 0) {
- ctx.textAlign = 'center';
- ctx.textBaseline = 'top';
- }
- else if (Math.sin(armAngle * 2) < 0){
- ctx.textAlign = 'right';
- ctx.textBaseline = 'middle';
- }
- else {
- ctx.textAlign = 'left';
- ctx.textBaseline = 'middle';
- }
- ctx.fillStyle = this.colorAxis;
- ctx.fillText(xLabel, text.x, text.y);
- }
-
- // draw y-label
- var yLabel = this.yLabel;
- if (yLabel.length > 0) {
- xOffset = 0.1 / this.scale.x;
- xText = (Math.sin(armAngle ) > 0) ? this.xMin - xOffset : this.xMax + xOffset;
- yText = (this.yMin + this.yMax) / 2;
- text = this._convert3Dto2D(new Point3d(xText, yText, this.zMin));
- if (Math.cos(armAngle * 2) < 0) {
- ctx.textAlign = 'center';
- ctx.textBaseline = 'top';
- }
- else if (Math.sin(armAngle * 2) > 0){
- ctx.textAlign = 'right';
- ctx.textBaseline = 'middle';
- }
- else {
- ctx.textAlign = 'left';
- ctx.textBaseline = 'middle';
- }
- ctx.fillStyle = this.colorAxis;
- ctx.fillText(yLabel, text.x, text.y);
- }
-
- // draw z-label
- var zLabel = this.zLabel;
- if (zLabel.length > 0) {
- offset = 30; // pixels. // TODO: relate to the max width of the values on the z axis?
- xText = (Math.cos(armAngle ) > 0) ? this.xMin : this.xMax;
- yText = (Math.sin(armAngle ) < 0) ? this.yMin : this.yMax;
- zText = (this.zMin + this.zMax) / 2;
- text = this._convert3Dto2D(new Point3d(xText, yText, zText));
- ctx.textAlign = 'right';
- ctx.textBaseline = 'middle';
- ctx.fillStyle = this.colorAxis;
- ctx.fillText(zLabel, text.x - offset, text.y);
- }
- };
-
- /**
- * Calculate the color based on the given value.
- * @param {Number} H Hue, a value be between 0 and 360
- * @param {Number} S Saturation, a value between 0 and 1
- * @param {Number} V Value, a value between 0 and 1
- */
- Graph3d.prototype._hsv2rgb = function(H, S, V) {
- var R, G, B, C, Hi, X;
-
- C = V * S;
- Hi = Math.floor(H/60); // hi = 0,1,2,3,4,5
- X = C * (1 - Math.abs(((H/60) % 2) - 1));
-
- switch (Hi) {
- case 0: R = C; G = X; B = 0; break;
- case 1: R = X; G = C; B = 0; break;
- case 2: R = 0; G = C; B = X; break;
- case 3: R = 0; G = X; B = C; break;
- case 4: R = X; G = 0; B = C; break;
- case 5: R = C; G = 0; B = X; break;
-
- default: R = 0; G = 0; B = 0; break;
- }
-
- return 'RGB(' + parseInt(R*255) + ',' + parseInt(G*255) + ',' + parseInt(B*255) + ')';
- };
-
-
- /**
- * Draw all datapoints as a grid
- * This function can be used when the style is 'grid'
- */
- Graph3d.prototype._redrawDataGrid = function() {
- var canvas = this.frame.canvas,
- ctx = canvas.getContext('2d'),
- point, right, top, cross,
- i,
- topSideVisible, fillStyle, strokeStyle, lineWidth,
- h, s, v, zAvg;
-
-
- if (this.dataPoints === undefined || this.dataPoints.length <= 0)
- return; // TODO: throw exception?
-
- // calculate the translations and screen position of all points
- for (i = 0; i < this.dataPoints.length; i++) {
- var trans = this._convertPointToTranslation(this.dataPoints[i].point);
- var screen = this._convertTranslationToScreen(trans);
-
- this.dataPoints[i].trans = trans;
- this.dataPoints[i].screen = screen;
-
- // calculate the translation of the point at the bottom (needed for sorting)
- var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom);
- this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z;
- }
-
- // sort the points on depth of their (x,y) position (not on z)
- var sortDepth = function (a, b) {
- return b.dist - a.dist;
- };
- this.dataPoints.sort(sortDepth);
-
- if (this.style === Graph3d.STYLE.SURFACE) {
- for (i = 0; i < this.dataPoints.length; i++) {
- point = this.dataPoints[i];
- right = this.dataPoints[i].pointRight;
- top = this.dataPoints[i].pointTop;
- cross = this.dataPoints[i].pointCross;
-
- if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) {
-
- if (this.showGrayBottom || this.showShadow) {
- // calculate the cross product of the two vectors from center
- // to left and right, in order to know whether we are looking at the
- // bottom or at the top side. We can also use the cross product
- // for calculating light intensity
- var aDiff = Point3d.subtract(cross.trans, point.trans);
- var bDiff = Point3d.subtract(top.trans, right.trans);
- var crossproduct = Point3d.crossProduct(aDiff, bDiff);
- var len = crossproduct.length();
- // FIXME: there is a bug with determining the surface side (shadow or colored)
-
- topSideVisible = (crossproduct.z > 0);
- }
- else {
- topSideVisible = true;
- }
-
- if (topSideVisible) {
- // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
- zAvg = (point.point.z + right.point.z + top.point.z + cross.point.z) / 4;
- h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240;
- s = 1; // saturation
-
- if (this.showShadow) {
- v = Math.min(1 + (crossproduct.x / len) / 2, 1); // value. TODO: scale
- fillStyle = this._hsv2rgb(h, s, v);
- strokeStyle = fillStyle;
- }
- else {
- v = 1;
- fillStyle = this._hsv2rgb(h, s, v);
- strokeStyle = this.colorAxis;
- }
- }
- else {
- fillStyle = 'gray';
- strokeStyle = this.colorAxis;
- }
- lineWidth = 0.5;
-
- ctx.lineWidth = lineWidth;
- ctx.fillStyle = fillStyle;
- ctx.strokeStyle = strokeStyle;
- ctx.beginPath();
- ctx.moveTo(point.screen.x, point.screen.y);
- ctx.lineTo(right.screen.x, right.screen.y);
- ctx.lineTo(cross.screen.x, cross.screen.y);
- ctx.lineTo(top.screen.x, top.screen.y);
- ctx.closePath();
- ctx.fill();
- ctx.stroke();
- }
- }
- }
- else { // grid style
- for (i = 0; i < this.dataPoints.length; i++) {
- point = this.dataPoints[i];
- right = this.dataPoints[i].pointRight;
- top = this.dataPoints[i].pointTop;
-
- if (point !== undefined) {
- if (this.showPerspective) {
- lineWidth = 2 / -point.trans.z;
- }
- else {
- lineWidth = 2 * -(this.eye.z / this.camera.getArmLength());
- }
- }
-
- if (point !== undefined && right !== undefined) {
- // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
- zAvg = (point.point.z + right.point.z) / 2;
- h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240;
-
- ctx.lineWidth = lineWidth;
- ctx.strokeStyle = this._hsv2rgb(h, 1, 1);
- ctx.beginPath();
- ctx.moveTo(point.screen.x, point.screen.y);
- ctx.lineTo(right.screen.x, right.screen.y);
- ctx.stroke();
- }
-
- if (point !== undefined && top !== undefined) {
- // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
- zAvg = (point.point.z + top.point.z) / 2;
- h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240;
-
- ctx.lineWidth = lineWidth;
- ctx.strokeStyle = this._hsv2rgb(h, 1, 1);
- ctx.beginPath();
- ctx.moveTo(point.screen.x, point.screen.y);
- ctx.lineTo(top.screen.x, top.screen.y);
- ctx.stroke();
- }
- }
- }
- };
-
-
- /**
- * Draw all datapoints as dots.
- * This function can be used when the style is 'dot' or 'dot-line'
- */
- Graph3d.prototype._redrawDataDot = function() {
- var canvas = this.frame.canvas;
- var ctx = canvas.getContext('2d');
- var i;
-
- if (this.dataPoints === undefined || this.dataPoints.length <= 0)
- return; // TODO: throw exception?
-
- // calculate the translations of all points
- for (i = 0; i < this.dataPoints.length; i++) {
- var trans = this._convertPointToTranslation(this.dataPoints[i].point);
- var screen = this._convertTranslationToScreen(trans);
- this.dataPoints[i].trans = trans;
- this.dataPoints[i].screen = screen;
-
- // calculate the distance from the point at the bottom to the camera
- var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom);
- this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z;
- }
-
- // order the translated points by depth
- var sortDepth = function (a, b) {
- return b.dist - a.dist;
- };
- this.dataPoints.sort(sortDepth);
-
- // draw the datapoints as colored circles
- var dotSize = this.frame.clientWidth * 0.02; // px
- for (i = 0; i < this.dataPoints.length; i++) {
- var point = this.dataPoints[i];
-
- if (this.style === Graph3d.STYLE.DOTLINE) {
- // draw a vertical line from the bottom to the graph value
- //var from = this._convert3Dto2D(new Point3d(point.point.x, point.point.y, this.zMin));
- var from = this._convert3Dto2D(point.bottom);
- ctx.lineWidth = 1;
- ctx.strokeStyle = this.colorGrid;
- ctx.beginPath();
- ctx.moveTo(from.x, from.y);
- ctx.lineTo(point.screen.x, point.screen.y);
- ctx.stroke();
- }
-
- // calculate radius for the circle
- var size;
- if (this.style === Graph3d.STYLE.DOTSIZE) {
- size = dotSize/2 + 2*dotSize * (point.point.value - this.valueMin) / (this.valueMax - this.valueMin);
- }
- else {
- size = dotSize;
- }
-
- var radius;
- if (this.showPerspective) {
- radius = size / -point.trans.z;
- }
- else {
- radius = size * -(this.eye.z / this.camera.getArmLength());
- }
- if (radius < 0) {
- radius = 0;
- }
-
- var hue, color, borderColor;
- if (this.style === Graph3d.STYLE.DOTCOLOR ) {
- // calculate the color based on the value
- hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240;
- color = this._hsv2rgb(hue, 1, 1);
- borderColor = this._hsv2rgb(hue, 1, 0.8);
- }
- else if (this.style === Graph3d.STYLE.DOTSIZE) {
- color = this.colorDot;
- borderColor = this.colorDotBorder;
- }
- else {
- // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
- hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240;
- color = this._hsv2rgb(hue, 1, 1);
- borderColor = this._hsv2rgb(hue, 1, 0.8);
- }
-
- // draw the circle
- ctx.lineWidth = 1.0;
- ctx.strokeStyle = borderColor;
- ctx.fillStyle = color;
- ctx.beginPath();
- ctx.arc(point.screen.x, point.screen.y, radius, 0, Math.PI*2, true);
- ctx.fill();
- ctx.stroke();
- }
- };
-
- /**
- * Draw all datapoints as bars.
- * This function can be used when the style is 'bar', 'bar-color', or 'bar-size'
- */
- Graph3d.prototype._redrawDataBar = function() {
- var canvas = this.frame.canvas;
- var ctx = canvas.getContext('2d');
- var i, j, surface, corners;
-
- if (this.dataPoints === undefined || this.dataPoints.length <= 0)
- return; // TODO: throw exception?
-
- // calculate the translations of all points
- for (i = 0; i < this.dataPoints.length; i++) {
- var trans = this._convertPointToTranslation(this.dataPoints[i].point);
- var screen = this._convertTranslationToScreen(trans);
- this.dataPoints[i].trans = trans;
- this.dataPoints[i].screen = screen;
-
- // calculate the distance from the point at the bottom to the camera
- var transBottom = this._convertPointToTranslation(this.dataPoints[i].bottom);
- this.dataPoints[i].dist = this.showPerspective ? transBottom.length() : -transBottom.z;
- }
-
- // order the translated points by depth
- var sortDepth = function (a, b) {
- return b.dist - a.dist;
- };
- this.dataPoints.sort(sortDepth);
-
- // draw the datapoints as bars
- var xWidth = this.xBarWidth / 2;
- var yWidth = this.yBarWidth / 2;
- for (i = 0; i < this.dataPoints.length; i++) {
- var point = this.dataPoints[i];
-
- // determine color
- var hue, color, borderColor;
- if (this.style === Graph3d.STYLE.BARCOLOR ) {
- // calculate the color based on the value
- hue = (1 - (point.point.value - this.valueMin) * this.scale.value) * 240;
- color = this._hsv2rgb(hue, 1, 1);
- borderColor = this._hsv2rgb(hue, 1, 0.8);
- }
- else if (this.style === Graph3d.STYLE.BARSIZE) {
- color = this.colorDot;
- borderColor = this.colorDotBorder;
- }
- else {
- // calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
- hue = (1 - (point.point.z - this.zMin) * this.scale.z / this.verticalRatio) * 240;
- color = this._hsv2rgb(hue, 1, 1);
- borderColor = this._hsv2rgb(hue, 1, 0.8);
- }
-
- // calculate size for the bar
- if (this.style === Graph3d.STYLE.BARSIZE) {
- xWidth = (this.xBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2);
- yWidth = (this.yBarWidth / 2) * ((point.point.value - this.valueMin) / (this.valueMax - this.valueMin) * 0.8 + 0.2);
- }
-
- // calculate all corner points
- var me = this;
- var point3d = point.point;
- var top = [
- {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, point3d.z)},
- {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, point3d.z)},
- {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, point3d.z)},
- {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, point3d.z)}
- ];
- var bottom = [
- {point: new Point3d(point3d.x - xWidth, point3d.y - yWidth, this.zMin)},
- {point: new Point3d(point3d.x + xWidth, point3d.y - yWidth, this.zMin)},
- {point: new Point3d(point3d.x + xWidth, point3d.y + yWidth, this.zMin)},
- {point: new Point3d(point3d.x - xWidth, point3d.y + yWidth, this.zMin)}
- ];
-
- // calculate screen location of the points
- top.forEach(function (obj) {
- obj.screen = me._convert3Dto2D(obj.point);
- });
- bottom.forEach(function (obj) {
- obj.screen = me._convert3Dto2D(obj.point);
- });
-
- // create five sides, calculate both corner points and center points
- var surfaces = [
- {corners: top, center: Point3d.avg(bottom[0].point, bottom[2].point)},
- {corners: [top[0], top[1], bottom[1], bottom[0]], center: Point3d.avg(bottom[1].point, bottom[0].point)},
- {corners: [top[1], top[2], bottom[2], bottom[1]], center: Point3d.avg(bottom[2].point, bottom[1].point)},
- {corners: [top[2], top[3], bottom[3], bottom[2]], center: Point3d.avg(bottom[3].point, bottom[2].point)},
- {corners: [top[3], top[0], bottom[0], bottom[3]], center: Point3d.avg(bottom[0].point, bottom[3].point)}
- ];
- point.surfaces = surfaces;
-
- // calculate the distance of each of the surface centers to the camera
- for (j = 0; j < surfaces.length; j++) {
- surface = surfaces[j];
- var transCenter = this._convertPointToTranslation(surface.center);
- surface.dist = this.showPerspective ? transCenter.length() : -transCenter.z;
- // TODO: this dept calculation doesn't work 100% of the cases due to perspective,
- // but the current solution is fast/simple and works in 99.9% of all cases
- // the issue is visible in example 14, with graph.setCameraPosition({horizontal: 2.97, vertical: 0.5, distance: 0.9})
- }
-
- // order the surfaces by their (translated) depth
- surfaces.sort(function (a, b) {
- var diff = b.dist - a.dist;
- if (diff) return diff;
-
- // if equal depth, sort the top surface last
- if (a.corners === top) return 1;
- if (b.corners === top) return -1;
-
- // both are equal
- return 0;
- });
-
- // draw the ordered surfaces
- ctx.lineWidth = 1;
- ctx.strokeStyle = borderColor;
- ctx.fillStyle = color;
- // NOTE: we start at j=2 instead of j=0 as we don't need to draw the two surfaces at the backside
- for (j = 2; j < surfaces.length; j++) {
- surface = surfaces[j];
- corners = surface.corners;
- ctx.beginPath();
- ctx.moveTo(corners[3].screen.x, corners[3].screen.y);
- ctx.lineTo(corners[0].screen.x, corners[0].screen.y);
- ctx.lineTo(corners[1].screen.x, corners[1].screen.y);
- ctx.lineTo(corners[2].screen.x, corners[2].screen.y);
- ctx.lineTo(corners[3].screen.x, corners[3].screen.y);
- ctx.fill();
- ctx.stroke();
- }
- }
- };
-
-
- /**
- * Draw a line through all datapoints.
- * This function can be used when the style is 'line'
- */
- Graph3d.prototype._redrawDataLine = function() {
- var canvas = this.frame.canvas,
- ctx = canvas.getContext('2d'),
- point, i;
-
- if (this.dataPoints === undefined || this.dataPoints.length <= 0)
- return; // TODO: throw exception?
-
- // calculate the translations of all points
- for (i = 0; i < this.dataPoints.length; i++) {
- var trans = this._convertPointToTranslation(this.dataPoints[i].point);
- var screen = this._convertTranslationToScreen(trans);
-
- this.dataPoints[i].trans = trans;
- this.dataPoints[i].screen = screen;
- }
-
- // start the line
- if (this.dataPoints.length > 0) {
- point = this.dataPoints[0];
-
- ctx.lineWidth = 1; // TODO: make customizable
- ctx.strokeStyle = 'blue'; // TODO: make customizable
- ctx.beginPath();
- ctx.moveTo(point.screen.x, point.screen.y);
- }
-
- // draw the datapoints as colored circles
- for (i = 1; i < this.dataPoints.length; i++) {
- point = this.dataPoints[i];
- ctx.lineTo(point.screen.x, point.screen.y);
- }
-
- // finish the line
- if (this.dataPoints.length > 0) {
- ctx.stroke();
- }
- };
-
- /**
- * Start a moving operation inside the provided parent element
- * @param {Event} event The event that occurred (required for
- * retrieving the mouse position)
- */
- Graph3d.prototype._onMouseDown = function(event) {
- event = event || window.event;
-
- // check if mouse is still down (may be up when focus is lost for example
- // in an iframe)
- if (this.leftButtonDown) {
- this._onMouseUp(event);
- }
-
- // only react on left mouse button down
- this.leftButtonDown = event.which ? (event.which === 1) : (event.button === 1);
- if (!this.leftButtonDown && !this.touchDown) return;
-
- // get mouse position (different code for IE and all other browsers)
- this.startMouseX = getMouseX(event);
- this.startMouseY = getMouseY(event);
-
- this.startStart = new Date(this.start);
- this.startEnd = new Date(this.end);
- this.startArmRotation = this.camera.getArmRotation();
-
- this.frame.style.cursor = 'move';
-
- // add event listeners to handle moving the contents
- // we store the function onmousemove and onmouseup in the graph, so we can
- // remove the eventlisteners lateron in the function mouseUp()
- var me = this;
- this.onmousemove = function (event) {me._onMouseMove(event);};
- this.onmouseup = function (event) {me._onMouseUp(event);};
- util.addEventListener(document, 'mousemove', me.onmousemove);
- util.addEventListener(document, 'mouseup', me.onmouseup);
- util.preventDefault(event);
- };
-
-
- /**
- * Perform moving operating.
- * This function activated from within the funcion Graph.mouseDown().
- * @param {Event} event Well, eehh, the event
- */
- Graph3d.prototype._onMouseMove = function (event) {
- event = event || window.event;
-
- // calculate change in mouse position
- var diffX = parseFloat(getMouseX(event)) - this.startMouseX;
- var diffY = parseFloat(getMouseY(event)) - this.startMouseY;
-
- var horizontalNew = this.startArmRotation.horizontal + diffX / 200;
- var verticalNew = this.startArmRotation.vertical + diffY / 200;
-
- var snapAngle = 4; // degrees
- var snapValue = Math.sin(snapAngle / 360 * 2 * Math.PI);
-
- // snap horizontally to nice angles at 0pi, 0.5pi, 1pi, 1.5pi, etc...
- // the -0.001 is to take care that the vertical axis is always drawn at the left front corner
- if (Math.abs(Math.sin(horizontalNew)) < snapValue) {
- horizontalNew = Math.round((horizontalNew / Math.PI)) * Math.PI - 0.001;
- }
- if (Math.abs(Math.cos(horizontalNew)) < snapValue) {
- horizontalNew = (Math.round((horizontalNew/ Math.PI - 0.5)) + 0.5) * Math.PI - 0.001;
- }
-
- // snap vertically to nice angles
- if (Math.abs(Math.sin(verticalNew)) < snapValue) {
- verticalNew = Math.round((verticalNew / Math.PI)) * Math.PI;
- }
- if (Math.abs(Math.cos(verticalNew)) < snapValue) {
- verticalNew = (Math.round((verticalNew/ Math.PI - 0.5)) + 0.5) * Math.PI;
- }
-
- this.camera.setArmRotation(horizontalNew, verticalNew);
- this.redraw();
-
- // fire a cameraPositionChange event
- var parameters = this.getCameraPosition();
- this.emit('cameraPositionChange', parameters);
-
- util.preventDefault(event);
- };
-
-
- /**
- * Stop moving operating.
- * This function activated from within the funcion Graph.mouseDown().
- * @param {event} event The event
- */
- Graph3d.prototype._onMouseUp = function (event) {
- this.frame.style.cursor = 'auto';
- this.leftButtonDown = false;
-
- // remove event listeners here
- util.removeEventListener(document, 'mousemove', this.onmousemove);
- util.removeEventListener(document, 'mouseup', this.onmouseup);
- util.preventDefault(event);
- };
-
- /**
- * After having moved the mouse, a tooltip should pop up when the mouse is resting on a data point
- * @param {Event} event A mouse move event
- */
- Graph3d.prototype._onTooltip = function (event) {
- var delay = 300; // ms
- var mouseX = getMouseX(event) - util.getAbsoluteLeft(this.frame);
- var mouseY = getMouseY(event) - util.getAbsoluteTop(this.frame);
-
- if (!this.showTooltip) {
- return;
- }
-
- if (this.tooltipTimeout) {
- clearTimeout(this.tooltipTimeout);
- }
-
- // (delayed) display of a tooltip only if no mouse button is down
- if (this.leftButtonDown) {
- this._hideTooltip();
- return;
- }
-
- if (this.tooltip && this.tooltip.dataPoint) {
- // tooltip is currently visible
- var dataPoint = this._dataPointFromXY(mouseX, mouseY);
- if (dataPoint !== this.tooltip.dataPoint) {
- // datapoint changed
- if (dataPoint) {
- this._showTooltip(dataPoint);
- }
- else {
- this._hideTooltip();
- }
- }
- }
- else {
- // tooltip is currently not visible
- var me = this;
- this.tooltipTimeout = setTimeout(function () {
- me.tooltipTimeout = null;
-
- // show a tooltip if we have a data point
- var dataPoint = me._dataPointFromXY(mouseX, mouseY);
- if (dataPoint) {
- me._showTooltip(dataPoint);
- }
- }, delay);
- }
- };
-
- /**
- * Event handler for touchstart event on mobile devices
- */
- Graph3d.prototype._onTouchStart = function(event) {
- this.touchDown = true;
-
- var me = this;
- this.ontouchmove = function (event) {me._onTouchMove(event);};
- this.ontouchend = function (event) {me._onTouchEnd(event);};
- util.addEventListener(document, 'touchmove', me.ontouchmove);
- util.addEventListener(document, 'touchend', me.ontouchend);
-
- this._onMouseDown(event);
- };
-
- /**
- * Event handler for touchmove event on mobile devices
- */
- Graph3d.prototype._onTouchMove = function(event) {
- this._onMouseMove(event);
- };
-
- /**
- * Event handler for touchend event on mobile devices
- */
- Graph3d.prototype._onTouchEnd = function(event) {
- this.touchDown = false;
-
- util.removeEventListener(document, 'touchmove', this.ontouchmove);
- util.removeEventListener(document, 'touchend', this.ontouchend);
-
- this._onMouseUp(event);
- };
-
-
- /**
- * Event handler for mouse wheel event, used to zoom the graph
- * Code from http://adomas.org/javascript-mouse-wheel/
- * @param {event} event The event
- */
- Graph3d.prototype._onWheel = function(event) {
- if (!event) /* For IE. */
- event = window.event;
-
- // retrieve delta
- var delta = 0;
- if (event.wheelDelta) { /* IE/Opera. */
- delta = event.wheelDelta/120;
- } else if (event.detail) { /* Mozilla case. */
- // In Mozilla, sign of delta is different than in IE.
- // Also, delta is multiple of 3.
- delta = -event.detail/3;
- }
-
- // If delta is nonzero, handle it.
- // Basically, delta is now positive if wheel was scrolled up,
- // and negative, if wheel was scrolled down.
- if (delta) {
- var oldLength = this.camera.getArmLength();
- var newLength = oldLength * (1 - delta / 10);
-
- this.camera.setArmLength(newLength);
- this.redraw();
-
- this._hideTooltip();
- }
-
- // fire a cameraPositionChange event
- var parameters = this.getCameraPosition();
- this.emit('cameraPositionChange', parameters);
-
- // Prevent default actions caused by mouse wheel.
- // That might be ugly, but we handle scrolls somehow
- // anyway, so don't bother here..
- util.preventDefault(event);
- };
-
- /**
- * Test whether a point lies inside given 2D triangle
- * @param {Point2d} point
- * @param {Point2d[]} triangle
- * @return {boolean} Returns true if given point lies inside or on the edge of the triangle
- * @private
- */
- Graph3d.prototype._insideTriangle = function (point, triangle) {
- var a = triangle[0],
- b = triangle[1],
- c = triangle[2];
-
- function sign (x) {
- return x > 0 ? 1 : x < 0 ? -1 : 0;
- }
-
- var as = sign((b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x));
- var bs = sign((c.x - b.x) * (point.y - b.y) - (c.y - b.y) * (point.x - b.x));
- var cs = sign((a.x - c.x) * (point.y - c.y) - (a.y - c.y) * (point.x - c.x));
-
- // each of the three signs must be either equal to each other or zero
- return (as == 0 || bs == 0 || as == bs) &&
- (bs == 0 || cs == 0 || bs == cs) &&
- (as == 0 || cs == 0 || as == cs);
- };
-
- /**
- * Find a data point close to given screen position (x, y)
- * @param {Number} x
- * @param {Number} y
- * @return {Object | null} The closest data point or null if not close to any data point
- * @private
- */
- Graph3d.prototype._dataPointFromXY = function (x, y) {
- var i,
- distMax = 100, // px
- dataPoint = null,
- closestDataPoint = null,
- closestDist = null,
- center = new Point2d(x, y);
-
- if (this.style === Graph3d.STYLE.BAR ||
- this.style === Graph3d.STYLE.BARCOLOR ||
- this.style === Graph3d.STYLE.BARSIZE) {
- // the data points are ordered from far away to closest
- for (i = this.dataPoints.length - 1; i >= 0; i--) {
- dataPoint = this.dataPoints[i];
- var surfaces = dataPoint.surfaces;
- if (surfaces) {
- for (var s = surfaces.length - 1; s >= 0; s--) {
- // split each surface in two triangles, and see if the center point is inside one of these
- var surface = surfaces[s];
- var corners = surface.corners;
- var triangle1 = [corners[0].screen, corners[1].screen, corners[2].screen];
- var triangle2 = [corners[2].screen, corners[3].screen, corners[0].screen];
- if (this._insideTriangle(center, triangle1) ||
- this._insideTriangle(center, triangle2)) {
- // return immediately at the first hit
- return dataPoint;
- }
- }
- }
- }
- }
- else {
- // find the closest data point, using distance to the center of the point on 2d screen
- for (i = 0; i < this.dataPoints.length; i++) {
- dataPoint = this.dataPoints[i];
- var point = dataPoint.screen;
- if (point) {
- var distX = Math.abs(x - point.x);
- var distY = Math.abs(y - point.y);
- var dist = Math.sqrt(distX * distX + distY * distY);
-
- if ((closestDist === null || dist < closestDist) && dist < distMax) {
- closestDist = dist;
- closestDataPoint = dataPoint;
- }
- }
- }
- }
-
-
- return closestDataPoint;
- };
-
- /**
- * Display a tooltip for given data point
- * @param {Object} dataPoint
- * @private
- */
- Graph3d.prototype._showTooltip = function (dataPoint) {
- var content, line, dot;
-
- if (!this.tooltip) {
- content = document.createElement('div');
- content.style.position = 'absolute';
- content.style.padding = '10px';
- content.style.border = '1px solid #4d4d4d';
- content.style.color = '#1a1a1a';
- content.style.background = 'rgba(255,255,255,0.7)';
- content.style.borderRadius = '2px';
- content.style.boxShadow = '5px 5px 10px rgba(128,128,128,0.5)';
-
- line = document.createElement('div');
- line.style.position = 'absolute';
- line.style.height = '40px';
- line.style.width = '0';
- line.style.borderLeft = '1px solid #4d4d4d';
-
- dot = document.createElement('div');
- dot.style.position = 'absolute';
- dot.style.height = '0';
- dot.style.width = '0';
- dot.style.border = '5px solid #4d4d4d';
- dot.style.borderRadius = '5px';
-
- this.tooltip = {
- dataPoint: null,
- dom: {
- content: content,
- line: line,
- dot: dot
- }
- };
- }
- else {
- content = this.tooltip.dom.content;
- line = this.tooltip.dom.line;
- dot = this.tooltip.dom.dot;
- }
-
- this._hideTooltip();
-
- this.tooltip.dataPoint = dataPoint;
- if (typeof this.showTooltip === 'function') {
- content.innerHTML = this.showTooltip(dataPoint.point);
- }
- else {
- content.innerHTML = '' +
- 'x: | ' + dataPoint.point.x + ' |
' +
- 'y: | ' + dataPoint.point.y + ' |
' +
- 'z: | ' + dataPoint.point.z + ' |
' +
- '
';
- }
-
- content.style.left = '0';
- content.style.top = '0';
- this.frame.appendChild(content);
- this.frame.appendChild(line);
- this.frame.appendChild(dot);
-
- // calculate sizes
- var contentWidth = content.offsetWidth;
- var contentHeight = content.offsetHeight;
- var lineHeight = line.offsetHeight;
- var dotWidth = dot.offsetWidth;
- var dotHeight = dot.offsetHeight;
-
- var left = dataPoint.screen.x - contentWidth / 2;
- left = Math.min(Math.max(left, 10), this.frame.clientWidth - 10 - contentWidth);
-
- line.style.left = dataPoint.screen.x + 'px';
- line.style.top = (dataPoint.screen.y - lineHeight) + 'px';
- content.style.left = left + 'px';
- content.style.top = (dataPoint.screen.y - lineHeight - contentHeight) + 'px';
- dot.style.left = (dataPoint.screen.x - dotWidth / 2) + 'px';
- dot.style.top = (dataPoint.screen.y - dotHeight / 2) + 'px';
- };
-
- /**
- * Hide the tooltip when displayed
- * @private
- */
- Graph3d.prototype._hideTooltip = function () {
- if (this.tooltip) {
- this.tooltip.dataPoint = null;
-
- for (var prop in this.tooltip.dom) {
- if (this.tooltip.dom.hasOwnProperty(prop)) {
- var elem = this.tooltip.dom[prop];
- if (elem && elem.parentNode) {
- elem.parentNode.removeChild(elem);
- }
- }
- }
- }
- };
-
- /**--------------------------------------------------------------------------**/
-
-
- /**
- * Get the horizontal mouse position from a mouse event
- * @param {Event} event
- * @return {Number} mouse x
- */
- function getMouseX (event) {
- if ('clientX' in event) return event.clientX;
- return event.targetTouches[0] && event.targetTouches[0].clientX || 0;
- }
-
- /**
- * Get the vertical mouse position from a mouse event
- * @param {Event} event
- * @return {Number} mouse y
- */
- function getMouseY (event) {
- if ('clientY' in event) return event.clientY;
- return event.targetTouches[0] && event.targetTouches[0].clientY || 0;
- }
-
- module.exports = Graph3d;
-
-
-/***/ },
-/* 11 */
-/***/ function(module, exports, __webpack_require__) {
-
-
- /**
- * Expose `Emitter`.
- */
-
- module.exports = Emitter;
-
- /**
- * Initialize a new `Emitter`.
- *
- * @api public
- */
-
- function Emitter(obj) {
- if (obj) return mixin(obj);
- };
-
- /**
- * Mixin the emitter properties.
- *
- * @param {Object} obj
- * @return {Object}
- * @api private
- */
-
- function mixin(obj) {
- for (var key in Emitter.prototype) {
- obj[key] = Emitter.prototype[key];
- }
- return obj;
- }
-
- /**
- * Listen on the given `event` with `fn`.
- *
- * @param {String} event
- * @param {Function} fn
- * @return {Emitter}
- * @api public
- */
-
- Emitter.prototype.on =
- Emitter.prototype.addEventListener = function(event, fn){
- this._callbacks = this._callbacks || {};
- (this._callbacks[event] = this._callbacks[event] || [])
- .push(fn);
- return this;
- };
-
- /**
- * Adds an `event` listener that will be invoked a single
- * time then automatically removed.
- *
- * @param {String} event
- * @param {Function} fn
- * @return {Emitter}
- * @api public
- */
-
- Emitter.prototype.once = function(event, fn){
- var self = this;
- this._callbacks = this._callbacks || {};
-
- function on() {
- self.off(event, on);
- fn.apply(this, arguments);
- }
-
- on.fn = fn;
- this.on(event, on);
- return this;
- };
-
- /**
- * Remove the given callback for `event` or all
- * registered callbacks.
- *
- * @param {String} event
- * @param {Function} fn
- * @return {Emitter}
- * @api public
- */
-
- Emitter.prototype.off =
- Emitter.prototype.removeListener =
- Emitter.prototype.removeAllListeners =
- Emitter.prototype.removeEventListener = function(event, fn){
- this._callbacks = this._callbacks || {};
-
- // all
- if (0 == arguments.length) {
- this._callbacks = {};
- return this;
- }
-
- // specific event
- var callbacks = this._callbacks[event];
- if (!callbacks) return this;
-
- // remove all handlers
- if (1 == arguments.length) {
- delete this._callbacks[event];
- return this;
- }
-
- // remove specific handler
- var cb;
- for (var i = 0; i < callbacks.length; i++) {
- cb = callbacks[i];
- if (cb === fn || cb.fn === fn) {
- callbacks.splice(i, 1);
- break;
- }
- }
- return this;
- };
-
- /**
- * Emit `event` with the given args.
- *
- * @param {String} event
- * @param {Mixed} ...
- * @return {Emitter}
- */
-
- Emitter.prototype.emit = function(event){
- this._callbacks = this._callbacks || {};
- var args = [].slice.call(arguments, 1)
- , callbacks = this._callbacks[event];
-
- if (callbacks) {
- callbacks = callbacks.slice(0);
- for (var i = 0, len = callbacks.length; i < len; ++i) {
- callbacks[i].apply(this, args);
- }
- }
-
- return this;
- };
-
- /**
- * Return array of callbacks for `event`.
- *
- * @param {String} event
- * @return {Array}
- * @api public
- */
-
- Emitter.prototype.listeners = function(event){
- this._callbacks = this._callbacks || {};
- return this._callbacks[event] || [];
- };
-
- /**
- * Check if this emitter has `event` handlers.
- *
- * @param {String} event
- * @return {Boolean}
- * @api public
- */
-
- Emitter.prototype.hasListeners = function(event){
- return !! this.listeners(event).length;
- };
-
-
-/***/ },
-/* 12 */
-/***/ function(module, exports, __webpack_require__) {
-
- /**
- * @prototype Point3d
- * @param {Number} [x]
- * @param {Number} [y]
- * @param {Number} [z]
- */
- function Point3d(x, y, z) {
- this.x = x !== undefined ? x : 0;
- this.y = y !== undefined ? y : 0;
- this.z = z !== undefined ? z : 0;
- };
-
- /**
- * Subtract the two provided points, returns a-b
- * @param {Point3d} a
- * @param {Point3d} b
- * @return {Point3d} a-b
- */
- Point3d.subtract = function(a, b) {
- var sub = new Point3d();
- sub.x = a.x - b.x;
- sub.y = a.y - b.y;
- sub.z = a.z - b.z;
- return sub;
- };
-
- /**
- * Add the two provided points, returns a+b
- * @param {Point3d} a
- * @param {Point3d} b
- * @return {Point3d} a+b
- */
- Point3d.add = function(a, b) {
- var sum = new Point3d();
- sum.x = a.x + b.x;
- sum.y = a.y + b.y;
- sum.z = a.z + b.z;
- return sum;
- };
-
- /**
- * Calculate the average of two 3d points
- * @param {Point3d} a
- * @param {Point3d} b
- * @return {Point3d} The average, (a+b)/2
- */
- Point3d.avg = function(a, b) {
- return new Point3d(
- (a.x + b.x) / 2,
- (a.y + b.y) / 2,
- (a.z + b.z) / 2
- );
- };
-
- /**
- * Calculate the cross product of the two provided points, returns axb
- * Documentation: http://en.wikipedia.org/wiki/Cross_product
- * @param {Point3d} a
- * @param {Point3d} b
- * @return {Point3d} cross product axb
- */
- Point3d.crossProduct = function(a, b) {
- var crossproduct = new Point3d();
-
- crossproduct.x = a.y * b.z - a.z * b.y;
- crossproduct.y = a.z * b.x - a.x * b.z;
- crossproduct.z = a.x * b.y - a.y * b.x;
-
- return crossproduct;
- };
-
-
- /**
- * Rtrieve the length of the vector (or the distance from this point to the origin
- * @return {Number} length
- */
- Point3d.prototype.length = function() {
- return Math.sqrt(
- this.x * this.x +
- this.y * this.y +
- this.z * this.z
- );
- };
-
- module.exports = Point3d;
-
-
-/***/ },
-/* 13 */
-/***/ function(module, exports, __webpack_require__) {
-
- /**
- * @prototype Point2d
- * @param {Number} [x]
- * @param {Number} [y]
- */
- function Point2d (x, y) {
- this.x = x !== undefined ? x : 0;
- this.y = y !== undefined ? y : 0;
- }
-
- module.exports = Point2d;
-
-
-/***/ },
-/* 14 */
-/***/ function(module, exports, __webpack_require__) {
-
- var Point3d = __webpack_require__(12);
-
- /**
- * @class Camera
- * The camera is mounted on a (virtual) camera arm. The camera arm can rotate
- * The camera is always looking in the direction of the origin of the arm.
- * This way, the camera always rotates around one fixed point, the location
- * of the camera arm.
- *
- * Documentation:
- * http://en.wikipedia.org/wiki/3D_projection
- */
- function Camera() {
- this.armLocation = new Point3d();
- this.armRotation = {};
- this.armRotation.horizontal = 0;
- this.armRotation.vertical = 0;
- this.armLength = 1.7;
-
- this.cameraLocation = new Point3d();
- this.cameraRotation = new Point3d(0.5*Math.PI, 0, 0);
-
- this.calculateCameraOrientation();
- }
-
- /**
- * Set the location (origin) of the arm
- * @param {Number} x Normalized value of x
- * @param {Number} y Normalized value of y
- * @param {Number} z Normalized value of z
- */
- Camera.prototype.setArmLocation = function(x, y, z) {
- this.armLocation.x = x;
- this.armLocation.y = y;
- this.armLocation.z = z;
-
- this.calculateCameraOrientation();
- };
-
- /**
- * Set the rotation of the camera arm
- * @param {Number} horizontal The horizontal rotation, between 0 and 2*PI.
- * Optional, can be left undefined.
- * @param {Number} vertical The vertical rotation, between 0 and 0.5*PI
- * if vertical=0.5*PI, the graph is shown from the
- * top. Optional, can be left undefined.
- */
- Camera.prototype.setArmRotation = function(horizontal, vertical) {
- if (horizontal !== undefined) {
- this.armRotation.horizontal = horizontal;
- }
-
- if (vertical !== undefined) {
- this.armRotation.vertical = vertical;
- if (this.armRotation.vertical < 0) this.armRotation.vertical = 0;
- if (this.armRotation.vertical > 0.5*Math.PI) this.armRotation.vertical = 0.5*Math.PI;
- }
-
- if (horizontal !== undefined || vertical !== undefined) {
- this.calculateCameraOrientation();
- }
- };
-
- /**
- * Retrieve the current arm rotation
- * @return {object} An object with parameters horizontal and vertical
- */
- Camera.prototype.getArmRotation = function() {
- var rot = {};
- rot.horizontal = this.armRotation.horizontal;
- rot.vertical = this.armRotation.vertical;
-
- return rot;
- };
-
- /**
- * Set the (normalized) length of the camera arm.
- * @param {Number} length A length between 0.71 and 5.0
- */
- Camera.prototype.setArmLength = function(length) {
- if (length === undefined)
- return;
-
- this.armLength = length;
-
- // Radius must be larger than the corner of the graph,
- // which has a distance of sqrt(0.5^2+0.5^2) = 0.71 from the center of the
- // graph
- if (this.armLength < 0.71) this.armLength = 0.71;
- if (this.armLength > 5.0) this.armLength = 5.0;
-
- this.calculateCameraOrientation();
- };
-
- /**
- * Retrieve the arm length
- * @return {Number} length
- */
- Camera.prototype.getArmLength = function() {
- return this.armLength;
- };
-
- /**
- * Retrieve the camera location
- * @return {Point3d} cameraLocation
- */
- Camera.prototype.getCameraLocation = function() {
- return this.cameraLocation;
- };
-
- /**
- * Retrieve the camera rotation
- * @return {Point3d} cameraRotation
- */
- Camera.prototype.getCameraRotation = function() {
- return this.cameraRotation;
- };
-
- /**
- * Calculate the location and rotation of the camera based on the
- * position and orientation of the camera arm
- */
- Camera.prototype.calculateCameraOrientation = function() {
- // calculate location of the camera
- this.cameraLocation.x = this.armLocation.x - this.armLength * Math.sin(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
- this.cameraLocation.y = this.armLocation.y - this.armLength * Math.cos(this.armRotation.horizontal) * Math.cos(this.armRotation.vertical);
- this.cameraLocation.z = this.armLocation.z + this.armLength * Math.sin(this.armRotation.vertical);
-
- // calculate rotation of the camera
- this.cameraRotation.x = Math.PI/2 - this.armRotation.vertical;
- this.cameraRotation.y = 0;
- this.cameraRotation.z = -this.armRotation.horizontal;
- };
-
- module.exports = Camera;
-
-/***/ },
-/* 15 */
-/***/ function(module, exports, __webpack_require__) {
-
- var DataView = __webpack_require__(9);
-
- /**
- * @class Filter
- *
- * @param {DataSet} data The google data table
- * @param {Number} column The index of the column to be filtered
- * @param {Graph} graph The graph
- */
- function Filter (data, column, graph) {
- this.data = data;
- this.column = column;
- this.graph = graph; // the parent graph
-
- this.index = undefined;
- this.value = undefined;
-
- // read all distinct values and select the first one
- this.values = graph.getDistinctValues(data.get(), this.column);
-
- // sort both numeric and string values correctly
- this.values.sort(function (a, b) {
- return a > b ? 1 : a < b ? -1 : 0;
- });
-
- if (this.values.length > 0) {
- this.selectValue(0);
- }
-
- // create an array with the filtered datapoints. this will be loaded afterwards
- this.dataPoints = [];
-
- this.loaded = false;
- this.onLoadCallback = undefined;
-
- if (graph.animationPreload) {
- this.loaded = false;
- this.loadInBackground();
- }
- else {
- this.loaded = true;
- }
- };
-
-
- /**
- * Return the label
- * @return {string} label
- */
- Filter.prototype.isLoaded = function() {
- return this.loaded;
- };
-
-
- /**
- * Return the loaded progress
- * @return {Number} percentage between 0 and 100
- */
- Filter.prototype.getLoadedProgress = function() {
- var len = this.values.length;
-
- var i = 0;
- while (this.dataPoints[i]) {
- i++;
- }
-
- return Math.round(i / len * 100);
- };
-
-
- /**
- * Return the label
- * @return {string} label
- */
- Filter.prototype.getLabel = function() {
- return this.graph.filterLabel;
- };
-
-
- /**
- * Return the columnIndex of the filter
- * @return {Number} columnIndex
- */
- Filter.prototype.getColumn = function() {
- return this.column;
- };
-
- /**
- * Return the currently selected value. Returns undefined if there is no selection
- * @return {*} value
- */
- Filter.prototype.getSelectedValue = function() {
- if (this.index === undefined)
- return undefined;
-
- return this.values[this.index];
- };
-
- /**
- * Retrieve all values of the filter
- * @return {Array} values
- */
- Filter.prototype.getValues = function() {
- return this.values;
- };
-
- /**
- * Retrieve one value of the filter
- * @param {Number} index
- * @return {*} value
- */
- Filter.prototype.getValue = function(index) {
- if (index >= this.values.length)
- throw 'Error: index out of range';
-
- return this.values[index];
- };
-
-
- /**
- * Retrieve the (filtered) dataPoints for the currently selected filter index
- * @param {Number} [index] (optional)
- * @return {Array} dataPoints
- */
- Filter.prototype._getDataPoints = function(index) {
- if (index === undefined)
- index = this.index;
-
- if (index === undefined)
- return [];
-
- var dataPoints;
- if (this.dataPoints[index]) {
- dataPoints = this.dataPoints[index];
- }
- else {
- var f = {};
- f.column = this.column;
- f.value = this.values[index];
-
- var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get();
- dataPoints = this.graph._getDataPoints(dataView);
-
- this.dataPoints[index] = dataPoints;
- }
-
- return dataPoints;
- };
-
-
-
- /**
- * Set a callback function when the filter is fully loaded.
- */
- Filter.prototype.setOnLoadCallback = function(callback) {
- this.onLoadCallback = callback;
- };
-
-
- /**
- * Add a value to the list with available values for this filter
- * No double entries will be created.
- * @param {Number} index
- */
- Filter.prototype.selectValue = function(index) {
- if (index >= this.values.length)
- throw 'Error: index out of range';
-
- this.index = index;
- this.value = this.values[index];
- };
-
- /**
- * Load all filtered rows in the background one by one
- * Start this method without providing an index!
- */
- Filter.prototype.loadInBackground = function(index) {
- if (index === undefined)
- index = 0;
-
- var frame = this.graph.frame;
-
- if (index < this.values.length) {
- var dataPointsTemp = this._getDataPoints(index);
- //this.graph.redrawInfo(); // TODO: not neat
-
- // create a progress box
- if (frame.progress === undefined) {
- frame.progress = document.createElement('DIV');
- frame.progress.style.position = 'absolute';
- frame.progress.style.color = 'gray';
- frame.appendChild(frame.progress);
- }
- var progress = this.getLoadedProgress();
- frame.progress.innerHTML = 'Loading animation... ' + progress + '%';
- // TODO: this is no nice solution...
- frame.progress.style.bottom = 60 + 'px'; // TODO: use height of slider
- frame.progress.style.left = 10 + 'px';
-
- var me = this;
- setTimeout(function() {me.loadInBackground(index+1);}, 10);
- this.loaded = false;
- }
- else {
- this.loaded = true;
-
- // remove the progress box
- if (frame.progress !== undefined) {
- frame.removeChild(frame.progress);
- frame.progress = undefined;
- }
-
- if (this.onLoadCallback)
- this.onLoadCallback();
- }
- };
-
- module.exports = Filter;
-
-
-/***/ },
-/* 16 */
-/***/ function(module, exports, __webpack_require__) {
-
- var util = __webpack_require__(1);
-
- /**
- * @constructor Slider
- *
- * An html slider control with start/stop/prev/next buttons
- * @param {Element} container The element where the slider will be created
- * @param {Object} options Available options:
- * {boolean} visible If true (default) the
- * slider is visible.
- */
- function Slider(container, options) {
- if (container === undefined) {
- throw 'Error: No container element defined';
- }
- this.container = container;
- this.visible = (options && options.visible != undefined) ? options.visible : true;
-
- if (this.visible) {
- this.frame = document.createElement('DIV');
- //this.frame.style.backgroundColor = '#E5E5E5';
- this.frame.style.width = '100%';
- this.frame.style.position = 'relative';
- this.container.appendChild(this.frame);
-
- this.frame.prev = document.createElement('INPUT');
- this.frame.prev.type = 'BUTTON';
- this.frame.prev.value = 'Prev';
- this.frame.appendChild(this.frame.prev);
-
- this.frame.play = document.createElement('INPUT');
- this.frame.play.type = 'BUTTON';
- this.frame.play.value = 'Play';
- this.frame.appendChild(this.frame.play);
-
- this.frame.next = document.createElement('INPUT');
- this.frame.next.type = 'BUTTON';
- this.frame.next.value = 'Next';
- this.frame.appendChild(this.frame.next);
-
- this.frame.bar = document.createElement('INPUT');
- this.frame.bar.type = 'BUTTON';
- this.frame.bar.style.position = 'absolute';
- this.frame.bar.style.border = '1px solid red';
- this.frame.bar.style.width = '100px';
- this.frame.bar.style.height = '6px';
- this.frame.bar.style.borderRadius = '2px';
- this.frame.bar.style.MozBorderRadius = '2px';
- this.frame.bar.style.border = '1px solid #7F7F7F';
- this.frame.bar.style.backgroundColor = '#E5E5E5';
- this.frame.appendChild(this.frame.bar);
-
- this.frame.slide = document.createElement('INPUT');
- this.frame.slide.type = 'BUTTON';
- this.frame.slide.style.margin = '0px';
- this.frame.slide.value = ' ';
- this.frame.slide.style.position = 'relative';
- this.frame.slide.style.left = '-100px';
- this.frame.appendChild(this.frame.slide);
-
- // create events
- var me = this;
- this.frame.slide.onmousedown = function (event) {me._onMouseDown(event);};
- this.frame.prev.onclick = function (event) {me.prev(event);};
- this.frame.play.onclick = function (event) {me.togglePlay(event);};
- this.frame.next.onclick = function (event) {me.next(event);};
- }
-
- this.onChangeCallback = undefined;
-
- this.values = [];
- this.index = undefined;
-
- this.playTimeout = undefined;
- this.playInterval = 1000; // milliseconds
- this.playLoop = true;
- }
-
- /**
- * Select the previous index
- */
- Slider.prototype.prev = function() {
- var index = this.getIndex();
- if (index > 0) {
- index--;
- this.setIndex(index);
- }
- };
-
- /**
- * Select the next index
- */
- Slider.prototype.next = function() {
- var index = this.getIndex();
- if (index < this.values.length - 1) {
- index++;
- this.setIndex(index);
- }
- };
-
- /**
- * Select the next index
- */
- Slider.prototype.playNext = function() {
- var start = new Date();
-
- var index = this.getIndex();
- if (index < this.values.length - 1) {
- index++;
- this.setIndex(index);
- }
- else if (this.playLoop) {
- // jump to the start
- index = 0;
- this.setIndex(index);
- }
-
- var end = new Date();
- var diff = (end - start);
-
- // calculate how much time it to to set the index and to execute the callback
- // function.
- var interval = Math.max(this.playInterval - diff, 0);
- // document.title = diff // TODO: cleanup
-
- var me = this;
- this.playTimeout = setTimeout(function() {me.playNext();}, interval);
- };
-
- /**
- * Toggle start or stop playing
- */
- Slider.prototype.togglePlay = function() {
- if (this.playTimeout === undefined) {
- this.play();
- } else {
- this.stop();
- }
- };
-
- /**
- * Start playing
- */
- Slider.prototype.play = function() {
- // Test whether already playing
- if (this.playTimeout) return;
-
- this.playNext();
-
- if (this.frame) {
- this.frame.play.value = 'Stop';
- }
- };
-
- /**
- * Stop playing
- */
- Slider.prototype.stop = function() {
- clearInterval(this.playTimeout);
- this.playTimeout = undefined;
-
- if (this.frame) {
- this.frame.play.value = 'Play';
- }
- };
-
- /**
- * Set a callback function which will be triggered when the value of the
- * slider bar has changed.
- */
- Slider.prototype.setOnChangeCallback = function(callback) {
- this.onChangeCallback = callback;
- };
-
- /**
- * Set the interval for playing the list
- * @param {Number} interval The interval in milliseconds
- */
- Slider.prototype.setPlayInterval = function(interval) {
- this.playInterval = interval;
- };
-
- /**
- * Retrieve the current play interval
- * @return {Number} interval The interval in milliseconds
- */
- Slider.prototype.getPlayInterval = function(interval) {
- return this.playInterval;
- };
-
- /**
- * Set looping on or off
- * @pararm {boolean} doLoop If true, the slider will jump to the start when
- * the end is passed, and will jump to the end
- * when the start is passed.
- */
- Slider.prototype.setPlayLoop = function(doLoop) {
- this.playLoop = doLoop;
- };
-
-
- /**
- * Execute the onchange callback function
- */
- Slider.prototype.onChange = function() {
- if (this.onChangeCallback !== undefined) {
- this.onChangeCallback();
- }
- };
-
- /**
- * redraw the slider on the correct place
- */
- Slider.prototype.redraw = function() {
- if (this.frame) {
- // resize the bar
- this.frame.bar.style.top = (this.frame.clientHeight/2 -
- this.frame.bar.offsetHeight/2) + 'px';
- this.frame.bar.style.width = (this.frame.clientWidth -
- this.frame.prev.clientWidth -
- this.frame.play.clientWidth -
- this.frame.next.clientWidth - 30) + 'px';
-
- // position the slider button
- var left = this.indexToLeft(this.index);
- this.frame.slide.style.left = (left) + 'px';
- }
- };
-
-
- /**
- * Set the list with values for the slider
- * @param {Array} values A javascript array with values (any type)
- */
- Slider.prototype.setValues = function(values) {
- this.values = values;
-
- if (this.values.length > 0)
- this.setIndex(0);
- else
- this.index = undefined;
- };
-
- /**
- * Select a value by its index
- * @param {Number} index
- */
- Slider.prototype.setIndex = function(index) {
- if (index < this.values.length) {
- this.index = index;
-
- this.redraw();
- this.onChange();
- }
- else {
- throw 'Error: index out of range';
- }
- };
-
- /**
- * retrieve the index of the currently selected vaue
- * @return {Number} index
- */
- Slider.prototype.getIndex = function() {
- return this.index;
- };
-
-
- /**
- * retrieve the currently selected value
- * @return {*} value
- */
- Slider.prototype.get = function() {
- return this.values[this.index];
- };
-
-
- Slider.prototype._onMouseDown = function(event) {
- // only react on left mouse button down
- var leftButtonDown = event.which ? (event.which === 1) : (event.button === 1);
- if (!leftButtonDown) return;
-
- this.startClientX = event.clientX;
- this.startSlideX = parseFloat(this.frame.slide.style.left);
-
- this.frame.style.cursor = 'move';
-
- // add event listeners to handle moving the contents
- // we store the function onmousemove and onmouseup in the graph, so we can
- // remove the eventlisteners lateron in the function mouseUp()
- var me = this;
- this.onmousemove = function (event) {me._onMouseMove(event);};
- this.onmouseup = function (event) {me._onMouseUp(event);};
- util.addEventListener(document, 'mousemove', this.onmousemove);
- util.addEventListener(document, 'mouseup', this.onmouseup);
- util.preventDefault(event);
- };
-
-
- Slider.prototype.leftToIndex = function (left) {
- var width = parseFloat(this.frame.bar.style.width) -
- this.frame.slide.clientWidth - 10;
- var x = left - 3;
-
- var index = Math.round(x / width * (this.values.length-1));
- if (index < 0) index = 0;
- if (index > this.values.length-1) index = this.values.length-1;
-
- return index;
- };
-
- Slider.prototype.indexToLeft = function (index) {
- var width = parseFloat(this.frame.bar.style.width) -
- this.frame.slide.clientWidth - 10;
-
- var x = index / (this.values.length-1) * width;
- var left = x + 3;
-
- return left;
- };
-
-
-
- Slider.prototype._onMouseMove = function (event) {
- var diff = event.clientX - this.startClientX;
- var x = this.startSlideX + diff;
-
- var index = this.leftToIndex(x);
-
- this.setIndex(index);
-
- util.preventDefault();
- };
-
-
- Slider.prototype._onMouseUp = function (event) {
- this.frame.style.cursor = 'auto';
-
- // remove event listeners
- util.removeEventListener(document, 'mousemove', this.onmousemove);
- util.removeEventListener(document, 'mouseup', this.onmouseup);
-
- util.preventDefault();
- };
-
- module.exports = Slider;
-
-
-/***/ },
-/* 17 */
-/***/ function(module, exports, __webpack_require__) {
-
- /**
- * @prototype StepNumber
- * The class StepNumber is an iterator for Numbers. You provide a start and end
- * value, and a best step size. StepNumber itself rounds to fixed values and
- * a finds the step that best fits the provided step.
- *
- * If prettyStep is true, the step size is chosen as close as possible to the
- * provided step, but being a round value like 1, 2, 5, 10, 20, 50, ....
- *
- * Example usage:
- * var step = new StepNumber(0, 10, 2.5, true);
- * step.start();
- * while (!step.end()) {
- * alert(step.getCurrent());
- * step.next();
- * }
- *
- * Version: 1.0
- *
- * @param {Number} start The start value
- * @param {Number} end The end value
- * @param {Number} step Optional. Step size. Must be a positive value.
- * @param {boolean} prettyStep Optional. If true, the step size is rounded
- * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...)
- */
- function StepNumber(start, end, step, prettyStep) {
- // set default values
- this._start = 0;
- this._end = 0;
- this._step = 1;
- this.prettyStep = true;
- this.precision = 5;
-
- this._current = 0;
- this.setRange(start, end, step, prettyStep);
- };
-
- /**
- * Set a new range: start, end and step.
- *
- * @param {Number} start The start value
- * @param {Number} end The end value
- * @param {Number} step Optional. Step size. Must be a positive value.
- * @param {boolean} prettyStep Optional. If true, the step size is rounded
- * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...)
- */
- StepNumber.prototype.setRange = function(start, end, step, prettyStep) {
- this._start = start ? start : 0;
- this._end = end ? end : 0;
-
- this.setStep(step, prettyStep);
- };
-
- /**
- * Set a new step size
- * @param {Number} step New step size. Must be a positive value
- * @param {boolean} prettyStep Optional. If true, the provided step is rounded
- * to a pretty step size (like 1, 2, 5, 10, 20, 50, ...)
- */
- StepNumber.prototype.setStep = function(step, prettyStep) {
- if (step === undefined || step <= 0)
- return;
-
- if (prettyStep !== undefined)
- this.prettyStep = prettyStep;
-
- if (this.prettyStep === true)
- this._step = StepNumber.calculatePrettyStep(step);
- else
- this._step = step;
- };
-
- /**
- * Calculate a nice step size, closest to the desired step size.
- * Returns a value in one of the ranges 1*10^n, 2*10^n, or 5*10^n, where n is an
- * integer Number. For example 1, 2, 5, 10, 20, 50, etc...
- * @param {Number} step Desired step size
- * @return {Number} Nice step size
- */
- StepNumber.calculatePrettyStep = function (step) {
- var log10 = function (x) {return Math.log(x) / Math.LN10;};
-
- // try three steps (multiple of 1, 2, or 5
- var step1 = Math.pow(10, Math.round(log10(step))),
- step2 = 2 * Math.pow(10, Math.round(log10(step / 2))),
- step5 = 5 * Math.pow(10, Math.round(log10(step / 5)));
-
- // choose the best step (closest to minimum step)
- var prettyStep = step1;
- if (Math.abs(step2 - step) <= Math.abs(prettyStep - step)) prettyStep = step2;
- if (Math.abs(step5 - step) <= Math.abs(prettyStep - step)) prettyStep = step5;
-
- // for safety
- if (prettyStep <= 0) {
- prettyStep = 1;
- }
-
- return prettyStep;
- };
-
- /**
- * returns the current value of the step
- * @return {Number} current value
- */
- StepNumber.prototype.getCurrent = function () {
- return parseFloat(this._current.toPrecision(this.precision));
- };
-
- /**
- * returns the current step size
- * @return {Number} current step size
- */
- StepNumber.prototype.getStep = function () {
- return this._step;
- };
-
- /**
- * Set the current value to the largest value smaller than start, which
- * is a multiple of the step size
- */
- StepNumber.prototype.start = function() {
- this._current = this._start - this._start % this._step;
- };
-
- /**
- * Do a step, add the step size to the current value
- */
- StepNumber.prototype.next = function () {
- this._current += this._step;
- };
-
- /**
- * Returns true whether the end is reached
- * @return {boolean} True if the current value has passed the end value.
- */
- StepNumber.prototype.end = function () {
- return (this._current > this._end);
- };
-
- module.exports = StepNumber;
-
-
-/***/ },
-/* 18 */
-/***/ function(module, exports, __webpack_require__) {
-
- var Emitter = __webpack_require__(11);
- var Hammer = __webpack_require__(19);
- var util = __webpack_require__(1);
- var DataSet = __webpack_require__(7);
- var DataView = __webpack_require__(9);
- var Range = __webpack_require__(21);
- var Core = __webpack_require__(25);
- var TimeAxis = __webpack_require__(37);
- var CurrentTime = __webpack_require__(39);
- var CustomTime = __webpack_require__(41);
- var ItemSet = __webpack_require__(26);
-
- /**
- * Create a timeline visualization
- * @param {HTMLElement} container
- * @param {vis.DataSet | Array | google.visualization.DataTable} [items]
- * @param {vis.DataSet | Array | google.visualization.DataTable} [groups]
- * @param {Object} [options] See Timeline.setOptions for the available options.
- * @constructor
- * @extends Core
- */
- function Timeline (container, items, groups, options) {
- if (!(this instanceof Timeline)) {
- throw new SyntaxError('Constructor must be called with the new operator');
- }
-
- // if the third element is options, the forth is groups (optionally);
- if (!(Array.isArray(groups) || groups instanceof DataSet) && groups instanceof Object) {
- var forthArgument = options;
- options = groups;
- groups = forthArgument;
- }
-
- var me = this;
- this.defaultOptions = {
- start: null,
- end: null,
-
- autoResize: true,
-
- orientation: 'bottom',
- width: null,
- height: null,
- maxHeight: null,
- minHeight: null
- };
- this.options = util.deepExtend({}, this.defaultOptions);
-
- // Create the DOM, props, and emitter
- this._create(container);
-
- // all components listed here will be repainted automatically
- this.components = [];
-
- this.body = {
- dom: this.dom,
- domProps: this.props,
- emitter: {
- on: this.on.bind(this),
- off: this.off.bind(this),
- emit: this.emit.bind(this)
- },
- hiddenDates: [],
- util: {
- snap: null, // will be specified after TimeAxis is created
- toScreen: me._toScreen.bind(me),
- toGlobalScreen: me._toGlobalScreen.bind(me), // this refers to the root.width
- toTime: me._toTime.bind(me),
- toGlobalTime : me._toGlobalTime.bind(me)
- }
- };
-
- // range
- this.range = new Range(this.body);
- this.components.push(this.range);
- this.body.range = this.range;
-
- // time axis
- this.timeAxis = new TimeAxis(this.body);
- this.components.push(this.timeAxis);
- this.body.util.snap = this.timeAxis.snap.bind(this.timeAxis);
-
- // current time bar
- this.currentTime = new CurrentTime(this.body);
- this.components.push(this.currentTime);
-
- // custom time bar
- // Note: time bar will be attached in this.setOptions when selected
- this.customTime = new CustomTime(this.body);
- this.components.push(this.customTime);
-
- // item set
- this.itemSet = new ItemSet(this.body);
- this.components.push(this.itemSet);
-
- this.itemsData = null; // DataSet
- this.groupsData = null; // DataSet
-
- // apply options
- if (options) {
- this.setOptions(options);
- }
-
- // IMPORTANT: THIS HAPPENS BEFORE SET ITEMS!
- if (groups) {
- this.setGroups(groups);
- }
-
- // create itemset
- if (items) {
- this.setItems(items);
- }
- else {
- this.redraw();
- }
- }
-
- // Extend the functionality from Core
- Timeline.prototype = new Core();
-
- /**
- * Set items
- * @param {vis.DataSet | Array | google.visualization.DataTable | null} items
- */
- Timeline.prototype.setItems = function(items) {
- var initialLoad = (this.itemsData == null);
-
- // convert to type DataSet when needed
- var newDataSet;
- if (!items) {
- newDataSet = null;
- }
- else if (items instanceof DataSet || items instanceof DataView) {
- newDataSet = items;
- }
- else {
- // turn an array into a dataset
- newDataSet = new DataSet(items, {
- type: {
- start: 'Date',
- end: 'Date'
- }
- });
- }
-
- // set items
- this.itemsData = newDataSet;
- this.itemSet && this.itemSet.setItems(newDataSet);
-
- if (initialLoad) {
- if (this.options.start != undefined || this.options.end != undefined) {
- if (this.options.start == undefined || this.options.end == undefined) {
- var dataRange = this._getDataRange();
- }
-
- var start = this.options.start != undefined ? this.options.start : dataRange.start;
- var end = this.options.end != undefined ? this.options.end : dataRange.end;
-
- this.setWindow(start, end, {animate: false});
- }
- else {
- this.fit({animate: false});
- }
- }
- };
-
- /**
- * Set groups
- * @param {vis.DataSet | Array | google.visualization.DataTable} groups
- */
- Timeline.prototype.setGroups = function(groups) {
- // convert to type DataSet when needed
- var newDataSet;
- if (!groups) {
- newDataSet = null;
- }
- else if (groups instanceof DataSet || groups instanceof DataView) {
- newDataSet = groups;
- }
- else {
- // turn an array into a dataset
- newDataSet = new DataSet(groups);
- }
-
- this.groupsData = newDataSet;
- this.itemSet.setGroups(newDataSet);
- };
-
- /**
- * Set selected items by their id. Replaces the current selection
- * Unknown id's are silently ignored.
- * @param {string[] | string} [ids] An array with zero or more id's of the items to be
- * selected. If ids is an empty array, all items will be
- * unselected.
- * @param {Object} [options] Available options:
- * `focus: boolean`
- * If true, focus will be set to the selected item(s)
- * `animate: boolean | number`
- * If true (default), the range is animated
- * smoothly to the new window.
- * If a number, the number is taken as duration
- * for the animation. Default duration is 500 ms.
- * Only applicable when option focus is true.
- */
- Timeline.prototype.setSelection = function(ids, options) {
- this.itemSet && this.itemSet.setSelection(ids);
-
- if (options && options.focus) {
- this.focus(ids, options);
- }
- };
-
- /**
- * Get the selected items by their id
- * @return {Array} ids The ids of the selected items
- */
- Timeline.prototype.getSelection = function() {
- return this.itemSet && this.itemSet.getSelection() || [];
- };
-
- /**
- * Adjust the visible window such that the selected item (or multiple items)
- * are centered on screen.
- * @param {String | String[]} id An item id or array with item ids
- * @param {Object} [options] Available options:
- * `animate: boolean | number`
- * If true (default), the range is animated
- * smoothly to the new window.
- * If a number, the number is taken as duration
- * for the animation. Default duration is 500 ms.
- * Only applicable when option focus is true
- */
- Timeline.prototype.focus = function(id, options) {
- if (!this.itemsData || id == undefined) return;
-
- var ids = Array.isArray(id) ? id : [id];
-
- // get the specified item(s)
- var itemsData = this.itemsData.getDataSet().get(ids, {
- type: {
- start: 'Date',
- end: 'Date'
- }
- });
-
- // calculate minimum start and maximum end of specified items
- var start = null;
- var end = null;
- itemsData.forEach(function (itemData) {
- var s = itemData.start.valueOf();
- var e = 'end' in itemData ? itemData.end.valueOf() : itemData.start.valueOf();
-
- if (start === null || s < start) {
- start = s;
- }
-
- if (end === null || e > end) {
- end = e;
- }
- });
-
- if (start !== null && end !== null) {
- // calculate the new middle and interval for the window
- var middle = (start + end) / 2;
- var interval = Math.max((this.range.end - this.range.start), (end - start) * 1.1);
-
- var animate = (options && options.animate !== undefined) ? options.animate : true;
- this.range.setRange(middle - interval / 2, middle + interval / 2, animate);
- }
- };
-
- /**
- * Get the data range of the item set.
- * @returns {{min: Date, max: Date}} range A range with a start and end Date.
- * When no minimum is found, min==null
- * When no maximum is found, max==null
- */
- Timeline.prototype.getItemRange = function() {
- // calculate min from start filed
- var dataset = this.itemsData.getDataSet(),
- min = null,
- max = null;
-
- if (dataset) {
- // calculate the minimum value of the field 'start'
- var minItem = dataset.min('start');
- min = minItem ? util.convert(minItem.start, 'Date').valueOf() : null;
- // Note: we convert first to Date and then to number because else
- // a conversion from ISODate to Number will fail
-
- // calculate maximum value of fields 'start' and 'end'
- var maxStartItem = dataset.max('start');
- if (maxStartItem) {
- max = util.convert(maxStartItem.start, 'Date').valueOf();
- }
- var maxEndItem = dataset.max('end');
- if (maxEndItem) {
- if (max == null) {
- max = util.convert(maxEndItem.end, 'Date').valueOf();
- }
- else {
- max = Math.max(max, util.convert(maxEndItem.end, 'Date').valueOf());
- }
- }
- }
-
- return {
- min: (min != null) ? new Date(min) : null,
- max: (max != null) ? new Date(max) : null
- };
- };
-
-
- module.exports = Timeline;
-
-
-/***/ },
-/* 19 */
-/***/ function(module, exports, __webpack_require__) {
-
- // Only load hammer.js when in a browser environment
- // (loading hammer.js in a node.js environment gives errors)
- if (typeof window !== 'undefined') {
- module.exports = window['Hammer'] || __webpack_require__(20);
- }
- else {
- module.exports = function () {
- throw Error('hammer.js is only available in a browser, not in node.js.');
- }
- }
-
-
-/***/ },
-/* 20 */
-/***/ function(module, exports, __webpack_require__) {
-
- var __WEBPACK_AMD_DEFINE_RESULT__;/*! Hammer.JS - v1.1.3 - 2014-05-20
- * http://eightmedia.github.io/hammer.js
- *
- * Copyright (c) 2014 Jorik Tangelder ;
- * Licensed under the MIT license */
-
- (function(window, undefined) {
- 'use strict';
-
- /**
- * @main
- * @module hammer
- *
- * @class Hammer
- * @static
- */
-
- /**
- * Hammer, use this to create instances
- * ````
- * var hammertime = new Hammer(myElement);
- * ````
- *
- * @method Hammer
- * @param {HTMLElement} element
- * @param {Object} [options={}]
- * @return {Hammer.Instance}
- */
- var Hammer = function Hammer(element, options) {
- return new Hammer.Instance(element, options || {});
- };
-
- /**
- * version, as defined in package.json
- * the value will be set at each build
- * @property VERSION
- * @final
- * @type {String}
- */
- Hammer.VERSION = '1.1.3';
-
- /**
- * default settings.
- * more settings are defined per gesture at `/gestures`. Each gesture can be disabled/enabled
- * by setting it's name (like `swipe`) to false.
- * You can set the defaults for all instances by changing this object before creating an instance.
- * @example
- * ````
- * Hammer.defaults.drag = false;
- * Hammer.defaults.behavior.touchAction = 'pan-y';
- * delete Hammer.defaults.behavior.userSelect;
- * ````
- * @property defaults
- * @type {Object}
- */
- Hammer.defaults = {
- /**
- * this setting object adds styles and attributes to the element to prevent the browser from doing
- * its native behavior. The css properties are auto prefixed for the browsers when needed.
- * @property defaults.behavior
- * @type {Object}
- */
- behavior: {
- /**
- * Disables text selection to improve the dragging gesture. When the value is `none` it also sets
- * `onselectstart=false` for IE on the element. Mainly for desktop browsers.
- * @property defaults.behavior.userSelect
- * @type {String}
- * @default 'none'
- */
- userSelect: 'none',
-
- /**
- * Specifies whether and how a given region can be manipulated by the user (for instance, by panning or zooming).
- * Used by Chrome 35> and IE10>. By default this makes the element blocking any touch event.
- * @property defaults.behavior.touchAction
- * @type {String}
- * @default: 'pan-y'
- */
- touchAction: 'pan-y',
-
- /**
- * Disables the default callout shown when you touch and hold a touch target.
- * On iOS, when you touch and hold a touch target such as a link, Safari displays
- * a callout containing information about the link. This property allows you to disable that callout.
- * @property defaults.behavior.touchCallout
- * @type {String}
- * @default 'none'
- */
- touchCallout: 'none',
-
- /**
- * Specifies whether zooming is enabled. Used by IE10>
- * @property defaults.behavior.contentZooming
- * @type {String}
- * @default 'none'
- */
- contentZooming: 'none',
-
- /**
- * Specifies that an entire element should be draggable instead of its contents.
- * Mainly for desktop browsers.
- * @property defaults.behavior.userDrag
- * @type {String}
- * @default 'none'
- */
- userDrag: 'none',
-
- /**
- * Overrides the highlight color shown when the user taps a link or a JavaScript
- * clickable element in Safari on iPhone. This property obeys the alpha value, if specified.
- *
- * If you don't specify an alpha value, Safari on iPhone applies a default alpha value
- * to the color. To disable tap highlighting, set the alpha value to 0 (invisible).
- * If you set the alpha value to 1.0 (opaque), the element is not visible when tapped.
- * @property defaults.behavior.tapHighlightColor
- * @type {String}
- * @default 'rgba(0,0,0,0)'
- */
- tapHighlightColor: 'rgba(0,0,0,0)'
- }
- };
-
- /**
- * hammer document where the base events are added at
- * @property DOCUMENT
- * @type {HTMLElement}
- * @default window.document
- */
- Hammer.DOCUMENT = document;
-
- /**
- * detect support for pointer events
- * @property HAS_POINTEREVENTS
- * @type {Boolean}
- */
- Hammer.HAS_POINTEREVENTS = navigator.pointerEnabled || navigator.msPointerEnabled;
-
- /**
- * detect support for touch events
- * @property HAS_TOUCHEVENTS
- * @type {Boolean}
- */
- Hammer.HAS_TOUCHEVENTS = ('ontouchstart' in window);
-
- /**
- * detect mobile browsers
- * @property IS_MOBILE
- * @type {Boolean}
- */
- Hammer.IS_MOBILE = /mobile|tablet|ip(ad|hone|od)|android|silk/i.test(navigator.userAgent);
-
- /**
- * detect if we want to support mouseevents at all
- * @property NO_MOUSEEVENTS
- * @type {Boolean}
- */
- Hammer.NO_MOUSEEVENTS = (Hammer.HAS_TOUCHEVENTS && Hammer.IS_MOBILE) || Hammer.HAS_POINTEREVENTS;
-
- /**
- * interval in which Hammer recalculates current velocity/direction/angle in ms
- * @property CALCULATE_INTERVAL
- * @type {Number}
- * @default 25
- */
- Hammer.CALCULATE_INTERVAL = 25;
-
- /**
- * eventtypes per touchevent (start, move, end) are filled by `Event.determineEventTypes` on `setup`
- * the object contains the DOM event names per type (`EVENT_START`, `EVENT_MOVE`, `EVENT_END`)
- * @property EVENT_TYPES
- * @private
- * @writeOnce
- * @type {Object}
- */
- var EVENT_TYPES = {};
-
- /**
- * direction strings, for safe comparisons
- * @property DIRECTION_DOWN|LEFT|UP|RIGHT
- * @final
- * @type {String}
- * @default 'down' 'left' 'up' 'right'
- */
- var DIRECTION_DOWN = Hammer.DIRECTION_DOWN = 'down';
- var DIRECTION_LEFT = Hammer.DIRECTION_LEFT = 'left';
- var DIRECTION_UP = Hammer.DIRECTION_UP = 'up';
- var DIRECTION_RIGHT = Hammer.DIRECTION_RIGHT = 'right';
-
- /**
- * pointertype strings, for safe comparisons
- * @property POINTER_MOUSE|TOUCH|PEN
- * @final
- * @type {String}
- * @default 'mouse' 'touch' 'pen'
- */
- var POINTER_MOUSE = Hammer.POINTER_MOUSE = 'mouse';
- var POINTER_TOUCH = Hammer.POINTER_TOUCH = 'touch';
- var POINTER_PEN = Hammer.POINTER_PEN = 'pen';
-
- /**
- * eventtypes
- * @property EVENT_START|MOVE|END|RELEASE|TOUCH
- * @final
- * @type {String}
- * @default 'start' 'change' 'move' 'end' 'release' 'touch'
- */
- var EVENT_START = Hammer.EVENT_START = 'start';
- var EVENT_MOVE = Hammer.EVENT_MOVE = 'move';
- var EVENT_END = Hammer.EVENT_END = 'end';
- var EVENT_RELEASE = Hammer.EVENT_RELEASE = 'release';
- var EVENT_TOUCH = Hammer.EVENT_TOUCH = 'touch';
-
- /**
- * if the window events are set...
- * @property READY
- * @writeOnce
- * @type {Boolean}
- * @default false
- */
- Hammer.READY = false;
-
- /**
- * plugins namespace
- * @property plugins
- * @type {Object}
- */
- Hammer.plugins = Hammer.plugins || {};
-
- /**
- * gestures namespace
- * see `/gestures` for the definitions
- * @property gestures
- * @type {Object}
- */
- Hammer.gestures = Hammer.gestures || {};
-
- /**
- * setup events to detect gestures on the document
- * this function is called when creating an new instance
- * @private
- */
- function setup() {
- if(Hammer.READY) {
- return;
- }
-
- // find what eventtypes we add listeners to
- Event.determineEventTypes();
-
- // Register all gestures inside Hammer.gestures
- Utils.each(Hammer.gestures, function(gesture) {
- Detection.register(gesture);
- });
-
- // Add touch events on the document
- Event.onTouch(Hammer.DOCUMENT, EVENT_MOVE, Detection.detect);
- Event.onTouch(Hammer.DOCUMENT, EVENT_END, Detection.detect);
-
- // Hammer is ready...!
- Hammer.READY = true;
- }
-
- /**
- * @module hammer
- *
- * @class Utils
- * @static
- */
- var Utils = Hammer.utils = {
- /**
- * extend method, could also be used for cloning when `dest` is an empty object.
- * changes the dest object
- * @method extend
- * @param {Object} dest
- * @param {Object} src
- * @param {Boolean} [merge=false] do a merge
- * @return {Object} dest
- */
- extend: function extend(dest, src, merge) {
- for(var key in src) {
- if(!src.hasOwnProperty(key) || (dest[key] !== undefined && merge)) {
- continue;
- }
- dest[key] = src[key];
- }
- return dest;
- },
-
- /**
- * simple addEventListener wrapper
- * @method on
- * @param {HTMLElement} element
- * @param {String} type
- * @param {Function} handler
- */
- on: function on(element, type, handler) {
- element.addEventListener(type, handler, false);
- },
-
- /**
- * simple removeEventListener wrapper
- * @method off
- * @param {HTMLElement} element
- * @param {String} type
- * @param {Function} handler
- */
- off: function off(element, type, handler) {
- element.removeEventListener(type, handler, false);
- },
-
- /**
- * forEach over arrays and objects
- * @method each
- * @param {Object|Array} obj
- * @param {Function} iterator
- * @param {any} iterator.item
- * @param {Number} iterator.index
- * @param {Object|Array} iterator.obj the source object
- * @param {Object} context value to use as `this` in the iterator
- */
- each: function each(obj, iterator, context) {
- var i, len;
-
- // native forEach on arrays
- if('forEach' in obj) {
- obj.forEach(iterator, context);
- // arrays
- } else if(obj.length !== undefined) {
- for(i = 0, len = obj.length; i < len; i++) {
- if(iterator.call(context, obj[i], i, obj) === false) {
- return;
- }
- }
- // objects
- } else {
- for(i in obj) {
- if(obj.hasOwnProperty(i) &&
- iterator.call(context, obj[i], i, obj) === false) {
- return;
- }
- }
- }
- },
-
- /**
- * find if a string contains the string using indexOf
- * @method inStr
- * @param {String} src
- * @param {String} find
- * @return {Boolean} found
- */
- inStr: function inStr(src, find) {
- return src.indexOf(find) > -1;
- },
-
- /**
- * find if a array contains the object using indexOf or a simple polyfill
- * @method inArray
- * @param {String} src
- * @param {String} find
- * @return {Boolean|Number} false when not found, or the index
- */
- inArray: function inArray(src, find) {
- if(src.indexOf) {
- var index = src.indexOf(find);
- return (index === -1) ? false : index;
- } else {
- for(var i = 0, len = src.length; i < len; i++) {
- if(src[i] === find) {
- return i;
- }
- }
- return false;
- }
- },
-
- /**
- * convert an array-like object (`arguments`, `touchlist`) to an array
- * @method toArray
- * @param {Object} obj
- * @return {Array}
- */
- toArray: function toArray(obj) {
- return Array.prototype.slice.call(obj, 0);
- },
-
- /**
- * find if a node is in the given parent
- * @method hasParent
- * @param {HTMLElement} node
- * @param {HTMLElement} parent
- * @return {Boolean} found
- */
- hasParent: function hasParent(node, parent) {
- while(node) {
- if(node == parent) {
- return true;
- }
- node = node.parentNode;
- }
- return false;
- },
-
- /**
- * get the center of all the touches
- * @method getCenter
- * @param {Array} touches
- * @return {Object} center contains `pageX`, `pageY`, `clientX` and `clientY` properties
- */
- getCenter: function getCenter(touches) {
- var pageX = [],
- pageY = [],
- clientX = [],
- clientY = [],
- min = Math.min,
- max = Math.max;
-
- // no need to loop when only one touch
- if(touches.length === 1) {
- return {
- pageX: touches[0].pageX,
- pageY: touches[0].pageY,
- clientX: touches[0].clientX,
- clientY: touches[0].clientY
- };
- }
-
- Utils.each(touches, function(touch) {
- pageX.push(touch.pageX);
- pageY.push(touch.pageY);
- clientX.push(touch.clientX);
- clientY.push(touch.clientY);
- });
-
- return {
- pageX: (min.apply(Math, pageX) + max.apply(Math, pageX)) / 2,
- pageY: (min.apply(Math, pageY) + max.apply(Math, pageY)) / 2,
- clientX: (min.apply(Math, clientX) + max.apply(Math, clientX)) / 2,
- clientY: (min.apply(Math, clientY) + max.apply(Math, clientY)) / 2
- };
- },
-
- /**
- * calculate the velocity between two points. unit is in px per ms.
- * @method getVelocity
- * @param {Number} deltaTime
- * @param {Number} deltaX
- * @param {Number} deltaY
- * @return {Object} velocity `x` and `y`
- */
- getVelocity: function getVelocity(deltaTime, deltaX, deltaY) {
- return {
- x: Math.abs(deltaX / deltaTime) || 0,
- y: Math.abs(deltaY / deltaTime) || 0
- };
- },
-
- /**
- * calculate the angle between two coordinates
- * @method getAngle
- * @param {Touch} touch1
- * @param {Touch} touch2
- * @return {Number} angle
- */
- getAngle: function getAngle(touch1, touch2) {
- var x = touch2.clientX - touch1.clientX,
- y = touch2.clientY - touch1.clientY;
-
- return Math.atan2(y, x) * 180 / Math.PI;
- },
-
- /**
- * do a small comparision to get the direction between two touches.
- * @method getDirection
- * @param {Touch} touch1
- * @param {Touch} touch2
- * @return {String} direction matches `DIRECTION_LEFT|RIGHT|UP|DOWN`
- */
- getDirection: function getDirection(touch1, touch2) {
- var x = Math.abs(touch1.clientX - touch2.clientX),
- y = Math.abs(touch1.clientY - touch2.clientY);
-
- if(x >= y) {
- return touch1.clientX - touch2.clientX > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
- }
- return touch1.clientY - touch2.clientY > 0 ? DIRECTION_UP : DIRECTION_DOWN;
- },
-
- /**
- * calculate the distance between two touches
- * @method getDistance
- * @param {Touch}touch1
- * @param {Touch} touch2
- * @return {Number} distance
- */
- getDistance: function getDistance(touch1, touch2) {
- var x = touch2.clientX - touch1.clientX,
- y = touch2.clientY - touch1.clientY;
-
- return Math.sqrt((x * x) + (y * y));
- },
-
- /**
- * calculate the scale factor between two touchLists
- * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out
- * @method getScale
- * @param {Array} start array of touches
- * @param {Array} end array of touches
- * @return {Number} scale
- */
- getScale: function getScale(start, end) {
- // need two fingers...
- if(start.length >= 2 && end.length >= 2) {
- return this.getDistance(end[0], end[1]) / this.getDistance(start[0], start[1]);
- }
- return 1;
- },
-
- /**
- * calculate the rotation degrees between two touchLists
- * @method getRotation
- * @param {Array} start array of touches
- * @param {Array} end array of touches
- * @return {Number} rotation
- */
- getRotation: function getRotation(start, end) {
- // need two fingers
- if(start.length >= 2 && end.length >= 2) {
- return this.getAngle(end[1], end[0]) - this.getAngle(start[1], start[0]);
- }
- return 0;
- },
-
- /**
- * find out if the direction is vertical *
- * @method isVertical
- * @param {String} direction matches `DIRECTION_UP|DOWN`
- * @return {Boolean} is_vertical
- */
- isVertical: function isVertical(direction) {
- return direction == DIRECTION_UP || direction == DIRECTION_DOWN;
- },
-
- /**
- * set css properties with their prefixes
- * @param {HTMLElement} element
- * @param {String} prop
- * @param {String} value
- * @param {Boolean} [toggle=true]
- * @return {Boolean}
- */
- setPrefixedCss: function setPrefixedCss(element, prop, value, toggle) {
- var prefixes = ['', 'Webkit', 'Moz', 'O', 'ms'];
- prop = Utils.toCamelCase(prop);
-
- for(var i = 0; i < prefixes.length; i++) {
- var p = prop;
- // prefixes
- if(prefixes[i]) {
- p = prefixes[i] + p.slice(0, 1).toUpperCase() + p.slice(1);
- }
-
- // test the style
- if(p in element.style) {
- element.style[p] = (toggle == null || toggle) && value || '';
- break;
- }
- }
- },
-
- /**
- * toggle browser default behavior by setting css properties.
- * `userSelect='none'` also sets `element.onselectstart` to false
- * `userDrag='none'` also sets `element.ondragstart` to false
- *
- * @method toggleBehavior
- * @param {HtmlElement} element
- * @param {Object} props
- * @param {Boolean} [toggle=true]
- */
- toggleBehavior: function toggleBehavior(element, props, toggle) {
- if(!props || !element || !element.style) {
- return;
- }
-
- // set the css properties
- Utils.each(props, function(value, prop) {
- Utils.setPrefixedCss(element, prop, value, toggle);
- });
-
- var falseFn = toggle && function() {
- return false;
- };
-
- // also the disable onselectstart
- if(props.userSelect == 'none') {
- element.onselectstart = falseFn;
- }
- // and disable ondragstart
- if(props.userDrag == 'none') {
- element.ondragstart = falseFn;
- }
- },
-
- /**
- * convert a string with underscores to camelCase
- * so prevent_default becomes preventDefault
- * @param {String} str
- * @return {String} camelCaseStr
- */
- toCamelCase: function toCamelCase(str) {
- return str.replace(/[_-]([a-z])/g, function(s) {
- return s[1].toUpperCase();
- });
- }
- };
-
-
- /**
- * @module hammer
- */
- /**
- * @class Event
- * @static
- */
- var Event = Hammer.event = {
- /**
- * when touch events have been fired, this is true
- * this is used to stop mouse events
- * @property prevent_mouseevents
- * @private
- * @type {Boolean}
- */
- preventMouseEvents: false,
-
- /**
- * if EVENT_START has been fired
- * @property started
- * @private
- * @type {Boolean}
- */
- started: false,
-
- /**
- * when the mouse is hold down, this is true
- * @property should_detect
- * @private
- * @type {Boolean}
- */
- shouldDetect: false,
-
- /**
- * simple event binder with a hook and support for multiple types
- * @method on
- * @param {HTMLElement} element
- * @param {String} type
- * @param {Function} handler
- * @param {Function} [hook]
- * @param {Object} hook.type
- */
- on: function on(element, type, handler, hook) {
- var types = type.split(' ');
- Utils.each(types, function(type) {
- Utils.on(element, type, handler);
- hook && hook(type);
- });
- },
-
- /**
- * simple event unbinder with a hook and support for multiple types
- * @method off
- * @param {HTMLElement} element
- * @param {String} type
- * @param {Function} handler
- * @param {Function} [hook]
- * @param {Object} hook.type
- */
- off: function off(element, type, handler, hook) {
- var types = type.split(' ');
- Utils.each(types, function(type) {
- Utils.off(element, type, handler);
- hook && hook(type);
- });
- },
-
- /**
- * the core touch event handler.
- * this finds out if we should to detect gestures
- * @method onTouch
- * @param {HTMLElement} element
- * @param {String} eventType matches `EVENT_START|MOVE|END`
- * @param {Function} handler
- * @return onTouchHandler {Function} the core event handler
- */
- onTouch: function onTouch(element, eventType, handler) {
- var self = this;
-
- var onTouchHandler = function onTouchHandler(ev) {
- var srcType = ev.type.toLowerCase(),
- isPointer = Hammer.HAS_POINTEREVENTS,
- isMouse = Utils.inStr(srcType, 'mouse'),
- triggerType;
-
- // if we are in a mouseevent, but there has been a touchevent triggered in this session
- // we want to do nothing. simply break out of the event.
- if(isMouse && self.preventMouseEvents) {
- return;
-
- // mousebutton must be down
- } else if(isMouse && eventType == EVENT_START && ev.button === 0) {
- self.preventMouseEvents = false;
- self.shouldDetect = true;
- } else if(isPointer && eventType == EVENT_START) {
- self.shouldDetect = (ev.buttons === 1 || PointerEvent.matchType(POINTER_TOUCH, ev));
- // just a valid start event, but no mouse
- } else if(!isMouse && eventType == EVENT_START) {
- self.preventMouseEvents = true;
- self.shouldDetect = true;
- }
-
- // update the pointer event before entering the detection
- if(isPointer && eventType != EVENT_END) {
- PointerEvent.updatePointer(eventType, ev);
- }
-
- // we are in a touch/down state, so allowed detection of gestures
- if(self.shouldDetect) {
- triggerType = self.doDetect.call(self, ev, eventType, element, handler);
- }
-
- // ...and we are done with the detection
- // so reset everything to start each detection totally fresh
- if(triggerType == EVENT_END) {
- self.preventMouseEvents = false;
- self.shouldDetect = false;
- PointerEvent.reset();
- // update the pointerevent object after the detection
- }
-
- if(isPointer && eventType == EVENT_END) {
- PointerEvent.updatePointer(eventType, ev);
- }
- };
-
- this.on(element, EVENT_TYPES[eventType], onTouchHandler);
- return onTouchHandler;
- },
-
- /**
- * the core detection method
- * this finds out what hammer-touch-events to trigger
- * @method doDetect
- * @param {Object} ev
- * @param {String} eventType matches `EVENT_START|MOVE|END`
- * @param {HTMLElement} element
- * @param {Function} handler
- * @return {String} triggerType matches `EVENT_START|MOVE|END`
- */
- doDetect: function doDetect(ev, eventType, element, handler) {
- var touchList = this.getTouchList(ev, eventType);
- var touchListLength = touchList.length;
- var triggerType = eventType;
- var triggerChange = touchList.trigger; // used by fakeMultitouch plugin
- var changedLength = touchListLength;
-
- // at each touchstart-like event we want also want to trigger a TOUCH event...
- if(eventType == EVENT_START) {
- triggerChange = EVENT_TOUCH;
- // ...the same for a touchend-like event
- } else if(eventType == EVENT_END) {
- triggerChange = EVENT_RELEASE;
-
- // keep track of how many touches have been removed
- changedLength = touchList.length - ((ev.changedTouches) ? ev.changedTouches.length : 1);
- }
-
- // after there are still touches on the screen,
- // we just want to trigger a MOVE event. so change the START or END to a MOVE
- // but only after detection has been started, the first time we actualy want a START
- if(changedLength > 0 && this.started) {
- triggerType = EVENT_MOVE;
- }
-
- // detection has been started, we keep track of this, see above
- this.started = true;
-
- // generate some event data, some basic information
- var evData = this.collectEventData(element, triggerType, touchList, ev);
-
- // trigger the triggerType event before the change (TOUCH, RELEASE) events
- // but the END event should be at last
- if(eventType != EVENT_END) {
- handler.call(Detection, evData);
- }
-
- // trigger a change (TOUCH, RELEASE) event, this means the length of the touches changed
- if(triggerChange) {
- evData.changedLength = changedLength;
- evData.eventType = triggerChange;
-
- handler.call(Detection, evData);
-
- evData.eventType = triggerType;
- delete evData.changedLength;
- }
-
- // trigger the END event
- if(triggerType == EVENT_END) {
- handler.call(Detection, evData);
-
- // ...and we are done with the detection
- // so reset everything to start each detection totally fresh
- this.started = false;
- }
-
- return triggerType;
- },
-
- /**
- * we have different events for each device/browser
- * determine what we need and set them in the EVENT_TYPES constant
- * the `onTouch` method is bind to these properties.
- * @method determineEventTypes
- * @return {Object} events
- */
- determineEventTypes: function determineEventTypes() {
- var types;
- if(Hammer.HAS_POINTEREVENTS) {
- if(window.PointerEvent) {
- types = [
- 'pointerdown',
- 'pointermove',
- 'pointerup pointercancel lostpointercapture'
- ];
- } else {
- types = [
- 'MSPointerDown',
- 'MSPointerMove',
- 'MSPointerUp MSPointerCancel MSLostPointerCapture'
- ];
- }
- } else if(Hammer.NO_MOUSEEVENTS) {
- types = [
- 'touchstart',
- 'touchmove',
- 'touchend touchcancel'
- ];
- } else {
- types = [
- 'touchstart mousedown',
- 'touchmove mousemove',
- 'touchend touchcancel mouseup'
- ];
- }
-
- EVENT_TYPES[EVENT_START] = types[0];
- EVENT_TYPES[EVENT_MOVE] = types[1];
- EVENT_TYPES[EVENT_END] = types[2];
- return EVENT_TYPES;
- },
-
- /**
- * create touchList depending on the event
- * @method getTouchList
- * @param {Object} ev
- * @param {String} eventType
- * @return {Array} touches
- */
- getTouchList: function getTouchList(ev, eventType) {
- // get the fake pointerEvent touchlist
- if(Hammer.HAS_POINTEREVENTS) {
- return PointerEvent.getTouchList();
- }
-
- // get the touchlist
- if(ev.touches) {
- if(eventType == EVENT_MOVE) {
- return ev.touches;
- }
-
- var identifiers = [];
- var concat = [].concat(Utils.toArray(ev.touches), Utils.toArray(ev.changedTouches));
- var touchList = [];
-
- Utils.each(concat, function(touch) {
- if(Utils.inArray(identifiers, touch.identifier) === false) {
- touchList.push(touch);
- }
- identifiers.push(touch.identifier);
- });
-
- return touchList;
- }
-
- // make fake touchList from mouse position
- ev.identifier = 1;
- return [ev];
- },
-
- /**
- * collect basic event data
- * @method collectEventData
- * @param {HTMLElement} element
- * @param {String} eventType matches `EVENT_START|MOVE|END`
- * @param {Array} touches
- * @param {Object} ev
- * @return {Object} ev
- */
- collectEventData: function collectEventData(element, eventType, touches, ev) {
- // find out pointerType
- var pointerType = POINTER_TOUCH;
- if(Utils.inStr(ev.type, 'mouse') || PointerEvent.matchType(POINTER_MOUSE, ev)) {
- pointerType = POINTER_MOUSE;
- } else if(PointerEvent.matchType(POINTER_PEN, ev)) {
- pointerType = POINTER_PEN;
- }
-
- return {
- center: Utils.getCenter(touches),
- timeStamp: Date.now(),
- target: ev.target,
- touches: touches,
- eventType: eventType,
- pointerType: pointerType,
- srcEvent: ev,
-
- /**
- * prevent the browser default actions
- * mostly used to disable scrolling of the browser
- */
- preventDefault: function() {
- var srcEvent = this.srcEvent;
- srcEvent.preventManipulation && srcEvent.preventManipulation();
- srcEvent.preventDefault && srcEvent.preventDefault();
- },
-
- /**
- * stop bubbling the event up to its parents
- */
- stopPropagation: function() {
- this.srcEvent.stopPropagation();
- },
-
- /**
- * immediately stop gesture detection
- * might be useful after a swipe was detected
- * @return {*}
- */
- stopDetect: function() {
- return Detection.stopDetect();
- }
- };
- }
- };
-
-
- /**
- * @module hammer
- *
- * @class PointerEvent
- * @static
- */
- var PointerEvent = Hammer.PointerEvent = {
- /**
- * holds all pointers, by `identifier`
- * @property pointers
- * @type {Object}
- */
- pointers: {},
-
- /**
- * get the pointers as an array
- * @method getTouchList
- * @return {Array} touchlist
- */
- getTouchList: function getTouchList() {
- var touchlist = [];
- // we can use forEach since pointerEvents only is in IE10
- Utils.each(this.pointers, function(pointer) {
- touchlist.push(pointer);
- });
- return touchlist;
- },
-
- /**
- * update the position of a pointer
- * @method updatePointer
- * @param {String} eventType matches `EVENT_START|MOVE|END`
- * @param {Object} pointerEvent
- */
- updatePointer: function updatePointer(eventType, pointerEvent) {
- if(eventType == EVENT_END || (eventType != EVENT_END && pointerEvent.buttons !== 1)) {
- delete this.pointers[pointerEvent.pointerId];
- } else {
- pointerEvent.identifier = pointerEvent.pointerId;
- this.pointers[pointerEvent.pointerId] = pointerEvent;
- }
- },
-
- /**
- * check if ev matches pointertype
- * @method matchType
- * @param {String} pointerType matches `POINTER_MOUSE|TOUCH|PEN`
- * @param {PointerEvent} ev
- */
- matchType: function matchType(pointerType, ev) {
- if(!ev.pointerType) {
- return false;
- }
-
- var pt = ev.pointerType,
- types = {};
-
- types[POINTER_MOUSE] = (pt === (ev.MSPOINTER_TYPE_MOUSE || POINTER_MOUSE));
- types[POINTER_TOUCH] = (pt === (ev.MSPOINTER_TYPE_TOUCH || POINTER_TOUCH));
- types[POINTER_PEN] = (pt === (ev.MSPOINTER_TYPE_PEN || POINTER_PEN));
- return types[pointerType];
- },
-
- /**
- * reset the stored pointers
- * @method reset
- */
- reset: function resetList() {
- this.pointers = {};
- }
- };
-
-
- /**
- * @module hammer
- *
- * @class Detection
- * @static
- */
- var Detection = Hammer.detection = {
- // contains all registred Hammer.gestures in the correct order
- gestures: [],
-
- // data of the current Hammer.gesture detection session
- current: null,
-
- // the previous Hammer.gesture session data
- // is a full clone of the previous gesture.current object
- previous: null,
-
- // when this becomes true, no gestures are fired
- stopped: false,
-
- /**
- * start Hammer.gesture detection
- * @method startDetect
- * @param {Hammer.Instance} inst
- * @param {Object} eventData
- */
- startDetect: function startDetect(inst, eventData) {
- // already busy with a Hammer.gesture detection on an element
- if(this.current) {
- return;
- }
-
- this.stopped = false;
-
- // holds current session
- this.current = {
- inst: inst, // reference to HammerInstance we're working for
- startEvent: Utils.extend({}, eventData), // start eventData for distances, timing etc
- lastEvent: false, // last eventData
- lastCalcEvent: false, // last eventData for calculations.
- futureCalcEvent: false, // last eventData for calculations.
- lastCalcData: {}, // last lastCalcData
- name: '' // current gesture we're in/detected, can be 'tap', 'hold' etc
- };
-
- this.detect(eventData);
- },
-
- /**
- * Hammer.gesture detection
- * @method detect
- * @param {Object} eventData
- * @return {any}
- */
- detect: function detect(eventData) {
- if(!this.current || this.stopped) {
- return;
- }
-
- // extend event data with calculations about scale, distance etc
- eventData = this.extendEventData(eventData);
-
- // hammer instance and instance options
- var inst = this.current.inst,
- instOptions = inst.options;
-
- // call Hammer.gesture handlers
- Utils.each(this.gestures, function triggerGesture(gesture) {
- // only when the instance options have enabled this gesture
- if(!this.stopped && inst.enabled && instOptions[gesture.name]) {
- gesture.handler.call(gesture, eventData, inst);
- }
- }, this);
-
- // store as previous event event
- if(this.current) {
- this.current.lastEvent = eventData;
- }
-
- if(eventData.eventType == EVENT_END) {
- this.stopDetect();
- }
-
- return eventData;
- },
-
- /**
- * clear the Hammer.gesture vars
- * this is called on endDetect, but can also be used when a final Hammer.gesture has been detected
- * to stop other Hammer.gestures from being fired
- * @method stopDetect
- */
- stopDetect: function stopDetect() {
- // clone current data to the store as the previous gesture
- // used for the double tap gesture, since this is an other gesture detect session
- this.previous = Utils.extend({}, this.current);
-
- // reset the current
- this.current = null;
- this.stopped = true;
- },
-
- /**
- * calculate velocity, angle and direction
- * @method getVelocityData
- * @param {Object} ev
- * @param {Object} center
- * @param {Number} deltaTime
- * @param {Number} deltaX
- * @param {Number} deltaY
- */
- getCalculatedData: function getCalculatedData(ev, center, deltaTime, deltaX, deltaY) {
- var cur = this.current,
- recalc = false,
- calcEv = cur.lastCalcEvent,
- calcData = cur.lastCalcData;
-
- if(calcEv && ev.timeStamp - calcEv.timeStamp > Hammer.CALCULATE_INTERVAL) {
- center = calcEv.center;
- deltaTime = ev.timeStamp - calcEv.timeStamp;
- deltaX = ev.center.clientX - calcEv.center.clientX;
- deltaY = ev.center.clientY - calcEv.center.clientY;
- recalc = true;
- }
-
- if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) {
- cur.futureCalcEvent = ev;
- }
-
- if(!cur.lastCalcEvent || recalc) {
- calcData.velocity = Utils.getVelocity(deltaTime, deltaX, deltaY);
- calcData.angle = Utils.getAngle(center, ev.center);
- calcData.direction = Utils.getDirection(center, ev.center);
-
- cur.lastCalcEvent = cur.futureCalcEvent || ev;
- cur.futureCalcEvent = ev;
- }
-
- ev.velocityX = calcData.velocity.x;
- ev.velocityY = calcData.velocity.y;
- ev.interimAngle = calcData.angle;
- ev.interimDirection = calcData.direction;
- },
-
- /**
- * extend eventData for Hammer.gestures
- * @method extendEventData
- * @param {Object} ev
- * @return {Object} ev
- */
- extendEventData: function extendEventData(ev) {
- var cur = this.current,
- startEv = cur.startEvent,
- lastEv = cur.lastEvent || startEv;
-
- // update the start touchlist to calculate the scale/rotation
- if(ev.eventType == EVENT_TOUCH || ev.eventType == EVENT_RELEASE) {
- startEv.touches = [];
- Utils.each(ev.touches, function(touch) {
- startEv.touches.push({
- clientX: touch.clientX,
- clientY: touch.clientY
- });
- });
- }
-
- var deltaTime = ev.timeStamp - startEv.timeStamp,
- deltaX = ev.center.clientX - startEv.center.clientX,
- deltaY = ev.center.clientY - startEv.center.clientY;
-
- this.getCalculatedData(ev, lastEv.center, deltaTime, deltaX, deltaY);
-
- Utils.extend(ev, {
- startEvent: startEv,
-
- deltaTime: deltaTime,
- deltaX: deltaX,
- deltaY: deltaY,
-
- distance: Utils.getDistance(startEv.center, ev.center),
- angle: Utils.getAngle(startEv.center, ev.center),
- direction: Utils.getDirection(startEv.center, ev.center),
- scale: Utils.getScale(startEv.touches, ev.touches),
- rotation: Utils.getRotation(startEv.touches, ev.touches)
- });
-
- return ev;
- },
-
- /**
- * register new gesture
- * @method register
- * @param {Object} gesture object, see `gestures/` for documentation
- * @return {Array} gestures
- */
- register: function register(gesture) {
- // add an enable gesture options if there is no given
- var options = gesture.defaults || {};
- if(options[gesture.name] === undefined) {
- options[gesture.name] = true;
- }
-
- // extend Hammer default options with the Hammer.gesture options
- Utils.extend(Hammer.defaults, options, true);
-
- // set its index
- gesture.index = gesture.index || 1000;
-
- // add Hammer.gesture to the list
- this.gestures.push(gesture);
-
- // sort the list by index
- this.gestures.sort(function(a, b) {
- if(a.index < b.index) {
- return -1;
- }
- if(a.index > b.index) {
- return 1;
- }
- return 0;
- });
-
- return this.gestures;
- }
- };
-
-
- /**
- * @module hammer
- */
-
- /**
- * create new hammer instance
- * all methods should return the instance itself, so it is chainable.
- *
- * @class Instance
- * @constructor
- * @param {HTMLElement} element
- * @param {Object} [options={}] options are merged with `Hammer.defaults`
- * @return {Hammer.Instance}
- */
- Hammer.Instance = function(element, options) {
- var self = this;
-
- // setup HammerJS window events and register all gestures
- // this also sets up the default options
- setup();
-
- /**
- * @property element
- * @type {HTMLElement}
- */
- this.element = element;
-
- /**
- * @property enabled
- * @type {Boolean}
- * @protected
- */
- this.enabled = true;
-
- /**
- * options, merged with the defaults
- * options with an _ are converted to camelCase
- * @property options
- * @type {Object}
- */
- Utils.each(options, function(value, name) {
- delete options[name];
- options[Utils.toCamelCase(name)] = value;
- });
-
- this.options = Utils.extend(Utils.extend({}, Hammer.defaults), options || {});
-
- // add some css to the element to prevent the browser from doing its native behavoir
- if(this.options.behavior) {
- Utils.toggleBehavior(this.element, this.options.behavior, true);
- }
-
- /**
- * event start handler on the element to start the detection
- * @property eventStartHandler
- * @type {Object}
- */
- this.eventStartHandler = Event.onTouch(element, EVENT_START, function(ev) {
- if(self.enabled && ev.eventType == EVENT_START) {
- Detection.startDetect(self, ev);
- } else if(ev.eventType == EVENT_TOUCH) {
- Detection.detect(ev);
- }
- });
-
- /**
- * keep a list of user event handlers which needs to be removed when calling 'dispose'
- * @property eventHandlers
- * @type {Array}
- */
- this.eventHandlers = [];
- };
-
- Hammer.Instance.prototype = {
- /**
- * bind events to the instance
- * @method on
- * @chainable
- * @param {String} gestures multiple gestures by splitting with a space
- * @param {Function} handler
- * @param {Object} handler.ev event object
- */
- on: function onEvent(gestures, handler) {
- var self = this;
- Event.on(self.element, gestures, handler, function(type) {
- self.eventHandlers.push({ gesture: type, handler: handler });
- });
- return self;
- },
-
- /**
- * unbind events to the instance
- * @method off
- * @chainable
- * @param {String} gestures
- * @param {Function} handler
- */
- off: function offEvent(gestures, handler) {
- var self = this;
-
- Event.off(self.element, gestures, handler, function(type) {
- var index = Utils.inArray({ gesture: type, handler: handler });
- if(index !== false) {
- self.eventHandlers.splice(index, 1);
- }
- });
- return self;
- },
-
- /**
- * trigger gesture event
- * @method trigger
- * @chainable
- * @param {String} gesture
- * @param {Object} [eventData]
- */
- trigger: function triggerEvent(gesture, eventData) {
- // optional
- if(!eventData) {
- eventData = {};
- }
-
- // create DOM event
- var event = Hammer.DOCUMENT.createEvent('Event');
- event.initEvent(gesture, true, true);
- event.gesture = eventData;
-
- // trigger on the target if it is in the instance element,
- // this is for event delegation tricks
- var element = this.element;
- if(Utils.hasParent(eventData.target, element)) {
- element = eventData.target;
- }
-
- element.dispatchEvent(event);
- return this;
- },
-
- /**
- * enable of disable hammer.js detection
- * @method enable
- * @chainable
- * @param {Boolean} state
- */
- enable: function enable(state) {
- this.enabled = state;
- return this;
- },
-
- /**
- * dispose this hammer instance
- * @method dispose
- * @return {Null}
- */
- dispose: function dispose() {
- var i, eh;
-
- // undo all changes made by stop_browser_behavior
- Utils.toggleBehavior(this.element, this.options.behavior, false);
-
- // unbind all custom event handlers
- for(i = -1; (eh = this.eventHandlers[++i]);) {
- Utils.off(this.element, eh.gesture, eh.handler);
- }
-
- this.eventHandlers = [];
-
- // unbind the start event listener
- Event.off(this.element, EVENT_TYPES[EVENT_START], this.eventStartHandler);
-
- return null;
- }
- };
-
-
- /**
- * @module gestures
- */
- /**
- * Move with x fingers (default 1) around on the page.
- * Preventing the default browser behavior is a good way to improve feel and working.
- * ````
- * hammertime.on("drag", function(ev) {
- * console.log(ev);
- * ev.gesture.preventDefault();
- * });
- * ````
- *
- * @class Drag
- * @static
- */
- /**
- * @event drag
- * @param {Object} ev
- */
- /**
- * @event dragstart
- * @param {Object} ev
- */
- /**
- * @event dragend
- * @param {Object} ev
- */
- /**
- * @event drapleft
- * @param {Object} ev
- */
- /**
- * @event dragright
- * @param {Object} ev
- */
- /**
- * @event dragup
- * @param {Object} ev
- */
- /**
- * @event dragdown
- * @param {Object} ev
- */
-
- /**
- * @param {String} name
- */
- (function(name) {
- var triggered = false;
-
- function dragGesture(ev, inst) {
- var cur = Detection.current;
-
- // max touches
- if(inst.options.dragMaxTouches > 0 &&
- ev.touches.length > inst.options.dragMaxTouches) {
- return;
- }
-
- switch(ev.eventType) {
- case EVENT_START:
- triggered = false;
- break;
-
- case EVENT_MOVE:
- // when the distance we moved is too small we skip this gesture
- // or we can be already in dragging
- if(ev.distance < inst.options.dragMinDistance &&
- cur.name != name) {
- return;
- }
-
- var startCenter = cur.startEvent.center;
-
- // we are dragging!
- if(cur.name != name) {
- cur.name = name;
- if(inst.options.dragDistanceCorrection && ev.distance > 0) {
- // When a drag is triggered, set the event center to dragMinDistance pixels from the original event center.
- // Without this correction, the dragged distance would jumpstart at dragMinDistance pixels instead of at 0.
- // It might be useful to save the original start point somewhere
- var factor = Math.abs(inst.options.dragMinDistance / ev.distance);
- startCenter.pageX += ev.deltaX * factor;
- startCenter.pageY += ev.deltaY * factor;
- startCenter.clientX += ev.deltaX * factor;
- startCenter.clientY += ev.deltaY * factor;
-
- // recalculate event data using new start point
- ev = Detection.extendEventData(ev);
- }
- }
-
- // lock drag to axis?
- if(cur.lastEvent.dragLockToAxis ||
- ( inst.options.dragLockToAxis &&
- inst.options.dragLockMinDistance <= ev.distance
- )) {
- ev.dragLockToAxis = true;
- }
-
- // keep direction on the axis that the drag gesture started on
- var lastDirection = cur.lastEvent.direction;
- if(ev.dragLockToAxis && lastDirection !== ev.direction) {
- if(Utils.isVertical(lastDirection)) {
- ev.direction = (ev.deltaY < 0) ? DIRECTION_UP : DIRECTION_DOWN;
- } else {
- ev.direction = (ev.deltaX < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT;
- }
- }
-
- // first time, trigger dragstart event
- if(!triggered) {
- inst.trigger(name + 'start', ev);
- triggered = true;
- }
-
- // trigger events
- inst.trigger(name, ev);
- inst.trigger(name + ev.direction, ev);
-
- var isVertical = Utils.isVertical(ev.direction);
-
- // block the browser events
- if((inst.options.dragBlockVertical && isVertical) ||
- (inst.options.dragBlockHorizontal && !isVertical)) {
- ev.preventDefault();
- }
- break;
-
- case EVENT_RELEASE:
- if(triggered && ev.changedLength <= inst.options.dragMaxTouches) {
- inst.trigger(name + 'end', ev);
- triggered = false;
- }
- break;
-
- case EVENT_END:
- triggered = false;
- break;
- }
- }
-
- Hammer.gestures.Drag = {
- name: name,
- index: 50,
- handler: dragGesture,
- defaults: {
- /**
- * minimal movement that have to be made before the drag event gets triggered
- * @property dragMinDistance
- * @type {Number}
- * @default 10
- */
- dragMinDistance: 10,
-
- /**
- * Set dragDistanceCorrection to true to make the starting point of the drag
- * be calculated from where the drag was triggered, not from where the touch started.
- * Useful to avoid a jerk-starting drag, which can make fine-adjustments
- * through dragging difficult, and be visually unappealing.
- * @property dragDistanceCorrection
- * @type {Boolean}
- * @default true
- */
- dragDistanceCorrection: true,
-
- /**
- * set 0 for unlimited, but this can conflict with transform
- * @property dragMaxTouches
- * @type {Number}
- * @default 1
- */
- dragMaxTouches: 1,
-
- /**
- * prevent default browser behavior when dragging occurs
- * be careful with it, it makes the element a blocking element
- * when you are using the drag gesture, it is a good practice to set this true
- * @property dragBlockHorizontal
- * @type {Boolean}
- * @default false
- */
- dragBlockHorizontal: false,
-
- /**
- * same as `dragBlockHorizontal`, but for vertical movement
- * @property dragBlockVertical
- * @type {Boolean}
- * @default false
- */
- dragBlockVertical: false,
-
- /**
- * dragLockToAxis keeps the drag gesture on the axis that it started on,
- * It disallows vertical directions if the initial direction was horizontal, and vice versa.
- * @property dragLockToAxis
- * @type {Boolean}
- * @default false
- */
- dragLockToAxis: false,
-
- /**
- * drag lock only kicks in when distance > dragLockMinDistance
- * This way, locking occurs only when the distance has become large enough to reliably determine the direction
- * @property dragLockMinDistance
- * @type {Number}
- * @default 25
- */
- dragLockMinDistance: 25
- }
- };
- })('drag');
-
- /**
- * @module gestures
- */
- /**
- * trigger a simple gesture event, so you can do anything in your handler.
- * only usable if you know what your doing...
- *
- * @class Gesture
- * @static
- */
- /**
- * @event gesture
- * @param {Object} ev
- */
- Hammer.gestures.Gesture = {
- name: 'gesture',
- index: 1337,
- handler: function releaseGesture(ev, inst) {
- inst.trigger(this.name, ev);
- }
- };
-
- /**
- * @module gestures
- */
- /**
- * Touch stays at the same place for x time
- *
- * @class Hold
- * @static
- */
- /**
- * @event hold
- * @param {Object} ev
- */
-
- /**
- * @param {String} name
- */
- (function(name) {
- var timer;
-
- function holdGesture(ev, inst) {
- var options = inst.options,
- current = Detection.current;
-
- switch(ev.eventType) {
- case EVENT_START:
- clearTimeout(timer);
-
- // set the gesture so we can check in the timeout if it still is
- current.name = name;
-
- // set timer and if after the timeout it still is hold,
- // we trigger the hold event
- timer = setTimeout(function() {
- if(current && current.name == name) {
- inst.trigger(name, ev);
- }
- }, options.holdTimeout);
- break;
-
- case EVENT_MOVE:
- if(ev.distance > options.holdThreshold) {
- clearTimeout(timer);
- }
- break;
-
- case EVENT_RELEASE:
- clearTimeout(timer);
- break;
- }
- }
-
- Hammer.gestures.Hold = {
- name: name,
- index: 10,
- defaults: {
- /**
- * @property holdTimeout
- * @type {Number}
- * @default 500
- */
- holdTimeout: 500,
-
- /**
- * movement allowed while holding
- * @property holdThreshold
- * @type {Number}
- * @default 2
- */
- holdThreshold: 2
- },
- handler: holdGesture
- };
- })('hold');
-
- /**
- * @module gestures
- */
- /**
- * when a touch is being released from the page
- *
- * @class Release
- * @static
- */
- /**
- * @event release
- * @param {Object} ev
- */
- Hammer.gestures.Release = {
- name: 'release',
- index: Infinity,
- handler: function releaseGesture(ev, inst) {
- if(ev.eventType == EVENT_RELEASE) {
- inst.trigger(this.name, ev);
- }
- }
- };
-
- /**
- * @module gestures
- */
- /**
- * triggers swipe events when the end velocity is above the threshold
- * for best usage, set `preventDefault` (on the drag gesture) to `true`
- * ````
- * hammertime.on("dragleft swipeleft", function(ev) {
- * console.log(ev);
- * ev.gesture.preventDefault();
- * });
- * ````
- *
- * @class Swipe
- * @static
- */
- /**
- * @event swipe
- * @param {Object} ev
- */
- /**
- * @event swipeleft
- * @param {Object} ev
- */
- /**
- * @event swiperight
- * @param {Object} ev
- */
- /**
- * @event swipeup
- * @param {Object} ev
- */
- /**
- * @event swipedown
- * @param {Object} ev
- */
- Hammer.gestures.Swipe = {
- name: 'swipe',
- index: 40,
- defaults: {
- /**
- * @property swipeMinTouches
- * @type {Number}
- * @default 1
- */
- swipeMinTouches: 1,
-
- /**
- * @property swipeMaxTouches
- * @type {Number}
- * @default 1
- */
- swipeMaxTouches: 1,
-
- /**
- * horizontal swipe velocity
- * @property swipeVelocityX
- * @type {Number}
- * @default 0.6
- */
- swipeVelocityX: 0.6,
-
- /**
- * vertical swipe velocity
- * @property swipeVelocityY
- * @type {Number}
- * @default 0.6
- */
- swipeVelocityY: 0.6
- },
-
- handler: function swipeGesture(ev, inst) {
- if(ev.eventType == EVENT_RELEASE) {
- var touches = ev.touches.length,
- options = inst.options;
-
- // max touches
- if(touches < options.swipeMinTouches ||
- touches > options.swipeMaxTouches) {
- return;
- }
-
- // when the distance we moved is too small we skip this gesture
- // or we can be already in dragging
- if(ev.velocityX > options.swipeVelocityX ||
- ev.velocityY > options.swipeVelocityY) {
- // trigger swipe events
- inst.trigger(this.name, ev);
- inst.trigger(this.name + ev.direction, ev);
- }
- }
- }
- };
-
- /**
- * @module gestures
- */
- /**
- * Single tap and a double tap on a place
- *
- * @class Tap
- * @static
- */
- /**
- * @event tap
- * @param {Object} ev
- */
- /**
- * @event doubletap
- * @param {Object} ev
- */
-
- /**
- * @param {String} name
- */
- (function(name) {
- var hasMoved = false;
-
- function tapGesture(ev, inst) {
- var options = inst.options,
- current = Detection.current,
- prev = Detection.previous,
- sincePrev,
- didDoubleTap;
-
- switch(ev.eventType) {
- case EVENT_START:
- hasMoved = false;
- break;
-
- case EVENT_MOVE:
- hasMoved = hasMoved || (ev.distance > options.tapMaxDistance);
- break;
-
- case EVENT_END:
- if(!Utils.inStr(ev.srcEvent.type, 'cancel') && ev.deltaTime < options.tapMaxTime && !hasMoved) {
- // previous gesture, for the double tap since these are two different gesture detections
- sincePrev = prev && prev.lastEvent && ev.timeStamp - prev.lastEvent.timeStamp;
- didDoubleTap = false;
-
- // check if double tap
- if(prev && prev.name == name &&
- (sincePrev && sincePrev < options.doubleTapInterval) &&
- ev.distance < options.doubleTapDistance) {
- inst.trigger('doubletap', ev);
- didDoubleTap = true;
- }
-
- // do a single tap
- if(!didDoubleTap || options.tapAlways) {
- current.name = name;
- inst.trigger(current.name, ev);
- }
- }
- break;
- }
- }
-
- Hammer.gestures.Tap = {
- name: name,
- index: 100,
- handler: tapGesture,
- defaults: {
- /**
- * max time of a tap, this is for the slow tappers
- * @property tapMaxTime
- * @type {Number}
- * @default 250
- */
- tapMaxTime: 250,
-
- /**
- * max distance of movement of a tap, this is for the slow tappers
- * @property tapMaxDistance
- * @type {Number}
- * @default 10
- */
- tapMaxDistance: 10,
-
- /**
- * always trigger the `tap` event, even while double-tapping
- * @property tapAlways
- * @type {Boolean}
- * @default true
- */
- tapAlways: true,
-
- /**
- * max distance between two taps
- * @property doubleTapDistance
- * @type {Number}
- * @default 20
- */
- doubleTapDistance: 20,
-
- /**
- * max time between two taps
- * @property doubleTapInterval
- * @type {Number}
- * @default 300
- */
- doubleTapInterval: 300
- }
- };
- })('tap');
-
- /**
- * @module gestures
- */
- /**
- * when a touch is being touched at the page
- *
- * @class Touch
- * @static
- */
- /**
- * @event touch
- * @param {Object} ev
- */
- Hammer.gestures.Touch = {
- name: 'touch',
- index: -Infinity,
- defaults: {
- /**
- * call preventDefault at touchstart, and makes the element blocking by disabling the scrolling of the page,
- * but it improves gestures like transforming and dragging.
- * be careful with using this, it can be very annoying for users to be stuck on the page
- * @property preventDefault
- * @type {Boolean}
- * @default false
- */
- preventDefault: false,
-
- /**
- * disable mouse events, so only touch (or pen!) input triggers events
- * @property preventMouse
- * @type {Boolean}
- * @default false
- */
- preventMouse: false
- },
- handler: function touchGesture(ev, inst) {
- if(inst.options.preventMouse && ev.pointerType == POINTER_MOUSE) {
- ev.stopDetect();
- return;
- }
-
- if(inst.options.preventDefault) {
- ev.preventDefault();
- }
-
- if(ev.eventType == EVENT_TOUCH) {
- inst.trigger('touch', ev);
- }
- }
- };
-
- /**
- * @module gestures
- */
- /**
- * User want to scale or rotate with 2 fingers
- * Preventing the default browser behavior is a good way to improve feel and working. This can be done with the
- * `preventDefault` option.
- *
- * @class Transform
- * @static
- */
- /**
- * @event transform
- * @param {Object} ev
- */
- /**
- * @event transformstart
- * @param {Object} ev
- */
- /**
- * @event transformend
- * @param {Object} ev
- */
- /**
- * @event pinchin
- * @param {Object} ev
- */
- /**
- * @event pinchout
- * @param {Object} ev
- */
- /**
- * @event rotate
- * @param {Object} ev
- */
-
- /**
- * @param {String} name
- */
- (function(name) {
- var triggered = false;
-
- function transformGesture(ev, inst) {
- switch(ev.eventType) {
- case EVENT_START:
- triggered = false;
- break;
-
- case EVENT_MOVE:
- // at least multitouch
- if(ev.touches.length < 2) {
- return;
- }
-
- var scaleThreshold = Math.abs(1 - ev.scale);
- var rotationThreshold = Math.abs(ev.rotation);
-
- // when the distance we moved is too small we skip this gesture
- // or we can be already in dragging
- if(scaleThreshold < inst.options.transformMinScale &&
- rotationThreshold < inst.options.transformMinRotation) {
- return;
- }
-
- // we are transforming!
- Detection.current.name = name;
-
- // first time, trigger dragstart event
- if(!triggered) {
- inst.trigger(name + 'start', ev);
- triggered = true;
- }
-
- inst.trigger(name, ev); // basic transform event
-
- // trigger rotate event
- if(rotationThreshold > inst.options.transformMinRotation) {
- inst.trigger('rotate', ev);
- }
-
- // trigger pinch event
- if(scaleThreshold > inst.options.transformMinScale) {
- inst.trigger('pinch', ev);
- inst.trigger('pinch' + (ev.scale < 1 ? 'in' : 'out'), ev);
- }
- break;
-
- case EVENT_RELEASE:
- if(triggered && ev.changedLength < 2) {
- inst.trigger(name + 'end', ev);
- triggered = false;
- }
- break;
- }
- }
-
- Hammer.gestures.Transform = {
- name: name,
- index: 45,
- defaults: {
- /**
- * minimal scale factor, no scale is 1, zoomin is to 0 and zoomout until higher then 1
- * @property transformMinScale
- * @type {Number}
- * @default 0.01
- */
- transformMinScale: 0.01,
-
- /**
- * rotation in degrees
- * @property transformMinRotation
- * @type {Number}
- * @default 1
- */
- transformMinRotation: 1
- },
-
- handler: transformGesture
- };
- })('transform');
-
- /**
- * @module hammer
- */
-
- // AMD export
- if(true) {
- !(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
- return Hammer;
- }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
- // commonjs export
- } else if(typeof module !== 'undefined' && module.exports) {
- module.exports = Hammer;
- // browser export
- } else {
- window.Hammer = Hammer;
- }
-
- })(window);
-
-/***/ },
-/* 21 */
-/***/ function(module, exports, __webpack_require__) {
-
- var util = __webpack_require__(1);
- var hammerUtil = __webpack_require__(22);
- var moment = __webpack_require__(2);
- var Component = __webpack_require__(23);
- var DateUtil = __webpack_require__(24);
-
- /**
- * @constructor Range
- * A Range controls a numeric range with a start and end value.
- * The Range adjusts the range based on mouse events or programmatic changes,
- * and triggers events when the range is changing or has been changed.
- * @param {{dom: Object, domProps: Object, emitter: Emitter}} body
- * @param {Object} [options] See description at Range.setOptions
- */
- function Range(body, options) {
- var now = moment().hours(0).minutes(0).seconds(0).milliseconds(0);
- this.start = now.clone().add(-3, 'days').valueOf(); // Number
- this.end = now.clone().add(4, 'days').valueOf(); // Number
-
- this.body = body;
- this.deltaDifference = 0;
- this.scaleOffset = 0;
- this.startToFront = false;
- this.endToFront = true;
-
- // default options
- this.defaultOptions = {
- start: null,
- end: null,
- direction: 'horizontal', // 'horizontal' or 'vertical'
- moveable: true,
- zoomable: true,
- min: null,
- max: null,
- zoomMin: 10, // milliseconds
- zoomMax: 1000 * 60 * 60 * 24 * 365 * 10000 // milliseconds
- };
- this.options = util.extend({}, this.defaultOptions);
-
- this.props = {
- touch: {}
- };
- this.animateTimer = null;
-
- // drag listeners for dragging
- this.body.emitter.on('dragstart', this._onDragStart.bind(this));
- this.body.emitter.on('drag', this._onDrag.bind(this));
- this.body.emitter.on('dragend', this._onDragEnd.bind(this));
-
- // ignore dragging when holding
- this.body.emitter.on('hold', this._onHold.bind(this));
-
- // mouse wheel for zooming
- this.body.emitter.on('mousewheel', this._onMouseWheel.bind(this));
- this.body.emitter.on('DOMMouseScroll', this._onMouseWheel.bind(this)); // For FF
-
- // pinch to zoom
- this.body.emitter.on('touch', this._onTouch.bind(this));
- this.body.emitter.on('pinch', this._onPinch.bind(this));
-
- this.setOptions(options);
- }
-
- Range.prototype = new Component();
-
- /**
- * Set options for the range controller
- * @param {Object} options Available options:
- * {Number | Date | String} start Start date for the range
- * {Number | Date | String} end End date for the range
- * {Number} min Minimum value for start
- * {Number} max Maximum value for end
- * {Number} zoomMin Set a minimum value for
- * (end - start).
- * {Number} zoomMax Set a maximum value for
- * (end - start).
- * {Boolean} moveable Enable moving of the range
- * by dragging. True by default
- * {Boolean} zoomable Enable zooming of the range
- * by pinching/scrolling. True by default
- */
- Range.prototype.setOptions = function (options) {
- if (options) {
- // copy the options that we know
- var fields = ['direction', 'min', 'max', 'zoomMin', 'zoomMax', 'moveable', 'zoomable', 'activate', 'hiddenDates'];
- util.selectiveExtend(fields, this.options, options);
-
- if ('start' in options || 'end' in options) {
- // apply a new range. both start and end are optional
- this.setRange(options.start, options.end);
- }
- }
- };
-
- /**
- * Test whether direction has a valid value
- * @param {String} direction 'horizontal' or 'vertical'
- */
- function validateDirection (direction) {
- if (direction != 'horizontal' && direction != 'vertical') {
- throw new TypeError('Unknown direction "' + direction + '". ' +
- 'Choose "horizontal" or "vertical".');
- }
- }
-
- /**
- * Set a new start and end range
- * @param {Date | Number | String} [start]
- * @param {Date | Number | String} [end]
- * @param {boolean | number} [animate=false] If true, the range is animated
- * smoothly to the new window.
- * If animate is a number, the
- * number is taken as duration
- * Default duration is 500 ms.
- *
- */
- Range.prototype.setRange = function(start, end, animate) {
- var _start = start != undefined ? util.convert(start, 'Date').valueOf() : null;
- var _end = end != undefined ? util.convert(end, 'Date').valueOf() : null;
- this._cancelAnimation();
-
- if (animate) {
- var me = this;
- var initStart = this.start;
- var initEnd = this.end;
- var duration = typeof animate === 'number' ? animate : 500;
- var initTime = new Date().valueOf();
- var anyChanged = false;
-
- var next = function () {
- if (!me.props.touch.dragging) {
- var now = new Date().valueOf();
- var time = now - initTime;
- var done = time > duration;
- var s = (done || _start === null) ? _start : util.easeInOutQuad(time, initStart, _start, duration);
- var e = (done || _end === null) ? _end : util.easeInOutQuad(time, initEnd, _end, duration);
-
- changed = me._applyRange(s, e);
- DateUtil.updateHiddenDates(me.body, me.options.hiddenDates);
- anyChanged = anyChanged || changed;
- if (changed) {
- me.body.emitter.emit('rangechange', {start: new Date(me.start), end: new Date(me.end)});
- }
-
- if (done) {
- if (anyChanged) {
- me.body.emitter.emit('rangechanged', {start: new Date(me.start), end: new Date(me.end)});
- }
- }
- else {
- // animate with as high as possible frame rate, leave 20 ms in between
- // each to prevent the browser from blocking
- me.animateTimer = setTimeout(next, 20);
- }
- }
- }
-
- return next();
- }
- else {
- var changed = this._applyRange(_start, _end);
- DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
- if (changed) {
- var params = {start: new Date(this.start), end: new Date(this.end)};
- this.body.emitter.emit('rangechange', params);
- this.body.emitter.emit('rangechanged', params);
- }
- }
- };
-
- /**
- * Stop an animation
- * @private
- */
- Range.prototype._cancelAnimation = function () {
- if (this.animateTimer) {
- clearTimeout(this.animateTimer);
- this.animateTimer = null;
- }
- };
-
- /**
- * Set a new start and end range. This method is the same as setRange, but
- * does not trigger a range change and range changed event, and it returns
- * true when the range is changed
- * @param {Number} [start]
- * @param {Number} [end]
- * @return {Boolean} changed
- * @private
- */
- Range.prototype._applyRange = function(start, end) {
- var newStart = (start != null) ? util.convert(start, 'Date').valueOf() : this.start,
- newEnd = (end != null) ? util.convert(end, 'Date').valueOf() : this.end,
- max = (this.options.max != null) ? util.convert(this.options.max, 'Date').valueOf() : null,
- min = (this.options.min != null) ? util.convert(this.options.min, 'Date').valueOf() : null,
- diff;
-
- // check for valid number
- if (isNaN(newStart) || newStart === null) {
- throw new Error('Invalid start "' + start + '"');
- }
- if (isNaN(newEnd) || newEnd === null) {
- throw new Error('Invalid end "' + end + '"');
- }
-
- // prevent start < end
- if (newEnd < newStart) {
- newEnd = newStart;
- }
-
- // prevent start < min
- if (min !== null) {
- if (newStart < min) {
- diff = (min - newStart);
- newStart += diff;
- newEnd += diff;
-
- // prevent end > max
- if (max != null) {
- if (newEnd > max) {
- newEnd = max;
- }
- }
- }
- }
-
- // prevent end > max
- if (max !== null) {
- if (newEnd > max) {
- diff = (newEnd - max);
- newStart -= diff;
- newEnd -= diff;
-
- // prevent start < min
- if (min != null) {
- if (newStart < min) {
- newStart = min;
- }
- }
- }
- }
-
- // prevent (end-start) < zoomMin
- if (this.options.zoomMin !== null) {
- var zoomMin = parseFloat(this.options.zoomMin);
- if (zoomMin < 0) {
- zoomMin = 0;
- }
- if ((newEnd - newStart) < zoomMin) {
- if ((this.end - this.start) === zoomMin) {
- // ignore this action, we are already zoomed to the minimum
- newStart = this.start;
- newEnd = this.end;
- }
- else {
- // zoom to the minimum
- diff = (zoomMin - (newEnd - newStart));
- newStart -= diff / 2;
- newEnd += diff / 2;
- }
- }
- }
-
- // prevent (end-start) > zoomMax
- if (this.options.zoomMax !== null) {
- var zoomMax = parseFloat(this.options.zoomMax);
- if (zoomMax < 0) {
- zoomMax = 0;
- }
- if ((newEnd - newStart) > zoomMax) {
- if ((this.end - this.start) === zoomMax) {
- // ignore this action, we are already zoomed to the maximum
- newStart = this.start;
- newEnd = this.end;
- }
- else {
- // zoom to the maximum
- diff = ((newEnd - newStart) - zoomMax);
- newStart += diff / 2;
- newEnd -= diff / 2;
- }
- }
- }
-
- var changed = (this.start != newStart || this.end != newEnd);
-
- // if the new range does NOT overlap with the old range, emit checkRangedItems to avoid not showing ranged items (ranged meaning has end time, not neccesarily of type Range)
- if (!((newStart >= this.start && newStart <= this.end) || (newEnd >= this.start && newEnd <= this.end)) &&
- !((this.start >= newStart && this.start <= newEnd) || (this.end >= newStart && this.end <= newEnd) )) {
- this.body.emitter.emit('checkRangedItems');
- }
-
- this.start = newStart;
- this.end = newEnd;
- return changed;
- };
-
- /**
- * Retrieve the current range.
- * @return {Object} An object with start and end properties
- */
- Range.prototype.getRange = function() {
- return {
- start: this.start,
- end: this.end
- };
- };
-
- /**
- * Calculate the conversion offset and scale for current range, based on
- * the provided width
- * @param {Number} width
- * @returns {{offset: number, scale: number}} conversion
- */
- Range.prototype.conversion = function (width, totalHidden) {
- return Range.conversion(this.start, this.end, width, totalHidden);
- };
-
- /**
- * Static method to calculate the conversion offset and scale for a range,
- * based on the provided start, end, and width
- * @param {Number} start
- * @param {Number} end
- * @param {Number} width
- * @returns {{offset: number, scale: number}} conversion
- */
- Range.conversion = function (start, end, width, totalHidden) {
- if (totalHidden === undefined) {
- totalHidden = 0;
- }
- if (width != 0 && (end - start != 0)) {
- return {
- offset: start,
- scale: width / (end - start - totalHidden)
- }
- }
- else {
- return {
- offset: 0,
- scale: 1
- };
- }
- };
-
- /**
- * Start dragging horizontally or vertically
- * @param {Event} event
- * @private
- */
- Range.prototype._onDragStart = function(event) {
- this.deltaDifference = 0;
- this.previousDelta = 0;
- // only allow dragging when configured as movable
- if (!this.options.moveable) return;
-
- // refuse to drag when we where pinching to prevent the timeline make a jump
- // when releasing the fingers in opposite order from the touch screen
- if (!this.props.touch.allowDragging) return;
-
- this.props.touch.start = this.start;
- this.props.touch.end = this.end;
- this.props.touch.dragging = true;
-
- if (this.body.dom.root) {
- this.body.dom.root.style.cursor = 'move';
- }
- };
-
- /**
- * Perform dragging operation
- * @param {Event} event
- * @private
- */
- Range.prototype._onDrag = function (event) {
- // only allow dragging when configured as movable
- if (!this.options.moveable) return;
- // refuse to drag when we where pinching to prevent the timeline make a jump
- // when releasing the fingers in opposite order from the touch screen
- if (!this.props.touch.allowDragging) return;
-
- var direction = this.options.direction;
- validateDirection(direction);
-
- var delta = (direction == 'horizontal') ? event.gesture.deltaX : event.gesture.deltaY;
- delta -= this.deltaDifference;
- var interval = (this.props.touch.end - this.props.touch.start);
-
- // normalize dragging speed if cutout is in between.
- var duration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
- interval -= duration;
-
- var width = (direction == 'horizontal') ? this.body.domProps.center.width : this.body.domProps.center.height;
- var diffRange = -delta / width * interval;
- var newStart = this.props.touch.start + diffRange;
- var newEnd = this.props.touch.end + diffRange;
-
-
- // snapping times away from hidden zones
- var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, this.previousDelta-delta, true);
- var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, this.previousDelta-delta, true);
- if (safeStart != newStart || safeEnd != newEnd) {
- this.deltaDifference += delta;
- this.props.touch.start = safeStart;
- this.props.touch.end = safeEnd;
- this._onDrag(event);
- return;
- }
-
- this.previousDelta = delta;
- this._applyRange(newStart, newEnd);
-
- // fire a rangechange event
- this.body.emitter.emit('rangechange', {
- start: new Date(this.start),
- end: new Date(this.end)
- });
- };
-
- /**
- * Stop dragging operation
- * @param {event} event
- * @private
- */
- Range.prototype._onDragEnd = function (event) {
- // only allow dragging when configured as movable
- if (!this.options.moveable) return;
-
- // refuse to drag when we where pinching to prevent the timeline make a jump
- // when releasing the fingers in opposite order from the touch screen
- if (!this.props.touch.allowDragging) return;
-
- this.props.touch.dragging = false;
- if (this.body.dom.root) {
- this.body.dom.root.style.cursor = 'auto';
- }
-
- // fire a rangechanged event
- this.body.emitter.emit('rangechanged', {
- start: new Date(this.start),
- end: new Date(this.end)
- });
- };
-
- /**
- * Event handler for mouse wheel event, used to zoom
- * Code from http://adomas.org/javascript-mouse-wheel/
- * @param {Event} event
- * @private
- */
- Range.prototype._onMouseWheel = function(event) {
- // only allow zooming when configured as zoomable and moveable
- if (!(this.options.zoomable && this.options.moveable)) return;
-
- // retrieve delta
- var delta = 0;
- if (event.wheelDelta) { /* IE/Opera. */
- delta = event.wheelDelta / 120;
- } else if (event.detail) { /* Mozilla case. */
- // In Mozilla, sign of delta is different than in IE.
- // Also, delta is multiple of 3.
- delta = -event.detail / 3;
- }
-
- // If delta is nonzero, handle it.
- // Basically, delta is now positive if wheel was scrolled up,
- // and negative, if wheel was scrolled down.
- if (delta) {
- // perform the zoom action. Delta is normally 1 or -1
-
- // adjust a negative delta such that zooming in with delta 0.1
- // equals zooming out with a delta -0.1
- var scale;
- if (delta < 0) {
- scale = 1 - (delta / 5);
- }
- else {
- scale = 1 / (1 + (delta / 5)) ;
- }
-
- // calculate center, the date to zoom around
- var gesture = hammerUtil.fakeGesture(this, event),
- pointer = getPointer(gesture.center, this.body.dom.center),
- pointerDate = this._pointerToDate(pointer);
-
- this.zoom(scale, pointerDate, delta);
- }
-
- // Prevent default actions caused by mouse wheel
- // (else the page and timeline both zoom and scroll)
- event.preventDefault();
- };
-
- /**
- * Start of a touch gesture
- * @private
- */
- Range.prototype._onTouch = function (event) {
- this.props.touch.start = this.start;
- this.props.touch.end = this.end;
- this.props.touch.allowDragging = true;
- this.props.touch.center = null;
- this.scaleOffset = 0;
- this.deltaDifference = 0;
- };
-
- /**
- * On start of a hold gesture
- * @private
- */
- Range.prototype._onHold = function () {
- this.props.touch.allowDragging = false;
- };
-
- /**
- * Handle pinch event
- * @param {Event} event
- * @private
- */
- Range.prototype._onPinch = function (event) {
- // only allow zooming when configured as zoomable and moveable
- if (!(this.options.zoomable && this.options.moveable)) return;
-
- this.props.touch.allowDragging = false;
-
- if (event.gesture.touches.length > 1) {
- if (!this.props.touch.center) {
- this.props.touch.center = getPointer(event.gesture.center, this.body.dom.center);
- }
-
- var scale = 1 / (event.gesture.scale + this.scaleOffset);
- var centerDate = this._pointerToDate(this.props.touch.center);
-
- var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
- var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, centerDate);
- var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore;
-
- // calculate new start and end
- var newStart = (centerDate - hiddenDurationBefore) + (this.props.touch.start - (centerDate - hiddenDurationBefore)) * scale;
- var newEnd = (centerDate + hiddenDurationAfter) + (this.props.touch.end - (centerDate + hiddenDurationAfter)) * scale;
-
- // snapping times away from hidden zones
- this.startToFront = 1 - scale > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
- this.endToFront = scale - 1 > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
-
- var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, 1 - scale, true);
- var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, scale - 1, true);
- if (safeStart != newStart || safeEnd != newEnd) {
- this.props.touch.start = safeStart;
- this.props.touch.end = safeEnd;
- this.scaleOffset = 1 - event.gesture.scale;
- newStart = safeStart;
- newEnd = safeEnd;
- }
-
- this.setRange(newStart, newEnd);
-
- this.startToFront = false; // revert to default
- this.endToFront = true; // revert to default
- }
- };
-
- /**
- * Helper function to calculate the center date for zooming
- * @param {{x: Number, y: Number}} pointer
- * @return {number} date
- * @private
- */
- Range.prototype._pointerToDate = function (pointer) {
- var conversion;
- var direction = this.options.direction;
-
- validateDirection(direction);
-
- if (direction == 'horizontal') {
- return this.body.util.toTime(pointer.x).valueOf();
- }
- else {
- var height = this.body.domProps.center.height;
- conversion = this.conversion(height);
- return pointer.y / conversion.scale + conversion.offset;
- }
- };
-
- /**
- * Get the pointer location relative to the location of the dom element
- * @param {{pageX: Number, pageY: Number}} touch
- * @param {Element} element HTML DOM element
- * @return {{x: Number, y: Number}} pointer
- * @private
- */
- function getPointer (touch, element) {
- return {
- x: touch.pageX - util.getAbsoluteLeft(element),
- y: touch.pageY - util.getAbsoluteTop(element)
- };
- }
-
- /**
- * Zoom the range the given scale in or out. Start and end date will
- * be adjusted, and the timeline will be redrawn. You can optionally give a
- * date around which to zoom.
- * For example, try scale = 0.9 or 1.1
- * @param {Number} scale Scaling factor. Values above 1 will zoom out,
- * values below 1 will zoom in.
- * @param {Number} [center] Value representing a date around which will
- * be zoomed.
- */
- Range.prototype.zoom = function(scale, center, delta) {
- // if centerDate is not provided, take it half between start Date and end Date
- if (center == null) {
- center = (this.start + this.end) / 2;
- }
-
- var hiddenDuration = DateUtil.getHiddenDurationBetween(this.body.hiddenDates, this.start, this.end);
- var hiddenDurationBefore = DateUtil.getHiddenDurationBefore(this.body.hiddenDates, this, center);
- var hiddenDurationAfter = hiddenDuration - hiddenDurationBefore;
-
- // calculate new start and end
- var newStart = (center-hiddenDurationBefore) + (this.start - (center-hiddenDurationBefore)) * scale;
- var newEnd = (center+hiddenDurationAfter) + (this.end - (center+hiddenDurationAfter)) * scale;
-
- // snapping times away from hidden zones
- this.startToFront = delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
- this.endToFront = -delta > 0 ? false : true; // used to do the right autocorrection with periodic hidden times
- var safeStart = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newStart, delta, true);
- var safeEnd = DateUtil.snapAwayFromHidden(this.body.hiddenDates, newEnd, -delta, true);
- if (safeStart != newStart || safeEnd != newEnd) {
- newStart = safeStart;
- newEnd = safeEnd;
- }
-
- this.setRange(newStart, newEnd);
-
- this.startToFront = false; // revert to default
- this.endToFront = true; // revert to default
- };
-
-
-
- /**
- * Move the range with a given delta to the left or right. Start and end
- * value will be adjusted. For example, try delta = 0.1 or -0.1
- * @param {Number} delta Moving amount. Positive value will move right,
- * negative value will move left
- */
- Range.prototype.move = function(delta) {
- // zoom start Date and end Date relative to the centerDate
- var diff = (this.end - this.start);
-
- // apply new values
- var newStart = this.start + diff * delta;
- var newEnd = this.end + diff * delta;
-
- // TODO: reckon with min and max range
-
- this.start = newStart;
- this.end = newEnd;
- };
-
- /**
- * Move the range to a new center point
- * @param {Number} moveTo New center point of the range
- */
- Range.prototype.moveTo = function(moveTo) {
- var center = (this.start + this.end) / 2;
-
- var diff = center - moveTo;
-
- // calculate new start and end
- var newStart = this.start - diff;
- var newEnd = this.end - diff;
-
- this.setRange(newStart, newEnd);
- };
-
- module.exports = Range;
-
-
-/***/ },
-/* 22 */
-/***/ function(module, exports, __webpack_require__) {
-
- var Hammer = __webpack_require__(19);
-
- /**
- * Fake a hammer.js gesture. Event can be a ScrollEvent or MouseMoveEvent
- * @param {Element} element
- * @param {Event} event
- */
- exports.fakeGesture = function(element, event) {
- var eventType = null;
-
- // for hammer.js 1.0.5
- // var gesture = Hammer.event.collectEventData(this, eventType, event);
-
- // for hammer.js 1.0.6+
- var touches = Hammer.event.getTouchList(event, eventType);
- var gesture = Hammer.event.collectEventData(this, eventType, touches, event);
-
- // on IE in standards mode, no touches are recognized by hammer.js,
- // resulting in NaN values for center.pageX and center.pageY
- if (isNaN(gesture.center.pageX)) {
- gesture.center.pageX = event.pageX;
- }
- if (isNaN(gesture.center.pageY)) {
- gesture.center.pageY = event.pageY;
- }
-
- return gesture;
- };
-
-
-/***/ },
-/* 23 */
-/***/ function(module, exports, __webpack_require__) {
-
- /**
- * Prototype for visual components
- * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} [body]
- * @param {Object} [options]
- */
- function Component (body, options) {
- this.options = null;
- this.props = null;
- }
-
- /**
- * Set options for the component. The new options will be merged into the
- * current options.
- * @param {Object} options
- */
- Component.prototype.setOptions = function(options) {
- if (options) {
- util.extend(this.options, options);
- }
- };
-
- /**
- * Repaint the component
- * @return {boolean} Returns true if the component is resized
- */
- Component.prototype.redraw = function() {
- // should be implemented by the component
- return false;
- };
-
- /**
- * Destroy the component. Cleanup DOM and event listeners
- */
- Component.prototype.destroy = function() {
- // should be implemented by the component
- };
-
- /**
- * Test whether the component is resized since the last time _isResized() was
- * called.
- * @return {Boolean} Returns true if the component is resized
- * @protected
- */
- Component.prototype._isResized = function() {
- var resized = (this.props._previousWidth !== this.props.width ||
- this.props._previousHeight !== this.props.height);
-
- this.props._previousWidth = this.props.width;
- this.props._previousHeight = this.props.height;
-
- return resized;
- };
-
- module.exports = Component;
-
-
-/***/ },
-/* 24 */
-/***/ function(module, exports, __webpack_require__) {
-
- /**
- * Created by Alex on 10/3/2014.
- */
- var moment = __webpack_require__(2);
-
-
- /**
- * used in Core to convert the options into a volatile variable
- *
- * @param Core
- */
- exports.convertHiddenOptions = function(body, hiddenDates) {
- body.hiddenDates = [];
- if (hiddenDates) {
- if (Array.isArray(hiddenDates) == true) {
- for (var i = 0; i < hiddenDates.length; i++) {
- if (hiddenDates[i].repeat === undefined) {
- var dateItem = {};
- dateItem.start = moment(hiddenDates[i].start).toDate().valueOf();
- dateItem.end = moment(hiddenDates[i].end).toDate().valueOf();
- body.hiddenDates.push(dateItem);
- }
- }
- body.hiddenDates.sort(function (a, b) {
- return a.start - b.start;
- }); // sort by start time
- }
- }
- };
-
-
- /**
- * create new entrees for the repeating hidden dates
- * @param body
- * @param hiddenDates
- */
- exports.updateHiddenDates = function (body, hiddenDates) {
- if (hiddenDates && body.domProps.centerContainer.width !== undefined) {
- exports.convertHiddenOptions(body, hiddenDates);
-
- var start = moment(body.range.start);
- var end = moment(body.range.end);
-
- var totalRange = (body.range.end - body.range.start);
- var pixelTime = totalRange / body.domProps.centerContainer.width;
-
- for (var i = 0; i < hiddenDates.length; i++) {
- if (hiddenDates[i].repeat !== undefined) {
- var startDate = moment(hiddenDates[i].start);
- var endDate = moment(hiddenDates[i].end);
-
- if (startDate._d == "Invalid Date") {
- throw new Error("Supplied start date is not valid: " + hiddenDates[i].start);
- }
- if (endDate._d == "Invalid Date") {
- throw new Error("Supplied end date is not valid: " + hiddenDates[i].end);
- }
-
- var duration = endDate - startDate;
- if (duration >= 4 * pixelTime) {
-
- var offset = 0;
- var runUntil = end.clone();
- switch (hiddenDates[i].repeat) {
- case "daily": // case of time
- if (startDate.day() != endDate.day()) {
- offset = 1;
- }
- startDate.dayOfYear(start.dayOfYear());
- startDate.year(start.year());
- startDate.subtract(7,'days');
-
- endDate.dayOfYear(start.dayOfYear());
- endDate.year(start.year());
- endDate.subtract(7 - offset,'days');
-
- runUntil.add(1, 'weeks');
- break;
- case "weekly":
- var dayOffset = endDate.diff(startDate,'days')
- var day = startDate.day();
-
- // set the start date to the range.start
- startDate.date(start.date());
- startDate.month(start.month());
- startDate.year(start.year());
- endDate = startDate.clone();
-
- // force
- startDate.day(day);
- endDate.day(day);
- endDate.add(dayOffset,'days');
-
- startDate.subtract(1,'weeks');
- endDate.subtract(1,'weeks');
-
- runUntil.add(1, 'weeks');
- break
- case "monthly":
- if (startDate.month() != endDate.month()) {
- offset = 1;
- }
- startDate.month(start.month());
- startDate.year(start.year());
- startDate.subtract(1,'months');
-
- endDate.month(start.month());
- endDate.year(start.year());
- endDate.subtract(1,'months');
- endDate.add(offset,'months');
-
- runUntil.add(1, 'months');
- break;
- case "yearly":
- if (startDate.year() != endDate.year()) {
- offset = 1;
- }
- startDate.year(start.year());
- startDate.subtract(1,'years');
- endDate.year(start.year());
- endDate.subtract(1,'years');
- endDate.add(offset,'years');
-
- runUntil.add(1, 'years');
- break;
- default:
- console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
- return;
- }
- while (startDate < runUntil) {
- body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()});
- switch (hiddenDates[i].repeat) {
- case "daily":
- startDate.add(1, 'days');
- endDate.add(1, 'days');
- break;
- case "weekly":
- startDate.add(1, 'weeks');
- endDate.add(1, 'weeks');
- break
- case "monthly":
- startDate.add(1, 'months');
- endDate.add(1, 'months');
- break;
- case "yearly":
- startDate.add(1, 'y');
- endDate.add(1, 'y');
- break;
- default:
- console.log("Wrong repeat format, allowed are: daily, weekly, monthly, yearly. Given:", hiddenDates[i].repeat);
- return;
- }
- }
- body.hiddenDates.push({start: startDate.valueOf(), end: endDate.valueOf()});
- }
- }
- }
- // remove duplicates, merge where possible
- exports.removeDuplicates(body);
- // ensure the new positions are not on hidden dates
- var startHidden = exports.isHidden(body.range.start, body.hiddenDates);
- var endHidden = exports.isHidden(body.range.end,body.hiddenDates);
- var rangeStart = body.range.start;
- var rangeEnd = body.range.end;
- if (startHidden.hidden == true) {rangeStart = body.range.startToFront == true ? startHidden.startDate - 1 : startHidden.endDate + 1;}
- if (endHidden.hidden == true) {rangeEnd = body.range.endToFront == true ? endHidden.startDate - 1 : endHidden.endDate + 1;}
- if (startHidden.hidden == true || endHidden.hidden == true) {
- body.range._applyRange(rangeStart, rangeEnd);
- }
- }
-
- }
-
-
- /**
- * remove duplicates from the hidden dates list. Duplicates are evil. They mess everything up.
- * Scales with N^2
- * @param body
- */
- exports.removeDuplicates = function(body) {
- var hiddenDates = body.hiddenDates;
- var safeDates = [];
- for (var i = 0; i < hiddenDates.length; i++) {
- for (var j = 0; j < hiddenDates.length; j++) {
- if (i != j && hiddenDates[j].remove != true && hiddenDates[i].remove != true) {
- // j inside i
- if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
- hiddenDates[j].remove = true;
- }
- // j start inside i
- else if (hiddenDates[j].start >= hiddenDates[i].start && hiddenDates[j].start <= hiddenDates[i].end) {
- hiddenDates[i].end = hiddenDates[j].end;
- hiddenDates[j].remove = true;
- }
- // j end inside i
- else if (hiddenDates[j].end >= hiddenDates[i].start && hiddenDates[j].end <= hiddenDates[i].end) {
- hiddenDates[i].start = hiddenDates[j].start;
- hiddenDates[j].remove = true;
- }
- }
- }
- }
-
- for (var i = 0; i < hiddenDates.length; i++) {
- if (hiddenDates[i].remove !== true) {
- safeDates.push(hiddenDates[i]);
- }
- }
-
- body.hiddenDates = safeDates;
- body.hiddenDates.sort(function (a, b) {
- return a.start - b.start;
- }); // sort by start time
- }
-
- exports.printDates = function(dates) {
- for (var i =0; i < dates.length; i++) {
- console.log(i, new Date(dates[i].start),new Date(dates[i].end), dates[i].start, dates[i].end, dates[i].remove);
- }
- }
-
- /**
- * Used in TimeStep to avoid the hidden times.
- * @param timeStep
- * @param previousTime
- */
- exports.stepOverHiddenDates = function(timeStep, previousTime) {
- var stepInHidden = false;
- var currentValue = timeStep.current.valueOf();
- for (var i = 0; i < timeStep.hiddenDates.length; i++) {
- var startDate = timeStep.hiddenDates[i].start;
- var endDate = timeStep.hiddenDates[i].end;
- if (currentValue >= startDate && currentValue < endDate) {
- stepInHidden = true;
- break;
- }
- }
-
- if (stepInHidden == true && currentValue < timeStep._end.valueOf() && currentValue != previousTime) {
- var prevValue = moment(previousTime);
- var newValue = moment(endDate);
- //check if the next step should be major
- if (prevValue.year() != newValue.year()) {timeStep.switchedYear = true;}
- else if (prevValue.month() != newValue.month()) {timeStep.switchedMonth = true;}
- else if (prevValue.dayOfYear() != newValue.dayOfYear()) {timeStep.switchedDay = true;}
-
- timeStep.current = newValue.toDate();
- }
- };
-
-
- ///**
- // * Used in TimeStep to avoid the hidden times.
- // * @param timeStep
- // * @param previousTime
- // */
- //exports.checkFirstStep = function(timeStep) {
- // var stepInHidden = false;
- // var currentValue = timeStep.current.valueOf();
- // for (var i = 0; i < timeStep.hiddenDates.length; i++) {
- // var startDate = timeStep.hiddenDates[i].start;
- // var endDate = timeStep.hiddenDates[i].end;
- // if (currentValue >= startDate && currentValue < endDate) {
- // stepInHidden = true;
- // break;
- // }
- // }
- //
- // if (stepInHidden == true && currentValue <= timeStep._end.valueOf()) {
- // var newValue = moment(endDate);
- // timeStep.current = newValue.toDate();
- // }
- //};
-
- /**
- * replaces the Core toScreen methods
- * @param Core
- * @param time
- * @param width
- * @returns {number}
- */
- exports.toScreen = function(Core, time, width) {
- if (Core.body.hiddenDates.length == 0) {
- var conversion = Core.range.conversion(width);
- return (time.valueOf() - conversion.offset) * conversion.scale;
- }
- else {
- var hidden = exports.isHidden(time, Core.body.hiddenDates)
- if (hidden.hidden == true) {
- time = hidden.startDate;
- }
-
- var duration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
- time = exports.correctTimeForHidden(Core.body.hiddenDates, Core.range, time);
-
- var conversion = Core.range.conversion(width, duration);
- return (time.valueOf() - conversion.offset) * conversion.scale;
- }
- };
-
-
- /**
- * Replaces the core toTime methods
- * @param body
- * @param range
- * @param x
- * @param width
- * @returns {Date}
- */
- exports.toTime = function(Core, x, width) {
- if (Core.body.hiddenDates.length == 0) {
- var conversion = Core.range.conversion(width);
- return new Date(x / conversion.scale + conversion.offset);
- }
- else {
- var hiddenDuration = exports.getHiddenDurationBetween(Core.body.hiddenDates, Core.range.start, Core.range.end);
- var totalDuration = Core.range.end - Core.range.start - hiddenDuration;
- var partialDuration = totalDuration * x / width;
- var accumulatedHiddenDuration = exports.getAccumulatedHiddenDuration(Core.body.hiddenDates, Core.range, partialDuration);
-
- var newTime = new Date(accumulatedHiddenDuration + partialDuration + Core.range.start);
- return newTime;
- }
- };
-
-
- /**
- * Support function
- *
- * @param hiddenDates
- * @param range
- * @returns {number}
- */
- exports.getHiddenDurationBetween = function(hiddenDates, start, end) {
- var duration = 0;
- for (var i = 0; i < hiddenDates.length; i++) {
- var startDate = hiddenDates[i].start;
- var endDate = hiddenDates[i].end;
- // if time after the cutout, and the
- if (startDate >= start && endDate < end) {
- duration += endDate - startDate;
- }
- }
- return duration;
- };
-
-
- /**
- * Support function
- * @param hiddenDates
- * @param range
- * @param time
- * @returns {{duration: number, time: *, offset: number}}
- */
- exports.correctTimeForHidden = function(hiddenDates, range, time) {
- time = moment(time).toDate().valueOf();
- time -= exports.getHiddenDurationBefore(hiddenDates,range,time);
- return time;
- };
-
- exports.getHiddenDurationBefore = function(hiddenDates, range, time) {
- var timeOffset = 0;
- time = moment(time).toDate().valueOf();
-
- for (var i = 0; i < hiddenDates.length; i++) {
- var startDate = hiddenDates[i].start;
- var endDate = hiddenDates[i].end;
- // if time after the cutout, and the
- if (startDate >= range.start && endDate < range.end) {
- if (time >= endDate) {
- timeOffset += (endDate - startDate);
- }
- }
- }
- return timeOffset;
- }
-
- /**
- * sum the duration from start to finish, including the hidden duration,
- * until the required amount has been reached, return the accumulated hidden duration
- * @param hiddenDates
- * @param range
- * @param time
- * @returns {{duration: number, time: *, offset: number}}
- */
- exports.getAccumulatedHiddenDuration = function(hiddenDates, range, requiredDuration) {
- var hiddenDuration = 0;
- var duration = 0;
- var previousPoint = range.start;
- //exports.printDates(hiddenDates)
- for (var i = 0; i < hiddenDates.length; i++) {
- var startDate = hiddenDates[i].start;
- var endDate = hiddenDates[i].end;
- // if time after the cutout, and the
- if (startDate >= range.start && endDate < range.end) {
- duration += startDate - previousPoint;
- previousPoint = endDate;
- if (duration >= requiredDuration) {
- break;
- }
- else {
- hiddenDuration += endDate - startDate;
- }
- }
- }
-
- return hiddenDuration;
- };
-
-
-
- /**
- * used to step over to either side of a hidden block. Correction is disabled on tablets, might be set to true
- * @param hiddenDates
- * @param time
- * @param direction
- * @param correctionEnabled
- * @returns {*}
- */
- exports.snapAwayFromHidden = function(hiddenDates, time, direction, correctionEnabled) {
- var isHidden = exports.isHidden(time, hiddenDates);
- if (isHidden.hidden == true) {
- if (direction < 0) {
- if (correctionEnabled == true) {
- return isHidden.startDate - (isHidden.endDate - time) - 1;
- }
- else {
- return isHidden.startDate - 1;
- }
- }
- else {
- if (correctionEnabled == true) {
- return isHidden.endDate + (time - isHidden.startDate) + 1;
- }
- else {
- return isHidden.endDate + 1;
- }
- }
- }
- else {
- return time;
- }
-
- }
-
-
- /**
- * Check if a time is hidden
- *
- * @param time
- * @param hiddenDates
- * @returns {{hidden: boolean, startDate: Window.start, endDate: *}}
- */
- exports.isHidden = function(time, hiddenDates) {
- for (var i = 0; i < hiddenDates.length; i++) {
- var startDate = hiddenDates[i].start;
- var endDate = hiddenDates[i].end;
-
- if (time >= startDate && time < endDate) { // if the start is entering a hidden zone
- return {hidden: true, startDate: startDate, endDate: endDate};
- break;
- }
- }
- return {hidden: false, startDate: startDate, endDate: endDate};
- }
-
-/***/ },
-/* 25 */
-/***/ function(module, exports, __webpack_require__) {
-
- var Emitter = __webpack_require__(11);
- var Hammer = __webpack_require__(19);
- var util = __webpack_require__(1);
- var DataSet = __webpack_require__(7);
- var DataView = __webpack_require__(9);
- var Range = __webpack_require__(21);
- var ItemSet = __webpack_require__(26);
- var Activator = __webpack_require__(35);
- var DateUtil = __webpack_require__(24);
-
- /**
- * Create a timeline visualization
- * @param {HTMLElement} container
- * @param {vis.DataSet | Array | google.visualization.DataTable} [items]
- * @param {Object} [options] See Core.setOptions for the available options.
- * @constructor
- */
- function Core () {}
-
- // turn Core into an event emitter
- Emitter(Core.prototype);
-
- /**
- * Create the main DOM for the Core: a root panel containing left, right,
- * top, bottom, content, and background panel.
- * @param {Element} container The container element where the Core will
- * be attached.
- * @private
- */
- Core.prototype._create = function (container) {
- this.dom = {};
-
- this.dom.root = document.createElement('div');
- this.dom.background = document.createElement('div');
- this.dom.backgroundVertical = document.createElement('div');
- this.dom.backgroundHorizontal = document.createElement('div');
- this.dom.centerContainer = document.createElement('div');
- this.dom.leftContainer = document.createElement('div');
- this.dom.rightContainer = document.createElement('div');
- this.dom.center = document.createElement('div');
- this.dom.left = document.createElement('div');
- this.dom.right = document.createElement('div');
- this.dom.top = document.createElement('div');
- this.dom.bottom = document.createElement('div');
- this.dom.shadowTop = document.createElement('div');
- this.dom.shadowBottom = document.createElement('div');
- this.dom.shadowTopLeft = document.createElement('div');
- this.dom.shadowBottomLeft = document.createElement('div');
- this.dom.shadowTopRight = document.createElement('div');
- this.dom.shadowBottomRight = document.createElement('div');
-
- this.dom.root.className = 'vis timeline root';
- this.dom.background.className = 'vispanel background';
- this.dom.backgroundVertical.className = 'vispanel background vertical';
- this.dom.backgroundHorizontal.className = 'vispanel background horizontal';
- this.dom.centerContainer.className = 'vispanel center';
- this.dom.leftContainer.className = 'vispanel left';
- this.dom.rightContainer.className = 'vispanel right';
- this.dom.top.className = 'vispanel top';
- this.dom.bottom.className = 'vispanel bottom';
- this.dom.left.className = 'content';
- this.dom.center.className = 'content';
- this.dom.right.className = 'content';
- this.dom.shadowTop.className = 'shadow top';
- this.dom.shadowBottom.className = 'shadow bottom';
- this.dom.shadowTopLeft.className = 'shadow top';
- this.dom.shadowBottomLeft.className = 'shadow bottom';
- this.dom.shadowTopRight.className = 'shadow top';
- this.dom.shadowBottomRight.className = 'shadow bottom';
-
- this.dom.root.appendChild(this.dom.background);
- this.dom.root.appendChild(this.dom.backgroundVertical);
- this.dom.root.appendChild(this.dom.backgroundHorizontal);
- this.dom.root.appendChild(this.dom.centerContainer);
- this.dom.root.appendChild(this.dom.leftContainer);
- this.dom.root.appendChild(this.dom.rightContainer);
- this.dom.root.appendChild(this.dom.top);
- this.dom.root.appendChild(this.dom.bottom);
-
- this.dom.centerContainer.appendChild(this.dom.center);
- this.dom.leftContainer.appendChild(this.dom.left);
- this.dom.rightContainer.appendChild(this.dom.right);
-
- this.dom.centerContainer.appendChild(this.dom.shadowTop);
- this.dom.centerContainer.appendChild(this.dom.shadowBottom);
- this.dom.leftContainer.appendChild(this.dom.shadowTopLeft);
- this.dom.leftContainer.appendChild(this.dom.shadowBottomLeft);
- this.dom.rightContainer.appendChild(this.dom.shadowTopRight);
- this.dom.rightContainer.appendChild(this.dom.shadowBottomRight);
-
- this.on('rangechange', this.redraw.bind(this));
- this.on('touch', this._onTouch.bind(this));
- this.on('pinch', this._onPinch.bind(this));
- this.on('dragstart', this._onDragStart.bind(this));
- this.on('drag', this._onDrag.bind(this));
-
- var me = this;
- this.on('change', function (properties) {
- if (properties && properties.queue == true) {
- // redraw once on next tick
- if (!me._redrawTimer) {
- me._redrawTimer = setTimeout(function () {
- me._redrawTimer = null;
- me.redraw();
- }, 0)
- }
- }
- else {
- // redraw immediately
- me.redraw();
- }
- });
-
- // create event listeners for all interesting events, these events will be
- // emitted via emitter
- this.hammer = Hammer(this.dom.root, {
- preventDefault: true
- });
- this.listeners = {};
-
- var events = [
- 'touch', 'pinch',
- 'tap', 'doubletap', 'hold',
- 'dragstart', 'drag', 'dragend',
- 'mousewheel', 'DOMMouseScroll' // DOMMouseScroll is needed for Firefox
- ];
- events.forEach(function (event) {
- var listener = function () {
- var args = [event].concat(Array.prototype.slice.call(arguments, 0));
- if (me.isActive()) {
- me.emit.apply(me, args);
- }
- };
- me.hammer.on(event, listener);
- me.listeners[event] = listener;
- });
-
- // size properties of each of the panels
- this.props = {
- root: {},
- background: {},
- centerContainer: {},
- leftContainer: {},
- rightContainer: {},
- center: {},
- left: {},
- right: {},
- top: {},
- bottom: {},
- border: {},
- scrollTop: 0,
- scrollTopMin: 0
- };
- this.touch = {}; // store state information needed for touch events
-
- this.redrawCount = 0;
-
- // attach the root panel to the provided container
- if (!container) throw new Error('No container provided');
- container.appendChild(this.dom.root);
- };
-
- /**
- * Set options. Options will be passed to all components loaded in the Timeline.
- * @param {Object} [options]
- * {String} orientation
- * Vertical orientation for the Timeline,
- * can be 'bottom' (default) or 'top'.
- * {String | Number} width
- * Width for the timeline, a number in pixels or
- * a css string like '1000px' or '75%'. '100%' by default.
- * {String | Number} height
- * Fixed height for the Timeline, a number in pixels or
- * a css string like '400px' or '75%'. If undefined,
- * The Timeline will automatically size such that
- * its contents fit.
- * {String | Number} minHeight
- * Minimum height for the Timeline, a number in pixels or
- * a css string like '400px' or '75%'.
- * {String | Number} maxHeight
- * Maximum height for the Timeline, a number in pixels or
- * a css string like '400px' or '75%'.
- * {Number | Date | String} start
- * Start date for the visible window
- * {Number | Date | String} end
- * End date for the visible window
- */
- Core.prototype.setOptions = function (options) {
- if (options) {
- // copy the known options
- var fields = ['width', 'height', 'minHeight', 'maxHeight', 'autoResize', 'start', 'end', 'orientation', 'clickToUse', 'dataAttributes', 'hiddenDates'];
- util.selectiveExtend(fields, this.options, options);
-
- if ('hiddenDates' in this.options) {
- DateUtil.convertHiddenOptions(this.body, this.options.hiddenDates);
- }
-
- if ('clickToUse' in options) {
- if (options.clickToUse) {
- this.activator = new Activator(this.dom.root);
- }
- else {
- if (this.activator) {
- this.activator.destroy();
- delete this.activator;
- }
- }
- }
-
- // enable/disable autoResize
- this._initAutoResize();
- }
-
- // propagate options to all components
- this.components.forEach(function (component) {
- component.setOptions(options);
- });
-
- // TODO: remove deprecation error one day (deprecated since version 0.8.0)
- if (options && options.order) {
- throw new Error('Option order is deprecated. There is no replacement for this feature.');
- }
-
- // redraw everything
- this.redraw();
- };
-
- /**
- * Returns true when the Timeline is active.
- * @returns {boolean}
- */
- Core.prototype.isActive = function () {
- return !this.activator || this.activator.active;
- };
-
- /**
- * Destroy the Core, clean up all DOM elements and event listeners.
- */
- Core.prototype.destroy = function () {
- // unbind datasets
- this.clear();
-
- // remove all event listeners
- this.off();
-
- // stop checking for changed size
- this._stopAutoResize();
-
- // remove from DOM
- if (this.dom.root.parentNode) {
- this.dom.root.parentNode.removeChild(this.dom.root);
- }
- this.dom = null;
-
- // remove Activator
- if (this.activator) {
- this.activator.destroy();
- delete this.activator;
- }
-
- // cleanup hammer touch events
- for (var event in this.listeners) {
- if (this.listeners.hasOwnProperty(event)) {
- delete this.listeners[event];
- }
- }
- this.listeners = null;
- this.hammer = null;
-
- // give all components the opportunity to cleanup
- this.components.forEach(function (component) {
- component.destroy();
- });
-
- this.body = null;
- };
-
-
- /**
- * Set a custom time bar
- * @param {Date} time
- */
- Core.prototype.setCustomTime = function (time) {
- if (!this.customTime) {
- throw new Error('Cannot get custom time: Custom time bar is not enabled');
- }
-
- this.customTime.setCustomTime(time);
- };
-
- /**
- * Retrieve the current custom time.
- * @return {Date} customTime
- */
- Core.prototype.getCustomTime = function() {
- if (!this.customTime) {
- throw new Error('Cannot get custom time: Custom time bar is not enabled');
- }
-
- return this.customTime.getCustomTime();
- };
-
-
- /**
- * Get the id's of the currently visible items.
- * @returns {Array} The ids of the visible items
- */
- Core.prototype.getVisibleItems = function() {
- return this.itemSet && this.itemSet.getVisibleItems() || [];
- };
-
-
-
- /**
- * Clear the Core. By Default, items, groups and options are cleared.
- * Example usage:
- *
- * timeline.clear(); // clear items, groups, and options
- * timeline.clear({options: true}); // clear options only
- *
- * @param {Object} [what] Optionally specify what to clear. By default:
- * {items: true, groups: true, options: true}
- */
- Core.prototype.clear = function(what) {
- // clear items
- if (!what || what.items) {
- this.setItems(null);
- }
-
- // clear groups
- if (!what || what.groups) {
- this.setGroups(null);
- }
-
- // clear options of timeline and of each of the components
- if (!what || what.options) {
- this.components.forEach(function (component) {
- component.setOptions(component.defaultOptions);
- });
-
- this.setOptions(this.defaultOptions); // this will also do a redraw
- }
- };
-
- /**
- * Set Core window such that it fits all items
- * @param {Object} [options] Available options:
- * `animate: boolean | number`
- * If true (default), the range is animated
- * smoothly to the new window.
- * If a number, the number is taken as duration
- * for the animation. Default duration is 500 ms.
- */
- Core.prototype.fit = function(options) {
- var range = this._getDataRange();
-
- // skip range set if there is no start and end date
- if (range.start === null && range.end === null) {
- return;
- }
-
- var animate = (options && options.animate !== undefined) ? options.animate : true;
- this.range.setRange(range.start, range.end, animate);
- };
-
- /**
- * Calculate the data range of the items and applies a 5% window around it.
- * @returns {{start: Date | null, end: Date | null}}
- * @protected
- */
- Core.prototype._getDataRange = function() {
- // apply the data range as range
- var dataRange = this.getItemRange();
-
- // add 5% space on both sides
- var start = dataRange.min;
- var end = dataRange.max;
- if (start != null && end != null) {
- var interval = (end.valueOf() - start.valueOf());
- if (interval <= 0) {
- // prevent an empty interval
- interval = 24 * 60 * 60 * 1000; // 1 day
- }
- start = new Date(start.valueOf() - interval * 0.05);
- end = new Date(end.valueOf() + interval * 0.05);
- }
-
- return {
- start: start,
- end: end
- }
- };
-
- /**
- * Set the visible window. Both parameters are optional, you can change only
- * start or only end. Syntax:
- *
- * TimeLine.setWindow(start, end)
- * TimeLine.setWindow(range)
- *
- * Where start and end can be a Date, number, or string, and range is an
- * object with properties start and end.
- *
- * @param {Date | Number | String | Object} [start] Start date of visible window
- * @param {Date | Number | String} [end] End date of visible window
- * @param {Object} [options] Available options:
- * `animate: boolean | number`
- * If true (default), the range is animated
- * smoothly to the new window.
- * If a number, the number is taken as duration
- * for the animation. Default duration is 500 ms.
- */
- Core.prototype.setWindow = function(start, end, options) {
- var animate = (options && options.animate !== undefined) ? options.animate : true;
- if (arguments.length == 1) {
- var range = arguments[0];
- this.range.setRange(range.start, range.end, animate);
- }
- else {
- this.range.setRange(start, end, animate);
- }
- };
-
- /**
- * Move the window such that given time is centered on screen.
- * @param {Date | Number | String} time
- * @param {Object} [options] Available options:
- * `animate: boolean | number`
- * If true (default), the range is animated
- * smoothly to the new window.
- * If a number, the number is taken as duration
- * for the animation. Default duration is 500 ms.
- */
- Core.prototype.moveTo = function(time, options) {
- var interval = this.range.end - this.range.start;
- var t = util.convert(time, 'Date').valueOf();
-
- var start = t - interval / 2;
- var end = t + interval / 2;
- var animate = (options && options.animate !== undefined) ? options.animate : true;
-
- this.range.setRange(start, end, animate);
- };
-
- /**
- * Get the visible window
- * @return {{start: Date, end: Date}} Visible range
- */
- Core.prototype.getWindow = function() {
- var range = this.range.getRange();
- return {
- start: new Date(range.start),
- end: new Date(range.end)
- };
- };
-
- /**
- * Force a redraw of the Core. Can be useful to manually redraw when
- * option autoResize=false
- */
- Core.prototype.redraw = function() {
- var resized = false;
- var options = this.options;
- var props = this.props;
- var dom = this.dom;
-
- if (!dom) return; // when destroyed
-
- DateUtil.updateHiddenDates(this.body, this.options.hiddenDates);
-
- // update class names
- if (options.orientation == 'top') {
- util.addClassName(dom.root, 'top');
- util.removeClassName(dom.root, 'bottom');
- }
- else {
- util.removeClassName(dom.root, 'top');
- util.addClassName(dom.root, 'bottom');
- }
-
- // update root width and height options
- dom.root.style.maxHeight = util.option.asSize(options.maxHeight, '');
- dom.root.style.minHeight = util.option.asSize(options.minHeight, '');
- dom.root.style.width = util.option.asSize(options.width, '');
-
- // calculate border widths
- props.border.left = (dom.centerContainer.offsetWidth - dom.centerContainer.clientWidth) / 2;
- props.border.right = props.border.left;
- props.border.top = (dom.centerContainer.offsetHeight - dom.centerContainer.clientHeight) / 2;
- props.border.bottom = props.border.top;
- var borderRootHeight= dom.root.offsetHeight - dom.root.clientHeight;
- var borderRootWidth = dom.root.offsetWidth - dom.root.clientWidth;
-
- // workaround for a bug in IE: the clientWidth of an element with
- // a height:0px and overflow:hidden is not calculated and always has value 0
- if (dom.centerContainer.clientHeight === 0) {
- props.border.left = props.border.top;
- props.border.right = props.border.left;
- }
- if (dom.root.clientHeight === 0) {
- borderRootWidth = borderRootHeight;
- }
-
- // calculate the heights. If any of the side panels is empty, we set the height to
- // minus the border width, such that the border will be invisible
- props.center.height = dom.center.offsetHeight;
- props.left.height = dom.left.offsetHeight;
- props.right.height = dom.right.offsetHeight;
- props.top.height = dom.top.clientHeight || -props.border.top;
- props.bottom.height = dom.bottom.clientHeight || -props.border.bottom;
-
- // TODO: compensate borders when any of the panels is empty.
-
- // apply auto height
- // TODO: only calculate autoHeight when needed (else we cause an extra reflow/repaint of the DOM)
- var contentHeight = Math.max(props.left.height, props.center.height, props.right.height);
- var autoHeight = props.top.height + contentHeight + props.bottom.height +
- borderRootHeight + props.border.top + props.border.bottom;
- dom.root.style.height = util.option.asSize(options.height, autoHeight + 'px');
-
- // calculate heights of the content panels
- props.root.height = dom.root.offsetHeight;
- props.background.height = props.root.height - borderRootHeight;
- var containerHeight = props.root.height - props.top.height - props.bottom.height -
- borderRootHeight;
- props.centerContainer.height = containerHeight;
- props.leftContainer.height = containerHeight;
- props.rightContainer.height = props.leftContainer.height;
-
- // calculate the widths of the panels
- props.root.width = dom.root.offsetWidth;
- props.background.width = props.root.width - borderRootWidth;
- props.left.width = dom.leftContainer.clientWidth || -props.border.left;
- props.leftContainer.width = props.left.width;
- props.right.width = dom.rightContainer.clientWidth || -props.border.right;
- props.rightContainer.width = props.right.width;
- var centerWidth = props.root.width - props.left.width - props.right.width - borderRootWidth;
- props.center.width = centerWidth;
- props.centerContainer.width = centerWidth;
- props.top.width = centerWidth;
- props.bottom.width = centerWidth;
-
- // resize the panels
- dom.background.style.height = props.background.height + 'px';
- dom.backgroundVertical.style.height = props.background.height + 'px';
- dom.backgroundHorizontal.style.height = props.centerContainer.height + 'px';
- dom.centerContainer.style.height = props.centerContainer.height + 'px';
- dom.leftContainer.style.height = props.leftContainer.height + 'px';
- dom.rightContainer.style.height = props.rightContainer.height + 'px';
-
- dom.background.style.width = props.background.width + 'px';
- dom.backgroundVertical.style.width = props.centerContainer.width + 'px';
- dom.backgroundHorizontal.style.width = props.background.width + 'px';
- dom.centerContainer.style.width = props.center.width + 'px';
- dom.top.style.width = props.top.width + 'px';
- dom.bottom.style.width = props.bottom.width + 'px';
-
- // reposition the panels
- dom.background.style.left = '0';
- dom.background.style.top = '0';
- dom.backgroundVertical.style.left = (props.left.width + props.border.left) + 'px';
- dom.backgroundVertical.style.top = '0';
- dom.backgroundHorizontal.style.left = '0';
- dom.backgroundHorizontal.style.top = props.top.height + 'px';
- dom.centerContainer.style.left = props.left.width + 'px';
- dom.centerContainer.style.top = props.top.height + 'px';
- dom.leftContainer.style.left = '0';
- dom.leftContainer.style.top = props.top.height + 'px';
- dom.rightContainer.style.left = (props.left.width + props.center.width) + 'px';
- dom.rightContainer.style.top = props.top.height + 'px';
- dom.top.style.left = props.left.width + 'px';
- dom.top.style.top = '0';
- dom.bottom.style.left = props.left.width + 'px';
- dom.bottom.style.top = (props.top.height + props.centerContainer.height) + 'px';
-
- // update the scrollTop, feasible range for the offset can be changed
- // when the height of the Core or of the contents of the center changed
- this._updateScrollTop();
-
- // reposition the scrollable contents
- var offset = this.props.scrollTop;
- if (options.orientation == 'bottom') {
- offset += Math.max(this.props.centerContainer.height - this.props.center.height -
- this.props.border.top - this.props.border.bottom, 0);
- }
- dom.center.style.left = '0';
- dom.center.style.top = offset + 'px';
- dom.left.style.left = '0';
- dom.left.style.top = offset + 'px';
- dom.right.style.left = '0';
- dom.right.style.top = offset + 'px';
-
- // show shadows when vertical scrolling is available
- var visibilityTop = this.props.scrollTop == 0 ? 'hidden' : '';
- var visibilityBottom = this.props.scrollTop == this.props.scrollTopMin ? 'hidden' : '';
- dom.shadowTop.style.visibility = visibilityTop;
- dom.shadowBottom.style.visibility = visibilityBottom;
- dom.shadowTopLeft.style.visibility = visibilityTop;
- dom.shadowBottomLeft.style.visibility = visibilityBottom;
- dom.shadowTopRight.style.visibility = visibilityTop;
- dom.shadowBottomRight.style.visibility = visibilityBottom;
-
- // redraw all components
- this.components.forEach(function (component) {
- resized = component.redraw() || resized;
- });
- if (resized) {
- // keep repainting until all sizes are settled
- var MAX_REDRAWS = 3; // maximum number of consecutive redraws
- if (this.redrawCount < MAX_REDRAWS) {
- this.redrawCount++;
- this.redraw();
- }
- else {
- console.log('WARNING: infinite loop in redraw?')
- }
- this.redrawCount = 0;
- }
-
- this.emit("finishedRedraw");
- };
-
- // TODO: deprecated since version 1.1.0, remove some day
- Core.prototype.repaint = function () {
- throw new Error('Function repaint is deprecated. Use redraw instead.');
- };
-
- /**
- * Set a current time. This can be used for example to ensure that a client's
- * time is synchronized with a shared server time.
- * Only applicable when option `showCurrentTime` is true.
- * @param {Date | String | Number} time A Date, unix timestamp, or
- * ISO date string.
- */
- Core.prototype.setCurrentTime = function(time) {
- if (!this.currentTime) {
- throw new Error('Option showCurrentTime must be true');
- }
-
- this.currentTime.setCurrentTime(time);
- };
-
- /**
- * Get the current time.
- * Only applicable when option `showCurrentTime` is true.
- * @return {Date} Returns the current time.
- */
- Core.prototype.getCurrentTime = function() {
- if (!this.currentTime) {
- throw new Error('Option showCurrentTime must be true');
- }
-
- return this.currentTime.getCurrentTime();
- };
-
- /**
- * Convert a position on screen (pixels) to a datetime
- * @param {int} x Position on the screen in pixels
- * @return {Date} time The datetime the corresponds with given position x
- * @private
- */
- // TODO: move this function to Range
- Core.prototype._toTime = function(x) {
- return DateUtil.toTime(this, x, this.props.center.width);
- };
-
- /**
- * Convert a position on the global screen (pixels) to a datetime
- * @param {int} x Position on the screen in pixels
- * @return {Date} time The datetime the corresponds with given position x
- * @private
- */
- // TODO: move this function to Range
- Core.prototype._toGlobalTime = function(x) {
- return DateUtil.toTime(this, x, this.props.root.width);
- //var conversion = this.range.conversion(this.props.root.width);
- //return new Date(x / conversion.scale + conversion.offset);
- };
-
- /**
- * Convert a datetime (Date object) into a position on the screen
- * @param {Date} time A date
- * @return {int} x The position on the screen in pixels which corresponds
- * with the given date.
- * @private
- */
- // TODO: move this function to Range
- Core.prototype._toScreen = function(time) {
- return DateUtil.toScreen(this, time, this.props.center.width);
- };
-
-
-
- /**
- * Convert a datetime (Date object) into a position on the root
- * This is used to get the pixel density estimate for the screen, not the center panel
- * @param {Date} time A date
- * @return {int} x The position on root in pixels which corresponds
- * with the given date.
- * @private
- */
- // TODO: move this function to Range
- Core.prototype._toGlobalScreen = function(time) {
- return DateUtil.toScreen(this, time, this.props.root.width);
- //var conversion = this.range.conversion(this.props.root.width);
- //return (time.valueOf() - conversion.offset) * conversion.scale;
- };
-
-
- /**
- * Initialize watching when option autoResize is true
- * @private
- */
- Core.prototype._initAutoResize = function () {
- if (this.options.autoResize == true) {
- this._startAutoResize();
- }
- else {
- this._stopAutoResize();
- }
- };
-
- /**
- * Watch for changes in the size of the container. On resize, the Panel will
- * automatically redraw itself.
- * @private
- */
- Core.prototype._startAutoResize = function () {
- var me = this;
-
- this._stopAutoResize();
-
- this._onResize = function() {
- if (me.options.autoResize != true) {
- // stop watching when the option autoResize is changed to false
- me._stopAutoResize();
- return;
- }
-
- if (me.dom.root) {
- // check whether the frame is resized
- // Note: we compare offsetWidth here, not clientWidth. For some reason,
- // IE does not restore the clientWidth from 0 to the actual width after
- // changing the timeline's container display style from none to visible
- if ((me.dom.root.offsetWidth != me.props.lastWidth) ||
- (me.dom.root.offsetHeight != me.props.lastHeight)) {
- me.props.lastWidth = me.dom.root.offsetWidth;
- me.props.lastHeight = me.dom.root.offsetHeight;
-
- me.emit('change');
- }
- }
- };
-
- // add event listener to window resize
- util.addEventListener(window, 'resize', this._onResize);
-
- this.watchTimer = setInterval(this._onResize, 1000);
- };
-
- /**
- * Stop watching for a resize of the frame.
- * @private
- */
- Core.prototype._stopAutoResize = function () {
- if (this.watchTimer) {
- clearInterval(this.watchTimer);
- this.watchTimer = undefined;
- }
-
- // remove event listener on window.resize
- util.removeEventListener(window, 'resize', this._onResize);
- this._onResize = null;
- };
-
- /**
- * Start moving the timeline vertically
- * @param {Event} event
- * @private
- */
- Core.prototype._onTouch = function (event) {
- this.touch.allowDragging = true;
- };
-
- /**
- * Start moving the timeline vertically
- * @param {Event} event
- * @private
- */
- Core.prototype._onPinch = function (event) {
- this.touch.allowDragging = false;
- };
-
- /**
- * Start moving the timeline vertically
- * @param {Event} event
- * @private
- */
- Core.prototype._onDragStart = function (event) {
- this.touch.initialScrollTop = this.props.scrollTop;
- };
-
- /**
- * Move the timeline vertically
- * @param {Event} event
- * @private
- */
- Core.prototype._onDrag = function (event) {
- // refuse to drag when we where pinching to prevent the timeline make a jump
- // when releasing the fingers in opposite order from the touch screen
- if (!this.touch.allowDragging) return;
-
- var delta = event.gesture.deltaY;
-
- var oldScrollTop = this._getScrollTop();
- var newScrollTop = this._setScrollTop(this.touch.initialScrollTop + delta);
-
-
- if (newScrollTop != oldScrollTop) {
- this.redraw(); // TODO: this causes two redraws when dragging, the other is triggered by rangechange already
- this.emit("verticalDrag");
- }
- };
-
- /**
- * Apply a scrollTop
- * @param {Number} scrollTop
- * @returns {Number} scrollTop Returns the applied scrollTop
- * @private
- */
- Core.prototype._setScrollTop = function (scrollTop) {
- this.props.scrollTop = scrollTop;
- this._updateScrollTop();
- return this.props.scrollTop;
- };
-
- /**
- * Update the current scrollTop when the height of the containers has been changed
- * @returns {Number} scrollTop Returns the applied scrollTop
- * @private
- */
- Core.prototype._updateScrollTop = function () {
- // recalculate the scrollTopMin
- var scrollTopMin = Math.min(this.props.centerContainer.height - this.props.center.height, 0); // is negative or zero
- if (scrollTopMin != this.props.scrollTopMin) {
- // in case of bottom orientation, change the scrollTop such that the contents
- // do not move relative to the time axis at the bottom
- if (this.options.orientation == 'bottom') {
- this.props.scrollTop += (scrollTopMin - this.props.scrollTopMin);
- }
- this.props.scrollTopMin = scrollTopMin;
- }
-
- // limit the scrollTop to the feasible scroll range
- if (this.props.scrollTop > 0) this.props.scrollTop = 0;
- if (this.props.scrollTop < scrollTopMin) this.props.scrollTop = scrollTopMin;
-
- return this.props.scrollTop;
- };
-
- /**
- * Get the current scrollTop
- * @returns {number} scrollTop
- * @private
- */
- Core.prototype._getScrollTop = function () {
- return this.props.scrollTop;
- };
-
- module.exports = Core;
-
-
-/***/ },
-/* 26 */
-/***/ function(module, exports, __webpack_require__) {
-
- var Hammer = __webpack_require__(19);
- var util = __webpack_require__(1);
- var DataSet = __webpack_require__(7);
- var DataView = __webpack_require__(9);
- var Component = __webpack_require__(23);
- var Group = __webpack_require__(27);
- var BackgroundGroup = __webpack_require__(31);
- var BoxItem = __webpack_require__(32);
- var PointItem = __webpack_require__(33);
- var RangeItem = __webpack_require__(29);
- var BackgroundItem = __webpack_require__(34);
-
-
- var UNGROUPED = '__ungrouped__'; // reserved group id for ungrouped items
- var BACKGROUND = '__background__'; // reserved group id for background items without group
-
- /**
- * An ItemSet holds a set of items and ranges which can be displayed in a
- * range. The width is determined by the parent of the ItemSet, and the height
- * is determined by the size of the items.
- * @param {{dom: Object, domProps: Object, emitter: Emitter, range: Range}} body
- * @param {Object} [options] See ItemSet.setOptions for the available options.
- * @constructor ItemSet
- * @extends Component
- */
- function ItemSet(body, options) {
- this.body = body;
-
- this.defaultOptions = {
- type: null, // 'box', 'point', 'range', 'background'
- orientation: 'bottom', // 'top' or 'bottom'
- align: 'auto', // alignment of box items
- stack: true,
- groupOrder: null,
-
- selectable: true,
- editable: {
- updateTime: false,
- updateGroup: false,
- add: false,
- remove: false
- },
-
- onAdd: function (item, callback) {
- callback(item);
- },
- onUpdate: function (item, callback) {
- callback(item);
- },
- onMove: function (item, callback) {
- callback(item);
- },
- onRemove: function (item, callback) {
- callback(item);
- },
- onMoving: function (item, callback) {
- callback(item);
- },
-
- margin: {
- item: {
- horizontal: 10,
- vertical: 10
- },
- axis: 20
- },
- padding: 5
- };
-
- // options is shared by this ItemSet and all its items
- this.options = util.extend({}, this.defaultOptions);
-
- // options for getting items from the DataSet with the correct type
- this.itemOptions = {
- type: {start: 'Date', end: 'Date'}
- };
-
- this.conversion = {
- toScreen: body.util.toScreen,
- toTime: body.util.toTime
- };
- this.dom = {};
- this.props = {};
- this.hammer = null;
-
- var me = this;
- this.itemsData = null; // DataSet
- this.groupsData = null; // DataSet
-
- // listeners for the DataSet of the items
- this.itemListeners = {
- 'add': function (event, params, senderId) {
- me._onAdd(params.items);
- },
- 'update': function (event, params, senderId) {
- me._onUpdate(params.items);
- },
- 'remove': function (event, params, senderId) {
- me._onRemove(params.items);
- }
- };
-
- // listeners for the DataSet of the groups
- this.groupListeners = {
- 'add': function (event, params, senderId) {
- me._onAddGroups(params.items);
- },
- 'update': function (event, params, senderId) {
- me._onUpdateGroups(params.items);
- },
- 'remove': function (event, params, senderId) {
- me._onRemoveGroups(params.items);
- }
- };
-
- this.items = {}; // object with an Item for every data item
- this.groups = {}; // Group object for every group
- this.groupIds = [];
-
- this.selection = []; // list with the ids of all selected nodes
- this.stackDirty = true; // if true, all items will be restacked on next redraw
-
- this.touchParams = {}; // stores properties while dragging
- // create the HTML DOM
-
- this._create();
-
- this.setOptions(options);
- }
-
- ItemSet.prototype = new Component();
-
- // available item types will be registered here
- ItemSet.types = {
- background: BackgroundItem,
- box: BoxItem,
- range: RangeItem,
- point: PointItem
- };
-
- /**
- * Create the HTML DOM for the ItemSet
- */
- ItemSet.prototype._create = function(){
- var frame = document.createElement('div');
- frame.className = 'itemset';
- frame['timeline-itemset'] = this;
- this.dom.frame = frame;
-
- // create background panel
- var background = document.createElement('div');
- background.className = 'background';
- frame.appendChild(background);
- this.dom.background = background;
-
- // create foreground panel
- var foreground = document.createElement('div');
- foreground.className = 'foreground';
- frame.appendChild(foreground);
- this.dom.foreground = foreground;
-
- // create axis panel
- var axis = document.createElement('div');
- axis.className = 'axis';
- this.dom.axis = axis;
-
- // create labelset
- var labelSet = document.createElement('div');
- labelSet.className = 'labelset';
- this.dom.labelSet = labelSet;
-
- // create ungrouped Group
- this._updateUngrouped();
-
- // create background Group
- var backgroundGroup = new BackgroundGroup(BACKGROUND, null, this);
- backgroundGroup.show();
- this.groups[BACKGROUND] = backgroundGroup;
-
- // attach event listeners
- // Note: we bind to the centerContainer for the case where the height
- // of the center container is larger than of the ItemSet, so we
- // can click in the empty area to create a new item or deselect an item.
- this.hammer = Hammer(this.body.dom.centerContainer, {
- preventDefault: true
- });
-
- // drag items when selected
- this.hammer.on('touch', this._onTouch.bind(this));
- this.hammer.on('dragstart', this._onDragStart.bind(this));
- this.hammer.on('drag', this._onDrag.bind(this));
- this.hammer.on('dragend', this._onDragEnd.bind(this));
-
- // single select (or unselect) when tapping an item
- this.hammer.on('tap', this._onSelectItem.bind(this));
-
- // multi select when holding mouse/touch, or on ctrl+click
- this.hammer.on('hold', this._onMultiSelectItem.bind(this));
-
- // add item on doubletap
- this.hammer.on('doubletap', this._onAddItem.bind(this));
-
- // attach to the DOM
- this.show();
- };
-
- /**
- * Set options for the ItemSet. Existing options will be extended/overwritten.
- * @param {Object} [options] The following options are available:
- * {String} type
- * Default type for the items. Choose from 'box'
- * (default), 'point', 'range', or 'background'.
- * The default style can be overwritten by
- * individual items.
- * {String} align
- * Alignment for the items, only applicable for
- * BoxItem. Choose 'center' (default), 'left', or
- * 'right'.
- * {String} orientation
- * Orientation of the item set. Choose 'top' or
- * 'bottom' (default).
- * {Function} groupOrder
- * A sorting function for ordering groups
- * {Boolean} stack
- * If true (deafult), items will be stacked on
- * top of each other.
- * {Number} margin.axis
- * Margin between the axis and the items in pixels.
- * Default is 20.
- * {Number} margin.item.horizontal
- * Horizontal margin between items in pixels.
- * Default is 10.
- * {Number} margin.item.vertical
- * Vertical Margin between items in pixels.
- * Default is 10.
- * {Number} margin.item
- * Margin between items in pixels in both horizontal
- * and vertical direction. Default is 10.
- * {Number} margin
- * Set margin for both axis and items in pixels.
- * {Number} padding
- * Padding of the contents of an item in pixels.
- * Must correspond with the items css. Default is 5.
- * {Boolean} selectable
- * If true (default), items can be selected.
- * {Boolean} editable
- * Set all editable options to true or false
- * {Boolean} editable.updateTime
- * Allow dragging an item to an other moment in time
- * {Boolean} editable.updateGroup
- * Allow dragging an item to an other group
- * {Boolean} editable.add
- * Allow creating new items on double tap
- * {Boolean} editable.remove
- * Allow removing items by clicking the delete button
- * top right of a selected item.
- * {Function(item: Item, callback: Function)} onAdd
- * Callback function triggered when an item is about to be added:
- * when the user double taps an empty space in the Timeline.
- * {Function(item: Item, callback: Function)} onUpdate
- * Callback function fired when an item is about to be updated.
- * This function typically has to show a dialog where the user
- * change the item. If not implemented, nothing happens.
- * {Function(item: Item, callback: Function)} onMove
- * Fired when an item has been moved. If not implemented,
- * the move action will be accepted.
- * {Function(item: Item, callback: Function)} onRemove
- * Fired when an item is about to be deleted.
- * If not implemented, the item will be always removed.
- */
- ItemSet.prototype.setOptions = function(options) {
- if (options) {
- // copy all options that we know
- var fields = ['type', 'align', 'orientation', 'padding', 'stack', 'selectable', 'groupOrder', 'dataAttributes', 'template','hide'];
- util.selectiveExtend(fields, this.options, options);
-
- if ('margin' in options) {
- if (typeof options.margin === 'number') {
- this.options.margin.axis = options.margin;
- this.options.margin.item.horizontal = options.margin;
- this.options.margin.item.vertical = options.margin;
- }
- else if (typeof options.margin === 'object') {
- util.selectiveExtend(['axis'], this.options.margin, options.margin);
- if ('item' in options.margin) {
- if (typeof options.margin.item === 'number') {
- this.options.margin.item.horizontal = options.margin.item;
- this.options.margin.item.vertical = options.margin.item;
- }
- else if (typeof options.margin.item === 'object') {
- util.selectiveExtend(['horizontal', 'vertical'], this.options.margin.item, options.margin.item);
- }
- }
- }
- }
-
- if ('editable' in options) {
- if (typeof options.editable === 'boolean') {
- this.options.editable.updateTime = options.editable;
- this.options.editable.updateGroup = options.editable;
- this.options.editable.add = options.editable;
- this.options.editable.remove = options.editable;
- }
- else if (typeof options.editable === 'object') {
- util.selectiveExtend(['updateTime', 'updateGroup', 'add', 'remove'], this.options.editable, options.editable);
- }
- }
-
- // callback functions
- var addCallback = (function (name) {
- var fn = options[name];
- if (fn) {
- if (!(fn instanceof Function)) {
- throw new Error('option ' + name + ' must be a function ' + name + '(item, callback)');
- }
- this.options[name] = fn;
- }
- }).bind(this);
- ['onAdd', 'onUpdate', 'onRemove', 'onMove', 'onMoving'].forEach(addCallback);
-
- // force the itemSet to refresh: options like orientation and margins may be changed
- this.markDirty();
- }
- };
-
- /**
- * Mark the ItemSet dirty so it will refresh everything with next redraw
- */
- ItemSet.prototype.markDirty = function() {
- this.groupIds = [];
- this.stackDirty = true;
- };
-
- /**
- * Destroy the ItemSet
- */
- ItemSet.prototype.destroy = function() {
- this.hide();
- this.setItems(null);
- this.setGroups(null);
-
- this.hammer = null;
-
- this.body = null;
- this.conversion = null;
- };
-
- /**
- * Hide the component from the DOM
- */
- ItemSet.prototype.hide = function() {
- // remove the frame containing the items
- if (this.dom.frame.parentNode) {
- this.dom.frame.parentNode.removeChild(this.dom.frame);
- }
-
- // remove the axis with dots
- if (this.dom.axis.parentNode) {
- this.dom.axis.parentNode.removeChild(this.dom.axis);
- }
-
- // remove the labelset containing all group labels
- if (this.dom.labelSet.parentNode) {
- this.dom.labelSet.parentNode.removeChild(this.dom.labelSet);
- }
- };
-
- /**
- * Show the component in the DOM (when not already visible).
- * @return {Boolean} changed
- */
- ItemSet.prototype.show = function() {
- // show frame containing the items
- if (!this.dom.frame.parentNode) {
- this.body.dom.center.appendChild(this.dom.frame);
- }
-
- // show axis with dots
- if (!this.dom.axis.parentNode) {
- this.body.dom.backgroundVertical.appendChild(this.dom.axis);
- }
-
- // show labelset containing labels
- if (!this.dom.labelSet.parentNode) {
- this.body.dom.left.appendChild(this.dom.labelSet);
- }
- };
-
- /**
- * Set selected items by their id. Replaces the current selection
- * Unknown id's are silently ignored.
- * @param {string[] | string} [ids] An array with zero or more id's of the items to be
- * selected, or a single item id. If ids is undefined
- * or an empty array, all items will be unselected.
- */
- ItemSet.prototype.setSelection = function(ids) {
- var i, ii, id, item;
-
- if (ids == undefined) ids = [];
- if (!Array.isArray(ids)) ids = [ids];
-
- // unselect currently selected items
- for (i = 0, ii = this.selection.length; i < ii; i++) {
- id = this.selection[i];
- item = this.items[id];
- if (item) item.unselect();
- }
-
- // select items
- this.selection = [];
- for (i = 0, ii = ids.length; i < ii; i++) {
- id = ids[i];
- item = this.items[id];
- if (item) {
- this.selection.push(id);
- item.select();
- }
- }
- };
-
- /**
- * Get the selected items by their id
- * @return {Array} ids The ids of the selected items
- */
- ItemSet.prototype.getSelection = function() {
- return this.selection.concat([]);
- };
-
- /**
- * Get the id's of the currently visible items.
- * @returns {Array} The ids of the visible items
- */
- ItemSet.prototype.getVisibleItems = function() {
- var range = this.body.range.getRange();
- var left = this.body.util.toScreen(range.start);
- var right = this.body.util.toScreen(range.end);
-
- var ids = [];
- for (var groupId in this.groups) {
- if (this.groups.hasOwnProperty(groupId)) {
- var group = this.groups[groupId];
- var rawVisibleItems = group.visibleItems;
-
- // filter the "raw" set with visibleItems into a set which is really
- // visible by pixels
- for (var i = 0; i < rawVisibleItems.length; i++) {
- var item = rawVisibleItems[i];
- // TODO: also check whether visible vertically
- if ((item.left < right) && (item.left + item.width > left)) {
- ids.push(item.id);
- }
- }
- }
- }
-
- return ids;
- };
-
- /**
- * Deselect a selected item
- * @param {String | Number} id
- * @private
- */
- ItemSet.prototype._deselect = function(id) {
- var selection = this.selection;
- for (var i = 0, ii = selection.length; i < ii; i++) {
- if (selection[i] == id) { // non-strict comparison!
- selection.splice(i, 1);
- break;
- }
- }
- };
-
- /**
- * Repaint the component
- * @return {boolean} Returns true if the component is resized
- */
- ItemSet.prototype.redraw = function() {
- var margin = this.options.margin,
- range = this.body.range,
- asSize = util.option.asSize,
- options = this.options,
- orientation = options.orientation,
- resized = false,
- frame = this.dom.frame,
- editable = options.editable.updateTime || options.editable.updateGroup;
-
- // recalculate absolute position (before redrawing groups)
- this.props.top = this.body.domProps.top.height + this.body.domProps.border.top;
- this.props.left = this.body.domProps.left.width + this.body.domProps.border.left;
-
- // update class name
- frame.className = 'itemset' + (editable ? ' editable' : '');
-
- // reorder the groups (if needed)
- resized = this._orderGroups() || resized;
-
- // check whether zoomed (in that case we need to re-stack everything)
- // TODO: would be nicer to get this as a trigger from Range
- var visibleInterval = range.end - range.start;
- var zoomed = (visibleInterval != this.lastVisibleInterval) || (this.props.width != this.props.lastWidth);
- if (zoomed) this.stackDirty = true;
- this.lastVisibleInterval = visibleInterval;
- this.props.lastWidth = this.props.width;
-
- var restack = this.stackDirty;
- var firstGroup = this._firstGroup();
- var firstMargin = {
- item: margin.item,
- axis: margin.axis
- };
- var nonFirstMargin = {
- item: margin.item,
- axis: margin.item.vertical / 2
- };
- var height = 0;
- var minHeight = margin.axis + margin.item.vertical;
-
- // redraw the background group
- this.groups[BACKGROUND].redraw(range, nonFirstMargin, restack);
-
- // redraw all regular groups
- util.forEach(this.groups, function (group) {
- var groupMargin = (group == firstGroup) ? firstMargin : nonFirstMargin;
- var groupResized = group.redraw(range, groupMargin, restack);
- resized = groupResized || resized;
- height += group.height;
- });
- height = Math.max(height, minHeight);
- this.stackDirty = false;
-
- // update frame height
- frame.style.height = asSize(height);
-
- // calculate actual size
- this.props.width = frame.offsetWidth;
- this.props.height = height;
-
- // reposition axis
- this.dom.axis.style.top = asSize((orientation == 'top') ?
- (this.body.domProps.top.height + this.body.domProps.border.top) :
- (this.body.domProps.top.height + this.body.domProps.centerContainer.height));
- this.dom.axis.style.left = '0';
-
- // check if this component is resized
- resized = this._isResized() || resized;
-
- return resized;
- };
-
- /**
- * Get the first group, aligned with the axis
- * @return {Group | null} firstGroup
- * @private
- */
- ItemSet.prototype._firstGroup = function() {
- var firstGroupIndex = (this.options.orientation == 'top') ? 0 : (this.groupIds.length - 1);
- var firstGroupId = this.groupIds[firstGroupIndex];
- var firstGroup = this.groups[firstGroupId] || this.groups[UNGROUPED];
-
- return firstGroup || null;
- };
-
- /**
- * Create or delete the group holding all ungrouped items. This group is used when
- * there are no groups specified.
- * @protected
- */
- ItemSet.prototype._updateUngrouped = function() {
- var ungrouped = this.groups[UNGROUPED];
- var background = this.groups[BACKGROUND];
- var item, itemId;
-
- if (this.groupsData) {
- // remove the group holding all ungrouped items
- if (ungrouped) {
- ungrouped.hide();
- delete this.groups[UNGROUPED];
-
- for (itemId in this.items) {
- if (this.items.hasOwnProperty(itemId)) {
- item = this.items[itemId];
- item.parent && item.parent.remove(item);
- var groupId = this._getGroupId(item.data);
- var group = this.groups[groupId];
- group && group.add(item) || item.hide();
- }
- }
- }
- }
- else {
- // create a group holding all (unfiltered) items
- if (!ungrouped) {
- var id = null;
- var data = null;
- ungrouped = new Group(id, data, this);
- this.groups[UNGROUPED] = ungrouped;
-
- for (itemId in this.items) {
- if (this.items.hasOwnProperty(itemId)) {
- item = this.items[itemId];
- ungrouped.add(item);
- }
- }
-
- ungrouped.show();
- }
- }
- };
-
- /**
- * Get the element for the labelset
- * @return {HTMLElement} labelSet
- */
- ItemSet.prototype.getLabelSet = function() {
- return this.dom.labelSet;
- };
-
- /**
- * Set items
- * @param {vis.DataSet | null} items
- */
- ItemSet.prototype.setItems = function(items) {
- var me = this,
- ids,
- oldItemsData = this.itemsData;
-
- // replace the dataset
- if (!items) {
- this.itemsData = null;
- }
- else if (items instanceof DataSet || items instanceof DataView) {
- this.itemsData = items;
- }
- else {
- throw new TypeError('Data must be an instance of DataSet or DataView');
- }
-
- if (oldItemsData) {
- // unsubscribe from old dataset
- util.forEach(this.itemListeners, function (callback, event) {
- oldItemsData.off(event, callback);
- });
-
- // remove all drawn items
- ids = oldItemsData.getIds();
- this._onRemove(ids);
- }
-
- if (this.itemsData) {
- // subscribe to new dataset
- var id = this.id;
- util.forEach(this.itemListeners, function (callback, event) {
- me.itemsData.on(event, callback, id);
- });
-
- // add all new items
- ids = this.itemsData.getIds();
- this._onAdd(ids);
-
- // update the group holding all ungrouped items
- this._updateUngrouped();
- }
- };
-
- /**
- * Get the current items
- * @returns {vis.DataSet | null}
- */
- ItemSet.prototype.getItems = function() {
- return this.itemsData;
- };
-
- /**
- * Set groups
- * @param {vis.DataSet} groups
- */
- ItemSet.prototype.setGroups = function(groups) {
- var me = this,
- ids;
-
- // unsubscribe from current dataset
- if (this.groupsData) {
- util.forEach(this.groupListeners, function (callback, event) {
- me.groupsData.unsubscribe(event, callback);
- });
-
- // remove all drawn groups
- ids = this.groupsData.getIds();
- this.groupsData = null;
- this._onRemoveGroups(ids); // note: this will cause a redraw
- }
-
- // replace the dataset
- if (!groups) {
- this.groupsData = null;
- }
- else if (groups instanceof DataSet || groups instanceof DataView) {
- this.groupsData = groups;
- }
- else {
- throw new TypeError('Data must be an instance of DataSet or DataView');
- }
-
- if (this.groupsData) {
- // subscribe to new dataset
- var id = this.id;
- util.forEach(this.groupListeners, function (callback, event) {
- me.groupsData.on(event, callback, id);
- });
-
- // draw all ms
- ids = this.groupsData.getIds();
- this._onAddGroups(ids);
- }
-
- // update the group holding all ungrouped items
- this._updateUngrouped();
-
- // update the order of all items in each group
- this._order();
-
- this.body.emitter.emit('change', {queue: true});
- };
-
- /**
- * Get the current groups
- * @returns {vis.DataSet | null} groups
- */
- ItemSet.prototype.getGroups = function() {
- return this.groupsData;
- };
-
- /**
- * Remove an item by its id
- * @param {String | Number} id
- */
- ItemSet.prototype.removeItem = function(id) {
- var item = this.itemsData.get(id),
- dataset = this.itemsData.getDataSet();
-
- if (item) {
- // confirm deletion
- this.options.onRemove(item, function (item) {
- if (item) {
- // remove by id here, it is possible that an item has no id defined
- // itself, so better not delete by the item itself
- dataset.remove(id);
- }
- });
- }
- };
-
- /**
- * Get the time of an item based on it's data and options.type
- * @param {Object} itemData
- * @returns {string} Returns the type
- * @private
- */
- ItemSet.prototype._getType = function (itemData) {
- return itemData.type || this.options.type || (itemData.end ? 'range' : 'box');
- };
-
-
- /**
- * Get the group id for an item
- * @param {Object} itemData
- * @returns {string} Returns the groupId
- * @private
- */
- ItemSet.prototype._getGroupId = function (itemData) {
- var type = this._getType(itemData);
- if (type == 'background' && itemData.group == undefined) {
- return BACKGROUND;
- }
- else {
- return this.groupsData ? itemData.group : UNGROUPED;
- }
- };
-
- /**
- * Handle updated items
- * @param {Number[]} ids
- * @protected
- */
- ItemSet.prototype._onUpdate = function(ids) {
- var me = this;
-
- ids.forEach(function (id) {
- var itemData = me.itemsData.get(id, me.itemOptions);
- var item = me.items[id];
- var type = me._getType(itemData);
-
- var constructor = ItemSet.types[type];
-
- if (item) {
- // update item
- if (!constructor || !(item instanceof constructor)) {
- // item type has changed, delete the item and recreate it
- me._removeItem(item);
- item = null;
- }
- else {
- me._updateItem(item, itemData);
- }
- }
-
- if (!item) {
- // create item
- if (constructor) {
- item = new constructor(itemData, me.conversion, me.options);
- item.id = id; // TODO: not so nice setting id afterwards
- me._addItem(item);
- }
- else if (type == 'rangeoverflow') {
- // TODO: deprecated since version 2.1.0 (or 3.0.0?). cleanup some day
- throw new TypeError('Item type "rangeoverflow" is deprecated. Use css styling instead: ' +
- '.vis.timeline .item.range .content {overflow: visible;}');
- }
- else {
- throw new TypeError('Unknown item type "' + type + '"');
- }
- }
- });
-
- this._order();
- this.stackDirty = true; // force re-stacking of all items next redraw
- this.body.emitter.emit('change', {queue: true});
- };
-
- /**
- * Handle added items
- * @param {Number[]} ids
- * @protected
- */
- ItemSet.prototype._onAdd = ItemSet.prototype._onUpdate;
-
- /**
- * Handle removed items
- * @param {Number[]} ids
- * @protected
- */
- ItemSet.prototype._onRemove = function(ids) {
- var count = 0;
- var me = this;
- ids.forEach(function (id) {
- var item = me.items[id];
- if (item) {
- count++;
- me._removeItem(item);
- }
- });
-
- if (count) {
- // update order
- this._order();
- this.stackDirty = true; // force re-stacking of all items next redraw
- this.body.emitter.emit('change', {queue: true});
- }
- };
-
- /**
- * Update the order of item in all groups
- * @private
- */
- ItemSet.prototype._order = function() {
- // reorder the items in all groups
- // TODO: optimization: only reorder groups affected by the changed items
- util.forEach(this.groups, function (group) {
- group.order();
- });
- };
-
- /**
- * Handle updated groups
- * @param {Number[]} ids
- * @private
- */
- ItemSet.prototype._onUpdateGroups = function(ids) {
- this._onAddGroups(ids);
- };
-
- /**
- * Handle changed groups (added or updated)
- * @param {Number[]} ids
- * @private
- */
- ItemSet.prototype._onAddGroups = function(ids) {
- var me = this;
-
- ids.forEach(function (id) {
- var groupData = me.groupsData.get(id);
- var group = me.groups[id];
-
- if (!group) {
- // check for reserved ids
- if (id == UNGROUPED || id == BACKGROUND) {
- throw new Error('Illegal group id. ' + id + ' is a reserved id.');
- }
-
- var groupOptions = Object.create(me.options);
- util.extend(groupOptions, {
- height: null
- });
-
- group = new Group(id, groupData, me);
- me.groups[id] = group;
-
- // add items with this groupId to the new group
- for (var itemId in me.items) {
- if (me.items.hasOwnProperty(itemId)) {
- var item = me.items[itemId];
- if (item.data.group == id) {
- group.add(item);
- }
- }
- }
-
- group.order();
- group.show();
- }
- else {
- // update group
- group.setData(groupData);
- }
- });
-
- this.body.emitter.emit('change', {queue: true});
- };
-
- /**
- * Handle removed groups
- * @param {Number[]} ids
- * @private
- */
- ItemSet.prototype._onRemoveGroups = function(ids) {
- var groups = this.groups;
- ids.forEach(function (id) {
- var group = groups[id];
-
- if (group) {
- group.hide();
- delete groups[id];
- }
- });
-
- this.markDirty();
-
- this.body.emitter.emit('change', {queue: true});
- };
-
- /**
- * Reorder the groups if needed
- * @return {boolean} changed
- * @private
- */
- ItemSet.prototype._orderGroups = function () {
- if (this.groupsData) {
- // reorder the groups
- var groupIds = this.groupsData.getIds({
- order: this.options.groupOrder
- });
-
- var changed = !util.equalArray(groupIds, this.groupIds);
- if (changed) {
- // hide all groups, removes them from the DOM
- var groups = this.groups;
- groupIds.forEach(function (groupId) {
- groups[groupId].hide();
- });
-
- // show the groups again, attach them to the DOM in correct order
- groupIds.forEach(function (groupId) {
- groups[groupId].show();
- });
-
- this.groupIds = groupIds;
- }
-
- return changed;
- }
- else {
- return false;
- }
- };
-
- /**
- * Add a new item
- * @param {Item} item
- * @private
- */
- ItemSet.prototype._addItem = function(item) {
- this.items[item.id] = item;
-
- // add to group
- var groupId = this._getGroupId(item.data);
- var group = this.groups[groupId];
- if (group) group.add(item);
- };
-
- /**
- * Update an existing item
- * @param {Item} item
- * @param {Object} itemData
- * @private
- */
- ItemSet.prototype._updateItem = function(item, itemData) {
- var oldGroupId = item.data.group;
-
- // update the items data (will redraw the item when displayed)
- item.setData(itemData);
-
- // update group
- if (oldGroupId != item.data.group) {
- var oldGroup = this.groups[oldGroupId];
- if (oldGroup) oldGroup.remove(item);
-
- var groupId = this._getGroupId(item.data);
- var group = this.groups[groupId];
- if (group) group.add(item);
- }
- };
-
- /**
- * Delete an item from the ItemSet: remove it from the DOM, from the map
- * with items, and from the map with visible items, and from the selection
- * @param {Item} item
- * @private
- */
- ItemSet.prototype._removeItem = function(item) {
- // remove from DOM
- item.hide();
-
- // remove from items
- delete this.items[item.id];
-
- // remove from selection
- var index = this.selection.indexOf(item.id);
- if (index != -1) this.selection.splice(index, 1);
-
- // remove from group
- item.parent && item.parent.remove(item);
- };
-
- /**
- * Create an array containing all items being a range (having an end date)
- * @param array
- * @returns {Array}
- * @private
- */
- ItemSet.prototype._constructByEndArray = function(array) {
- var endArray = [];
-
- for (var i = 0; i < array.length; i++) {
- if (array[i] instanceof RangeItem) {
- endArray.push(array[i]);
- }
- }
- return endArray;
- };
-
- /**
- * Register the clicked item on touch, before dragStart is initiated.
- *
- * dragStart is initiated from a mousemove event, which can have left the item
- * already resulting in an item == null
- *
- * @param {Event} event
- * @private
- */
- ItemSet.prototype._onTouch = function (event) {
- // store the touched item, used in _onDragStart
- this.touchParams.item = ItemSet.itemFromTarget(event);
- };
-
- /**
- * Start dragging the selected events
- * @param {Event} event
- * @private
- */
- ItemSet.prototype._onDragStart = function (event) {
- if (!this.options.editable.updateTime && !this.options.editable.updateGroup) {
- return;
- }
-
- var item = this.touchParams.item || null;
- var me = this;
- var props;
-
- if (item && item.selected) {
- var dragLeftItem = event.target.dragLeftItem;
- var dragRightItem = event.target.dragRightItem;
-
- if (dragLeftItem) {
- props = {
- item: dragLeftItem,
- initialX: event.gesture.center.clientX
- };
-
- if (me.options.editable.updateTime) {
- props.start = item.data.start.valueOf();
- }
- if (me.options.editable.updateGroup) {
- if ('group' in item.data) props.group = item.data.group;
- }
-
- this.touchParams.itemProps = [props];
- }
- else if (dragRightItem) {
- props = {
- item: dragRightItem,
- initialX: event.gesture.center.clientX
- };
-
- if (me.options.editable.updateTime) {
- props.end = item.data.end.valueOf();
- }
- if (me.options.editable.updateGroup) {
- if ('group' in item.data) props.group = item.data.group;
- }
-
- this.touchParams.itemProps = [props];
- }
- else {
- this.touchParams.itemProps = this.getSelection().map(function (id) {
- var item = me.items[id];
- var props = {
- item: item,
- initialX: event.gesture.center.clientX
- };
-
- if (me.options.editable.updateTime) {
- if ('start' in item.data) props.start = item.data.start.valueOf();
- if ('end' in item.data) props.end = item.data.end.valueOf();
- }
- if (me.options.editable.updateGroup) {
- if ('group' in item.data) props.group = item.data.group;
- }
-
- return props;
- });
- }
-
- event.stopPropagation();
- }
- };
-
- /**
- * Drag selected items
- * @param {Event} event
- * @private
- */
- ItemSet.prototype._onDrag = function (event) {
- event.preventDefault()
-
- if (this.touchParams.itemProps) {
- var me = this;
- var snap = this.body.util.snap || null;
- var xOffset = this.body.dom.root.offsetLeft + this.body.domProps.left.width;
-
- // move
- this.touchParams.itemProps.forEach(function (props) {
- var newProps = {};
- var current = me.body.util.toTime(event.gesture.center.clientX - xOffset);
- var initial = me.body.util.toTime(props.initialX - xOffset);
- var offset = current - initial;
-
- if ('start' in props) {
- var start = new Date(props.start + offset);
- newProps.start = snap ? snap(start) : start;
- }
-
- if ('end' in props) {
- var end = new Date(props.end + offset);
- newProps.end = snap ? snap(end) : end;
- }
-
- if ('group' in props) {
- // drag from one group to another
- var group = ItemSet.groupFromTarget(event);
- newProps.group = group && group.groupId;
- }
-
- // confirm moving the item
- var itemData = util.extend({}, props.item.data, newProps);
- me.options.onMoving(itemData, function (itemData) {
- if (itemData) {
- me._updateItemProps(props.item, itemData);
- }
- });
- });
-
- this.stackDirty = true; // force re-stacking of all items next redraw
- this.body.emitter.emit('change');
-
- event.stopPropagation();
- }
- };
-
- /**
- * Update an items properties
- * @param {Item} item
- * @param {Object} props Can contain properties start, end, and group.
- * @private
- */
- ItemSet.prototype._updateItemProps = function(item, props) {
- // TODO: copy all properties from props to item? (also new ones)
- if ('start' in props) item.data.start = props.start;
- if ('end' in props) item.data.end = props.end;
- if ('group' in props && item.data.group != props.group) {
- this._moveToGroup(item, props.group)
- }
- };
-
- /**
- * Move an item to another group
- * @param {Item} item
- * @param {String | Number} groupId
- * @private
- */
- ItemSet.prototype._moveToGroup = function(item, groupId) {
- var group = this.groups[groupId];
- if (group && group.groupId != item.data.group) {
- var oldGroup = item.parent;
- oldGroup.remove(item);
- oldGroup.order();
- group.add(item);
- group.order();
-
- item.data.group = group.groupId;
- }
- };
-
- /**
- * End of dragging selected items
- * @param {Event} event
- * @private
- */
- ItemSet.prototype._onDragEnd = function (event) {
- event.preventDefault()
-
- if (this.touchParams.itemProps) {
- // prepare a change set for the changed items
- var changes = [],
- me = this,
- dataset = this.itemsData.getDataSet();
-
- var itemProps = this.touchParams.itemProps ;
- this.touchParams.itemProps = null;
- itemProps.forEach(function (props) {
- var id = props.item.id,
- itemData = me.itemsData.get(id, me.itemOptions);
-
- var changed = false;
- if ('start' in props.item.data) {
- changed = (props.start != props.item.data.start.valueOf());
- itemData.start = util.convert(props.item.data.start,
- dataset._options.type && dataset._options.type.start || 'Date');
- }
- if ('end' in props.item.data) {
- changed = changed || (props.end != props.item.data.end.valueOf());
- itemData.end = util.convert(props.item.data.end,
- dataset._options.type && dataset._options.type.end || 'Date');
- }
- if ('group' in props.item.data) {
- changed = changed || (props.group != props.item.data.group);
- itemData.group = props.item.data.group;
- }
-
- // only apply changes when start or end is actually changed
- if (changed) {
- me.options.onMove(itemData, function (itemData) {
- if (itemData) {
- // apply changes
- itemData[dataset._fieldId] = id; // ensure the item contains its id (can be undefined)
- changes.push(itemData);
- }
- else {
- // restore original values
- me._updateItemProps(props.item, props);
-
- me.stackDirty = true; // force re-stacking of all items next redraw
- me.body.emitter.emit('change');
- }
- });
- }
- });
-
- // apply the changes to the data (if there are changes)
- if (changes.length) {
- dataset.update(changes);
- }
-
- event.stopPropagation();
- }
- };
-
- /**
- * Handle selecting/deselecting an item when tapping it
- * @param {Event} event
- * @private
- */
- ItemSet.prototype._onSelectItem = function (event) {
- if (!this.options.selectable) return;
-
- var ctrlKey = event.gesture.srcEvent && event.gesture.srcEvent.ctrlKey;
- var shiftKey = event.gesture.srcEvent && event.gesture.srcEvent.shiftKey;
- if (ctrlKey || shiftKey) {
- this._onMultiSelectItem(event);
- return;
- }
-
- var oldSelection = this.getSelection();
-
- var item = ItemSet.itemFromTarget(event);
- var selection = item ? [item.id] : [];
- this.setSelection(selection);
-
- var newSelection = this.getSelection();
-
- // emit a select event,
- // except when old selection is empty and new selection is still empty
- if (newSelection.length > 0 || oldSelection.length > 0) {
- this.body.emitter.emit('select', {
- items: newSelection
- });
- }
- };
-
- /**
- * Handle creation and updates of an item on double tap
- * @param event
- * @private
- */
- ItemSet.prototype._onAddItem = function (event) {
- if (!this.options.selectable) return;
- if (!this.options.editable.add) return;
-
- var me = this,
- snap = this.body.util.snap || null,
- item = ItemSet.itemFromTarget(event);
-
- if (item) {
- // update item
-
- // execute async handler to update the item (or cancel it)
- var itemData = me.itemsData.get(item.id); // get a clone of the data from the dataset
- this.options.onUpdate(itemData, function (itemData) {
- if (itemData) {
- me.itemsData.getDataSet().update(itemData);
- }
- });
- }
- else {
- // add item
- var xAbs = util.getAbsoluteLeft(this.dom.frame);
- var x = event.gesture.center.pageX - xAbs;
- var start = this.body.util.toTime(x);
- var newItem = {
- start: snap ? snap(start) : start,
- content: 'new item'
- };
-
- // when default type is a range, add a default end date to the new item
- if (this.options.type === 'range') {
- var end = this.body.util.toTime(x + this.props.width / 5);
- newItem.end = snap ? snap(end) : end;
- }
-
- newItem[this.itemsData._fieldId] = util.randomUUID();
-
- var group = ItemSet.groupFromTarget(event);
- if (group) {
- newItem.group = group.groupId;
- }
-
- // execute async handler to customize (or cancel) adding an item
- this.options.onAdd(newItem, function (item) {
- if (item) {
- me.itemsData.getDataSet().add(item);
- // TODO: need to trigger a redraw?
- }
- });
- }
- };
-
- /**
- * Handle selecting/deselecting multiple items when holding an item
- * @param {Event} event
- * @private
- */
- ItemSet.prototype._onMultiSelectItem = function (event) {
- if (!this.options.selectable) return;
-
- var selection,
- item = ItemSet.itemFromTarget(event);
-
- if (item) {
- // multi select items
- selection = this.getSelection(); // current selection
-
- var shiftKey = event.gesture.touches[0] && event.gesture.touches[0].shiftKey || false;
- if (shiftKey) {
- // select all items between the old selection and the tapped item
-
- // determine the selection range
- selection.push(item.id);
- var range = ItemSet._getItemRange(this.itemsData.get(selection, this.itemOptions));
-
- // select all items within the selection range
- selection = [];
- for (var id in this.items) {
- if (this.items.hasOwnProperty(id)) {
- var _item = this.items[id];
- var start = _item.data.start;
- var end = (_item.data.end !== undefined) ? _item.data.end : start;
-
- if (start >= range.min && end <= range.max) {
- selection.push(_item.id); // do not use id but item.id, id itself is stringified
- }
- }
- }
- }
- else {
- // add/remove this item from the current selection
- var index = selection.indexOf(item.id);
- if (index == -1) {
- // item is not yet selected -> select it
- selection.push(item.id);
- }
- else {
- // item is already selected -> deselect it
- selection.splice(index, 1);
- }
- }
-
- this.setSelection(selection);
-
- this.body.emitter.emit('select', {
- items: this.getSelection()
- });
- }
- };
-
- /**
- * Calculate the time range of a list of items
- * @param {Array.
-