diff --git a/Gemfile b/Gemfile index f1886456d..5ca88c10c 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,6 @@ source 'http://rubygems.org' ruby '3.2.2' gem 'bootsnap', require: false -gem 'bootstrap-sass', '~> 3.4.1' gem 'devise', '~> 4.8.1' gem 'devise_invitable' gem 'figaro' @@ -37,20 +36,7 @@ gem 'kaminari' gem 'rack-attack' gem 'rack-cors' -# JS datetime library, requirement of datetime picker -gem 'momentjs-rails', '~> 2.17.1' -# JS datetime picker -gem 'bootstrap3-datetimepicker-rails', '~> 4.17' -# Select elements for Bootstrap -gem 'bootstrap-select-rails', '~> 1.12.4' gem 'uglifier', '>= 1.3.0' -# jQuery & plugins -gem 'autosize-rails' # jQuery autosize plugin -gem 'hammerjs-rails' -gem 'jquery-rails' -gem 'jquery-ui-rails' -gem 'js_cookie_rails' # Simple JS API for cookies -gem 'spinjs-rails' gem 'activerecord-import' gem 'ajax-datatables-rails', '~> 0.3.1' diff --git a/Gemfile.lock b/Gemfile.lock index cfe58bb88..96f0188a1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -141,10 +141,6 @@ GEM ast (2.4.2) auto_strip_attributes (2.6.0) activerecord (>= 4.0) - autoprefixer-rails (10.4.13.0) - execjs (~> 2) - autosize-rails (1.18.17) - rails (>= 3.1) awesome_print (1.9.2) aws-eventstream (1.2.0) aws-partitions (1.780.0) @@ -201,12 +197,6 @@ GEM debug_inspector (>= 0.0.1) bootsnap (1.16.0) msgpack (~> 1.2) - bootstrap-sass (3.4.1) - autoprefixer-rails (>= 5.2.1) - sassc (>= 2.0.0) - bootstrap-select-rails (1.12.4) - bootstrap3-datetimepicker-rails (4.17.47) - momentjs-rails (>= 2.8.1) builder (3.2.4) bullet (7.0.7) activesupport (>= 3.0.0) @@ -336,7 +326,6 @@ GEM activesupport (>= 5.0) graphviz (1.2.1) process-pipeline - hammerjs-rails (2.0.8) hashdiff (1.0.1) hashie (5.0.0) httparty (0.21.0) @@ -358,10 +347,6 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - jquery-ui-rails (6.0.1) - railties (>= 3.2.16) - js_cookie_rails (2.2.0) - railties (>= 3.1) jsbundling-rails (1.1.1) railties (>= 6.0.0) json (2.6.3) @@ -413,8 +398,6 @@ GEM mini_mime (1.1.2) mini_portile2 (2.8.2) minitest (5.18.0) - momentjs-rails (2.17.1) - railties (>= 3.1) msgpack (1.7.1) multi_json (1.15.0) multi_test (1.1.0) @@ -646,8 +629,6 @@ GEM snaky_hash (2.0.1) hashie version_gem (~> 1.1, >= 1.1.1) - spinjs-rails (1.4) - rails (>= 3.1) sprockets (4.2.0) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) @@ -714,7 +695,6 @@ DEPENDENCIES ajax-datatables-rails (~> 0.3.1) aspector auto_strip_attributes (~> 2.1) - autosize-rails awesome_print aws-sdk-lambda aws-sdk-rails @@ -724,9 +704,6 @@ DEPENDENCIES better_errors binding_of_caller bootsnap - bootstrap-sass (~> 3.4.1) - bootstrap-select-rails (~> 1.12.4) - bootstrap3-datetimepicker-rails (~> 4.17) bullet byebug canaid! @@ -748,15 +725,11 @@ DEPENDENCIES fastimage figaro graphviz - hammerjs-rails httparty (~> 0.21.0) i18n-js (~> 3.6) image_processing img2zpl! jbuilder - jquery-rails - jquery-ui-rails - js_cookie_rails jsbundling-rails json-jwt json_matchers @@ -765,7 +738,6 @@ DEPENDENCIES kaminari listen logging (~> 2.0.0) - momentjs-rails (~> 2.17.1) nested_form_fields newrelic_rpm nokogiri (~> 1.14.3) @@ -808,7 +780,6 @@ DEPENDENCIES silencer simplecov sneaky-save! - spinjs-rails sprockets-rails tailwindcss-rails (~> 2.0) timecop diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index d0d01b904..94e662e81 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -1,8 +1,5 @@ // turbolinks MUST BE THE LAST inclusion -//= require hammer -//= require js.cookie -//= require bootstrap-sprockets -//= require moment + //= require bootstrap-datetimepicker //= require bootstrap-colorselector //= require bootstrap-tagsinput.min @@ -11,7 +8,6 @@ //= require nested_form_fields //= require jsPlumb-2.0.4-min //= require jsnetworkx -//= require bootstrap-select //= require_directory ./repository_columns/columns_initializers //= require datatables //= require clndr.min @@ -157,7 +153,7 @@ window.onbeforeunload = function () { * Disable Firefox caching to get rid of issues with pressing * browser back, like opening canvas in edit mode. */ -$(window).unload(function () { +$(window).on("unload", function() { $(window).unbind('unload'); }); diff --git a/app/assets/javascripts/jquery_bundle.js b/app/assets/javascripts/jquery_bundle.js index 4661cb946..6c79d3cc9 100644 --- a/app/assets/javascripts/jquery_bundle.js +++ b/app/assets/javascripts/jquery_bundle.js @@ -1,14 +1,4 @@ -//= require jquery -//= require jquery_ujs -//= require jquery.mousewheel.min -//= require jquery.autosize -//= require jquery-ui/widget -//= require jquery-ui/widgets/mouse -//= require jquery-ui/widgets/draggable -//= require jquery-ui/widgets/droppable + //= require jquery.ui.touch-punch.min -//= require jquery-ui/effects/effect-slide //= require jquery.caret.min //= require jquery.atwho.min -//= require spin -//= require jquery.spin diff --git a/app/assets/javascripts/repositories/repository_datatable.js b/app/assets/javascripts/repositories/repository_datatable.js index 009d7be73..e03b9e4eb 100644 --- a/app/assets/javascripts/repositories/repository_datatable.js +++ b/app/assets/javascripts/repositories/repository_datatable.js @@ -4,7 +4,6 @@ initAssignedTasksDropdown initBMTFilter initReminderDropdown initBSTooltips */ -//= require jquery-ui/widgets/sortable //= require repositories/row_editor.js diff --git a/app/assets/javascripts/repository_columns/index.js b/app/assets/javascripts/repository_columns/index.js index cf604556e..5eeb482d9 100644 --- a/app/assets/javascripts/repository_columns/index.js +++ b/app/assets/javascripts/repository_columns/index.js @@ -3,7 +3,6 @@ /* global RepositoryDateColumnType RepositoryDatatable */ /* eslint-disable no-restricted-globals */ -//= require jquery-ui/widgets/sortable var RepositoryColumns = (function() { var TABLE_ID = ''; diff --git a/app/assets/stylesheets/access_permissions/access_modal.scss b/app/assets/stylesheets/access_permissions/access_modal.scss index 714a71471..121e96c6f 100644 --- a/app/assets/stylesheets/access_permissions/access_modal.scss +++ b/app/assets/stylesheets/access_permissions/access_modal.scss @@ -28,7 +28,7 @@ } .member-item { - border-bottom: 1px solid $modal-header-border-color; + border-bottom: 1px solid $color-concrete; padding: .5em 0; } diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 0c9950a7c..8f4c91ce6 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -1,7 +1,5 @@ /* *= require_self - *= require jquery-ui/draggable - *= require bootstrap-select *= require sn-icon-font *= require sn-inter-font *= stub reports_pdf @@ -11,57 +9,6 @@ @import "constants"; -@import "bootstrap-sprockets"; -// Core variables and mixins -@import "bootstrap/variables"; -@import "bootstrap/mixins"; - -// Reset and dependencies -@import "bootstrap/normalize"; -@import "bootstrap/print"; -@import "bootstrap/glyphicons"; - -// Core CSS -@import "bootstrap/scaffolding"; -@import "bootstrap/type"; -@import "bootstrap/code"; -@import "bootstrap/grid"; -@import "bootstrap/tables"; -@import "bootstrap/forms"; - -// Components -@import "bootstrap/component-animations"; -@import "bootstrap/dropdowns"; -@import "bootstrap/button-groups"; -@import "bootstrap/input-groups"; -@import "bootstrap/navs"; -@import "bootstrap/navbar"; -@import "bootstrap/breadcrumbs"; -@import "bootstrap/pagination"; -@import "bootstrap/pager"; -@import "bootstrap/labels"; -@import "bootstrap/badges"; -@import "bootstrap/jumbotron"; -@import "bootstrap/thumbnails"; -@import "bootstrap/alerts"; -@import "bootstrap/progress-bars"; -@import "bootstrap/media"; -@import "bootstrap/list-group"; -@import "bootstrap/panels"; -@import "bootstrap/responsive-embed"; -@import "bootstrap/wells"; -@import "bootstrap/close"; - -// Components w/ JavaScript -@import "bootstrap/modals"; -@import "bootstrap/tooltip"; -@import "bootstrap/popovers"; -@import "bootstrap/carousel"; - -// Utility classes -@import "bootstrap/utilities"; -@import "bootstrap/responsive-utilities"; - @import "bootstrap-datetimepicker"; @import "bootstrap-colorselector"; @import "bootstrap-tagsinput"; diff --git a/app/assets/stylesheets/constants.scss b/app/assets/stylesheets/constants.scss index e22b9cccd..f1fe73841 100644 --- a/app/assets/stylesheets/constants.scss +++ b/app/assets/stylesheets/constants.scss @@ -1,509 +1,3 @@ -//============================================================================== -// Colors -//============================================================================== - -// Grayscale colors -/*$color-white: #fff; -$color-alabaster: #fcfcfc; -$color-concrete: #f2f2f2; -$color-gainsboro: #e3e3e3; -$color-alto: #d2d2d2; -$color-silver: #c5c5c5; -$color-silver-chalice: #a0a0a0; -$color-dove-gray: #666; -$color-emperor: #555; -$color-black: #000; - -// Theme colors -$brand-default: $color-white; -$brand-primary: #37a0d9; -$brand-primary-new: #104da9; -$brand-success: #2dbe61; -$brand-info: #5bc0de; -$brand-warning: #f0ad4e; -$brand-danger: #e54e42; -$brand-other: #8fd13f; -$brand-extra: #34495e; -$brand-primary-light: #dcedf6; -$brand-success-light: #e2eed8; -$brand-warning-light: #fcf7e4; -$brand-danger-light: #efdfdf; -$brand-light-blue: #e3f1f9; -$brand-academy: #de0170; -$brand-academy-dark: #c60064; - -// MS Office colors: -$office-ms-word: #2b579a; -$office-ms-excel: #217346; -$office-ms-powerpoint: #d24726; - -// MarvinJS color: -$marvinjs-color: #29999c; -*/ -//============================================================================== -// Other -//============================================================================== - -// Some big value which is still supported by all browsers -$infinity: 9999999; - - -//============================================================================== -// Bootstrap -//============================================================================== - -// Grayscale Colors in bootstrap -$gray-darker: $color-black; -$gray-dark: $color-emperor; -$gray: $color-emperor; -$gray-light: $color-dove-gray; -$gray-lighter: $color-concrete; - -// Scaffolding -// $body-bg: $color-concrete; -// $text-color: $color-emperor; -// $link-color: $brand-primary; -// $link-hover-color: darken($link-color, 15%); -// $link-hover-decoration: underline; - -// Typography -/*$font-family-lato: Lato, "Open Sans", Arial, Helvetica, sans-serif; -$font-family-sans-serif: "Open Sans", Arial, Helvetica, sans-serif; -$font-family-serif: Georgia, "Times New Roman", Times, serif; -$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; -$font-family-base: $font-family-lato; -$font-family-icons: "Font Awesome 5 Free"; -$font-size-base: 14px; -$font-size-large: ceil(($font-size-base * 1.1)); //16px -$font-size-small: ceil(($font-size-base * .9)); //13px -$font-size-h1: floor(($font-size-base * 2.6)); //36px -$font-size-h2: floor(($font-size-base * 2.15)); //30px -$font-size-h3: ceil(($font-size-base * 1.7)); //24px -$font-size-h4: ceil(($font-size-base * 1.25)); //18px -$font-size-h5: $font-size-base; //14px -$font-size-h6: ceil(($font-size-base * .85)); //12px -$line-height-base: 1.428571429; -$line-height-computed: floor(($font-size-base * $line-height-base)); -$headings-font-family: inherit; -$headings-font-weight: 500; -$headings-line-height: 1.1; -$headings-color: inherit;*/ - -// Components -$padding-base-vertical: 6px; -$padding-base-horizontal: 12px; -$padding-large-vertical: 10px; -$padding-large-horizontal: 16px; -$padding-small-vertical: 5px; -$padding-small-horizontal: 10px; -$padding-xs-vertical: 1px; -$padding-xs-horizontal: 5px; -$line-height-large: 1.3333333; -$line-height-small: 1.5; -$border-radius-base: 4px; -$border-radius-large: 6px; -$border-radius-small: 3px; -$component-active-color: $color-white; -$component-active-bg: $brand-primary; -$caret-width-base: 4px; -$caret-width-large: 5px; - -// // Tables -// $table-cell-padding: 8px; -// $table-condensed-cell-padding: 5px; -// $table-bg: transparent; -// $table-bg-accent: #f9f9f9; -// $table-bg-hover: #f5f5f5; -// $table-bg-active: $table-bg-hover; -// $table-border-color: #ddd; - -// Buttons -$btn-font-weight: normal; -$btn-default-color: $gray-dark; -$btn-default-bg: $brand-default; -$btn-default-border: $color-silver; -$btn-primary-color: $color-white; -$btn-primary-bg: $brand-primary; -$btn-primary-border: darken($btn-primary-bg, 5%); -$btn-primary-new-color: $color-white; -$btn-primary-new-bg: $brand-primary; -$btn-primary-new-border: darken($brand-primary , 5%); -$btn-success-color: $color-white; -$btn-success-bg: $brand-primary; -$btn-success-border: darken($btn-success-bg, 5%); -$btn-info-color: $color-white; -$btn-info-bg: $brand-primary; -$btn-info-border: $brand-primary; -$btn-warning-color: $color-white; -$btn-warning-bg: $brand-warning; -$btn-warning-border: darken($btn-warning-bg, 5%); -$btn-danger-color: $color-white; -$btn-danger-bg: $brand-danger; -$btn-danger-border: darken($btn-danger-bg, 5%); -$btn-link-disabled-color: $gray-light; -$btn-border-radius-base: $border-radius-base; -$btn-border-radius-large: $border-radius-large; -$btn-border-radius-small: $border-radius-small; - -// // Forms -// $input-bg: #fff; -// $input-bg-disabled: $gray-lighter; -// $input-color: $gray; -$input-border: #ccc; -// $input-border-radius: $border-radius-base; -// $input-border-radius-large: $border-radius-large; -// $input-border-radius-small: $border-radius-small; -// $input-border-focus: #66afe9; -// $input-color-placeholder: #999; -// $input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2); -// $input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2); -// $input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2); -// $form-group-margin-bottom: 15px; -// $legend-color: $gray-dark; -// $legend-border-color: #e5e5e5; -// $input-group-addon-bg: $gray-lighter; -// $input-group-addon-border-color: $input-border; -// $cursor-disabled: not-allowed; -// -// // Dropdowns -// $dropdown-bg: #fff; -// $dropdown-border: rgba(0,0,0,.15); -// $dropdown-fallback-border: #ccc; -// $dropdown-divider-bg: #e5e5e5; -// $dropdown-link-color: $gray-dark; -// $dropdown-link-hover-color: darken($gray-dark, 5%); -// $dropdown-link-hover-bg: #f5f5f5; -// $dropdown-link-active-color: $component-active-color; -// $dropdown-link-active-bg: $component-active-bg; -// $dropdown-link-disabled-color: $gray-light; -// $dropdown-header-color: $gray-light; -// $dropdown-caret-color: #000; -// -// // Media queries breakpoints -// $screen-xs: 480px; -// $screen-xs-min: $screen-xs; -// $screen-phone: $screen-xs-min; -// $screen-sm: 768px; -// $screen-sm-min: $screen-sm; -// $screen-tablet: $screen-sm-min; -// $screen-md: 992px; -// $screen-md-min: $screen-md; -// $screen-desktop: $screen-md-min; -// $screen-lg: 1200px; -// $screen-lg-min: $screen-lg; -// $screen-lg-desktop: $screen-lg-min; -// $screen-xs-max: ($screen-sm-min - 1); -// $screen-sm-max: ($screen-md-min - 1); -// $screen-md-max: ($screen-lg-min - 1); -// -// // Grid system -// $grid-columns: 12; -// $grid-gutter-width: 30px; -// $grid-float-breakpoint: $screen-sm-min; -// $grid-float-breakpoint-max: ($grid-float-breakpoint - 1); - -// Container sizes -$container-tablet: auto; -$container-sm: auto; -$container-desktop: auto; -$container-md: auto; -$container-large-desktop: auto; -$container-lg: auto; - -// // Navbar -// $navbar-height: 50px; -// $navbar-margin-bottom: $line-height-computed; -// $navbar-border-radius: $border-radius-base; -// $navbar-padding-horizontal: floor(($grid-gutter-width / 2)); -// $navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2); -// $navbar-collapse-max-height: 340px; -// $navbar-default-color: #777; -// $navbar-default-bg: #f8f8f8; -// $navbar-default-border: darken($navbar-default-bg, 6.5%); -// $navbar-default-link-color: #777; -// $navbar-default-link-hover-color: #333; -// $navbar-default-link-hover-bg: transparent; -// $navbar-default-link-active-color: #555; -// $navbar-default-link-active-bg: darken($navbar-default-bg, 6.5%); -// $navbar-default-link-disabled-color: #ccc; -// $navbar-default-link-disabled-bg: transparent; -// $navbar-default-brand-color: $navbar-default-link-color; -// $navbar-default-brand-hover-color: darken($navbar-default-brand-color, 10%); -// $navbar-default-brand-hover-bg: transparent; -// $navbar-default-toggle-hover-bg: #ddd; -// $navbar-default-toggle-icon-bar-bg: #888; -// $navbar-default-toggle-border-color: #ddd; -// -// // Inverted navbar -// $navbar-inverse-color: lighten($gray-light, 15%); -// $navbar-inverse-bg: #222; -// $navbar-inverse-border: darken($navbar-inverse-bg, 10%); -// $navbar-inverse-link-color: lighten($gray-light, 15%); -// $navbar-inverse-link-hover-color: #fff; -// $navbar-inverse-link-hover-bg: transparent; -// $navbar-inverse-link-active-color: $navbar-inverse-link-hover-color; -// $navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 10%); -// $navbar-inverse-link-disabled-color: #444; -// $navbar-inverse-link-disabled-bg: transparent; -// $navbar-inverse-brand-color: $navbar-inverse-link-color; -// $navbar-inverse-brand-hover-color: #fff; -// $navbar-inverse-brand-hover-bg: transparent; -// $navbar-inverse-toggle-hover-bg: #333; -// $navbar-inverse-toggle-icon-bar-bg: #fff; -// $navbar-inverse-toggle-border-color: #333; -// -// // Navs -// $nav-link-padding: 10px 15px; -// $nav-link-hover-bg: $gray-lighter; -// $nav-disabled-link-color: $gray-light; -// $nav-disabled-link-hover-color: $gray-light; -// -// // Tabs -// $nav-tabs-border-color: #ddd; -// $nav-tabs-link-hover-border-color: $gray-lighter; -// $nav-tabs-active-link-hover-bg: $body-bg; -// $nav-tabs-active-link-hover-color: $gray; -// $nav-tabs-active-link-hover-border-color: #ddd; -// $nav-tabs-justified-link-border-color: #ddd; -// $nav-tabs-justified-active-link-border-color: $body-bg; -// -// // Pills -// $nav-pills-border-radius: $border-radius-base; -// $nav-pills-active-link-hover-bg: $component-active-bg; -// $nav-pills-active-link-hover-color: $component-active-color; -// -// // Pagination -// $pagination-color: $link-color; -// $pagination-bg: #fff; -// $pagination-border: #ddd; -// $pagination-hover-color: $link-hover-color; -// $pagination-hover-bg: $gray-lighter; -// $pagination-hover-border: #ddd; -// $pagination-active-color: #fff; -// $pagination-active-bg: $brand-primary; -// $pagination-active-border: $brand-primary; -// $pagination-disabled-color: $gray-light; -// $pagination-disabled-bg: #fff; -// $pagination-disabled-border: #ddd; -// -// // Pager -// $pager-bg: $pagination-bg; -// $pager-border: $pagination-border; -// $pager-border-radius: 15px; -// $pager-hover-bg: $pagination-hover-bg; -// $pager-active-bg: $pagination-active-bg; -// $pager-active-color: $pagination-active-color; -// $pager-disabled-color: $pagination-disabled-color; -// -// // Jumbotron -// $jumbotron-padding: 30px; -// $jumbotron-color: inherit; -// $jumbotron-bg: $gray-lighter; -// $jumbotron-heading-color: inherit; -// $jumbotron-font-size: ceil(($font-size-base * 1.5)); -// $jumbotron-heading-font-size: ceil(($font-size-base * 4.5)); - -// Form states and alerts -$state-success-text: $brand-success; -$state-success-bg: $brand-success-light; -$state-success-border: adjust-hue(darken($state-success-bg, 5%), -10); -$state-info-text: $brand-focus; -$state-info-bg: $brand-focus-light; -$state-info-border: adjust-hue(darken($state-info-bg, 7%), -10); -$state-warning-text: $brand-warning; -$state-warning-bg: $brand-warning-light; -$state-warning-border: adjust-hue(darken($state-warning-bg, 5%), -10); -$state-danger-text: $brand-danger; -$state-danger-bg: $brand-danger-light; -$state-danger-border: adjust-hue(darken($state-danger-bg, 5%), -10); - -// // Tooltips -// $tooltip-max-width: 200px; -// $tooltip-color: #fff; -// $tooltip-bg: #000; -// $tooltip-opacity: .9; -// $tooltip-arrow-width: 5px; -// $tooltip-arrow-color: $tooltip-bg; -// -// // Popovers -// $popover-bg: #fff; -// $popover-max-width: 276px; -// $popover-border-color: rgba(0,0,0,.2); -// $popover-fallback-border-color: #ccc; -// $popover-title-bg: darken($popover-bg, 3%); -// $popover-arrow-width: 10px; -// $popover-arrow-color: $popover-bg; -// $popover-arrow-outer-width: ($popover-arrow-width + 1); -// $popover-arrow-outer-color: fadein($popover-border-color, 5%); -// $popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%); -// -// // Labels -// $label-default-bg: $gray-light; -// $label-primary-bg: $brand-primary; -// $label-success-bg: $brand-success; -// $label-info-bg: $brand-info; -// $label-warning-bg: $brand-warning; -// $label-danger-bg: $brand-danger; -// $label-color: #fff; -// $label-link-hover-color: #fff; -// -// // Modals -// $modal-inner-padding: 15px; -// $modal-title-padding: 15px; -// $modal-title-line-height: $line-height-base; -// $modal-content-bg: #fff; -// $modal-content-border-color: rgba(0,0,0,.2); -// $modal-content-fallback-border-color: #999; -// $modal-backdrop-bg: #000; -// $modal-backdrop-opacity: .5; -// $modal-header-border-color: #e5e5e5; -// $modal-footer-border-color: $modal-header-border-color; -// $modal-lg: 900px; -// $modal-md: 600px; -// $modal-sm: 300px; -// -// Alerts -$alert-padding: 15px; -$alert-border-radius: $border-radius-base; -$alert-link-font-weight: bold; -$alert-success-bg: $state-success-bg; -$alert-success-text: $state-success-text; -$alert-success-border: $state-success-border; -$alert-info-bg: $state-info-bg; -$alert-info-text: $state-info-text; -$alert-info-border: $state-info-border; -$alert-warning-bg: $state-warning-bg; -$alert-warning-text: $state-warning-text; -$alert-warning-border: $state-warning-border; -$alert-danger-bg: $state-danger-bg; -$alert-danger-text: $state-danger-text; -$alert-danger-border: $state-danger-border; -// -// // Progress bars -// $progress-bg: #f5f5f5; -// $progress-bar-color: #fff; -// $progress-border-radius: $border-radius-base; -// $progress-bar-bg: $brand-primary; -// $progress-bar-success-bg: $brand-success; -// $progress-bar-warning-bg: $brand-warning; -// $progress-bar-danger-bg: $brand-danger; -// $progress-bar-info-bg: $brand-info; -// -// // List group -// $list-group-bg: #fff; -// $list-group-border: #ddd; -// $list-group-border-radius: $border-radius-base; -// $list-group-hover-bg: #f5f5f5; -// $list-group-active-color: $component-active-color; -// $list-group-active-bg: $component-active-bg; -// $list-group-active-border: $list-group-active-bg; -// $list-group-active-text-color: lighten($list-group-active-bg, 40%); -// $list-group-disabled-color: $gray-light; -// $list-group-disabled-bg: $gray-lighter; -// $list-group-disabled-text-color: $list-group-disabled-color; -// $list-group-link-color: #555; -// $list-group-link-hover-color: $list-group-link-color; -// $list-group-link-heading-color: #333; -// -// // Panels -// $panel-bg: #fff; -// $panel-body-padding: 15px; -// $panel-heading-padding: 10px 15px; -// $panel-footer-padding: $panel-heading-padding; -// $panel-border-radius: $border-radius-base; -// $panel-inner-border: #ddd; -// $panel-footer-bg: #f5f5f5; -// $panel-default-text: $gray-dark; -// $panel-default-border: #ddd; -// $panel-default-heading-bg: #f5f5f5; -// $panel-primary-text: #fff; -// $panel-primary-border: $brand-primary; -// $panel-primary-heading-bg: $brand-primary; -// $panel-success-text: $state-success-text; -// $panel-success-border: $state-success-border; -// $panel-success-heading-bg: $state-success-bg; -// $panel-info-text: $state-info-text; -// $panel-info-border: $state-info-border; -// $panel-info-heading-bg: $state-info-bg; -// $panel-warning-text: $state-warning-text; -// $panel-warning-border: $state-warning-border; -// $panel-warning-heading-bg: $state-warning-bg; -// $panel-danger-text: $state-danger-text; -// $panel-danger-border: $state-danger-border; -// $panel-danger-heading-bg: $state-danger-bg; -// -// // Thumbnails -// $thumbnail-padding: 4px; -// $thumbnail-bg: $body-bg; -// $thumbnail-border: #ddd; -// $thumbnail-border-radius: $border-radius-base; -// $thumbnail-caption-color: $text-color; -// $thumbnail-caption-padding: 9px; -// -// // Wells -// $well-bg: #f5f5f5; -// $well-border: darken($well-bg, 7%); -// -// // Badges -// $badge-color: #fff; -// $badge-link-hover-color: #fff; -// $badge-bg: $gray-light; -// $badge-active-color: $link-color; -// $badge-active-bg: #fff; -// $badge-font-weight: bold; -// $badge-line-height: 1; -// $badge-border-radius: 10px; -// -// // Breadcrumbs -// $breadcrumb-padding-vertical: 8px; -// $breadcrumb-padding-horizontal: 15px; -// $breadcrumb-bg: #f5f5f5; -// $breadcrumb-color: #ccc; -// $breadcrumb-active-color: $gray-light; -// $breadcrumb-separator: "/"; -// -// // Carousel -// $carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6); -// $carousel-control-color: #fff; -// $carousel-control-width: 15%; -// $carousel-control-opacity: .5; -// $carousel-control-font-size: 20px; -// $carousel-indicator-active-bg: #fff; -// $carousel-indicator-border-color: #fff; -// $carousel-caption-color: #fff; -// -// // Close -// $close-font-weight: bold; -// $close-color: #000; -// $close-text-shadow: 0 1px 0 #fff; -// -// // Code -// $code-color: #c7254e; -// $code-bg: #f9f2f4; -// $kbd-color: #fff; -// $kbd-bg: #333; -// $pre-bg: #f5f5f5; -// $pre-color: $gray-dark; -// $pre-border-color: #ccc; -// $pre-scrollable-max-height: 340px; -// -// // Type -// $component-offset-horizontal: 180px; -// $text-muted: $gray-light; -// $abbr-border-color: $gray-light; -// $headings-small-color: $gray-light; -// $blockquote-small-color: $gray-light; -// $blockquote-font-size: ($font-size-base * 1.25); -// $blockquote-border-color: $gray-lighter; -// $page-header-border-color: $gray-lighter; -// $dl-horizontal-offset: $component-offset-horizontal; -// $dl-horizontal-breakpoint: $grid-float-breakpoint; -// $hr-border: $gray-lighter; -// -// -// -// // Material design - https://material.io/design/ -// // Card style $md-color-shadow-light: rgba(0, 0, 0, .12); $md-color-shadow-dark: rgba(0, 0, 0, .18); diff --git a/app/assets/stylesheets/notifications.scss b/app/assets/stylesheets/notifications.scss index 03744bdce..e59ded41b 100644 --- a/app/assets/stylesheets/notifications.scss +++ b/app/assets/stylesheets/notifications.scss @@ -53,7 +53,6 @@ border-radius: 50%; color: $color-concrete; display: inline-block; - font-size: $font-size-small; height: 30px; padding-top: 5px; width: 30px; @@ -64,7 +63,6 @@ border-radius: 50%; color: $color-concrete; display: inline-block; - font-size: $font-size-small; height: 30px; padding-top: 5px; width: 30px; @@ -75,7 +73,6 @@ border-radius: 50%; color: $color-concrete; display: inline-block; - font-size: $font-size-small; height: 30px; padding-top: 5px; width: 30px; @@ -86,7 +83,6 @@ border-radius: 50%; color: $color-concrete; display: inline-block; - font-size: $font-size-small; height: 30px; padding-top: 5px; width: 30px; diff --git a/app/assets/stylesheets/partials/_inline_editing.scss b/app/assets/stylesheets/partials/_inline_editing.scss index e099c1121..55eeec6f5 100644 --- a/app/assets/stylesheets/partials/_inline_editing.scss +++ b/app/assets/stylesheets/partials/_inline_editing.scss @@ -87,7 +87,7 @@ input { border: 1px solid $color-silver; - border-radius: $border-radius-small; + border-radius: $border-radius-default; cursor: pointer; display: block; line-height: 26px; diff --git a/app/assets/stylesheets/projects.scss b/app/assets/stylesheets/projects.scss index 70cf25b9d..97d33c292 100644 --- a/app/assets/stylesheets/projects.scss +++ b/app/assets/stylesheets/projects.scss @@ -428,7 +428,7 @@ path, ._jsPlumb_endpoint { span { font-weight: bold; - font-size: $font-size-large; + font-size: 20px; text-transform: uppercase; display: block; margin-top: 20%; @@ -517,7 +517,7 @@ li.module-hover { margin-top: 5px; font-family: 'Glyphicons Halflings'; color: $color-white; - font-size: $font-size-large; + font-size: 20px; &:before { content: "\e221"; diff --git a/app/assets/stylesheets/repository_columns/status_type.scss b/app/assets/stylesheets/repository_columns/status_type.scss index cd8ec745f..745993c97 100644 --- a/app/assets/stylesheets/repository_columns/status_type.scss +++ b/app/assets/stylesheets/repository_columns/status_type.scss @@ -16,7 +16,7 @@ transition: .3s; .status-item-field { - border: 1px solid $input-border; + border: 1px solid $color-alto; border-left: 0; border-radius: 0 4px 4px 0; flex-grow: 1; @@ -77,7 +77,7 @@ } .status-item-icon { - border-right-color: $input-border; + border-right-color: $color-alto; } &::before { diff --git a/app/assets/stylesheets/settings/teams.scss b/app/assets/stylesheets/settings/teams.scss index 8393b13ab..09e6bf0b5 100644 --- a/app/assets/stylesheets/settings/teams.scss +++ b/app/assets/stylesheets/settings/teams.scss @@ -63,7 +63,7 @@ .input-field { border: 1px solid $color-silver; - border-radius: $border-radius-small; + border-radius: $border-radius-default; cursor: pointer; display: block; float: left; @@ -175,7 +175,7 @@ .open { .input-field { border: 1px solid $brand-focus; - border-radius: $border-radius-small; + border-radius: $border-radius-default; .fa-search { color: $brand-focus; diff --git a/app/assets/stylesheets/shared/comments.scss b/app/assets/stylesheets/shared/comments.scss index ef5742c0a..5ebe718ce 100644 --- a/app/assets/stylesheets/shared/comments.scss +++ b/app/assets/stylesheets/shared/comments.scss @@ -45,7 +45,7 @@ .view-mode { border: 1px solid transparent; - border-radius: $border-radius-small; + border-radius: $border-radius-default; display: inline-block; line-height: 16px; min-height: 20px; @@ -60,7 +60,7 @@ textarea { border: 1px solid $color-silver; - border-radius: $border-radius-small; + border-radius: $border-radius-default; height: 22px; line-height: 16px; overflow: hidden; diff --git a/app/assets/stylesheets/shared/filter_dropdown.scss b/app/assets/stylesheets/shared/filter_dropdown.scss index 1cd1e54d6..38b7deb74 100644 --- a/app/assets/stylesheets/shared/filter_dropdown.scss +++ b/app/assets/stylesheets/shared/filter_dropdown.scss @@ -108,7 +108,7 @@ } .connect-line { - background: $input-border; + background: var(--sn-super-light-grey); height: 10px; margin: 5px auto; width: 2px; diff --git a/app/assets/stylesheets/shared_styles/constants/fonts.scss b/app/assets/stylesheets/shared_styles/constants/fonts.scss index e22b72840..f4e3becb1 100644 --- a/app/assets/stylesheets/shared_styles/constants/fonts.scss +++ b/app/assets/stylesheets/shared_styles/constants/fonts.scss @@ -86,3 +86,27 @@ html { // $headings-font-weight: 500; // $headings-line-height: 1.1; // $headings-color: inherit; + +h1 { + @include font-h1; +} + +h2 { + @include font-h2; +} + +h3 { + @include font-h3; +} + +h4 { + @include font-h3; +} + +h5 { + @include font-h3; +} + +h6 { + @include font-h3; +} diff --git a/app/assets/stylesheets/themes/main_navigation.scss b/app/assets/stylesheets/themes/main_navigation.scss index 190f90822..fd53fb8da 100644 --- a/app/assets/stylesheets/themes/main_navigation.scss +++ b/app/assets/stylesheets/themes/main_navigation.scss @@ -94,7 +94,6 @@ border-radius: 50%; color: $color-concrete; display: block; - font-size: $font-size-h3; height: 45px; padding-top: 5px; width: 45px; @@ -105,7 +104,6 @@ border-radius: 50%; color: $color-concrete; display: block; - font-size: $font-size-h3; height: 45px; padding-top: 5px; width: 45px; @@ -116,7 +114,6 @@ border-radius: 50%; color: $color-concrete; display: block; - font-size: $font-size-h3; height: 45px; padding-top: 5px; width: 45px; @@ -127,7 +124,6 @@ border-radius: 50%; color: $color-concrete; display: block; - font-size: $font-size-h3; height: 45px; padding-top: 8px; width: 45px; @@ -460,9 +456,5 @@ } - a { - font-size: $font-size-small; - font-weight: $headings-font-weight; - } } } diff --git a/app/assets/stylesheets/themes/repositories.scss b/app/assets/stylesheets/themes/repositories.scss index bea5e9f34..62c777bb1 100644 --- a/app/assets/stylesheets/themes/repositories.scss +++ b/app/assets/stylesheets/themes/repositories.scss @@ -24,16 +24,6 @@ float: right; } -.custom-alert-info { - background-color: $state-info-bg; - border: 1px solid $state-info-border; - color: $color-volcano; - margin: 10px 0; - opacity: .86; - padding: 15px; - width: 100%; -} - .repository-input-file-field { width: auto; @@ -46,10 +36,6 @@ justify-content: space-between; } - .form-group.has-error { - border-color: $state-danger-border; - } - button { height: 22px; margin: 5px; diff --git a/app/assets/stylesheets/themes/scinote.scss b/app/assets/stylesheets/themes/scinote.scss index 29c5b9760..15c9fa451 100644 --- a/app/assets/stylesheets/themes/scinote.scss +++ b/app/assets/stylesheets/themes/scinote.scss @@ -5,7 +5,6 @@ @import "mixins"; @import "main_navigation"; @import "repositories"; -@import "bootstrap/mixins/buttons"; /** Layout **/ // Word wrapping everywhere, except for table header of "datatables.js" @@ -91,7 +90,6 @@ table { .dropdown-header { background-color: $brand-primary; color: $color-concrete; - font-size: $font-size-base; font-weight: bold; padding: 8px 20px; @@ -105,10 +103,6 @@ table { margin-bottom: 5px; } -.blockquote-search { - font-size: $font-size-base; - margin-bottom: 10px; -} .vertical-spacer-one-half { display: inline-block; @@ -122,9 +116,6 @@ table { /** Skin **/ -body { - font-size: $font-size-small; -} .modal-body { font-size: $font-size-base; @@ -446,7 +437,6 @@ mark,.mark { padding: 13px; .fas.fa-info-circle { - font-size: $font-size-large; margin-top: 4px; } } @@ -1170,7 +1160,7 @@ body > .loading-overlay { visibility: hidden; width: 200px; word-wrap: break-word; - z-index: $infinity; + z-index: 10000; } } @@ -1385,7 +1375,6 @@ th.custom-field .modal-tooltiptext { .tooltip-open { background-color: $color-concrete; color: $color-black; - font-size: $font-size-base; .popover-footer { background-color: $color-gainsboro; diff --git a/app/assets/stylesheets/tiny_mce.scss b/app/assets/stylesheets/tiny_mce.scss index 13bd54b94..c6e2ca88a 100644 --- a/app/assets/stylesheets/tiny_mce.scss +++ b/app/assets/stylesheets/tiny_mce.scss @@ -102,7 +102,6 @@ .restore-draft-notification { align-items: center; - background: $state-info-bg !important; display: flex; flex-basis: 100%; height: 30px !important; diff --git a/app/datatables/protocols_datatable.rb b/app/datatables/protocols_datatable.rb index 9d09f67af..5ce0b5075 100644 --- a/app/datatables/protocols_datatable.rb +++ b/app/datatables/protocols_datatable.rb @@ -241,11 +241,12 @@ class ProtocolsDatatable < CustomDatatable def versions_html(record) @view.controller .render_to_string(partial: 'protocols/index/protocol_versions', + formats: :html, locals: { protocol: record, readable: can_read_protocol_in_repository?(@user, record) }) end def access_html(record) - @view.controller.render_to_string(partial: 'protocols/index/protocol_access', locals: { protocol: record }) + @view.controller.render_to_string(partial: 'protocols/index/protocol_access', formats: :html, locals: { protocol: record }) end def published_by(record) diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 40b03aee9..e36e385bc 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -1,6 +1,23 @@ +window.$ = window.jQuery = require('jquery'); + +require('jquery-ujs'); +require('jquery-mousewheel'); +require('jquery-autosize'); +require('jquery-ui/ui/widget'); +require('jquery-ui/ui/widgets/mouse'); +require('jquery-ui/ui/widgets/draggable'); +require('jquery-ui/ui/widgets/droppable'); +require('jquery-ui/ui/effects/effect-slide'); +require('hammerjs'); + +import 'bootstrap'; +window.moment = require('moment'); +require('bootstrap-select/js/bootstrap-select'); + window.bwipjs = require('bwip-js'); window.Decimal = require('decimal.js'); + $(document).on('click', '.sci--layout--menu-item[data-submenu=true]', (e) => { const item = $(e.currentTarget); const caret = item.find('.show-submenu'); diff --git a/app/javascript/packs/application.scss b/app/javascript/packs/application.scss new file mode 100644 index 000000000..848cb108a --- /dev/null +++ b/app/javascript/packs/application.scss @@ -0,0 +1 @@ +@import "bootstrap-select/sass/bootstrap-select" diff --git a/app/javascript/packs/bootstrap.scss b/app/javascript/packs/bootstrap.scss new file mode 100644 index 000000000..327bd375d --- /dev/null +++ b/app/javascript/packs/bootstrap.scss @@ -0,0 +1 @@ +@import "~bootstrap/dist/css/bootstrap.css"; diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index d9d7f1b05..4cb5f848c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -11,14 +11,16 @@ <% end %> <%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %> + <%= stylesheet_link_tag 'bootstrap', media: 'all' %> <%= stylesheet_link_tag 'application', media: 'all' %> + <%= stylesheet_link_tag 'application_pack', media: 'all' %> <%= csp_meta_tag %> <% if ::NewRelic::Agent.instance.started? %> <%= ::NewRelic::Agent.browser_timing_header(controller.request.content_security_policy_nonce) %> <% end %> + <%= javascript_include_tag 'application_pack', "data-turbolinks-track": :reload %> <%= javascript_include_tag 'jquery_bundle' %> <%= javascript_include_tag 'application' %> - <%= javascript_include_tag 'application_pack' %> <%= javascript_include_tag 'session_end' %> <%= javascript_include_tag 'sidebar_toggle' %> diff --git a/app/views/layouts/sign_in_halt.html.erb b/app/views/layouts/sign_in_halt.html.erb index fdccb04a5..9d78fa610 100644 --- a/app/views/layouts/sign_in_halt.html.erb +++ b/app/views/layouts/sign_in_halt.html.erb @@ -9,9 +9,12 @@ + <%= stylesheet_link_tag 'bootstrap', media: 'all' %> <%= stylesheet_link_tag 'application', media: 'all' %> + <%= stylesheet_link_tag 'application_pack', media: 'all' %> <%= javascript_include_tag 'jquery_bundle' %> <%= javascript_include_tag 'application' %> + <%= javascript_include_tag 'application_pack' %> <%= favicon_link_tag "favicon.ico" %> <%= favicon_link_tag "favicon-16.png", type: "image/png", size: "16x16" %> diff --git a/config/webpack/webpack.config.js b/config/webpack/webpack.config.js index b2bbee377..c69d14f86 100644 --- a/config/webpack/webpack.config.js +++ b/config/webpack/webpack.config.js @@ -11,6 +11,7 @@ const mode = process.env.NODE_ENV === 'development' ? 'development' : 'productio const entryList = { application_pack: './app/javascript/packs/application.js', + application_pack_styles: './app/javascript/packs/application.scss', emoji_button: './app/javascript/packs/emoji_button.js', fontawesome: './app/javascript/packs/fontawesome.scss', prism: './app/javascript/packs/prism.js', @@ -20,6 +21,7 @@ const entryList = { tui_image_editor_styles: './app/javascript/packs/tui_image_editor_styles.scss', croppie: './app/javascript/packs/custom/croppie.js', croppie_styles: './app/javascript/packs/custom/croppie_styles.scss', + bootstrap: './app/javascript/packs/bootstrap.scss', inputmask: './app/javascript/packs/custom/inputmask.js', pdfjs: './app/javascript/packs/pdfjs/pdf_js.js', pdf_js_styles: './app/javascript/packs/pdfjs/pdf_js_styles.scss', @@ -114,6 +116,10 @@ module.exports = { new VueLoaderPlugin(), new RemoveEmptyScriptsPlugin(), new MiniCssExtractPlugin(), - new NodePolyfillPlugin() + new NodePolyfillPlugin(), + new webpack.ProvidePlugin({ + $: 'jquery', + jQuery: 'jquery', + }) ] }; diff --git a/package.json b/package.json index 8f45e4ae4..b44ff290c 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,8 @@ "autoprefixer": "10.4.14", "babel-loader": "^8.2.5", "babel-plugin-macros": "^3.1.0", - "bootstrap-sass": "^3.3.7", + "bootstrap": "^3.4.1", + "bootstrap-select": "^1.13.18", "bwip-js": "^3.0.1", "compression-webpack-plugin": "8.0.1", "croppie": "^2.6.4", @@ -59,13 +60,21 @@ "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^6.2.0", "glob": "^7.1.2", + "hammerjs": "^2.0.8", "immutability-helper": "^2.7.1", "inputmask": "^5.0.0", "intl": "^1.2.5", + "jquery": "2.2.4", + "jquery-autosize": "^1.18.18", + "jquery-mousewheel": "^3.1.13", + "jquery-ui": "^1.13.2", + "jquery-ujs": "^1.2.3", "js-yaml": "^3.12.0", "jsdom": "16.6.0", "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.7.5", + "moment": "^2.29.4", + "momentjs": "^2.0.0", "node-gyp": "9.3.1", "node-polyfill-webpack-plugin": "^2.0.1", "pdfjs-dist": "^2.5.207", diff --git a/vendor/assets/javascripts/bootstrap-datetimepicker.js b/vendor/assets/javascripts/bootstrap-datetimepicker.js new file mode 100644 index 000000000..8838cbcd2 --- /dev/null +++ b/vendor/assets/javascripts/bootstrap-datetimepicker.js @@ -0,0 +1,2636 @@ +/*! version : 4.17.47 + ========================================================= + bootstrap-datetimejs + https://github.com/Eonasdan/bootstrap-datetimepicker + Copyright (c) 2015 Jonathan Peterson + ========================================================= + */ +/* + The MIT License (MIT) + + Copyright (c) 2015 Jonathan Peterson + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ +/*global define:false */ +/*global exports:false */ +/*global require:false */ +/*global jQuery:false */ +/*global moment:false */ +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // AMD is used - Register as an anonymous module. + define(['jquery', 'moment'], factory); + } else if (typeof exports === 'object') { + module.exports = factory(require('jquery'), require('moment')); + } else { + // Neither AMD nor CommonJS used. Use global variables. + if (typeof jQuery === 'undefined') { + throw 'bootstrap-datetimepicker requires jQuery to be loaded first'; + } + if (typeof moment === 'undefined') { + throw 'bootstrap-datetimepicker requires Moment.js to be loaded first'; + } + factory(jQuery, moment); + } +}(function ($, moment) { + 'use strict'; + if (!moment) { + throw new Error('bootstrap-datetimepicker requires Moment.js to be loaded first'); + } + + var dateTimePicker = function (element, options) { + var picker = {}, + date, + viewDate, + unset = true, + input, + component = false, + widget = false, + use24Hours, + minViewModeNumber = 0, + actualFormat, + parseFormats, + currentViewMode, + datePickerModes = [ + { + clsName: 'days', + navFnc: 'M', + navStep: 1 + }, + { + clsName: 'months', + navFnc: 'y', + navStep: 1 + }, + { + clsName: 'years', + navFnc: 'y', + navStep: 10 + }, + { + clsName: 'decades', + navFnc: 'y', + navStep: 100 + } + ], + viewModes = ['days', 'months', 'years', 'decades'], + verticalModes = ['top', 'bottom', 'auto'], + horizontalModes = ['left', 'right', 'auto'], + toolbarPlacements = ['default', 'top', 'bottom'], + keyMap = { + 'up': 38, + 38: 'up', + 'down': 40, + 40: 'down', + 'left': 37, + 37: 'left', + 'right': 39, + 39: 'right', + 'tab': 9, + 9: 'tab', + 'escape': 27, + 27: 'escape', + 'enter': 13, + 13: 'enter', + 'pageUp': 33, + 33: 'pageUp', + 'pageDown': 34, + 34: 'pageDown', + 'shift': 16, + 16: 'shift', + 'control': 17, + 17: 'control', + 'space': 32, + 32: 'space', + 't': 84, + 84: 't', + 'delete': 46, + 46: 'delete' + }, + keyState = {}, + + /******************************************************************************** + * + * Private functions + * + ********************************************************************************/ + + hasTimeZone = function () { + return moment.tz !== undefined && options.timeZone !== undefined && options.timeZone !== null && options.timeZone !== ''; + }, + + getMoment = function (d) { + var returnMoment; + + if (d === undefined || d === null) { + returnMoment = moment(); //TODO should this use format? and locale? + } else if (moment.isDate(d) || moment.isMoment(d)) { + // If the date that is passed in is already a Date() or moment() object, + // pass it directly to moment. + returnMoment = moment(d); + } else if (hasTimeZone()) { // There is a string to parse and a default time zone + // parse with the tz function which takes a default time zone if it is not in the format string + returnMoment = moment.tz(d, parseFormats, options.useStrict, options.timeZone); + } else { + returnMoment = moment(d, parseFormats, options.useStrict); + } + + if (hasTimeZone()) { + returnMoment.tz(options.timeZone); + } + + return returnMoment; + }, + + isEnabled = function (granularity) { + if (typeof granularity !== 'string' || granularity.length > 1) { + throw new TypeError('isEnabled expects a single character string parameter'); + } + switch (granularity) { + case 'y': + return actualFormat.indexOf('Y') !== -1; + case 'M': + return actualFormat.indexOf('M') !== -1; + case 'd': + return actualFormat.toLowerCase().indexOf('d') !== -1; + case 'h': + case 'H': + return actualFormat.toLowerCase().indexOf('h') !== -1; + case 'm': + return actualFormat.indexOf('m') !== -1; + case 's': + return actualFormat.indexOf('s') !== -1; + default: + return false; + } + }, + + hasTime = function () { + return (isEnabled('h') || isEnabled('m') || isEnabled('s')); + }, + + hasDate = function () { + return (isEnabled('y') || isEnabled('M') || isEnabled('d')); + }, + + getDatePickerTemplate = function () { + var headTemplate = $('') + .append($('') + .append($('').addClass('prev').attr('data-action', 'previous') + .append($('').addClass(options.icons.previous)) + ) + .append($('').addClass('picker-switch').attr('data-action', 'pickerSwitch').attr('colspan', (options.calendarWeeks ? '6' : '5'))) + .append($('').addClass('next').attr('data-action', 'next') + .append($('').addClass(options.icons.next)) + ) + ), + contTemplate = $('') + .append($('') + .append($('').attr('colspan', (options.calendarWeeks ? '8' : '7'))) + ); + + return [ + $('
').addClass('datepicker-days') + .append($('').addClass('table-condensed') + .append(headTemplate) + .append($('')) + ), + $('
').addClass('datepicker-months') + .append($('
').addClass('table-condensed') + .append(headTemplate.clone()) + .append(contTemplate.clone()) + ), + $('
').addClass('datepicker-years') + .append($('
').addClass('table-condensed') + .append(headTemplate.clone()) + .append(contTemplate.clone()) + ), + $('
').addClass('datepicker-decades') + .append($('
').addClass('table-condensed') + .append(headTemplate.clone()) + .append(contTemplate.clone()) + ) + ]; + }, + + getTimePickerMainTemplate = function () { + var topRow = $(''), + middleRow = $(''), + bottomRow = $(''); + + if (isEnabled('h')) { + topRow.append($('
') + .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementHour }).addClass('btn').attr('data-action', 'incrementHours').append($('').addClass(options.icons.up)))); + middleRow.append($('') + .append($('').addClass('timepicker-hour').attr({ 'data-time-component': 'hours', 'title': options.tooltips.pickHour }).attr('data-action', 'showHours'))); + bottomRow.append($('') + .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementHour }).addClass('btn').attr('data-action', 'decrementHours').append($('').addClass(options.icons.down)))); + } + if (isEnabled('m')) { + if (isEnabled('h')) { + topRow.append($('').addClass('separator')); + middleRow.append($('').addClass('separator').html(':')); + bottomRow.append($('').addClass('separator')); + } + topRow.append($('') + .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementMinute }).addClass('btn').attr('data-action', 'incrementMinutes') + .append($('').addClass(options.icons.up)))); + middleRow.append($('') + .append($('').addClass('timepicker-minute').attr({ 'data-time-component': 'minutes', 'title': options.tooltips.pickMinute }).attr('data-action', 'showMinutes'))); + bottomRow.append($('') + .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementMinute }).addClass('btn').attr('data-action', 'decrementMinutes') + .append($('').addClass(options.icons.down)))); + } + if (isEnabled('s')) { + if (isEnabled('m')) { + topRow.append($('').addClass('separator')); + middleRow.append($('').addClass('separator').html(':')); + bottomRow.append($('').addClass('separator')); + } + topRow.append($('') + .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.incrementSecond }).addClass('btn').attr('data-action', 'incrementSeconds') + .append($('').addClass(options.icons.up)))); + middleRow.append($('') + .append($('').addClass('timepicker-second').attr({ 'data-time-component': 'seconds', 'title': options.tooltips.pickSecond }).attr('data-action', 'showSeconds'))); + bottomRow.append($('') + .append($('').attr({ href: '#', tabindex: '-1', 'title': options.tooltips.decrementSecond }).addClass('btn').attr('data-action', 'decrementSeconds') + .append($('').addClass(options.icons.down)))); + } + + if (!use24Hours) { + topRow.append($('').addClass('separator')); + middleRow.append($('') + .append($('').addClass('separator')); + } + + return $('
').addClass('timepicker-picker') + .append($('').addClass('table-condensed') + .append([topRow, middleRow, bottomRow])); + }, + + getTimePickerTemplate = function () { + var hoursView = $('
').addClass('timepicker-hours') + .append($('
').addClass('table-condensed')), + minutesView = $('
').addClass('timepicker-minutes') + .append($('
').addClass('table-condensed')), + secondsView = $('
').addClass('timepicker-seconds') + .append($('
').addClass('table-condensed')), + ret = [getTimePickerMainTemplate()]; + + if (isEnabled('h')) { + ret.push(hoursView); + } + if (isEnabled('m')) { + ret.push(minutesView); + } + if (isEnabled('s')) { + ret.push(secondsView); + } + + return ret; + }, + + getToolbar = function () { + var row = []; + if (options.showTodayButton) { + row.push($('
').append($('').attr({ 'data-action': 'today', 'title': options.tooltips.today }).append($('').addClass(options.icons.today)))); + } + if (!options.sideBySide && hasDate() && hasTime()) { + row.push($('').append($('').attr({ 'data-action': 'togglePicker', 'title': options.tooltips.selectTime }).append($('').addClass(options.icons.time)))); + } + if (options.showClear) { + row.push($('').append($('').attr({ 'data-action': 'clear', 'title': options.tooltips.clear }).append($('').addClass(options.icons.clear)))); + } + if (options.showClose) { + row.push($('').append($('').attr({ 'data-action': 'close', 'title': options.tooltips.close }).append($('').addClass(options.icons.close)))); + } + return $('').addClass('table-condensed').append($('').append($('').append(row))); + }, + + getTemplate = function () { + var template = $('
').addClass('bootstrap-datetimepicker-widget dropdown-menu'), + dateView = $('
').addClass('datepicker').append(getDatePickerTemplate()), + timeView = $('
').addClass('timepicker').append(getTimePickerTemplate()), + content = $('
    ').addClass('list-unstyled'), + toolbar = $('
  • ').addClass('picker-switch' + (options.collapse ? ' accordion-toggle' : '')).append(getToolbar()); + + if (options.inline) { + template.removeClass('dropdown-menu'); + } + + if (use24Hours) { + template.addClass('usetwentyfour'); + } + + if (isEnabled('s') && !use24Hours) { + template.addClass('wider'); + } + + if (options.sideBySide && hasDate() && hasTime()) { + template.addClass('timepicker-sbs'); + if (options.toolbarPlacement === 'top') { + template.append(toolbar); + } + template.append( + $('
    ').addClass('row') + .append(dateView.addClass('col-md-6')) + .append(timeView.addClass('col-md-6')) + ); + if (options.toolbarPlacement === 'bottom') { + template.append(toolbar); + } + return template; + } + + if (options.toolbarPlacement === 'top') { + content.append(toolbar); + } + if (hasDate()) { + content.append($('
  • ').addClass((options.collapse && hasTime() ? 'collapse in' : '')).append(dateView)); + } + if (options.toolbarPlacement === 'default') { + content.append(toolbar); + } + if (hasTime()) { + content.append($('
  • ').addClass((options.collapse && hasDate() ? 'collapse' : '')).append(timeView)); + } + if (options.toolbarPlacement === 'bottom') { + content.append(toolbar); + } + return template.append(content); + }, + + dataToOptions = function () { + var eData, + dataOptions = {}; + + if (element.is('input') || options.inline) { + eData = element.data(); + } else { + eData = element.find('input').data(); + } + + if (eData.dateOptions && eData.dateOptions instanceof Object) { + dataOptions = $.extend(true, dataOptions, eData.dateOptions); + } + + $.each(options, function (key) { + var attributeName = 'date' + key.charAt(0).toUpperCase() + key.slice(1); + if (eData[attributeName] !== undefined) { + dataOptions[key] = eData[attributeName]; + } + }); + return dataOptions; + }, + + place = function () { + var position = (component || element).position(), + offset = (component || element).offset(), + vertical = options.widgetPositioning.vertical, + horizontal = options.widgetPositioning.horizontal, + parent; + + if (options.widgetParent) { + parent = options.widgetParent.append(widget); + } else if (element.is('input')) { + parent = element.after(widget).parent(); + } else if (options.inline) { + parent = element.append(widget); + return; + } else { + parent = element; + element.children().first().after(widget); + } + + // Top and bottom logic + if (vertical === 'auto') { + if (offset.top + widget.height() * 1.5 >= $(window).height() + $(window).scrollTop() && + widget.height() + element.outerHeight() < offset.top) { + vertical = 'top'; + } else { + vertical = 'bottom'; + } + } + + // Left and right logic + if (horizontal === 'auto') { + if (parent.width() < offset.left + widget.outerWidth() / 2 && + offset.left + widget.outerWidth() > $(window).width()) { + horizontal = 'right'; + } else { + horizontal = 'left'; + } + } + + if (vertical === 'top') { + widget.addClass('top').removeClass('bottom'); + } else { + widget.addClass('bottom').removeClass('top'); + } + + if (horizontal === 'right') { + widget.addClass('pull-right'); + } else { + widget.removeClass('pull-right'); + } + + // find the first parent element that has a non-static css positioning + if (parent.css('position') === 'static') { + parent = parent.parents().filter(function () { + return $(this).css('position') !== 'static'; + }).first(); + } + + if (parent.length === 0) { + throw new Error('datetimepicker component should be placed within a non-static positioned container'); + } + + widget.css({ + top: vertical === 'top' ? 'auto' : position.top + element.outerHeight(), + bottom: vertical === 'top' ? parent.outerHeight() - (parent === element ? 0 : position.top) : 'auto', + left: horizontal === 'left' ? (parent === element ? 0 : position.left) : 'auto', + right: horizontal === 'left' ? 'auto' : parent.outerWidth() - element.outerWidth() - (parent === element ? 0 : position.left) + }); + }, + + notifyEvent = function (e) { + if (e.type === 'dp.change' && ((e.date && e.date.isSame(e.oldDate)) || (!e.date && !e.oldDate))) { + return; + } + element.trigger(e); + }, + + viewUpdate = function (e) { + if (e === 'y') { + e = 'YYYY'; + } + notifyEvent({ + type: 'dp.update', + change: e, + viewDate: viewDate.clone() + }); + }, + + showMode = function (dir) { + if (!widget) { + return; + } + if (dir) { + currentViewMode = Math.max(minViewModeNumber, Math.min(3, currentViewMode + dir)); + } + widget.find('.datepicker > div').hide().filter('.datepicker-' + datePickerModes[currentViewMode].clsName).show(); + }, + + fillDow = function () { + var row = $('
'), + currentDate = viewDate.clone().startOf('w').startOf('d'); + + if (options.calendarWeeks === true) { + row.append($(''); + if (options.calendarWeeks) { + row.append(''); + } + html.push(row); + } + clsNames = ['day']; + if (currentDate.isBefore(viewDate, 'M')) { + clsNames.push('old'); + } + if (currentDate.isAfter(viewDate, 'M')) { + clsNames.push('new'); + } + if (currentDate.isSame(date, 'd') && !unset) { + clsNames.push('active'); + } + if (!isValid(currentDate, 'd')) { + clsNames.push('disabled'); + } + if (currentDate.isSame(getMoment(), 'd')) { + clsNames.push('today'); + } + if (currentDate.day() === 0 || currentDate.day() === 6) { + clsNames.push('weekend'); + } + notifyEvent({ + type: 'dp.classify', + date: currentDate, + classNames: clsNames + }); + row.append(''); + currentDate.add(1, 'd'); + } + + daysView.find('tbody').empty().append(html); + + updateMonths(); + + updateYears(); + + updateDecades(); + }, + + fillHours = function () { + var table = widget.find('.timepicker-hours table'), + currentHour = viewDate.clone().startOf('d'), + html = [], + row = $(''); + + if (viewDate.hour() > 11 && !use24Hours) { + currentHour.hour(12); + } + while (currentHour.isSame(viewDate, 'd') && (use24Hours || (viewDate.hour() < 12 && currentHour.hour() < 12) || viewDate.hour() > 11)) { + if (currentHour.hour() % 4 === 0) { + row = $(''); + html.push(row); + } + row.append(''); + currentHour.add(1, 'h'); + } + table.empty().append(html); + }, + + fillMinutes = function () { + var table = widget.find('.timepicker-minutes table'), + currentMinute = viewDate.clone().startOf('h'), + html = [], + row = $(''), + step = options.stepping === 1 ? 5 : options.stepping; + + while (viewDate.isSame(currentMinute, 'h')) { + if (currentMinute.minute() % (step * 4) === 0) { + row = $(''); + html.push(row); + } + row.append(''); + currentMinute.add(step, 'm'); + } + table.empty().append(html); + }, + + fillSeconds = function () { + var table = widget.find('.timepicker-seconds table'), + currentSecond = viewDate.clone().startOf('m'), + html = [], + row = $(''); + + while (viewDate.isSame(currentSecond, 'm')) { + if (currentSecond.second() % 20 === 0) { + row = $(''); + html.push(row); + } + row.append(''); + currentSecond.add(5, 's'); + } + + table.empty().append(html); + }, + + fillTime = function () { + var toggle, newDate, timeComponents = widget.find('.timepicker span[data-time-component]'); + + if (!use24Hours) { + toggle = widget.find('.timepicker [data-action=togglePeriod]'); + newDate = date.clone().add((date.hours() >= 12) ? -12 : 12, 'h'); + + toggle.text(date.format('A')); + + if (isValid(newDate, 'h')) { + toggle.removeClass('disabled'); + } else { + toggle.addClass('disabled'); + } + } + timeComponents.filter('[data-time-component=hours]').text(date.format(use24Hours ? 'HH' : 'hh')); + timeComponents.filter('[data-time-component=minutes]').text(date.format('mm')); + timeComponents.filter('[data-time-component=seconds]').text(date.format('ss')); + + fillHours(); + fillMinutes(); + fillSeconds(); + }, + + update = function () { + if (!widget) { + return; + } + fillDate(); + fillTime(); + }, + + setValue = function (targetMoment) { + var oldDate = unset ? null : date; + + // case of calling setValue(null or false) + if (!targetMoment) { + unset = true; + input.val(''); + element.data('date', ''); + notifyEvent({ + type: 'dp.change', + date: false, + oldDate: oldDate + }); + update(); + return; + } + + targetMoment = targetMoment.clone().locale(options.locale); + + if (hasTimeZone()) { + targetMoment.tz(options.timeZone); + } + + if (options.stepping !== 1) { + targetMoment.minutes((Math.round(targetMoment.minutes() / options.stepping) * options.stepping)).seconds(0); + + while (options.minDate && targetMoment.isBefore(options.minDate)) { + targetMoment.add(options.stepping, 'minutes'); + } + } + + if (isValid(targetMoment)) { + date = targetMoment; + viewDate = date.clone(); + input.val(date.format(actualFormat)); + element.data('date', date.format(actualFormat)); + unset = false; + update(); + notifyEvent({ + type: 'dp.change', + date: date.clone(), + oldDate: oldDate + }); + } else { + if (!options.keepInvalid) { + input.val(unset ? '' : date.format(actualFormat)); + } else { + notifyEvent({ + type: 'dp.change', + date: targetMoment, + oldDate: oldDate + }); + } + notifyEvent({ + type: 'dp.error', + date: targetMoment, + oldDate: oldDate + }); + } + }, + + /** + * Hides the widget. Possibly will emit dp.hide + */ + hide = function () { + var transitioning = false; + if (!widget) { + return picker; + } + // Ignore event if in the middle of a picker transition + widget.find('.collapse').each(function () { + var collapseData = $(this).data('collapse'); + if (collapseData && collapseData.transitioning) { + transitioning = true; + return false; + } + return true; + }); + if (transitioning) { + return picker; + } + if (component && component.hasClass('btn')) { + component.toggleClass('active'); + } + widget.hide(); + + $(window).off('resize', place); + widget.off('click', '[data-action]'); + widget.off('mousedown', false); + + widget.remove(); + widget = false; + + notifyEvent({ + type: 'dp.hide', + date: date.clone() + }); + + input.blur(); + + viewDate = date.clone(); + + return picker; + }, + + clear = function () { + setValue(null); + }, + + parseInputDate = function (inputDate) { + if (options.parseInputDate === undefined) { + if (!moment.isMoment(inputDate) || inputDate instanceof Date) { + inputDate = getMoment(inputDate); + } + } else { + inputDate = options.parseInputDate(inputDate); + } + //inputDate.locale(options.locale); + return inputDate; + }, + + /******************************************************************************** + * + * Widget UI interaction functions + * + ********************************************************************************/ + actions = { + next: function () { + var navFnc = datePickerModes[currentViewMode].navFnc; + viewDate.add(datePickerModes[currentViewMode].navStep, navFnc); + fillDate(); + viewUpdate(navFnc); + }, + + previous: function () { + var navFnc = datePickerModes[currentViewMode].navFnc; + viewDate.subtract(datePickerModes[currentViewMode].navStep, navFnc); + fillDate(); + viewUpdate(navFnc); + }, + + pickerSwitch: function () { + showMode(1); + }, + + selectMonth: function (e) { + var month = $(e.target).closest('tbody').find('span').index($(e.target)); + viewDate.month(month); + if (currentViewMode === minViewModeNumber) { + setValue(date.clone().year(viewDate.year()).month(viewDate.month())); + if (!options.inline) { + hide(); + } + } else { + showMode(-1); + fillDate(); + } + viewUpdate('M'); + }, + + selectYear: function (e) { + var year = parseInt($(e.target).text(), 10) || 0; + viewDate.year(year); + if (currentViewMode === minViewModeNumber) { + setValue(date.clone().year(viewDate.year())); + if (!options.inline) { + hide(); + } + } else { + showMode(-1); + fillDate(); + } + viewUpdate('YYYY'); + }, + + selectDecade: function (e) { + var year = parseInt($(e.target).data('selection'), 10) || 0; + viewDate.year(year); + if (currentViewMode === minViewModeNumber) { + setValue(date.clone().year(viewDate.year())); + if (!options.inline) { + hide(); + } + } else { + showMode(-1); + fillDate(); + } + viewUpdate('YYYY'); + }, + + selectDay: function (e) { + var day = viewDate.clone(); + if ($(e.target).is('.old')) { + day.subtract(1, 'M'); + } + if ($(e.target).is('.new')) { + day.add(1, 'M'); + } + setValue(day.date(parseInt($(e.target).text(), 10))); + if (!hasTime() && !options.keepOpen && !options.inline) { + hide(); + } + }, + + incrementHours: function () { + var newDate = date.clone().add(1, 'h'); + if (isValid(newDate, 'h')) { + setValue(newDate); + } + }, + + incrementMinutes: function () { + var newDate = date.clone().add(options.stepping, 'm'); + if (isValid(newDate, 'm')) { + setValue(newDate); + } + }, + + incrementSeconds: function () { + var newDate = date.clone().add(1, 's'); + if (isValid(newDate, 's')) { + setValue(newDate); + } + }, + + decrementHours: function () { + var newDate = date.clone().subtract(1, 'h'); + if (isValid(newDate, 'h')) { + setValue(newDate); + } + }, + + decrementMinutes: function () { + var newDate = date.clone().subtract(options.stepping, 'm'); + if (isValid(newDate, 'm')) { + setValue(newDate); + } + }, + + decrementSeconds: function () { + var newDate = date.clone().subtract(1, 's'); + if (isValid(newDate, 's')) { + setValue(newDate); + } + }, + + togglePeriod: function () { + setValue(date.clone().add((date.hours() >= 12) ? -12 : 12, 'h')); + }, + + togglePicker: function (e) { + var $this = $(e.target), + $parent = $this.closest('ul'), + expanded = $parent.find('.in'), + closed = $parent.find('.collapse:not(.in)'), + collapseData; + + if (expanded && expanded.length) { + collapseData = expanded.data('collapse'); + if (collapseData && collapseData.transitioning) { + return; + } + if (expanded.collapse) { // if collapse plugin is available through bootstrap.js then use it + expanded.collapse('hide'); + closed.collapse('show'); + } else { // otherwise just toggle in class on the two views + expanded.removeClass('in'); + closed.addClass('in'); + } + if ($this.is('span')) { + $this.toggleClass(options.icons.time + ' ' + options.icons.date); + } else { + $this.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); + } + + // NOTE: uncomment if toggled state will be restored in show() + //if (component) { + // component.find('span').toggleClass(options.icons.time + ' ' + options.icons.date); + //} + } + }, + + showPicker: function () { + widget.find('.timepicker > div:not(.timepicker-picker)').hide(); + widget.find('.timepicker .timepicker-picker').show(); + }, + + showHours: function () { + widget.find('.timepicker .timepicker-picker').hide(); + widget.find('.timepicker .timepicker-hours').show(); + }, + + showMinutes: function () { + widget.find('.timepicker .timepicker-picker').hide(); + widget.find('.timepicker .timepicker-minutes').show(); + }, + + showSeconds: function () { + widget.find('.timepicker .timepicker-picker').hide(); + widget.find('.timepicker .timepicker-seconds').show(); + }, + + selectHour: function (e) { + var hour = parseInt($(e.target).text(), 10); + + if (!use24Hours) { + if (date.hours() >= 12) { + if (hour !== 12) { + hour += 12; + } + } else { + if (hour === 12) { + hour = 0; + } + } + } + setValue(date.clone().hours(hour)); + actions.showPicker.call(picker); + }, + + selectMinute: function (e) { + setValue(date.clone().minutes(parseInt($(e.target).text(), 10))); + actions.showPicker.call(picker); + }, + + selectSecond: function (e) { + setValue(date.clone().seconds(parseInt($(e.target).text(), 10))); + actions.showPicker.call(picker); + }, + + clear: clear, + + today: function () { + var todaysDate = getMoment(); + if (isValid(todaysDate, 'd')) { + setValue(todaysDate); + } + }, + + close: hide + }, + + doAction = function (e) { + if ($(e.currentTarget).is('.disabled')) { + return false; + } + actions[$(e.currentTarget).data('action')].apply(picker, arguments); + return false; + }, + + /** + * Shows the widget. Possibly will emit dp.show and dp.change + */ + show = function () { + var currentMoment, + useCurrentGranularity = { + 'year': function (m) { + return m.month(0).date(1).hours(0).seconds(0).minutes(0); + }, + 'month': function (m) { + return m.date(1).hours(0).seconds(0).minutes(0); + }, + 'day': function (m) { + return m.hours(0).seconds(0).minutes(0); + }, + 'hour': function (m) { + return m.seconds(0).minutes(0); + }, + 'minute': function (m) { + return m.seconds(0); + } + }; + + if (input.prop('disabled') || (!options.ignoreReadonly && input.prop('readonly')) || widget) { + return picker; + } + if (input.val() !== undefined && input.val().trim().length !== 0) { + setValue(parseInputDate(input.val().trim())); + } else if (unset && options.useCurrent && (options.inline || (input.is('input') && input.val().trim().length === 0))) { + currentMoment = getMoment(); + if (typeof options.useCurrent === 'string') { + currentMoment = useCurrentGranularity[options.useCurrent](currentMoment); + } + setValue(currentMoment); + } + widget = getTemplate(); + + fillDow(); + fillMonths(); + + widget.find('.timepicker-hours').hide(); + widget.find('.timepicker-minutes').hide(); + widget.find('.timepicker-seconds').hide(); + + update(); + showMode(); + + $(window).on('resize', place); + widget.on('click', '[data-action]', doAction); // this handles clicks on the widget + widget.on('mousedown', false); + + if (component && component.hasClass('btn')) { + component.toggleClass('active'); + } + place(); + widget.show(); + if (options.focusOnShow && !input.is(':focus')) { + input.focus(); + } + + notifyEvent({ + type: 'dp.show' + }); + return picker; + }, + + /** + * Shows or hides the widget + */ + toggle = function () { + return (widget ? hide() : show()); + }, + + keydown = function (e) { + var handler = null, + index, + index2, + pressedKeys = [], + pressedModifiers = {}, + currentKey = e.which, + keyBindKeys, + allModifiersPressed, + pressed = 'p'; + + keyState[currentKey] = pressed; + + for (index in keyState) { + if (keyState.hasOwnProperty(index) && keyState[index] === pressed) { + pressedKeys.push(index); + if (parseInt(index, 10) !== currentKey) { + pressedModifiers[index] = true; + } + } + } + + for (index in options.keyBinds) { + if (options.keyBinds.hasOwnProperty(index) && typeof (options.keyBinds[index]) === 'function') { + keyBindKeys = index.split(' '); + if (keyBindKeys.length === pressedKeys.length && keyMap[currentKey] === keyBindKeys[keyBindKeys.length - 1]) { + allModifiersPressed = true; + for (index2 = keyBindKeys.length - 2; index2 >= 0; index2--) { + if (!(keyMap[keyBindKeys[index2]] in pressedModifiers)) { + allModifiersPressed = false; + break; + } + } + if (allModifiersPressed) { + handler = options.keyBinds[index]; + break; + } + } + } + } + + if (handler) { + handler.call(picker, widget); + e.stopPropagation(); + e.preventDefault(); + } + }, + + keyup = function (e) { + keyState[e.which] = 'r'; + e.stopPropagation(); + e.preventDefault(); + }, + + change = function (e) { + var val = $(e.target).val().trim(), + parsedDate = val ? parseInputDate(val) : null; + setValue(parsedDate); + e.stopImmediatePropagation(); + return false; + }, + + attachDatePickerElementEvents = function () { + input.on({ + 'change': change, + 'blur': options.debug ? '' : hide, + 'keydown': keydown, + 'keyup': keyup, + 'focus': options.allowInputToggle ? show : '' + }); + + if (element.is('input')) { + input.on({ + 'focus': show + }); + } else if (component) { + component.on('click', toggle); + component.on('mousedown', false); + } + }, + + detachDatePickerElementEvents = function () { + input.off({ + 'change': change, + 'blur': blur, + 'keydown': keydown, + 'keyup': keyup, + 'focus': options.allowInputToggle ? hide : '' + }); + + if (element.is('input')) { + input.off({ + 'focus': show + }); + } else if (component) { + component.off('click', toggle); + component.off('mousedown', false); + } + }, + + indexGivenDates = function (givenDatesArray) { + // Store given enabledDates and disabledDates as keys. + // This way we can check their existence in O(1) time instead of looping through whole array. + // (for example: options.enabledDates['2014-02-27'] === true) + var givenDatesIndexed = {}; + $.each(givenDatesArray, function () { + var dDate = parseInputDate(this); + if (dDate.isValid()) { + givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true; + } + }); + return (Object.keys(givenDatesIndexed).length) ? givenDatesIndexed : false; + }, + + indexGivenHours = function (givenHoursArray) { + // Store given enabledHours and disabledHours as keys. + // This way we can check their existence in O(1) time instead of looping through whole array. + // (for example: options.enabledHours['2014-02-27'] === true) + var givenHoursIndexed = {}; + $.each(givenHoursArray, function () { + givenHoursIndexed[this] = true; + }); + return (Object.keys(givenHoursIndexed).length) ? givenHoursIndexed : false; + }, + + initFormatting = function () { + var format = options.format || 'L LT'; + + actualFormat = format.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput) { + var newinput = date.localeData().longDateFormat(formatInput) || formatInput; + return newinput.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, function (formatInput2) { //temp fix for #740 + return date.localeData().longDateFormat(formatInput2) || formatInput2; + }); + }); + + + parseFormats = options.extraFormats ? options.extraFormats.slice() : []; + if (parseFormats.indexOf(format) < 0 && parseFormats.indexOf(actualFormat) < 0) { + parseFormats.push(actualFormat); + } + + use24Hours = (actualFormat.toLowerCase().indexOf('a') < 1 && actualFormat.replace(/\[.*?\]/g, '').indexOf('h') < 1); + + if (isEnabled('y')) { + minViewModeNumber = 2; + } + if (isEnabled('M')) { + minViewModeNumber = 1; + } + if (isEnabled('d')) { + minViewModeNumber = 0; + } + + currentViewMode = Math.max(minViewModeNumber, currentViewMode); + + if (!unset) { + setValue(date); + } + }; + + /******************************************************************************** + * + * Public API functions + * ===================== + * + * Important: Do not expose direct references to private objects or the options + * object to the outer world. Always return a clone when returning values or make + * a clone when setting a private variable. + * + ********************************************************************************/ + picker.destroy = function () { + ///Destroys the widget and removes all attached event listeners + hide(); + detachDatePickerElementEvents(); + element.removeData('DateTimePicker'); + element.removeData('date'); + }; + + picker.toggle = toggle; + + picker.show = show; + + picker.hide = hide; + + picker.disable = function () { + ///Disables the input element, the component is attached to, by adding a disabled="true" attribute to it. + ///If the widget was visible before that call it is hidden. Possibly emits dp.hide + hide(); + if (component && component.hasClass('btn')) { + component.addClass('disabled'); + } + input.prop('disabled', true); + return picker; + }; + + picker.enable = function () { + ///Enables the input element, the component is attached to, by removing disabled attribute from it. + if (component && component.hasClass('btn')) { + component.removeClass('disabled'); + } + input.prop('disabled', false); + return picker; + }; + + picker.ignoreReadonly = function (ignoreReadonly) { + if (arguments.length === 0) { + return options.ignoreReadonly; + } + if (typeof ignoreReadonly !== 'boolean') { + throw new TypeError('ignoreReadonly () expects a boolean parameter'); + } + options.ignoreReadonly = ignoreReadonly; + return picker; + }; + + picker.options = function (newOptions) { + if (arguments.length === 0) { + return $.extend(true, {}, options); + } + + if (!(newOptions instanceof Object)) { + throw new TypeError('options() options parameter should be an object'); + } + $.extend(true, options, newOptions); + $.each(options, function (key, value) { + if (picker[key] !== undefined) { + picker[key](value); + } else { + throw new TypeError('option ' + key + ' is not recognized!'); + } + }); + return picker; + }; + + picker.date = function (newDate) { + /// + ///Returns the component's model current date, a moment object or null if not set. + ///date.clone() + /// + /// + ///Sets the components model current moment to it. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration. + ///Takes string, Date, moment, null parameter. + /// + if (arguments.length === 0) { + if (unset) { + return null; + } + return date.clone(); + } + + if (newDate !== null && typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { + throw new TypeError('date() parameter must be one of [null, string, moment or Date]'); + } + + setValue(newDate === null ? null : parseInputDate(newDate)); + return picker; + }; + + picker.format = function (newFormat) { + ///test su + ///info about para + ///returns foo + if (arguments.length === 0) { + return options.format; + } + + if ((typeof newFormat !== 'string') && ((typeof newFormat !== 'boolean') || (newFormat !== false))) { + throw new TypeError('format() expects a string or boolean:false parameter ' + newFormat); + } + + options.format = newFormat; + if (actualFormat) { + initFormatting(); // reinit formatting + } + return picker; + }; + + picker.timeZone = function (newZone) { + if (arguments.length === 0) { + return options.timeZone; + } + + if (typeof newZone !== 'string') { + throw new TypeError('newZone() expects a string parameter'); + } + + options.timeZone = newZone; + + return picker; + }; + + picker.dayViewHeaderFormat = function (newFormat) { + if (arguments.length === 0) { + return options.dayViewHeaderFormat; + } + + if (typeof newFormat !== 'string') { + throw new TypeError('dayViewHeaderFormat() expects a string parameter'); + } + + options.dayViewHeaderFormat = newFormat; + return picker; + }; + + picker.extraFormats = function (formats) { + if (arguments.length === 0) { + return options.extraFormats; + } + + if (formats !== false && !(formats instanceof Array)) { + throw new TypeError('extraFormats() expects an array or false parameter'); + } + + options.extraFormats = formats; + if (parseFormats) { + initFormatting(); // reinit formatting + } + return picker; + }; + + picker.disabledDates = function (dates) { + /// + ///Returns an array with the currently set disabled dates on the component. + ///options.disabledDates + /// + /// + ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of + ///options.enabledDates if such exist. + ///Takes an [ string or Date or moment ] of values and allows the user to select only from those days. + /// + if (arguments.length === 0) { + return (options.disabledDates ? $.extend({}, options.disabledDates) : options.disabledDates); + } + + if (!dates) { + options.disabledDates = false; + update(); + return picker; + } + if (!(dates instanceof Array)) { + throw new TypeError('disabledDates() expects an array parameter'); + } + options.disabledDates = indexGivenDates(dates); + options.enabledDates = false; + update(); + return picker; + }; + + picker.enabledDates = function (dates) { + /// + ///Returns an array with the currently set enabled dates on the component. + ///options.enabledDates + /// + /// + ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledDates if such exist. + ///Takes an [ string or Date or moment ] of values and allows the user to select only from those days. + /// + if (arguments.length === 0) { + return (options.enabledDates ? $.extend({}, options.enabledDates) : options.enabledDates); + } + + if (!dates) { + options.enabledDates = false; + update(); + return picker; + } + if (!(dates instanceof Array)) { + throw new TypeError('enabledDates() expects an array parameter'); + } + options.enabledDates = indexGivenDates(dates); + options.disabledDates = false; + update(); + return picker; + }; + + picker.daysOfWeekDisabled = function (daysOfWeekDisabled) { + if (arguments.length === 0) { + return options.daysOfWeekDisabled.splice(0); + } + + if ((typeof daysOfWeekDisabled === 'boolean') && !daysOfWeekDisabled) { + options.daysOfWeekDisabled = false; + update(); + return picker; + } + + if (!(daysOfWeekDisabled instanceof Array)) { + throw new TypeError('daysOfWeekDisabled() expects an array parameter'); + } + options.daysOfWeekDisabled = daysOfWeekDisabled.reduce(function (previousValue, currentValue) { + currentValue = parseInt(currentValue, 10); + if (currentValue > 6 || currentValue < 0 || isNaN(currentValue)) { + return previousValue; + } + if (previousValue.indexOf(currentValue) === -1) { + previousValue.push(currentValue); + } + return previousValue; + }, []).sort(); + if (options.useCurrent && !options.keepInvalid) { + var tries = 0; + while (!isValid(date, 'd')) { + date.add(1, 'd'); + if (tries === 31) { + throw 'Tried 31 times to find a valid date'; + } + tries++; + } + setValue(date); + } + update(); + return picker; + }; + + picker.maxDate = function (maxDate) { + if (arguments.length === 0) { + return options.maxDate ? options.maxDate.clone() : options.maxDate; + } + + if ((typeof maxDate === 'boolean') && maxDate === false) { + options.maxDate = false; + update(); + return picker; + } + + if (typeof maxDate === 'string') { + if (maxDate === 'now' || maxDate === 'moment') { + maxDate = getMoment(); + } + } + + var parsedDate = parseInputDate(maxDate); + + if (!parsedDate.isValid()) { + throw new TypeError('maxDate() Could not parse date parameter: ' + maxDate); + } + if (options.minDate && parsedDate.isBefore(options.minDate)) { + throw new TypeError('maxDate() date parameter is before options.minDate: ' + parsedDate.format(actualFormat)); + } + options.maxDate = parsedDate; + if (options.useCurrent && !options.keepInvalid && date.isAfter(maxDate)) { + setValue(options.maxDate); + } + if (viewDate.isAfter(parsedDate)) { + viewDate = parsedDate.clone().subtract(options.stepping, 'm'); + } + update(); + return picker; + }; + + picker.minDate = function (minDate) { + if (arguments.length === 0) { + return options.minDate ? options.minDate.clone() : options.minDate; + } + + if ((typeof minDate === 'boolean') && minDate === false) { + options.minDate = false; + update(); + return picker; + } + + if (typeof minDate === 'string') { + if (minDate === 'now' || minDate === 'moment') { + minDate = getMoment(); + } + } + + var parsedDate = parseInputDate(minDate); + + if (!parsedDate.isValid()) { + throw new TypeError('minDate() Could not parse date parameter: ' + minDate); + } + if (options.maxDate && parsedDate.isAfter(options.maxDate)) { + throw new TypeError('minDate() date parameter is after options.maxDate: ' + parsedDate.format(actualFormat)); + } + options.minDate = parsedDate; + if (options.useCurrent && !options.keepInvalid && date.isBefore(minDate)) { + setValue(options.minDate); + } + if (viewDate.isBefore(parsedDate)) { + viewDate = parsedDate.clone().add(options.stepping, 'm'); + } + update(); + return picker; + }; + + picker.defaultDate = function (defaultDate) { + /// + ///Returns a moment with the options.defaultDate option configuration or false if not set + ///date.clone() + /// + /// + ///Will set the picker's inital date. If a boolean:false value is passed the options.defaultDate parameter is cleared. + ///Takes a string, Date, moment, boolean:false + /// + if (arguments.length === 0) { + return options.defaultDate ? options.defaultDate.clone() : options.defaultDate; + } + if (!defaultDate) { + options.defaultDate = false; + return picker; + } + + if (typeof defaultDate === 'string') { + if (defaultDate === 'now' || defaultDate === 'moment') { + defaultDate = getMoment(); + } else { + defaultDate = getMoment(defaultDate); + } + } + + var parsedDate = parseInputDate(defaultDate); + if (!parsedDate.isValid()) { + throw new TypeError('defaultDate() Could not parse date parameter: ' + defaultDate); + } + if (!isValid(parsedDate)) { + throw new TypeError('defaultDate() date passed is invalid according to component setup validations'); + } + + options.defaultDate = parsedDate; + + if ((options.defaultDate && options.inline) || input.val().trim() === '') { + setValue(options.defaultDate); + } + return picker; + }; + + picker.locale = function (locale) { + if (arguments.length === 0) { + return options.locale; + } + + if (!moment.localeData(locale)) { + throw new TypeError('locale() locale ' + locale + ' is not loaded from moment locales!'); + } + + options.locale = locale; + date.locale(options.locale); + viewDate.locale(options.locale); + + if (actualFormat) { + initFormatting(); // reinit formatting + } + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.stepping = function (stepping) { + if (arguments.length === 0) { + return options.stepping; + } + + stepping = parseInt(stepping, 10); + if (isNaN(stepping) || stepping < 1) { + stepping = 1; + } + options.stepping = stepping; + return picker; + }; + + picker.useCurrent = function (useCurrent) { + var useCurrentOptions = ['year', 'month', 'day', 'hour', 'minute']; + if (arguments.length === 0) { + return options.useCurrent; + } + + if ((typeof useCurrent !== 'boolean') && (typeof useCurrent !== 'string')) { + throw new TypeError('useCurrent() expects a boolean or string parameter'); + } + if (typeof useCurrent === 'string' && useCurrentOptions.indexOf(useCurrent.toLowerCase()) === -1) { + throw new TypeError('useCurrent() expects a string parameter of ' + useCurrentOptions.join(', ')); + } + options.useCurrent = useCurrent; + return picker; + }; + + picker.collapse = function (collapse) { + if (arguments.length === 0) { + return options.collapse; + } + + if (typeof collapse !== 'boolean') { + throw new TypeError('collapse() expects a boolean parameter'); + } + if (options.collapse === collapse) { + return picker; + } + options.collapse = collapse; + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.icons = function (icons) { + if (arguments.length === 0) { + return $.extend({}, options.icons); + } + + if (!(icons instanceof Object)) { + throw new TypeError('icons() expects parameter to be an Object'); + } + $.extend(options.icons, icons); + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.tooltips = function (tooltips) { + if (arguments.length === 0) { + return $.extend({}, options.tooltips); + } + + if (!(tooltips instanceof Object)) { + throw new TypeError('tooltips() expects parameter to be an Object'); + } + $.extend(options.tooltips, tooltips); + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.useStrict = function (useStrict) { + if (arguments.length === 0) { + return options.useStrict; + } + + if (typeof useStrict !== 'boolean') { + throw new TypeError('useStrict() expects a boolean parameter'); + } + options.useStrict = useStrict; + return picker; + }; + + picker.sideBySide = function (sideBySide) { + if (arguments.length === 0) { + return options.sideBySide; + } + + if (typeof sideBySide !== 'boolean') { + throw new TypeError('sideBySide() expects a boolean parameter'); + } + options.sideBySide = sideBySide; + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.viewMode = function (viewMode) { + if (arguments.length === 0) { + return options.viewMode; + } + + if (typeof viewMode !== 'string') { + throw new TypeError('viewMode() expects a string parameter'); + } + + if (viewModes.indexOf(viewMode) === -1) { + throw new TypeError('viewMode() parameter must be one of (' + viewModes.join(', ') + ') value'); + } + + options.viewMode = viewMode; + currentViewMode = Math.max(viewModes.indexOf(viewMode), minViewModeNumber); + + showMode(); + return picker; + }; + + picker.toolbarPlacement = function (toolbarPlacement) { + if (arguments.length === 0) { + return options.toolbarPlacement; + } + + if (typeof toolbarPlacement !== 'string') { + throw new TypeError('toolbarPlacement() expects a string parameter'); + } + if (toolbarPlacements.indexOf(toolbarPlacement) === -1) { + throw new TypeError('toolbarPlacement() parameter must be one of (' + toolbarPlacements.join(', ') + ') value'); + } + options.toolbarPlacement = toolbarPlacement; + + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.widgetPositioning = function (widgetPositioning) { + if (arguments.length === 0) { + return $.extend({}, options.widgetPositioning); + } + + if (({}).toString.call(widgetPositioning) !== '[object Object]') { + throw new TypeError('widgetPositioning() expects an object variable'); + } + if (widgetPositioning.horizontal) { + if (typeof widgetPositioning.horizontal !== 'string') { + throw new TypeError('widgetPositioning() horizontal variable must be a string'); + } + widgetPositioning.horizontal = widgetPositioning.horizontal.toLowerCase(); + if (horizontalModes.indexOf(widgetPositioning.horizontal) === -1) { + throw new TypeError('widgetPositioning() expects horizontal parameter to be one of (' + horizontalModes.join(', ') + ')'); + } + options.widgetPositioning.horizontal = widgetPositioning.horizontal; + } + if (widgetPositioning.vertical) { + if (typeof widgetPositioning.vertical !== 'string') { + throw new TypeError('widgetPositioning() vertical variable must be a string'); + } + widgetPositioning.vertical = widgetPositioning.vertical.toLowerCase(); + if (verticalModes.indexOf(widgetPositioning.vertical) === -1) { + throw new TypeError('widgetPositioning() expects vertical parameter to be one of (' + verticalModes.join(', ') + ')'); + } + options.widgetPositioning.vertical = widgetPositioning.vertical; + } + update(); + return picker; + }; + + picker.calendarWeeks = function (calendarWeeks) { + if (arguments.length === 0) { + return options.calendarWeeks; + } + + if (typeof calendarWeeks !== 'boolean') { + throw new TypeError('calendarWeeks() expects parameter to be a boolean value'); + } + + options.calendarWeeks = calendarWeeks; + update(); + return picker; + }; + + picker.showTodayButton = function (showTodayButton) { + if (arguments.length === 0) { + return options.showTodayButton; + } + + if (typeof showTodayButton !== 'boolean') { + throw new TypeError('showTodayButton() expects a boolean parameter'); + } + + options.showTodayButton = showTodayButton; + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.showClear = function (showClear) { + if (arguments.length === 0) { + return options.showClear; + } + + if (typeof showClear !== 'boolean') { + throw new TypeError('showClear() expects a boolean parameter'); + } + + options.showClear = showClear; + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.widgetParent = function (widgetParent) { + if (arguments.length === 0) { + return options.widgetParent; + } + + if (typeof widgetParent === 'string') { + widgetParent = $(widgetParent); + } + + if (widgetParent !== null && (typeof widgetParent !== 'string' && !(widgetParent instanceof $))) { + throw new TypeError('widgetParent() expects a string or a jQuery object parameter'); + } + + options.widgetParent = widgetParent; + if (widget) { + hide(); + show(); + } + return picker; + }; + + picker.keepOpen = function (keepOpen) { + if (arguments.length === 0) { + return options.keepOpen; + } + + if (typeof keepOpen !== 'boolean') { + throw new TypeError('keepOpen() expects a boolean parameter'); + } + + options.keepOpen = keepOpen; + return picker; + }; + + picker.focusOnShow = function (focusOnShow) { + if (arguments.length === 0) { + return options.focusOnShow; + } + + if (typeof focusOnShow !== 'boolean') { + throw new TypeError('focusOnShow() expects a boolean parameter'); + } + + options.focusOnShow = focusOnShow; + return picker; + }; + + picker.inline = function (inline) { + if (arguments.length === 0) { + return options.inline; + } + + if (typeof inline !== 'boolean') { + throw new TypeError('inline() expects a boolean parameter'); + } + + options.inline = inline; + return picker; + }; + + picker.clear = function () { + clear(); + return picker; + }; + + picker.keyBinds = function (keyBinds) { + if (arguments.length === 0) { + return options.keyBinds; + } + + options.keyBinds = keyBinds; + return picker; + }; + + picker.getMoment = function (d) { + return getMoment(d); + }; + + picker.debug = function (debug) { + if (typeof debug !== 'boolean') { + throw new TypeError('debug() expects a boolean parameter'); + } + + options.debug = debug; + return picker; + }; + + picker.allowInputToggle = function (allowInputToggle) { + if (arguments.length === 0) { + return options.allowInputToggle; + } + + if (typeof allowInputToggle !== 'boolean') { + throw new TypeError('allowInputToggle() expects a boolean parameter'); + } + + options.allowInputToggle = allowInputToggle; + return picker; + }; + + picker.showClose = function (showClose) { + if (arguments.length === 0) { + return options.showClose; + } + + if (typeof showClose !== 'boolean') { + throw new TypeError('showClose() expects a boolean parameter'); + } + + options.showClose = showClose; + return picker; + }; + + picker.keepInvalid = function (keepInvalid) { + if (arguments.length === 0) { + return options.keepInvalid; + } + + if (typeof keepInvalid !== 'boolean') { + throw new TypeError('keepInvalid() expects a boolean parameter'); + } + options.keepInvalid = keepInvalid; + return picker; + }; + + picker.datepickerInput = function (datepickerInput) { + if (arguments.length === 0) { + return options.datepickerInput; + } + + if (typeof datepickerInput !== 'string') { + throw new TypeError('datepickerInput() expects a string parameter'); + } + + options.datepickerInput = datepickerInput; + return picker; + }; + + picker.parseInputDate = function (parseInputDate) { + if (arguments.length === 0) { + return options.parseInputDate; + } + + if (typeof parseInputDate !== 'function') { + throw new TypeError('parseInputDate() sholud be as function'); + } + + options.parseInputDate = parseInputDate; + + return picker; + }; + + picker.disabledTimeIntervals = function (disabledTimeIntervals) { + /// + ///Returns an array with the currently set disabled dates on the component. + ///options.disabledTimeIntervals + /// + /// + ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of + ///options.enabledDates if such exist. + ///Takes an [ string or Date or moment ] of values and allows the user to select only from those days. + /// + if (arguments.length === 0) { + return (options.disabledTimeIntervals ? $.extend({}, options.disabledTimeIntervals) : options.disabledTimeIntervals); + } + + if (!disabledTimeIntervals) { + options.disabledTimeIntervals = false; + update(); + return picker; + } + if (!(disabledTimeIntervals instanceof Array)) { + throw new TypeError('disabledTimeIntervals() expects an array parameter'); + } + options.disabledTimeIntervals = disabledTimeIntervals; + update(); + return picker; + }; + + picker.disabledHours = function (hours) { + /// + ///Returns an array with the currently set disabled hours on the component. + ///options.disabledHours + /// + /// + ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of + ///options.enabledHours if such exist. + ///Takes an [ int ] of values and disallows the user to select only from those hours. + /// + if (arguments.length === 0) { + return (options.disabledHours ? $.extend({}, options.disabledHours) : options.disabledHours); + } + + if (!hours) { + options.disabledHours = false; + update(); + return picker; + } + if (!(hours instanceof Array)) { + throw new TypeError('disabledHours() expects an array parameter'); + } + options.disabledHours = indexGivenHours(hours); + options.enabledHours = false; + if (options.useCurrent && !options.keepInvalid) { + var tries = 0; + while (!isValid(date, 'h')) { + date.add(1, 'h'); + if (tries === 24) { + throw 'Tried 24 times to find a valid date'; + } + tries++; + } + setValue(date); + } + update(); + return picker; + }; + + picker.enabledHours = function (hours) { + /// + ///Returns an array with the currently set enabled hours on the component. + ///options.enabledHours + /// + /// + ///Setting this takes precedence over options.minDate, options.maxDate configuration. Also calling this function removes the configuration of options.disabledHours if such exist. + ///Takes an [ int ] of values and allows the user to select only from those hours. + /// + if (arguments.length === 0) { + return (options.enabledHours ? $.extend({}, options.enabledHours) : options.enabledHours); + } + + if (!hours) { + options.enabledHours = false; + update(); + return picker; + } + if (!(hours instanceof Array)) { + throw new TypeError('enabledHours() expects an array parameter'); + } + options.enabledHours = indexGivenHours(hours); + options.disabledHours = false; + if (options.useCurrent && !options.keepInvalid) { + var tries = 0; + while (!isValid(date, 'h')) { + date.add(1, 'h'); + if (tries === 24) { + throw 'Tried 24 times to find a valid date'; + } + tries++; + } + setValue(date); + } + update(); + return picker; + }; + /** + * Returns the component's model current viewDate, a moment object or null if not set. Passing a null value unsets the components model current moment. Parsing of the newDate parameter is made using moment library with the options.format and options.useStrict components configuration. + * @param {Takes string, viewDate, moment, null parameter.} newDate + * @returns {viewDate.clone()} + */ + picker.viewDate = function (newDate) { + if (arguments.length === 0) { + return viewDate.clone(); + } + + if (!newDate) { + viewDate = date.clone(); + return picker; + } + + if (typeof newDate !== 'string' && !moment.isMoment(newDate) && !(newDate instanceof Date)) { + throw new TypeError('viewDate() parameter must be one of [string, moment or Date]'); + } + + viewDate = parseInputDate(newDate); + viewUpdate(); + return picker; + }; + + // initializing element and component attributes + if (element.is('input')) { + input = element; + } else { + input = element.find(options.datepickerInput); + if (input.length === 0) { + input = element.find('input'); + } else if (!input.is('input')) { + throw new Error('CSS class "' + options.datepickerInput + '" cannot be applied to non input element'); + } + } + + if (element.hasClass('input-group')) { + // in case there is more then one 'input-group-addon' Issue #48 + if (element.find('.datepickerbutton').length === 0) { + component = element.find('.input-group-addon'); + } else { + component = element.find('.datepickerbutton'); + } + } + + if (!options.inline && !input.is('input')) { + throw new Error('Could not initialize DateTimePicker without an input element'); + } + + // Set defaults for date here now instead of in var declaration + date = getMoment(); + viewDate = date.clone(); + + $.extend(true, options, dataToOptions()); + + picker.options(options); + + initFormatting(); + + attachDatePickerElementEvents(); + + if (input.prop('disabled')) { + picker.disable(); + } + if (input.is('input') && input.val().trim().length !== 0) { + setValue(parseInputDate(input.val().trim())); + } + else if (options.defaultDate && input.attr('placeholder') === undefined) { + setValue(options.defaultDate); + } + if (options.inline) { + show(); + } + return picker; + }; + + /******************************************************************************** + * + * jQuery plugin constructor and defaults object + * + ********************************************************************************/ + + /** + * See (http://jquery.com/). + * @name jQuery + * @class + * See the jQuery Library (http://jquery.com/) for full details. This just + * documents the function and classes that are added to jQuery by this plug-in. + */ + /** + * See (http://jquery.com/) + * @name fn + * @class + * See the jQuery Library (http://jquery.com/) for full details. This just + * documents the function and classes that are added to jQuery by this plug-in. + * @memberOf jQuery + */ + /** + * Show comments + * @class datetimepicker + * @memberOf jQuery.fn + */ + $.fn.datetimepicker = function (options) { + options = options || {}; + + var args = Array.prototype.slice.call(arguments, 1), + isInstance = true, + thisMethods = ['destroy', 'hide', 'show', 'toggle'], + returnValue; + + if (typeof options === 'object') { + return this.each(function () { + var $this = $(this), + _options; + if (!$this.data('DateTimePicker')) { + // create a private copy of the defaults object + _options = $.extend(true, {}, $.fn.datetimepicker.defaults, options); + $this.data('DateTimePicker', dateTimePicker($this, _options)); + } + }); + } else if (typeof options === 'string') { + this.each(function () { + var $this = $(this), + instance = $this.data('DateTimePicker'); + if (!instance) { + throw new Error('bootstrap-datetimepicker("' + options + '") method was called on an element that is not using DateTimePicker'); + } + + returnValue = instance[options].apply(instance, args); + isInstance = returnValue === instance; + }); + + if (isInstance || $.inArray(options, thisMethods) > -1) { + return this; + } + + return returnValue; + } + + throw new TypeError('Invalid arguments for DateTimePicker: ' + options); + }; + + $.fn.datetimepicker.defaults = { + timeZone: '', + format: false, + dayViewHeaderFormat: 'MMMM YYYY', + extraFormats: false, + stepping: 1, + minDate: false, + maxDate: false, + useCurrent: true, + collapse: true, + locale: moment.locale(), + defaultDate: false, + disabledDates: false, + enabledDates: false, + icons: { + time: 'glyphicon glyphicon-time', + date: 'glyphicon glyphicon-calendar', + up: 'glyphicon glyphicon-chevron-up', + down: 'glyphicon glyphicon-chevron-down', + previous: 'glyphicon glyphicon-chevron-left', + next: 'glyphicon glyphicon-chevron-right', + today: 'glyphicon glyphicon-screenshot', + clear: 'glyphicon glyphicon-trash', + close: 'glyphicon glyphicon-remove' + }, + tooltips: { + today: 'Go to today', + clear: 'Clear selection', + close: 'Close the picker', + selectMonth: 'Select Month', + prevMonth: 'Previous Month', + nextMonth: 'Next Month', + selectYear: 'Select Year', + prevYear: 'Previous Year', + nextYear: 'Next Year', + selectDecade: 'Select Decade', + prevDecade: 'Previous Decade', + nextDecade: 'Next Decade', + prevCentury: 'Previous Century', + nextCentury: 'Next Century', + pickHour: 'Pick Hour', + incrementHour: 'Increment Hour', + decrementHour: 'Decrement Hour', + pickMinute: 'Pick Minute', + incrementMinute: 'Increment Minute', + decrementMinute: 'Decrement Minute', + pickSecond: 'Pick Second', + incrementSecond: 'Increment Second', + decrementSecond: 'Decrement Second', + togglePeriod: 'Toggle Period', + selectTime: 'Select Time' + }, + useStrict: false, + sideBySide: false, + daysOfWeekDisabled: false, + calendarWeeks: false, + viewMode: 'days', + toolbarPlacement: 'default', + showTodayButton: false, + showClear: false, + showClose: false, + widgetPositioning: { + horizontal: 'auto', + vertical: 'auto' + }, + widgetParent: null, + ignoreReadonly: false, + keepOpen: false, + focusOnShow: true, + inline: false, + keepInvalid: false, + datepickerInput: '.datepickerinput', + keyBinds: { + up: function (widget) { + if (!widget) { + return; + } + var d = this.date() || this.getMoment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().subtract(7, 'd')); + } else { + this.date(d.clone().add(this.stepping(), 'm')); + } + }, + down: function (widget) { + if (!widget) { + this.show(); + return; + } + var d = this.date() || this.getMoment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().add(7, 'd')); + } else { + this.date(d.clone().subtract(this.stepping(), 'm')); + } + }, + 'control up': function (widget) { + if (!widget) { + return; + } + var d = this.date() || this.getMoment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().subtract(1, 'y')); + } else { + this.date(d.clone().add(1, 'h')); + } + }, + 'control down': function (widget) { + if (!widget) { + return; + } + var d = this.date() || this.getMoment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().add(1, 'y')); + } else { + this.date(d.clone().subtract(1, 'h')); + } + }, + left: function (widget) { + if (!widget) { + return; + } + var d = this.date() || this.getMoment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().subtract(1, 'd')); + } + }, + right: function (widget) { + if (!widget) { + return; + } + var d = this.date() || this.getMoment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().add(1, 'd')); + } + }, + pageUp: function (widget) { + if (!widget) { + return; + } + var d = this.date() || this.getMoment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().subtract(1, 'M')); + } + }, + pageDown: function (widget) { + if (!widget) { + return; + } + var d = this.date() || this.getMoment(); + if (widget.find('.datepicker').is(':visible')) { + this.date(d.clone().add(1, 'M')); + } + }, + enter: function () { + this.hide(); + }, + escape: function () { + this.hide(); + }, + //tab: function (widget) { //this break the flow of the form. disabling for now + // var toggle = widget.find('.picker-switch a[data-action="togglePicker"]'); + // if(toggle.length > 0) toggle.click(); + //}, + 'control space': function (widget) { + if (!widget) { + return; + } + if (widget.find('.timepicker').is(':visible')) { + widget.find('.btn[data-action="togglePeriod"]').click(); + } + }, + t: function () { + this.date(this.getMoment()); + }, + 'delete': function () { + this.clear(); + } + }, + debug: false, + allowInputToggle: false, + disabledTimeIntervals: false, + disabledHours: false, + enabledHours: false, + viewDate: false + }; + + return $.fn.datetimepicker; +})); diff --git a/vendor/assets/javascripts/jquery.mousewheel.min.js b/vendor/assets/javascripts/jquery.mousewheel.min.js deleted file mode 100644 index 03bfd60c5..000000000 --- a/vendor/assets/javascripts/jquery.mousewheel.min.js +++ /dev/null @@ -1,8 +0,0 @@ -/*! - * jQuery Mousewheel 3.1.13 - * - * Copyright 2015 jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - */ -!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})}); \ No newline at end of file diff --git a/vendor/assets/stylesheets/bootstrap-datetimepicker.css b/vendor/assets/stylesheets/bootstrap-datetimepicker.css new file mode 100644 index 000000000..7e6488efe --- /dev/null +++ b/vendor/assets/stylesheets/bootstrap-datetimepicker.css @@ -0,0 +1,374 @@ +/*! + * Datetimepicker for Bootstrap 3 + * version : 4.17.47 + * https://github.com/Eonasdan/bootstrap-datetimepicker/ + */ +.bootstrap-datetimepicker-widget { + list-style: none; +} +.bootstrap-datetimepicker-widget.dropdown-menu { + display: block; + margin: 2px 0; + padding: 4px; + width: 19em; +} +@media (min-width: 768px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +@media (min-width: 992px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +@media (min-width: 1200px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} +.bootstrap-datetimepicker-widget.dropdown-menu:before, +.bootstrap-datetimepicker-widget.dropdown-menu:after { + content: ''; + display: inline-block; + position: absolute; +} +.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + top: -7px; + left: 7px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.bottom:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid white; + top: -6px; + left: 8px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.top:before { + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 7px solid #ccc; + border-top-color: rgba(0, 0, 0, 0.2); + bottom: -7px; + left: 6px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.top:after { + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 6px solid white; + bottom: -6px; + left: 7px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before { + left: auto; + right: 6px; +} +.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after { + left: auto; + right: 7px; +} +.bootstrap-datetimepicker-widget .list-unstyled { + margin: 0; +} +.bootstrap-datetimepicker-widget a[data-action] { + padding: 6px 0; +} +.bootstrap-datetimepicker-widget a[data-action]:active { + box-shadow: none; +} +.bootstrap-datetimepicker-widget .timepicker-hour, +.bootstrap-datetimepicker-widget .timepicker-minute, +.bootstrap-datetimepicker-widget .timepicker-second { + width: 54px; + font-weight: bold; + font-size: 1.2em; + margin: 0; +} +.bootstrap-datetimepicker-widget button[data-action] { + padding: 6px; +} +.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Increment Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Increment Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Decrement Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Decrement Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Show Hours"; +} +.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Show Minutes"; +} +.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Toggle AM/PM"; +} +.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Clear the picker"; +} +.bootstrap-datetimepicker-widget .btn[data-action="today"]::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Set the date to today"; +} +.bootstrap-datetimepicker-widget .picker-switch { + text-align: center; +} +.bootstrap-datetimepicker-widget .picker-switch::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Toggle Date and Time Screens"; +} +.bootstrap-datetimepicker-widget .picker-switch td { + padding: 0; + margin: 0; + height: auto; + width: auto; + line-height: inherit; +} +.bootstrap-datetimepicker-widget .picker-switch td span { + line-height: 2.5; + height: 2.5em; + width: 100%; +} +.bootstrap-datetimepicker-widget table { + width: 100%; + margin: 0; +} +.bootstrap-datetimepicker-widget table td, +.bootstrap-datetimepicker-widget table th { + text-align: center; + border-radius: 4px; +} +.bootstrap-datetimepicker-widget table th { + height: 20px; + line-height: 20px; + width: 20px; +} +.bootstrap-datetimepicker-widget table th.picker-switch { + width: 145px; +} +.bootstrap-datetimepicker-widget table th.disabled, +.bootstrap-datetimepicker-widget table th.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget table th.prev::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Previous Month"; +} +.bootstrap-datetimepicker-widget table th.next::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; + content: "Next Month"; +} +.bootstrap-datetimepicker-widget table thead tr:first-child th { + cursor: pointer; +} +.bootstrap-datetimepicker-widget table thead tr:first-child th:hover { + background: #eeeeee; +} +.bootstrap-datetimepicker-widget table td { + height: 54px; + line-height: 54px; + width: 54px; +} +.bootstrap-datetimepicker-widget table td.cw { + font-size: .8em; + height: 20px; + line-height: 20px; + color: #777777; +} +.bootstrap-datetimepicker-widget table td.day { + height: 20px; + line-height: 20px; + width: 20px; +} +.bootstrap-datetimepicker-widget table td.day:hover, +.bootstrap-datetimepicker-widget table td.hour:hover, +.bootstrap-datetimepicker-widget table td.minute:hover, +.bootstrap-datetimepicker-widget table td.second:hover { + background: #eeeeee; + cursor: pointer; +} +.bootstrap-datetimepicker-widget table td.old, +.bootstrap-datetimepicker-widget table td.new { + color: #777777; +} +.bootstrap-datetimepicker-widget table td.today { + position: relative; +} +.bootstrap-datetimepicker-widget table td.today:before { + content: ''; + display: inline-block; + border: solid transparent; + border-width: 0 0 7px 7px; + border-bottom-color: #337ab7; + border-top-color: rgba(0, 0, 0, 0.2); + position: absolute; + bottom: 4px; + right: 4px; +} +.bootstrap-datetimepicker-widget table td.active, +.bootstrap-datetimepicker-widget table td.active:hover { + background-color: #337ab7; + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.bootstrap-datetimepicker-widget table td.active.today:before { + border-bottom-color: #fff; +} +.bootstrap-datetimepicker-widget table td.disabled, +.bootstrap-datetimepicker-widget table td.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget table td span { + display: inline-block; + width: 54px; + height: 54px; + line-height: 54px; + margin: 2px 1.5px; + cursor: pointer; + border-radius: 4px; +} +.bootstrap-datetimepicker-widget table td span:hover { + background: #eeeeee; +} +.bootstrap-datetimepicker-widget table td span.active { + background-color: #337ab7; + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.bootstrap-datetimepicker-widget table td span.old { + color: #777777; +} +.bootstrap-datetimepicker-widget table td span.disabled, +.bootstrap-datetimepicker-widget table td span.disabled:hover { + background: none; + color: #777777; + cursor: not-allowed; +} +.bootstrap-datetimepicker-widget.usetwentyfour td.hour { + height: 27px; + line-height: 27px; +} +.bootstrap-datetimepicker-widget.wider { + width: 21em; +} +.bootstrap-datetimepicker-widget .datepicker-decades .decade { + line-height: 1.8em !important; +} +.input-group.date .input-group-addon { + cursor: pointer; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} diff --git a/yarn.lock b/yarn.lock index 94a1acdfd..2e5e9b9d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1995,10 +1995,15 @@ boolbase@^1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== -bootstrap-sass@^3.3.7: - version "3.4.3" - resolved "https://registry.yarnpkg.com/bootstrap-sass/-/bootstrap-sass-3.4.3.tgz#742cc8f4286303ae9fe8e4c95237321eae73766c" - integrity sha512-vPgFnGMp1jWZZupOND65WS6mkR8rxhJxndT/AcMbqcq1hHMdkcH4sMPhznLzzoHOHkSCrd6J9F8pWBriPCKP2Q== +bootstrap-select@^1.13.18: + version "1.13.18" + resolved "https://registry.yarnpkg.com/bootstrap-select/-/bootstrap-select-1.13.18.tgz#4557119d58dc1159189977161c803962220e4dda" + integrity sha512-V1IzK4rxBq5FrJtkzSH6RmFLFBsjx50byFbfAf8jYyXROWs7ZpprGjdHeoyq2HSsHyjJhMMwjsQhRoYAfxCGow== + +bootstrap@^3.3, bootstrap@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.4.1.tgz#c3a347d419e289ad11f4033e3c4132b87c081d72" + integrity sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA== brace-expansion@^1.1.7: version "1.1.11" @@ -2941,6 +2946,16 @@ envinfo@^7.7.3: resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== +eonasdan-bootstrap-datetimepicker@^4.17.49: + version "4.17.49" + resolved "https://registry.yarnpkg.com/eonasdan-bootstrap-datetimepicker/-/eonasdan-bootstrap-datetimepicker-4.17.49.tgz#5534ba581c1e7eb988dbf773e2fed8a7f48cc76a" + integrity sha512-7KZeDpkj+A6AtPR3XjX8gAnRPUkPSfW0OmMANG1dkUOPMtLSzbyoCjDIdEcfRtQPU5X0D9Gob7wWKn0h4QWy7A== + dependencies: + bootstrap "^3.3" + jquery "^3.5.1" + moment "^2.10" + moment-timezone "^0.4.0" + err-code@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" @@ -3864,6 +3879,11 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +hammerjs@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" + integrity sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ== + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -4386,6 +4406,38 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" +jquery-autosize@^1.18.18: + version "1.18.18" + resolved "https://registry.yarnpkg.com/jquery-autosize/-/jquery-autosize-1.18.18.tgz#28fcdfd671cbf3ee661ca484c19ee7aaa65e825a" + integrity sha512-Z2nzJEbchigrxqqqr026TMPdTZJ8syt2xeGMGkqCXbb1raI3J/X0LdiIhyIMcJ+nTjwTBf/p6bbRVP0vAuqd/w== + +jquery-mousewheel@^3.1.13: + version "3.1.13" + resolved "https://registry.yarnpkg.com/jquery-mousewheel/-/jquery-mousewheel-3.1.13.tgz#06f0335f16e353a695e7206bf50503cb523a6ee5" + integrity sha512-GXhSjfOPyDemM005YCEHvzrEALhKDIswtxSHSR2e4K/suHVJKJxxRCGz3skPjNxjJjQa9AVSGGlYjv1M3VLIPg== + +jquery-ui@^1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.13.2.tgz#de03580ae6604773602f8d786ad1abfb75232034" + integrity sha512-wBZPnqWs5GaYJmo1Jj0k/mrSkzdQzKDwhXNtHKcBdAcKVxMM3KNYFq+iJ2i1rwiG53Z8M4mTn3Qxrm17uH1D4Q== + dependencies: + jquery ">=1.8.0 <4.0.0" + +jquery-ujs@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/jquery-ujs/-/jquery-ujs-1.2.3.tgz#dcac6026ab7268e5ee41faf9d31c997cd4ddd603" + integrity sha512-59wvfx5vcCTHMeQT1/OwFiAj+UffLIwjRIoXdpO7Z7BCFGepzq9T9oLVeoItjTqjoXfUrHJvV7QU6pUR+UzOoA== + +jquery@2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.4.tgz#2c89d6889b5eac522a7eea32c14521559c6cbf02" + integrity sha512-lBHj60ezci2u1v2FqnZIraShGgEXq35qCzMv4lITyHGppTnA13rwR0MgwyNJh9TnDs3aXUvd1xjAotfraMHX/Q== + +"jquery@>=1.8.0 <4.0.0", jquery@^3.5.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.7.0.tgz#fe2c01a05da500709006d8790fe21c8a39d75612" + integrity sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ== + js-sdsl@^4.1.4: version "4.4.0" resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" @@ -4920,6 +4972,23 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +moment-timezone@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.4.1.tgz#81f598c3ad5e22cdad796b67ecd8d88d0f5baa06" + integrity sha512-5cNPVUwaVJDCe9JM8m/qz17f9SkaI8rpnRUyDJi2K5HAd6EwhuQ3n5nLclZkNC/qJnomKgQH2TIu70Gy2dxFKA== + dependencies: + moment ">= 2.6.0" + +"moment@>= 2.6.0", moment@^2.10, moment@^2.29.4: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + +momentjs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/momentjs/-/momentjs-2.0.0.tgz#73df904b4fa418f6e3c605e831cef6ed5518ebd4" + integrity sha512-GYMUxLyCwVhECkJR1/LMHEyb9gWYSPRnXi+elGN0m5bet7ngQOxU4QLWUI/eBzgN4N/T194n6yP7lQiE+Udw9A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
').addClass('cw').text('#')); + } + + while (currentDate.isBefore(viewDate.clone().endOf('w'))) { + row.append($('').addClass('dow').text(currentDate.format('dd'))); + currentDate.add(1, 'd'); + } + widget.find('.datepicker-days thead').append(row); + }, + + isInDisabledDates = function (testDate) { + return options.disabledDates[testDate.format('YYYY-MM-DD')] === true; + }, + + isInEnabledDates = function (testDate) { + return options.enabledDates[testDate.format('YYYY-MM-DD')] === true; + }, + + isInDisabledHours = function (testDate) { + return options.disabledHours[testDate.format('H')] === true; + }, + + isInEnabledHours = function (testDate) { + return options.enabledHours[testDate.format('H')] === true; + }, + + isValid = function (targetMoment, granularity) { + if (!targetMoment.isValid()) { + return false; + } + if (options.disabledDates && granularity === 'd' && isInDisabledDates(targetMoment)) { + return false; + } + if (options.enabledDates && granularity === 'd' && !isInEnabledDates(targetMoment)) { + return false; + } + if (options.minDate && targetMoment.isBefore(options.minDate, granularity)) { + return false; + } + if (options.maxDate && targetMoment.isAfter(options.maxDate, granularity)) { + return false; + } + if (options.daysOfWeekDisabled && granularity === 'd' && options.daysOfWeekDisabled.indexOf(targetMoment.day()) !== -1) { + return false; + } + if (options.disabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && isInDisabledHours(targetMoment)) { + return false; + } + if (options.enabledHours && (granularity === 'h' || granularity === 'm' || granularity === 's') && !isInEnabledHours(targetMoment)) { + return false; + } + if (options.disabledTimeIntervals && (granularity === 'h' || granularity === 'm' || granularity === 's')) { + var found = false; + $.each(options.disabledTimeIntervals, function () { + if (targetMoment.isBetween(this[0], this[1])) { + found = true; + return false; + } + }); + if (found) { + return false; + } + } + return true; + }, + + fillMonths = function () { + var spans = [], + monthsShort = viewDate.clone().startOf('y').startOf('d'); + while (monthsShort.isSame(viewDate, 'y')) { + spans.push($('').attr('data-action', 'selectMonth').addClass('month').text(monthsShort.format('MMM'))); + monthsShort.add(1, 'M'); + } + widget.find('.datepicker-months td').empty().append(spans); + }, + + updateMonths = function () { + var monthsView = widget.find('.datepicker-months'), + monthsViewHeader = monthsView.find('th'), + months = monthsView.find('tbody').find('span'); + + monthsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevYear); + monthsViewHeader.eq(1).attr('title', options.tooltips.selectYear); + monthsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextYear); + + monthsView.find('.disabled').removeClass('disabled'); + + if (!isValid(viewDate.clone().subtract(1, 'y'), 'y')) { + monthsViewHeader.eq(0).addClass('disabled'); + } + + monthsViewHeader.eq(1).text(viewDate.year()); + + if (!isValid(viewDate.clone().add(1, 'y'), 'y')) { + monthsViewHeader.eq(2).addClass('disabled'); + } + + months.removeClass('active'); + if (date.isSame(viewDate, 'y') && !unset) { + months.eq(date.month()).addClass('active'); + } + + months.each(function (index) { + if (!isValid(viewDate.clone().month(index), 'M')) { + $(this).addClass('disabled'); + } + }); + }, + + updateYears = function () { + var yearsView = widget.find('.datepicker-years'), + yearsViewHeader = yearsView.find('th'), + startYear = viewDate.clone().subtract(5, 'y'), + endYear = viewDate.clone().add(6, 'y'), + html = ''; + + yearsViewHeader.eq(0).find('span').attr('title', options.tooltips.prevDecade); + yearsViewHeader.eq(1).attr('title', options.tooltips.selectDecade); + yearsViewHeader.eq(2).find('span').attr('title', options.tooltips.nextDecade); + + yearsView.find('.disabled').removeClass('disabled'); + + if (options.minDate && options.minDate.isAfter(startYear, 'y')) { + yearsViewHeader.eq(0).addClass('disabled'); + } + + yearsViewHeader.eq(1).text(startYear.year() + '-' + endYear.year()); + + if (options.maxDate && options.maxDate.isBefore(endYear, 'y')) { + yearsViewHeader.eq(2).addClass('disabled'); + } + + while (!startYear.isAfter(endYear, 'y')) { + html += '' + startYear.year() + ''; + startYear.add(1, 'y'); + } + + yearsView.find('td').html(html); + }, + + updateDecades = function () { + var decadesView = widget.find('.datepicker-decades'), + decadesViewHeader = decadesView.find('th'), + startDecade = moment({ y: viewDate.year() - (viewDate.year() % 100) - 1 }), + endDecade = startDecade.clone().add(100, 'y'), + startedAt = startDecade.clone(), + minDateDecade = false, + maxDateDecade = false, + endDecadeYear, + html = ''; + + decadesViewHeader.eq(0).find('span').attr('title', options.tooltips.prevCentury); + decadesViewHeader.eq(2).find('span').attr('title', options.tooltips.nextCentury); + + decadesView.find('.disabled').removeClass('disabled'); + + if (startDecade.isSame(moment({ y: 1900 })) || (options.minDate && options.minDate.isAfter(startDecade, 'y'))) { + decadesViewHeader.eq(0).addClass('disabled'); + } + + decadesViewHeader.eq(1).text(startDecade.year() + '-' + endDecade.year()); + + if (startDecade.isSame(moment({ y: 2000 })) || (options.maxDate && options.maxDate.isBefore(endDecade, 'y'))) { + decadesViewHeader.eq(2).addClass('disabled'); + } + + while (!startDecade.isAfter(endDecade, 'y')) { + endDecadeYear = startDecade.year() + 12; + minDateDecade = options.minDate && options.minDate.isAfter(startDecade, 'y') && options.minDate.year() <= endDecadeYear; + maxDateDecade = options.maxDate && options.maxDate.isAfter(startDecade, 'y') && options.maxDate.year() <= endDecadeYear; + html += '' + (startDecade.year() + 1) + ' - ' + (startDecade.year() + 12) + ''; + startDecade.add(12, 'y'); + } + html += ''; //push the dangling block over, at least this way it's even + + decadesView.find('td').html(html); + decadesViewHeader.eq(1).text((startedAt.year() + 1) + '-' + (startDecade.year())); + }, + + fillDate = function () { + var daysView = widget.find('.datepicker-days'), + daysViewHeader = daysView.find('th'), + currentDate, + html = [], + row, + clsNames = [], + i; + + if (!hasDate()) { + return; + } + + daysViewHeader.eq(0).find('span').attr('title', options.tooltips.prevMonth); + daysViewHeader.eq(1).attr('title', options.tooltips.selectMonth); + daysViewHeader.eq(2).find('span').attr('title', options.tooltips.nextMonth); + + daysView.find('.disabled').removeClass('disabled'); + daysViewHeader.eq(1).text(viewDate.format(options.dayViewHeaderFormat)); + + if (!isValid(viewDate.clone().subtract(1, 'M'), 'M')) { + daysViewHeader.eq(0).addClass('disabled'); + } + if (!isValid(viewDate.clone().add(1, 'M'), 'M')) { + daysViewHeader.eq(2).addClass('disabled'); + } + + currentDate = viewDate.clone().startOf('M').startOf('w').startOf('d'); + + for (i = 0; i < 42; i++) { //always display 42 days (should show 6 weeks) + if (currentDate.weekday() === 0) { + row = $('
' + currentDate.week() + '' + currentDate.date() + '
' + currentHour.format(use24Hours ? 'HH' : 'hh') + '
' + currentMinute.format('mm') + '
' + currentSecond.format('ss') + '