diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a77410f5..0e30dfa9b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -67,6 +67,9 @@ jobs: with: bundler-cache: true + - name: Install packages + run: yarn install --pure-lockfile + - name: Precompile assets run: bin/rails assets:precompile @@ -110,7 +113,7 @@ jobs: uses: joshmfrankel/simplecov-check-action@main with: github_token: ${{ secrets.GITHUB_TOKEN }} - minimum_suite_coverage: 73 + minimum_suite_coverage: 84 minimum_file_coverage: 100 build-and-deploy: diff --git a/.gitignore b/.gitignore index b6fc7057b..548760b12 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,9 @@ vendor # Allow one tmp file /tmp/* !tmp/parallel_runtime_rspec.log + +/public/assets +/node_modules + +/app/assets/builds/* +!/app/assets/builds/.keep diff --git a/.ruby-version b/.ruby-version index 0aec50e6e..fa7adc7ac 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.1.4 +3.3.5 diff --git a/Dockerfile b/Dockerfile index ff93ec390..5ab004988 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.1.4-alpine as base +FROM ruby:3.3.5-alpine as base WORKDIR /app @@ -15,17 +15,23 @@ FROM base as builder RUN apk add --no-cache \ build-base \ ruby-dev \ - postgresql-dev + postgresql-dev \ + yarn -COPY Gemfile* .ruby-version ./ +COPY Gemfile* .ruby-version package.json yarn.lock ./ RUN bundle config deployment true && \ bundle config without development test && \ - bundle install --jobs 4 --retry 3 + bundle install --jobs 4 --retry 3 && \ + yarn install --frozen-lockfile --production COPY . . -RUN RAILS_ENV=production PQ_REST_API_HOST=localhost PQ_REST_API_USERNAME=user PQ_REST_API_PASSWORD=pass DEVISE_SECRET=secret bundle exec rake assets:precompile +RUN RAILS_ENV=production PQ_REST_API_HOST=localhost PQ_REST_API_USERNAME=user PQ_REST_API_PASSWORD=pass \ + SECRET_KEY_BASE_DUMMY=1 bundle exec rake assets:precompile + +# Copy govuk assets +RUN cp -r node_modules/govuk-frontend/dist/govuk/assets/. public/assets/ # Cleanup to save space in the production image RUN rm -rf node_modules log/* tmp/* /tmp && \ diff --git a/Gemfile b/Gemfile index 904ad0110..f1813848a 100644 --- a/Gemfile +++ b/Gemfile @@ -2,37 +2,32 @@ source "https://rubygems.org" ruby file: ".ruby-version" -gem "bootstrap-will_paginate", "~> 1.0" gem "business_time", "~> 0.13.0" gem "devise", "~> 4.8" gem "devise_invitable", "~> 2.0" -gem "font-awesome-rails", "~> 4.7" -gem "govuk_elements_rails", "~> 3.1", ">= 3.1.3" -gem "govuk_frontend_toolkit", "~> 9.0" +gem "govuk-components" gem "govuk_notify_rails", "~> 3.0" -gem "govuk_template", "~> 0.26.0" -gem "jquery-rails", "~> 4.6" gem "logstasher", "~> 2.1", ">= 2.1.5" gem "mail", ">= 2.8" gem "mechanize", "~> 2.12" -gem "momentjs-rails", "~> 2.29" gem "paper_trail" gem "pg", "~> 1.5" gem "puma", "~> 6.4" gem "rails", "~> 7.1" gem "responders", "~> 3.0", ">= 3.0.1" gem "rspec_junit_formatter", "~> 0.6.0" -gem "sass-rails", "~> 6.0" -gem "select2-rails", "~> 4.0", ">= 4.0.13" gem "sentry-rails", "~> 5.21" gem "sentry-ruby", "~> 5.19" gem "sidekiq" -gem "sinatra", "~> 3.1", require: false +gem "sinatra", "~> 4", require: false gem "slim", "~> 5.2" -gem "statsd-ruby", "~> 1.5" -gem "terser" gem "will_paginate", "~> 4.0" +gem "cssbundling-rails", "~> 1.4" +gem "jsbundling-rails", "~> 1.3" +gem "sprockets", "~> 4.2" +gem "sprockets-rails", "~> 3.5" + group :development do gem "annotate" end diff --git a/Gemfile.lock b/Gemfile.lock index d680561ea..667e5c110 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,80 +1,76 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.1.3.4) - actionpack (= 7.1.3.4) - activesupport (= 7.1.3.4) + actioncable (7.2.1.2) + actionpack (= 7.2.1.2) + activesupport (= 7.2.1.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.3.4) - actionpack (= 7.1.3.4) - activejob (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.1.3.4) - actionpack (= 7.1.3.4) - actionview (= 7.1.3.4) - activejob (= 7.1.3.4) - activesupport (= 7.1.3.4) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp + actionmailbox (7.2.1.2) + actionpack (= 7.2.1.2) + activejob (= 7.2.1.2) + activerecord (= 7.2.1.2) + activestorage (= 7.2.1.2) + activesupport (= 7.2.1.2) + mail (>= 2.8.0) + actionmailer (7.2.1.2) + actionpack (= 7.2.1.2) + actionview (= 7.2.1.2) + activejob (= 7.2.1.2) + activesupport (= 7.2.1.2) + mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.1.3.4) - actionview (= 7.1.3.4) - activesupport (= 7.1.3.4) + actionpack (7.2.1.2) + actionview (= 7.2.1.2) + activesupport (= 7.2.1.2) nokogiri (>= 1.8.5) racc - rack (>= 2.2.4) + rack (>= 2.2.4, < 3.2) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.3.4) - actionpack (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) + useragent (~> 0.16) + actiontext (7.2.1.2) + actionpack (= 7.2.1.2) + activerecord (= 7.2.1.2) + activestorage (= 7.2.1.2) + activesupport (= 7.2.1.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.3.4) - activesupport (= 7.1.3.4) + actionview (7.2.1.2) + activesupport (= 7.2.1.2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.1.3.4) - activesupport (= 7.1.3.4) + activejob (7.2.1.2) + activesupport (= 7.2.1.2) globalid (>= 0.3.6) - activemodel (7.1.3.4) - activesupport (= 7.1.3.4) - activerecord (7.1.3.4) - activemodel (= 7.1.3.4) - activesupport (= 7.1.3.4) + activemodel (7.2.1.2) + activesupport (= 7.2.1.2) + activerecord (7.2.1.2) + activemodel (= 7.2.1.2) + activesupport (= 7.2.1.2) timeout (>= 0.4.0) - activestorage (7.1.3.4) - actionpack (= 7.1.3.4) - activejob (= 7.1.3.4) - activerecord (= 7.1.3.4) - activesupport (= 7.1.3.4) + activestorage (7.2.1.2) + actionpack (= 7.2.1.2) + activejob (= 7.2.1.2) + activerecord (= 7.2.1.2) + activesupport (= 7.2.1.2) marcel (~> 1.0) - activesupport (7.1.3.4) + activesupport (7.2.1.2) base64 bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) annotate (3.2.0) @@ -90,8 +86,6 @@ GEM bigdecimal (3.1.8) binding_of_caller (1.0.1) debug_inspector (>= 1.2.0) - bootstrap-will_paginate (1.0.0) - will_paginate brakeman (6.2.2) racc builder (3.3.0) @@ -110,9 +104,11 @@ GEM concurrent-ruby (1.3.4) connection_pool (2.4.1) crass (1.0.6) - database_cleaner (2.0.2) + cssbundling-rails (1.4.1) + railties (>= 6.0.0) + database_cleaner (2.1.0) database_cleaner-active_record (>= 2, < 3) - database_cleaner-active_record (2.1.0) + database_cleaner-active_record (2.2.0) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) @@ -135,7 +131,6 @@ GEM domain_name (0.6.20240107) drb (2.2.1) erubi (1.13.0) - execjs (2.9.1) factory_bot (6.4.6) activesupport (>= 5.0.0) factory_bot_rails (6.4.3) @@ -143,22 +138,17 @@ GEM railties (>= 5.0.0) faker (3.4.2) i18n (>= 1.8.11, < 2) - ffi (1.15.5) - font-awesome-rails (4.7.0.8) - railties (>= 3.2, < 8.0) globalid (1.2.1) activesupport (>= 6.1) - govuk_elements_rails (3.1.3) - govuk_frontend_toolkit (>= 6.0.2) - rails (>= 4.1.0) - sass (>= 3.2.0) - govuk_frontend_toolkit (9.0.1) - railties (>= 3.1.0) + govuk-components (5.7.0) + html-attributes-utils (~> 1.0.0, >= 1.0.0) + pagy (>= 6, < 10) + view_component (>= 3.9, < 3.17) govuk_notify_rails (3.0.0) notifications-ruby-client (~> 6.2) rails (>= 4.1.0) - govuk_template (0.26.0) - rails (>= 3.1) + html-attributes-utils (1.0.2) + activesupport (>= 6.1.4.4) http-cookie (1.0.7) domain_name (~> 0.5) i18n (1.14.6) @@ -167,10 +157,8 @@ GEM irb (1.14.1) rdoc (>= 4.0.0) reline (>= 0.4.2) - jquery-rails (4.6.0) - rails-dom-testing (>= 1, < 3) - railties (>= 4.2.0) - thor (>= 0.14, < 2.0) + jsbundling-rails (1.3.1) + railties (>= 6.0.0) json (2.7.2) jwt (2.9.1) base64 @@ -180,7 +168,7 @@ GEM logstasher (2.1.5) activesupport (>= 5.2) request_store - loofah (2.22.0) + loofah (2.23.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -203,21 +191,19 @@ GEM rubyntlm (~> 0.6, >= 0.6.3) webrick (~> 1.7) webrobots (~> 0.1.2) + method_source (1.1.0) mime-types (3.5.2) mime-types-data (~> 3.2015) mime-types-data (3.2024.0903) mini_mime (1.1.5) mini_portile2 (2.8.7) minitest (5.25.1) - momentjs-rails (2.29.4.1) - railties (>= 3.1) - mustermann (3.0.0) + mustermann (3.0.3) ruby2_keywords (~> 0.0.1) - mutex_m (0.2.0) net-http-digest_auth (1.4.1) net-http-persistent (4.0.4) connection_pool (~> 2.2) - net-imap (0.4.16) + net-imap (0.5.0) date net-protocol net-pop (0.1.2) @@ -234,6 +220,7 @@ GEM notifications-ruby-client (6.2.0) jwt (>= 1.5, < 3) orm_adapter (0.5.0) + pagy (9.1.0) paper_trail (15.2.0) activerecord (>= 6.1) request_store (~> 1.4) @@ -250,30 +237,31 @@ GEM puma (6.4.3) nio4r (~> 2.0) racc (1.8.1) - rack (2.2.10) - rack-protection (3.1.0) - rack (~> 2.2, >= 2.2.4) - rack-session (1.0.2) - rack (< 3) + rack (3.1.8) + rack-protection (4.0.0) + base64 (>= 0.1.0) + rack (>= 3.0.0, < 4) + rack-session (2.0.0) + rack (>= 3.0.0) rack-test (2.1.0) rack (>= 1.3) - rackup (1.0.0) - rack (< 3) - webrick - rails (7.1.3.4) - actioncable (= 7.1.3.4) - actionmailbox (= 7.1.3.4) - actionmailer (= 7.1.3.4) - actionpack (= 7.1.3.4) - actiontext (= 7.1.3.4) - actionview (= 7.1.3.4) - activejob (= 7.1.3.4) - activemodel (= 7.1.3.4) - activerecord (= 7.1.3.4) - activestorage (= 7.1.3.4) - activesupport (= 7.1.3.4) + rackup (2.1.0) + rack (>= 3) + webrick (~> 1.8) + rails (7.2.1.2) + actioncable (= 7.2.1.2) + actionmailbox (= 7.2.1.2) + actionmailer (= 7.2.1.2) + actionpack (= 7.2.1.2) + actiontext (= 7.2.1.2) + actionview (= 7.2.1.2) + activejob (= 7.2.1.2) + activemodel (= 7.2.1.2) + activerecord (= 7.2.1.2) + activestorage (= 7.2.1.2) + activesupport (= 7.2.1.2) bundler (>= 1.15.0) - railties (= 7.1.3.4) + railties (= 7.2.1.2) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -285,10 +273,10 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - railties (7.1.3.4) - actionpack (= 7.1.3.4) - activesupport (= 7.1.3.4) - irb + railties (7.2.1.2) + actionpack (= 7.2.1.2) + activesupport (= 7.2.1.2) + irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) @@ -296,9 +284,6 @@ GEM rainbow (3.1.1) raindrops (0.20.1) rake (13.2.1) - rb-fsevent (0.11.2) - rb-inotify (0.10.1) - ffi (~> 1.0) rdoc (6.7.0) psych (>= 4.0.0) redis-client (0.22.2) @@ -368,23 +353,8 @@ GEM rubyntlm (0.6.5) base64 rubyzip (2.3.2) - sass (3.7.4) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - sass-rails (6.0.0) - sassc-rails (~> 2.1, >= 2.1.1) - sassc (2.4.0) - ffi (~> 1.9) - sassc-rails (2.1.2) - railties (>= 4.0.0) - sassc (>= 2.0) - sprockets (> 3.0) - sprockets-rails - tilt - select2-rails (4.0.13) - selenium-webdriver (4.26.0) + securerandom (0.3.1) + selenium-webdriver (4.23.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -410,10 +380,11 @@ GEM simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) simplecov_json_formatter (0.1.4) - sinatra (3.1.0) + sinatra (4.0.0) mustermann (~> 3.0) - rack (~> 2.2, >= 2.2.4) - rack-protection (= 3.1.0) + rack (>= 3.0.0, < 4) + rack-protection (= 4.0.0) + rack-session (>= 2.0.0, < 3) tilt (~> 2.0) slim (5.2.1) temple (~> 0.10.0) @@ -421,17 +392,14 @@ GEM sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - statsd-ruby (1.5.0) stringio (3.1.1) temple (0.10.3) - terser (1.2.3) - execjs (>= 0.3.0, < 3) thor (1.3.2) - tilt (2.3.0) + tilt (2.4.0) timecop (0.9.10) timeout (0.4.1) tzinfo (2.0.6) @@ -440,6 +408,11 @@ GEM unicorn (6.1.0) kgio (~> 2.6) raindrops (~> 0.7) + useragent (0.16.10) + view_component (3.16.0) + activesupport (>= 5.2.0, < 8.0) + concurrent-ruby (~> 1.0) + method_source (~> 1.0) warden (1.2.9) rack (>= 2.0.9) webrick (1.8.2) @@ -451,7 +424,7 @@ GEM will_paginate (4.0.1) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.18) + zeitwerk (2.7.1) PLATFORMS ruby @@ -460,26 +433,22 @@ DEPENDENCIES annotate better_errors binding_of_caller - bootstrap-will_paginate (~> 1.0) brakeman business_time (~> 0.13.0) capybara + cssbundling-rails (~> 1.4) database_cleaner debug devise (~> 4.8) devise_invitable (~> 2.0) factory_bot_rails faker - font-awesome-rails (~> 4.7) - govuk_elements_rails (~> 3.1, >= 3.1.3) - govuk_frontend_toolkit (~> 9.0) + govuk-components govuk_notify_rails (~> 3.0) - govuk_template (~> 0.26.0) - jquery-rails (~> 4.6) + jsbundling-rails (~> 1.3) logstasher (~> 2.1, >= 2.1.5) mail (>= 2.8) mechanize (~> 2.12) - momentjs-rails (~> 2.29) paper_trail parallel_tests pg (~> 1.5) @@ -490,24 +459,22 @@ DEPENDENCIES rspec-rails rspec_junit_formatter (~> 0.6.0) rubocop-govuk - sass-rails (~> 6.0) - select2-rails (~> 4.0, >= 4.0.13) selenium-webdriver sentry-rails (~> 5.21) sentry-ruby (~> 5.19) shoulda-matchers sidekiq simplecov - sinatra (~> 3.1) + sinatra (~> 4) slim (~> 5.2) - statsd-ruby (~> 1.5) - terser + sprockets (~> 4.2) + sprockets-rails (~> 3.5) timecop unicorn will_paginate (~> 4.0) RUBY VERSION - ruby 3.1.4p223 + ruby 3.3.5p100 BUNDLED WITH 2.4.19 diff --git a/Procfile.dev b/Procfile.dev new file mode 100644 index 000000000..3dc6eac01 --- /dev/null +++ b/Procfile.dev @@ -0,0 +1,3 @@ +web: bin/rails server +css: yarn build:css --watch +js: yarn build --watch diff --git a/README.md b/README.md index c553fc619..77b8cb054 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,12 @@ $ rbenv install #### Dependencies +Yarn + +``` +$ brew install yarn +``` + Postgresql ``` @@ -67,6 +73,7 @@ Use the following commands to install gems and javascript packages then create t ``` $ bin/setup +$ yarn install ``` #### Seeds @@ -85,11 +92,9 @@ $ bundle exec rake pqa:import_from_xml[path/to/question_file.xml] #### Running locally: -To just run the web server without any background jobs (usually sufficient): +Use the dev command to run the application. This will use Foreman to start the rails server as well as compiling the css and js. Any changes to the css and js will be live updated. -``` -$ bin/rails server -``` +$ bin/dev The site will be accessible at http://localhost:3000. diff --git a/app/assets/builds/.keep b/app/assets/builds/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index c2582f3bf..c6381c790 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -1,9 +1,8 @@ //= link_tree ../images -//= link_directory ../javascripts .js -//= link_directory ../stylesheets .css -//= link govuk-template.css -//= link govuk-template-ie6.css -//= link govuk-template-ie7.css -//= link govuk-template-ie8.css -//= link govuk-template-print.css -//= link fonts.css +//= link_tree ../builds +//= link_tree ../../../node_modules/govuk-frontend/dist/govuk/assets/images +//= link jquery/dist/jquery.js +//= link moment/moment.js +//= link select2/dist/js/select2.js +//= link select2/dist/css/select2.css +//= link font-awesome/css/font-awesome.css diff --git a/app/assets/images/moj_logo.png b/app/assets/images/moj_logo.png deleted file mode 100644 index ee00a0dff..000000000 Binary files a/app/assets/images/moj_logo.png and /dev/null differ diff --git a/app/assets/images/open-government-licence.png b/app/assets/images/open-government-licence.png deleted file mode 100644 index 3faae2d9a..000000000 Binary files a/app/assets/images/open-government-licence.png and /dev/null differ diff --git a/app/assets/images/progress-tab-bg.png b/app/assets/images/progress-tab-bg.png deleted file mode 100644 index c7bc69117..000000000 Binary files a/app/assets/images/progress-tab-bg.png and /dev/null differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js deleted file mode 100644 index d0469785c..000000000 --- a/app/assets/javascripts/application.js +++ /dev/null @@ -1,16 +0,0 @@ -// This is a manifest file that'll be compiled into application.js, which will include all the files -// listed below. -// -// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, -// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. -// -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// compiled file. -// -// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details -// about supported directives. -// -//= require jquery -//= require jquery_ujs -//= require select2 -//= require_tree . diff --git a/app/assets/javascripts/moment.js b/app/assets/javascripts/moment.js deleted file mode 100644 index 23cd3ede1..000000000 --- a/app/assets/javascripts/moment.js +++ /dev/null @@ -1,3195 +0,0 @@ -//! moment.js -//! version : 2.10.6 -//! authors : Tim Wood, Iskren Chernev, Moment.js contributors -//! license : MIT -//! momentjs.com - -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - global.moment = factory() -}(this, function () { 'use strict'; - - var hookCallback; - - function utils_hooks__hooks () { - return hookCallback.apply(null, arguments); - } - - // This is done to register the method called with moment() - // without creating circular dependencies. - function setHookCallback (callback) { - hookCallback = callback; - } - - function isArray(input) { - return Object.prototype.toString.call(input) === '[object Array]'; - } - - function isDate(input) { - return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]'; - } - - function map(arr, fn) { - var res = [], i; - for (i = 0; i < arr.length; ++i) { - res.push(fn(arr[i], i)); - } - return res; - } - - function hasOwnProp(a, b) { - return Object.prototype.hasOwnProperty.call(a, b); - } - - function extend(a, b) { - for (var i in b) { - if (hasOwnProp(b, i)) { - a[i] = b[i]; - } - } - - if (hasOwnProp(b, 'toString')) { - a.toString = b.toString; - } - - if (hasOwnProp(b, 'valueOf')) { - a.valueOf = b.valueOf; - } - - return a; - } - - function create_utc__createUTC (input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, true).utc(); - } - - function defaultParsingFlags() { - // We need to deep clone this object. - return { - empty : false, - unusedTokens : [], - unusedInput : [], - overflow : -2, - charsLeftOver : 0, - nullInput : false, - invalidMonth : null, - invalidFormat : false, - userInvalidated : false, - iso : false - }; - } - - function getParsingFlags(m) { - if (m._pf == null) { - m._pf = defaultParsingFlags(); - } - return m._pf; - } - - function valid__isValid(m) { - if (m._isValid == null) { - var flags = getParsingFlags(m); - m._isValid = !isNaN(m._d.getTime()) && - flags.overflow < 0 && - !flags.empty && - !flags.invalidMonth && - !flags.invalidWeekday && - !flags.nullInput && - !flags.invalidFormat && - !flags.userInvalidated; - - if (m._strict) { - m._isValid = m._isValid && - flags.charsLeftOver === 0 && - flags.unusedTokens.length === 0 && - flags.bigHour === undefined; - } - } - return m._isValid; - } - - function valid__createInvalid (flags) { - var m = create_utc__createUTC(NaN); - if (flags != null) { - extend(getParsingFlags(m), flags); - } - else { - getParsingFlags(m).userInvalidated = true; - } - - return m; - } - - var momentProperties = utils_hooks__hooks.momentProperties = []; - - function copyConfig(to, from) { - var i, prop, val; - - if (typeof from._isAMomentObject !== 'undefined') { - to._isAMomentObject = from._isAMomentObject; - } - if (typeof from._i !== 'undefined') { - to._i = from._i; - } - if (typeof from._f !== 'undefined') { - to._f = from._f; - } - if (typeof from._l !== 'undefined') { - to._l = from._l; - } - if (typeof from._strict !== 'undefined') { - to._strict = from._strict; - } - if (typeof from._tzm !== 'undefined') { - to._tzm = from._tzm; - } - if (typeof from._isUTC !== 'undefined') { - to._isUTC = from._isUTC; - } - if (typeof from._offset !== 'undefined') { - to._offset = from._offset; - } - if (typeof from._pf !== 'undefined') { - to._pf = getParsingFlags(from); - } - if (typeof from._locale !== 'undefined') { - to._locale = from._locale; - } - - if (momentProperties.length > 0) { - for (i in momentProperties) { - prop = momentProperties[i]; - val = from[prop]; - if (typeof val !== 'undefined') { - to[prop] = val; - } - } - } - - return to; - } - - var updateInProgress = false; - - // Moment prototype object - function Moment(config) { - copyConfig(this, config); - this._d = new Date(config._d != null ? config._d.getTime() : NaN); - // Prevent infinite loop in case updateOffset creates new moment - // objects. - if (updateInProgress === false) { - updateInProgress = true; - utils_hooks__hooks.updateOffset(this); - updateInProgress = false; - } - } - - function isMoment (obj) { - return obj instanceof Moment || (obj != null && obj._isAMomentObject != null); - } - - function absFloor (number) { - if (number < 0) { - return Math.ceil(number); - } else { - return Math.floor(number); - } - } - - function toInt(argumentForCoercion) { - var coercedNumber = +argumentForCoercion, - value = 0; - - if (coercedNumber !== 0 && isFinite(coercedNumber)) { - value = absFloor(coercedNumber); - } - - return value; - } - - function compareArrays(array1, array2, dontConvert) { - var len = Math.min(array1.length, array2.length), - lengthDiff = Math.abs(array1.length - array2.length), - diffs = 0, - i; - for (i = 0; i < len; i++) { - if ((dontConvert && array1[i] !== array2[i]) || - (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { - diffs++; - } - } - return diffs + lengthDiff; - } - - function Locale() { - } - - var locales = {}; - var globalLocale; - - function normalizeLocale(key) { - return key ? key.toLowerCase().replace('_', '-') : key; - } - - // pick the locale from the array - // try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each - // substring from most specific to least, but move to the next array item if it's a more specific variant than the current root - function chooseLocale(names) { - var i = 0, j, next, locale, split; - - while (i < names.length) { - split = normalizeLocale(names[i]).split('-'); - j = split.length; - next = normalizeLocale(names[i + 1]); - next = next ? next.split('-') : null; - while (j > 0) { - locale = loadLocale(split.slice(0, j).join('-')); - if (locale) { - return locale; - } - if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { - //the next array item is better than a shallower substring of this one - break; - } - j--; - } - i++; - } - return null; - } - - function loadLocale(name) { - var oldLocale = null; - // TODO: Find a better way to register and load all the locales in Node - if (!locales[name] && typeof module !== 'undefined' && - module && module.exports) { - try { - oldLocale = globalLocale._abbr; - require('./locale/' + name); - // because defineLocale currently also sets the global locale, we - // want to undo that for lazy loaded locales - locale_locales__getSetGlobalLocale(oldLocale); - } catch (e) { } - } - return locales[name]; - } - - // This function will load locale and then set the global locale. If - // no arguments are passed in, it will simply return the current global - // locale key. - function locale_locales__getSetGlobalLocale (key, values) { - var data; - if (key) { - if (typeof values === 'undefined') { - data = locale_locales__getLocale(key); - } - else { - data = defineLocale(key, values); - } - - if (data) { - // moment.duration._locale = moment._locale = data; - globalLocale = data; - } - } - - return globalLocale._abbr; - } - - function defineLocale (name, values) { - if (values !== null) { - values.abbr = name; - locales[name] = locales[name] || new Locale(); - locales[name].set(values); - - // backwards compat for now: also set the locale - locale_locales__getSetGlobalLocale(name); - - return locales[name]; - } else { - // useful for testing - delete locales[name]; - return null; - } - } - - // returns locale data - function locale_locales__getLocale (key) { - var locale; - - if (key && key._locale && key._locale._abbr) { - key = key._locale._abbr; - } - - if (!key) { - return globalLocale; - } - - if (!isArray(key)) { - //short-circuit everything else - locale = loadLocale(key); - if (locale) { - return locale; - } - key = [key]; - } - - return chooseLocale(key); - } - - var aliases = {}; - - function addUnitAlias (unit, shorthand) { - var lowerCase = unit.toLowerCase(); - aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit; - } - - function normalizeUnits(units) { - return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined; - } - - function normalizeObjectUnits(inputObject) { - var normalizedInput = {}, - normalizedProp, - prop; - - for (prop in inputObject) { - if (hasOwnProp(inputObject, prop)) { - normalizedProp = normalizeUnits(prop); - if (normalizedProp) { - normalizedInput[normalizedProp] = inputObject[prop]; - } - } - } - - return normalizedInput; - } - - function makeGetSet (unit, keepTime) { - return function (value) { - if (value != null) { - get_set__set(this, unit, value); - utils_hooks__hooks.updateOffset(this, keepTime); - return this; - } else { - return get_set__get(this, unit); - } - }; - } - - function get_set__get (mom, unit) { - return mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit](); - } - - function get_set__set (mom, unit, value) { - return mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value); - } - - // MOMENTS - - function getSet (units, value) { - var unit; - if (typeof units === 'object') { - for (unit in units) { - this.set(unit, units[unit]); - } - } else { - units = normalizeUnits(units); - if (typeof this[units] === 'function') { - return this[units](value); - } - } - return this; - } - - function zeroFill(number, targetLength, forceSign) { - var absNumber = '' + Math.abs(number), - zerosToFill = targetLength - absNumber.length, - sign = number >= 0; - return (sign ? (forceSign ? '+' : '') : '-') + - Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; - } - - var formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; - - var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; - - var formatFunctions = {}; - - var formatTokenFunctions = {}; - - // token: 'M' - // padded: ['MM', 2] - // ordinal: 'Mo' - // callback: function () { this.month() + 1 } - function addFormatToken (token, padded, ordinal, callback) { - var func = callback; - if (typeof callback === 'string') { - func = function () { - return this[callback](); - }; - } - if (token) { - formatTokenFunctions[token] = func; - } - if (padded) { - formatTokenFunctions[padded[0]] = function () { - return zeroFill(func.apply(this, arguments), padded[1], padded[2]); - }; - } - if (ordinal) { - formatTokenFunctions[ordinal] = function () { - return this.localeData().ordinal(func.apply(this, arguments), token); - }; - } - } - - function removeFormattingTokens(input) { - if (input.match(/\[[\s\S]/)) { - return input.replace(/^\[|\]$/g, ''); - } - return input.replace(/\\/g, ''); - } - - function makeFormatFunction(format) { - var array = format.match(formattingTokens), i, length; - - for (i = 0, length = array.length; i < length; i++) { - if (formatTokenFunctions[array[i]]) { - array[i] = formatTokenFunctions[array[i]]; - } else { - array[i] = removeFormattingTokens(array[i]); - } - } - - return function (mom) { - var output = ''; - for (i = 0; i < length; i++) { - output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; - } - return output; - }; - } - - // format date using native date object - function formatMoment(m, format) { - if (!m.isValid()) { - return m.localeData().invalidDate(); - } - - format = expandFormat(format, m.localeData()); - formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); - - return formatFunctions[format](m); - } - - function expandFormat(format, locale) { - var i = 5; - - function replaceLongDateFormatTokens(input) { - return locale.longDateFormat(input) || input; - } - - localFormattingTokens.lastIndex = 0; - while (i >= 0 && localFormattingTokens.test(format)) { - format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); - localFormattingTokens.lastIndex = 0; - i -= 1; - } - - return format; - } - - var match1 = /\d/; // 0 - 9 - var match2 = /\d\d/; // 00 - 99 - var match3 = /\d{3}/; // 000 - 999 - var match4 = /\d{4}/; // 0000 - 9999 - var match6 = /[+-]?\d{6}/; // -999999 - 999999 - var match1to2 = /\d\d?/; // 0 - 99 - var match1to3 = /\d{1,3}/; // 0 - 999 - var match1to4 = /\d{1,4}/; // 0 - 9999 - var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999 - - var matchUnsigned = /\d+/; // 0 - inf - var matchSigned = /[+-]?\d+/; // -inf - inf - - var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z - - var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123 - - // any word (or two) characters or numbers including two/three word month in arabic. - var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i; - - var regexes = {}; - - function isFunction (sth) { - // https://github.com/moment/moment/issues/2325 - return typeof sth === 'function' && - Object.prototype.toString.call(sth) === '[object Function]'; - } - - - function addRegexToken (token, regex, strictRegex) { - regexes[token] = isFunction(regex) ? regex : function (isStrict) { - return (isStrict && strictRegex) ? strictRegex : regex; - }; - } - - function getParseRegexForToken (token, config) { - if (!hasOwnProp(regexes, token)) { - return new RegExp(unescapeFormat(token)); - } - - return regexes[token](config._strict, config._locale); - } - - // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript - function unescapeFormat(s) { - return s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { - return p1 || p2 || p3 || p4; - }).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); - } - - var tokens = {}; - - function addParseToken (token, callback) { - var i, func = callback; - if (typeof token === 'string') { - token = [token]; - } - if (typeof callback === 'number') { - func = function (input, array) { - array[callback] = toInt(input); - }; - } - for (i = 0; i < token.length; i++) { - tokens[token[i]] = func; - } - } - - function addWeekParseToken (token, callback) { - addParseToken(token, function (input, array, config, token) { - config._w = config._w || {}; - callback(input, config._w, config, token); - }); - } - - function addTimeToArrayFromToken(token, input, config) { - if (input != null && hasOwnProp(tokens, token)) { - tokens[token](input, config._a, config, token); - } - } - - var YEAR = 0; - var MONTH = 1; - var DATE = 2; - var HOUR = 3; - var MINUTE = 4; - var SECOND = 5; - var MILLISECOND = 6; - - function daysInMonth(year, month) { - return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); - } - - // FORMATTING - - addFormatToken('M', ['MM', 2], 'Mo', function () { - return this.month() + 1; - }); - - addFormatToken('MMM', 0, 0, function (format) { - return this.localeData().monthsShort(this, format); - }); - - addFormatToken('MMMM', 0, 0, function (format) { - return this.localeData().months(this, format); - }); - - // ALIASES - - addUnitAlias('month', 'M'); - - // PARSING - - addRegexToken('M', match1to2); - addRegexToken('MM', match1to2, match2); - addRegexToken('MMM', matchWord); - addRegexToken('MMMM', matchWord); - - addParseToken(['M', 'MM'], function (input, array) { - array[MONTH] = toInt(input) - 1; - }); - - addParseToken(['MMM', 'MMMM'], function (input, array, config, token) { - var month = config._locale.monthsParse(input, token, config._strict); - // if we didn't find a month name, mark the date as invalid. - if (month != null) { - array[MONTH] = month; - } else { - getParsingFlags(config).invalidMonth = input; - } - }); - - // LOCALES - - var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'); - function localeMonths (m) { - return this._months[m.month()]; - } - - var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'); - function localeMonthsShort (m) { - return this._monthsShort[m.month()]; - } - - function localeMonthsParse (monthName, format, strict) { - var i, mom, regex; - - if (!this._monthsParse) { - this._monthsParse = []; - this._longMonthsParse = []; - this._shortMonthsParse = []; - } - - for (i = 0; i < 12; i++) { - // make the regex if we don't have it already - mom = create_utc__createUTC([2000, i]); - if (strict && !this._longMonthsParse[i]) { - this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i'); - this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i'); - } - if (!strict && !this._monthsParse[i]) { - regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); - this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) { - return i; - } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) { - return i; - } else if (!strict && this._monthsParse[i].test(monthName)) { - return i; - } - } - } - - // MOMENTS - - function setMonth (mom, value) { - var dayOfMonth; - - // TODO: Move this out of here! - if (typeof value === 'string') { - value = mom.localeData().monthsParse(value); - // TODO: Another silent failure? - if (typeof value !== 'number') { - return mom; - } - } - - dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value)); - mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth); - return mom; - } - - function getSetMonth (value) { - if (value != null) { - setMonth(this, value); - utils_hooks__hooks.updateOffset(this, true); - return this; - } else { - return get_set__get(this, 'Month'); - } - } - - function getDaysInMonth () { - return daysInMonth(this.year(), this.month()); - } - - function checkOverflow (m) { - var overflow; - var a = m._a; - - if (a && getParsingFlags(m).overflow === -2) { - overflow = - a[MONTH] < 0 || a[MONTH] > 11 ? MONTH : - a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE : - a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR : - a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE : - a[SECOND] < 0 || a[SECOND] > 59 ? SECOND : - a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND : - -1; - - if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { - overflow = DATE; - } - - getParsingFlags(m).overflow = overflow; - } - - return m; - } - - function warn(msg) { - if (utils_hooks__hooks.suppressDeprecationWarnings === false && typeof console !== 'undefined' && console.warn) { - console.warn('Deprecation warning: ' + msg); - } - } - - function deprecate(msg, fn) { - var firstTime = true; - - return extend(function () { - if (firstTime) { - warn(msg + '\n' + (new Error()).stack); - firstTime = false; - } - return fn.apply(this, arguments); - }, fn); - } - - var deprecations = {}; - - function deprecateSimple(name, msg) { - if (!deprecations[name]) { - warn(msg); - deprecations[name] = true; - } - } - - utils_hooks__hooks.suppressDeprecationWarnings = false; - - var from_string__isoRegex = /^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/; - - var isoDates = [ - ['YYYYYY-MM-DD', /[+-]\d{6}-\d{2}-\d{2}/], - ['YYYY-MM-DD', /\d{4}-\d{2}-\d{2}/], - ['GGGG-[W]WW-E', /\d{4}-W\d{2}-\d/], - ['GGGG-[W]WW', /\d{4}-W\d{2}/], - ['YYYY-DDD', /\d{4}-\d{3}/] - ]; - - // iso time formats and regexes - var isoTimes = [ - ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d+/], - ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], - ['HH:mm', /(T| )\d\d:\d\d/], - ['HH', /(T| )\d\d/] - ]; - - var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i; - - // date from iso format - function configFromISO(config) { - var i, l, - string = config._i, - match = from_string__isoRegex.exec(string); - - if (match) { - getParsingFlags(config).iso = true; - for (i = 0, l = isoDates.length; i < l; i++) { - if (isoDates[i][1].exec(string)) { - config._f = isoDates[i][0]; - break; - } - } - for (i = 0, l = isoTimes.length; i < l; i++) { - if (isoTimes[i][1].exec(string)) { - // match[6] should be 'T' or space - config._f += (match[6] || ' ') + isoTimes[i][0]; - break; - } - } - if (string.match(matchOffset)) { - config._f += 'Z'; - } - configFromStringAndFormat(config); - } else { - config._isValid = false; - } - } - - // date from iso format or fallback - function configFromString(config) { - var matched = aspNetJsonRegex.exec(config._i); - - if (matched !== null) { - config._d = new Date(+matched[1]); - return; - } - - configFromISO(config); - if (config._isValid === false) { - delete config._isValid; - utils_hooks__hooks.createFromInputFallback(config); - } - } - - utils_hooks__hooks.createFromInputFallback = deprecate( - 'moment construction falls back to js Date. This is ' + - 'discouraged and will be removed in upcoming major ' + - 'release. Please refer to ' + - 'https://github.com/moment/moment/issues/1407 for more info.', - function (config) { - config._d = new Date(config._i + (config._useUTC ? ' UTC' : '')); - } - ); - - function createDate (y, m, d, h, M, s, ms) { - //can't just apply() to create a date: - //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply - var date = new Date(y, m, d, h, M, s, ms); - - //the date constructor doesn't accept years < 1970 - if (y < 1970) { - date.setFullYear(y); - } - return date; - } - - function createUTCDate (y) { - var date = new Date(Date.UTC.apply(null, arguments)); - if (y < 1970) { - date.setUTCFullYear(y); - } - return date; - } - - addFormatToken(0, ['YY', 2], 0, function () { - return this.year() % 100; - }); - - addFormatToken(0, ['YYYY', 4], 0, 'year'); - addFormatToken(0, ['YYYYY', 5], 0, 'year'); - addFormatToken(0, ['YYYYYY', 6, true], 0, 'year'); - - // ALIASES - - addUnitAlias('year', 'y'); - - // PARSING - - addRegexToken('Y', matchSigned); - addRegexToken('YY', match1to2, match2); - addRegexToken('YYYY', match1to4, match4); - addRegexToken('YYYYY', match1to6, match6); - addRegexToken('YYYYYY', match1to6, match6); - - addParseToken(['YYYYY', 'YYYYYY'], YEAR); - addParseToken('YYYY', function (input, array) { - array[YEAR] = input.length === 2 ? utils_hooks__hooks.parseTwoDigitYear(input) : toInt(input); - }); - addParseToken('YY', function (input, array) { - array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input); - }); - - // HELPERS - - function daysInYear(year) { - return isLeapYear(year) ? 366 : 365; - } - - function isLeapYear(year) { - return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; - } - - // HOOKS - - utils_hooks__hooks.parseTwoDigitYear = function (input) { - return toInt(input) + (toInt(input) > 68 ? 1900 : 2000); - }; - - // MOMENTS - - var getSetYear = makeGetSet('FullYear', false); - - function getIsLeapYear () { - return isLeapYear(this.year()); - } - - addFormatToken('w', ['ww', 2], 'wo', 'week'); - addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek'); - - // ALIASES - - addUnitAlias('week', 'w'); - addUnitAlias('isoWeek', 'W'); - - // PARSING - - addRegexToken('w', match1to2); - addRegexToken('ww', match1to2, match2); - addRegexToken('W', match1to2); - addRegexToken('WW', match1to2, match2); - - addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) { - week[token.substr(0, 1)] = toInt(input); - }); - - // HELPERS - - // firstDayOfWeek 0 = sun, 6 = sat - // the day of the week that starts the week - // (usually sunday or monday) - // firstDayOfWeekOfYear 0 = sun, 6 = sat - // the first week is the week that contains the first - // of this day of the week - // (eg. ISO weeks use thursday (4)) - function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { - var end = firstDayOfWeekOfYear - firstDayOfWeek, - daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), - adjustedMoment; - - - if (daysToDayOfWeek > end) { - daysToDayOfWeek -= 7; - } - - if (daysToDayOfWeek < end - 7) { - daysToDayOfWeek += 7; - } - - adjustedMoment = local__createLocal(mom).add(daysToDayOfWeek, 'd'); - return { - week: Math.ceil(adjustedMoment.dayOfYear() / 7), - year: adjustedMoment.year() - }; - } - - // LOCALES - - function localeWeek (mom) { - return weekOfYear(mom, this._week.dow, this._week.doy).week; - } - - var defaultLocaleWeek = { - dow : 0, // Sunday is the first day of the week. - doy : 6 // The week that contains Jan 1st is the first week of the year. - }; - - function localeFirstDayOfWeek () { - return this._week.dow; - } - - function localeFirstDayOfYear () { - return this._week.doy; - } - - // MOMENTS - - function getSetWeek (input) { - var week = this.localeData().week(this); - return input == null ? week : this.add((input - week) * 7, 'd'); - } - - function getSetISOWeek (input) { - var week = weekOfYear(this, 1, 4).week; - return input == null ? week : this.add((input - week) * 7, 'd'); - } - - addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear'); - - // ALIASES - - addUnitAlias('dayOfYear', 'DDD'); - - // PARSING - - addRegexToken('DDD', match1to3); - addRegexToken('DDDD', match3); - addParseToken(['DDD', 'DDDD'], function (input, array, config) { - config._dayOfYear = toInt(input); - }); - - // HELPERS - - //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday - function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { - var week1Jan = 6 + firstDayOfWeek - firstDayOfWeekOfYear, janX = createUTCDate(year, 0, 1 + week1Jan), d = janX.getUTCDay(), dayOfYear; - if (d < firstDayOfWeek) { - d += 7; - } - - weekday = weekday != null ? 1 * weekday : firstDayOfWeek; - - dayOfYear = 1 + week1Jan + 7 * (week - 1) - d + weekday; - - return { - year: dayOfYear > 0 ? year : year - 1, - dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear - }; - } - - // MOMENTS - - function getSetDayOfYear (input) { - var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1; - return input == null ? dayOfYear : this.add((input - dayOfYear), 'd'); - } - - // Pick the first defined of two or three arguments. - function defaults(a, b, c) { - if (a != null) { - return a; - } - if (b != null) { - return b; - } - return c; - } - - function currentDateArray(config) { - var now = new Date(); - if (config._useUTC) { - return [now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()]; - } - return [now.getFullYear(), now.getMonth(), now.getDate()]; - } - - // convert an array to a date. - // the array should mirror the parameters below - // note: all values past the year are optional and will default to the lowest possible value. - // [year, month, day , hour, minute, second, millisecond] - function configFromArray (config) { - var i, date, input = [], currentDate, yearToUse; - - if (config._d) { - return; - } - - currentDate = currentDateArray(config); - - //compute day of the year from weeks and weekdays - if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { - dayOfYearFromWeekInfo(config); - } - - //if the day of the year is set, figure out what it is - if (config._dayOfYear) { - yearToUse = defaults(config._a[YEAR], currentDate[YEAR]); - - if (config._dayOfYear > daysInYear(yearToUse)) { - getParsingFlags(config)._overflowDayOfYear = true; - } - - date = createUTCDate(yearToUse, 0, config._dayOfYear); - config._a[MONTH] = date.getUTCMonth(); - config._a[DATE] = date.getUTCDate(); - } - - // Default to current date. - // * if no year, month, day of month are given, default to today - // * if day of month is given, default month and year - // * if month is given, default only year - // * if year is given, don't default anything - for (i = 0; i < 3 && config._a[i] == null; ++i) { - config._a[i] = input[i] = currentDate[i]; - } - - // Zero out whatever was not defaulted, including time - for (; i < 7; i++) { - config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; - } - - // Check for 24:00:00.000 - if (config._a[HOUR] === 24 && - config._a[MINUTE] === 0 && - config._a[SECOND] === 0 && - config._a[MILLISECOND] === 0) { - config._nextDay = true; - config._a[HOUR] = 0; - } - - config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input); - // Apply timezone offset from input. The actual utcOffset can be changed - // with parseZone. - if (config._tzm != null) { - config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm); - } - - if (config._nextDay) { - config._a[HOUR] = 24; - } - } - - function dayOfYearFromWeekInfo(config) { - var w, weekYear, week, weekday, dow, doy, temp; - - w = config._w; - if (w.GG != null || w.W != null || w.E != null) { - dow = 1; - doy = 4; - - // TODO: We need to take the current isoWeekYear, but that depends on - // how we interpret now (local, utc, fixed offset). So create - // a now version of current config (take local/utc/offset flags, and - // create now). - weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(local__createLocal(), 1, 4).year); - week = defaults(w.W, 1); - weekday = defaults(w.E, 1); - } else { - dow = config._locale._week.dow; - doy = config._locale._week.doy; - - weekYear = defaults(w.gg, config._a[YEAR], weekOfYear(local__createLocal(), dow, doy).year); - week = defaults(w.w, 1); - - if (w.d != null) { - // weekday -- low day numbers are considered next week - weekday = w.d; - if (weekday < dow) { - ++week; - } - } else if (w.e != null) { - // local weekday -- counting starts from begining of week - weekday = w.e + dow; - } else { - // default to begining of week - weekday = dow; - } - } - temp = dayOfYearFromWeeks(weekYear, week, weekday, doy, dow); - - config._a[YEAR] = temp.year; - config._dayOfYear = temp.dayOfYear; - } - - utils_hooks__hooks.ISO_8601 = function () {}; - - // date from string and format string - function configFromStringAndFormat(config) { - // TODO: Move this to another part of the creation flow to prevent circular deps - if (config._f === utils_hooks__hooks.ISO_8601) { - configFromISO(config); - return; - } - - config._a = []; - getParsingFlags(config).empty = true; - - // This array is used to make a Date, either with `new Date` or `Date.UTC` - var string = '' + config._i, - i, parsedInput, tokens, token, skipped, - stringLength = string.length, - totalParsedInputLength = 0; - - tokens = expandFormat(config._f, config._locale).match(formattingTokens) || []; - - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; - parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; - if (parsedInput) { - skipped = string.substr(0, string.indexOf(parsedInput)); - if (skipped.length > 0) { - getParsingFlags(config).unusedInput.push(skipped); - } - string = string.slice(string.indexOf(parsedInput) + parsedInput.length); - totalParsedInputLength += parsedInput.length; - } - // don't parse if it's not a known token - if (formatTokenFunctions[token]) { - if (parsedInput) { - getParsingFlags(config).empty = false; - } - else { - getParsingFlags(config).unusedTokens.push(token); - } - addTimeToArrayFromToken(token, parsedInput, config); - } - else if (config._strict && !parsedInput) { - getParsingFlags(config).unusedTokens.push(token); - } - } - - // add remaining unparsed input length to the string - getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength; - if (string.length > 0) { - getParsingFlags(config).unusedInput.push(string); - } - - // clear _12h flag if hour is <= 12 - if (getParsingFlags(config).bigHour === true && - config._a[HOUR] <= 12 && - config._a[HOUR] > 0) { - getParsingFlags(config).bigHour = undefined; - } - // handle meridiem - config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem); - - configFromArray(config); - checkOverflow(config); - } - - - function meridiemFixWrap (locale, hour, meridiem) { - var isPm; - - if (meridiem == null) { - // nothing to do - return hour; - } - if (locale.meridiemHour != null) { - return locale.meridiemHour(hour, meridiem); - } else if (locale.isPM != null) { - // Fallback - isPm = locale.isPM(meridiem); - if (isPm && hour < 12) { - hour += 12; - } - if (!isPm && hour === 12) { - hour = 0; - } - return hour; - } else { - // this is not supposed to happen - return hour; - } - } - - function configFromStringAndArray(config) { - var tempConfig, - bestMoment, - - scoreToBeat, - i, - currentScore; - - if (config._f.length === 0) { - getParsingFlags(config).invalidFormat = true; - config._d = new Date(NaN); - return; - } - - for (i = 0; i < config._f.length; i++) { - currentScore = 0; - tempConfig = copyConfig({}, config); - if (config._useUTC != null) { - tempConfig._useUTC = config._useUTC; - } - tempConfig._f = config._f[i]; - configFromStringAndFormat(tempConfig); - - if (!valid__isValid(tempConfig)) { - continue; - } - - // if there is any input that was not parsed add a penalty for that format - currentScore += getParsingFlags(tempConfig).charsLeftOver; - - //or tokens - currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10; - - getParsingFlags(tempConfig).score = currentScore; - - if (scoreToBeat == null || currentScore < scoreToBeat) { - scoreToBeat = currentScore; - bestMoment = tempConfig; - } - } - - extend(config, bestMoment || tempConfig); - } - - function configFromObject(config) { - if (config._d) { - return; - } - - var i = normalizeObjectUnits(config._i); - config._a = [i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond]; - - configFromArray(config); - } - - function createFromConfig (config) { - var res = new Moment(checkOverflow(prepareConfig(config))); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; - } - - return res; - } - - function prepareConfig (config) { - var input = config._i, - format = config._f; - - config._locale = config._locale || locale_locales__getLocale(config._l); - - if (input === null || (format === undefined && input === '')) { - return valid__createInvalid({nullInput: true}); - } - - if (typeof input === 'string') { - config._i = input = config._locale.preparse(input); - } - - if (isMoment(input)) { - return new Moment(checkOverflow(input)); - } else if (isArray(format)) { - configFromStringAndArray(config); - } else if (format) { - configFromStringAndFormat(config); - } else if (isDate(input)) { - config._d = input; - } else { - configFromInput(config); - } - - return config; - } - - function configFromInput(config) { - var input = config._i; - if (input === undefined) { - config._d = new Date(); - } else if (isDate(input)) { - config._d = new Date(+input); - } else if (typeof input === 'string') { - configFromString(config); - } else if (isArray(input)) { - config._a = map(input.slice(0), function (obj) { - return parseInt(obj, 10); - }); - configFromArray(config); - } else if (typeof(input) === 'object') { - configFromObject(config); - } else if (typeof(input) === 'number') { - // from milliseconds - config._d = new Date(input); - } else { - utils_hooks__hooks.createFromInputFallback(config); - } - } - - function createLocalOrUTC (input, format, locale, strict, isUTC) { - var c = {}; - - if (typeof(locale) === 'boolean') { - strict = locale; - locale = undefined; - } - // object construction must be done this way. - // https://github.com/moment/moment/issues/1423 - c._isAMomentObject = true; - c._useUTC = c._isUTC = isUTC; - c._l = locale; - c._i = input; - c._f = format; - c._strict = strict; - - return createFromConfig(c); - } - - function local__createLocal (input, format, locale, strict) { - return createLocalOrUTC(input, format, locale, strict, false); - } - - var prototypeMin = deprecate( - 'moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548', - function () { - var other = local__createLocal.apply(null, arguments); - return other < this ? this : other; - } - ); - - var prototypeMax = deprecate( - 'moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548', - function () { - var other = local__createLocal.apply(null, arguments); - return other > this ? this : other; - } - ); - - // Pick a moment m from moments so that m[fn](other) is true for all - // other. This relies on the function fn to be transitive. - // - // moments should either be an array of moment objects or an array, whose - // first element is an array of moment objects. - function pickBy(fn, moments) { - var res, i; - if (moments.length === 1 && isArray(moments[0])) { - moments = moments[0]; - } - if (!moments.length) { - return local__createLocal(); - } - res = moments[0]; - for (i = 1; i < moments.length; ++i) { - if (!moments[i].isValid() || moments[i][fn](res)) { - res = moments[i]; - } - } - return res; - } - - // TODO: Use [].sort instead? - function min () { - var args = [].slice.call(arguments, 0); - - return pickBy('isBefore', args); - } - - function max () { - var args = [].slice.call(arguments, 0); - - return pickBy('isAfter', args); - } - - function Duration (duration) { - var normalizedInput = normalizeObjectUnits(duration), - years = normalizedInput.year || 0, - quarters = normalizedInput.quarter || 0, - months = normalizedInput.month || 0, - weeks = normalizedInput.week || 0, - days = normalizedInput.day || 0, - hours = normalizedInput.hour || 0, - minutes = normalizedInput.minute || 0, - seconds = normalizedInput.second || 0, - milliseconds = normalizedInput.millisecond || 0; - - // representation for dateAddRemove - this._milliseconds = +milliseconds + - seconds * 1e3 + // 1000 - minutes * 6e4 + // 1000 * 60 - hours * 36e5; // 1000 * 60 * 60 - // Because of dateAddRemove treats 24 hours as different from a - // day when working around DST, we need to store them separately - this._days = +days + - weeks * 7; - // It is impossible translate months into days without knowing - // which months you are are talking about, so we have to store - // it separately. - this._months = +months + - quarters * 3 + - years * 12; - - this._data = {}; - - this._locale = locale_locales__getLocale(); - - this._bubble(); - } - - function isDuration (obj) { - return obj instanceof Duration; - } - - function offset (token, separator) { - addFormatToken(token, 0, 0, function () { - var offset = this.utcOffset(); - var sign = '+'; - if (offset < 0) { - offset = -offset; - sign = '-'; - } - return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); - }); - } - - offset('Z', ':'); - offset('ZZ', ''); - - // PARSING - - addRegexToken('Z', matchOffset); - addRegexToken('ZZ', matchOffset); - addParseToken(['Z', 'ZZ'], function (input, array, config) { - config._useUTC = true; - config._tzm = offsetFromString(input); - }); - - // HELPERS - - // timezone chunker - // '+10:00' > ['10', '00'] - // '-1530' > ['-15', '30'] - var chunkOffset = /([\+\-]|\d\d)/gi; - - function offsetFromString(string) { - var matches = ((string || '').match(matchOffset) || []); - var chunk = matches[matches.length - 1] || []; - var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; - var minutes = +(parts[1] * 60) + toInt(parts[2]); - - return parts[0] === '+' ? minutes : -minutes; - } - - // Return a moment from input, that is local/utc/zone equivalent to model. - function cloneWithOffset(input, model) { - var res, diff; - if (model._isUTC) { - res = model.clone(); - diff = (isMoment(input) || isDate(input) ? +input : +local__createLocal(input)) - (+res); - // Use low-level api, because this fn is low-level api. - res._d.setTime(+res._d + diff); - utils_hooks__hooks.updateOffset(res, false); - return res; - } else { - return local__createLocal(input).local(); - } - } - - function getDateOffset (m) { - // On Firefox.24 Date#getTimezoneOffset returns a floating point. - // https://github.com/moment/moment/pull/1871 - return -Math.round(m._d.getTimezoneOffset() / 15) * 15; - } - - // HOOKS - - // This function will be called whenever a moment is mutated. - // It is intended to keep the offset in sync with the timezone. - utils_hooks__hooks.updateOffset = function () {}; - - // MOMENTS - - // keepLocalTime = true means only change the timezone, without - // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> - // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset - // +0200, so we adjust the time as needed, to be valid. - // - // Keeping the time actually adds/subtracts (one hour) - // from the actual represented time. That is why we call updateOffset - // a second time. In case it wants us to change the offset again - // _changeInProgress == true case, then we have to adjust, because - // there is no such time in the given timezone. - function getSetOffset (input, keepLocalTime) { - var offset = this._offset || 0, - localAdjust; - if (input != null) { - if (typeof input === 'string') { - input = offsetFromString(input); - } - if (Math.abs(input) < 16) { - input = input * 60; - } - if (!this._isUTC && keepLocalTime) { - localAdjust = getDateOffset(this); - } - this._offset = input; - this._isUTC = true; - if (localAdjust != null) { - this.add(localAdjust, 'm'); - } - if (offset !== input) { - if (!keepLocalTime || this._changeInProgress) { - add_subtract__addSubtract(this, create__createDuration(input - offset, 'm'), 1, false); - } else if (!this._changeInProgress) { - this._changeInProgress = true; - utils_hooks__hooks.updateOffset(this, true); - this._changeInProgress = null; - } - } - return this; - } else { - return this._isUTC ? offset : getDateOffset(this); - } - } - - function getSetZone (input, keepLocalTime) { - if (input != null) { - if (typeof input !== 'string') { - input = -input; - } - - this.utcOffset(input, keepLocalTime); - - return this; - } else { - return -this.utcOffset(); - } - } - - function setOffsetToUTC (keepLocalTime) { - return this.utcOffset(0, keepLocalTime); - } - - function setOffsetToLocal (keepLocalTime) { - if (this._isUTC) { - this.utcOffset(0, keepLocalTime); - this._isUTC = false; - - if (keepLocalTime) { - this.subtract(getDateOffset(this), 'm'); - } - } - return this; - } - - function setOffsetToParsedOffset () { - if (this._tzm) { - this.utcOffset(this._tzm); - } else if (typeof this._i === 'string') { - this.utcOffset(offsetFromString(this._i)); - } - return this; - } - - function hasAlignedHourOffset (input) { - input = input ? local__createLocal(input).utcOffset() : 0; - - return (this.utcOffset() - input) % 60 === 0; - } - - function isDaylightSavingTime () { - return ( - this.utcOffset() > this.clone().month(0).utcOffset() || - this.utcOffset() > this.clone().month(5).utcOffset() - ); - } - - function isDaylightSavingTimeShifted () { - if (typeof this._isDSTShifted !== 'undefined') { - return this._isDSTShifted; - } - - var c = {}; - - copyConfig(c, this); - c = prepareConfig(c); - - if (c._a) { - var other = c._isUTC ? create_utc__createUTC(c._a) : local__createLocal(c._a); - this._isDSTShifted = this.isValid() && - compareArrays(c._a, other.toArray()) > 0; - } else { - this._isDSTShifted = false; - } - - return this._isDSTShifted; - } - - function isLocal () { - return !this._isUTC; - } - - function isUtcOffset () { - return this._isUTC; - } - - function isUtc () { - return this._isUTC && this._offset === 0; - } - - var aspNetRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/; - - // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html - // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere - var create__isoRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/; - - function create__createDuration (input, key) { - var duration = input, - // matching against regexp is expensive, do it on demand - match = null, - sign, - ret, - diffRes; - - if (isDuration(input)) { - duration = { - ms : input._milliseconds, - d : input._days, - M : input._months - }; - } else if (typeof input === 'number') { - duration = {}; - if (key) { - duration[key] = input; - } else { - duration.milliseconds = input; - } - } else if (!!(match = aspNetRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y : 0, - d : toInt(match[DATE]) * sign, - h : toInt(match[HOUR]) * sign, - m : toInt(match[MINUTE]) * sign, - s : toInt(match[SECOND]) * sign, - ms : toInt(match[MILLISECOND]) * sign - }; - } else if (!!(match = create__isoRegex.exec(input))) { - sign = (match[1] === '-') ? -1 : 1; - duration = { - y : parseIso(match[2], sign), - M : parseIso(match[3], sign), - d : parseIso(match[4], sign), - h : parseIso(match[5], sign), - m : parseIso(match[6], sign), - s : parseIso(match[7], sign), - w : parseIso(match[8], sign) - }; - } else if (duration == null) {// checks for null or undefined - duration = {}; - } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) { - diffRes = momentsDifference(local__createLocal(duration.from), local__createLocal(duration.to)); - - duration = {}; - duration.ms = diffRes.milliseconds; - duration.M = diffRes.months; - } - - ret = new Duration(duration); - - if (isDuration(input) && hasOwnProp(input, '_locale')) { - ret._locale = input._locale; - } - - return ret; - } - - create__createDuration.fn = Duration.prototype; - - function parseIso (inp, sign) { - // We'd normally use ~~inp for this, but unfortunately it also - // converts floats to ints. - // inp may be undefined, so careful calling replace on it. - var res = inp && parseFloat(inp.replace(',', '.')); - // apply sign while we're at it - return (isNaN(res) ? 0 : res) * sign; - } - - function positiveMomentsDifference(base, other) { - var res = {milliseconds: 0, months: 0}; - - res.months = other.month() - base.month() + - (other.year() - base.year()) * 12; - if (base.clone().add(res.months, 'M').isAfter(other)) { - --res.months; - } - - res.milliseconds = +other - +(base.clone().add(res.months, 'M')); - - return res; - } - - function momentsDifference(base, other) { - var res; - other = cloneWithOffset(other, base); - if (base.isBefore(other)) { - res = positiveMomentsDifference(base, other); - } else { - res = positiveMomentsDifference(other, base); - res.milliseconds = -res.milliseconds; - res.months = -res.months; - } - - return res; - } - - function createAdder(direction, name) { - return function (val, period) { - var dur, tmp; - //invert the arguments, but complain about it - if (period !== null && !isNaN(+period)) { - deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period).'); - tmp = val; val = period; period = tmp; - } - - val = typeof val === 'string' ? +val : val; - dur = create__createDuration(val, period); - add_subtract__addSubtract(this, dur, direction); - return this; - }; - } - - function add_subtract__addSubtract (mom, duration, isAdding, updateOffset) { - var milliseconds = duration._milliseconds, - days = duration._days, - months = duration._months; - updateOffset = updateOffset == null ? true : updateOffset; - - if (milliseconds) { - mom._d.setTime(+mom._d + milliseconds * isAdding); - } - if (days) { - get_set__set(mom, 'Date', get_set__get(mom, 'Date') + days * isAdding); - } - if (months) { - setMonth(mom, get_set__get(mom, 'Month') + months * isAdding); - } - if (updateOffset) { - utils_hooks__hooks.updateOffset(mom, days || months); - } - } - - var add_subtract__add = createAdder(1, 'add'); - var add_subtract__subtract = createAdder(-1, 'subtract'); - - function moment_calendar__calendar (time, formats) { - // We want to compare the start of today, vs this. - // Getting start-of-today depends on whether we're local/utc/offset or not. - var now = time || local__createLocal(), - sod = cloneWithOffset(now, this).startOf('day'), - diff = this.diff(sod, 'days', true), - format = diff < -6 ? 'sameElse' : - diff < -1 ? 'lastWeek' : - diff < 0 ? 'lastDay' : - diff < 1 ? 'sameDay' : - diff < 2 ? 'nextDay' : - diff < 7 ? 'nextWeek' : 'sameElse'; - return this.format(formats && formats[format] || this.localeData().calendar(format, this, local__createLocal(now))); - } - - function clone () { - return new Moment(this); - } - - function isAfter (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = isMoment(input) ? input : local__createLocal(input); - return +this > +input; - } else { - inputMs = isMoment(input) ? +input : +local__createLocal(input); - return inputMs < +this.clone().startOf(units); - } - } - - function isBefore (input, units) { - var inputMs; - units = normalizeUnits(typeof units !== 'undefined' ? units : 'millisecond'); - if (units === 'millisecond') { - input = isMoment(input) ? input : local__createLocal(input); - return +this < +input; - } else { - inputMs = isMoment(input) ? +input : +local__createLocal(input); - return +this.clone().endOf(units) < inputMs; - } - } - - function isBetween (from, to, units) { - return this.isAfter(from, units) && this.isBefore(to, units); - } - - function isSame (input, units) { - var inputMs; - units = normalizeUnits(units || 'millisecond'); - if (units === 'millisecond') { - input = isMoment(input) ? input : local__createLocal(input); - return +this === +input; - } else { - inputMs = +local__createLocal(input); - return +(this.clone().startOf(units)) <= inputMs && inputMs <= +(this.clone().endOf(units)); - } - } - - function diff (input, units, asFloat) { - var that = cloneWithOffset(input, this), - zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4, - delta, output; - - units = normalizeUnits(units); - - if (units === 'year' || units === 'month' || units === 'quarter') { - output = monthDiff(this, that); - if (units === 'quarter') { - output = output / 3; - } else if (units === 'year') { - output = output / 12; - } - } else { - delta = this - that; - output = units === 'second' ? delta / 1e3 : // 1000 - units === 'minute' ? delta / 6e4 : // 1000 * 60 - units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60 - units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst - units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst - delta; - } - return asFloat ? output : absFloor(output); - } - - function monthDiff (a, b) { - // difference in months - var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()), - // b is in (anchor - 1 month, anchor + 1 month) - anchor = a.clone().add(wholeMonthDiff, 'months'), - anchor2, adjust; - - if (b - anchor < 0) { - anchor2 = a.clone().add(wholeMonthDiff - 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor - anchor2); - } else { - anchor2 = a.clone().add(wholeMonthDiff + 1, 'months'); - // linear across the month - adjust = (b - anchor) / (anchor2 - anchor); - } - - return -(wholeMonthDiff + adjust); - } - - utils_hooks__hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ'; - - function toString () { - return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ'); - } - - function moment_format__toISOString () { - var m = this.clone().utc(); - if (0 < m.year() && m.year() <= 9999) { - if ('function' === typeof Date.prototype.toISOString) { - // native implementation is ~50x faster, use it when we can - return this.toDate().toISOString(); - } else { - return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - } else { - return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); - } - } - - function format (inputString) { - var output = formatMoment(this, inputString || utils_hooks__hooks.defaultFormat); - return this.localeData().postformat(output); - } - - function from (time, withoutSuffix) { - if (!this.isValid()) { - return this.localeData().invalidDate(); - } - return create__createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix); - } - - function fromNow (withoutSuffix) { - return this.from(local__createLocal(), withoutSuffix); - } - - function to (time, withoutSuffix) { - if (!this.isValid()) { - return this.localeData().invalidDate(); - } - return create__createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix); - } - - function toNow (withoutSuffix) { - return this.to(local__createLocal(), withoutSuffix); - } - - function locale (key) { - var newLocaleData; - - if (key === undefined) { - return this._locale._abbr; - } else { - newLocaleData = locale_locales__getLocale(key); - if (newLocaleData != null) { - this._locale = newLocaleData; - } - return this; - } - } - - var lang = deprecate( - 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.', - function (key) { - if (key === undefined) { - return this.localeData(); - } else { - return this.locale(key); - } - } - ); - - function localeData () { - return this._locale; - } - - function startOf (units) { - units = normalizeUnits(units); - // the following switch intentionally omits break keywords - // to utilize falling through the cases. - switch (units) { - case 'year': - this.month(0); - /* falls through */ - case 'quarter': - case 'month': - this.date(1); - /* falls through */ - case 'week': - case 'isoWeek': - case 'day': - this.hours(0); - /* falls through */ - case 'hour': - this.minutes(0); - /* falls through */ - case 'minute': - this.seconds(0); - /* falls through */ - case 'second': - this.milliseconds(0); - } - - // weeks are a special case - if (units === 'week') { - this.weekday(0); - } - if (units === 'isoWeek') { - this.isoWeekday(1); - } - - // quarters are also special - if (units === 'quarter') { - this.month(Math.floor(this.month() / 3) * 3); - } - - return this; - } - - function endOf (units) { - units = normalizeUnits(units); - if (units === undefined || units === 'millisecond') { - return this; - } - return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms'); - } - - function to_type__valueOf () { - return +this._d - ((this._offset || 0) * 60000); - } - - function unix () { - return Math.floor(+this / 1000); - } - - function toDate () { - return this._offset ? new Date(+this) : this._d; - } - - function toArray () { - var m = this; - return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; - } - - function toObject () { - var m = this; - return { - years: m.year(), - months: m.month(), - date: m.date(), - hours: m.hours(), - minutes: m.minutes(), - seconds: m.seconds(), - milliseconds: m.milliseconds() - }; - } - - function moment_valid__isValid () { - return valid__isValid(this); - } - - function parsingFlags () { - return extend({}, getParsingFlags(this)); - } - - function invalidAt () { - return getParsingFlags(this).overflow; - } - - addFormatToken(0, ['gg', 2], 0, function () { - return this.weekYear() % 100; - }); - - addFormatToken(0, ['GG', 2], 0, function () { - return this.isoWeekYear() % 100; - }); - - function addWeekYearFormatToken (token, getter) { - addFormatToken(0, [token, token.length], 0, getter); - } - - addWeekYearFormatToken('gggg', 'weekYear'); - addWeekYearFormatToken('ggggg', 'weekYear'); - addWeekYearFormatToken('GGGG', 'isoWeekYear'); - addWeekYearFormatToken('GGGGG', 'isoWeekYear'); - - // ALIASES - - addUnitAlias('weekYear', 'gg'); - addUnitAlias('isoWeekYear', 'GG'); - - // PARSING - - addRegexToken('G', matchSigned); - addRegexToken('g', matchSigned); - addRegexToken('GG', match1to2, match2); - addRegexToken('gg', match1to2, match2); - addRegexToken('GGGG', match1to4, match4); - addRegexToken('gggg', match1to4, match4); - addRegexToken('GGGGG', match1to6, match6); - addRegexToken('ggggg', match1to6, match6); - - addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) { - week[token.substr(0, 2)] = toInt(input); - }); - - addWeekParseToken(['gg', 'GG'], function (input, week, config, token) { - week[token] = utils_hooks__hooks.parseTwoDigitYear(input); - }); - - // HELPERS - - function weeksInYear(year, dow, doy) { - return weekOfYear(local__createLocal([year, 11, 31 + dow - doy]), dow, doy).week; - } - - // MOMENTS - - function getSetWeekYear (input) { - var year = weekOfYear(this, this.localeData()._week.dow, this.localeData()._week.doy).year; - return input == null ? year : this.add((input - year), 'y'); - } - - function getSetISOWeekYear (input) { - var year = weekOfYear(this, 1, 4).year; - return input == null ? year : this.add((input - year), 'y'); - } - - function getISOWeeksInYear () { - return weeksInYear(this.year(), 1, 4); - } - - function getWeeksInYear () { - var weekInfo = this.localeData()._week; - return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy); - } - - addFormatToken('Q', 0, 0, 'quarter'); - - // ALIASES - - addUnitAlias('quarter', 'Q'); - - // PARSING - - addRegexToken('Q', match1); - addParseToken('Q', function (input, array) { - array[MONTH] = (toInt(input) - 1) * 3; - }); - - // MOMENTS - - function getSetQuarter (input) { - return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3); - } - - addFormatToken('D', ['DD', 2], 'Do', 'date'); - - // ALIASES - - addUnitAlias('date', 'D'); - - // PARSING - - addRegexToken('D', match1to2); - addRegexToken('DD', match1to2, match2); - addRegexToken('Do', function (isStrict, locale) { - return isStrict ? locale._ordinalParse : locale._ordinalParseLenient; - }); - - addParseToken(['D', 'DD'], DATE); - addParseToken('Do', function (input, array) { - array[DATE] = toInt(input.match(match1to2)[0], 10); - }); - - // MOMENTS - - var getSetDayOfMonth = makeGetSet('Date', true); - - addFormatToken('d', 0, 'do', 'day'); - - addFormatToken('dd', 0, 0, function (format) { - return this.localeData().weekdaysMin(this, format); - }); - - addFormatToken('ddd', 0, 0, function (format) { - return this.localeData().weekdaysShort(this, format); - }); - - addFormatToken('dddd', 0, 0, function (format) { - return this.localeData().weekdays(this, format); - }); - - addFormatToken('e', 0, 0, 'weekday'); - addFormatToken('E', 0, 0, 'isoWeekday'); - - // ALIASES - - addUnitAlias('day', 'd'); - addUnitAlias('weekday', 'e'); - addUnitAlias('isoWeekday', 'E'); - - // PARSING - - addRegexToken('d', match1to2); - addRegexToken('e', match1to2); - addRegexToken('E', match1to2); - addRegexToken('dd', matchWord); - addRegexToken('ddd', matchWord); - addRegexToken('dddd', matchWord); - - addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config) { - var weekday = config._locale.weekdaysParse(input); - // if we didn't get a weekday name, mark the date as invalid - if (weekday != null) { - week.d = weekday; - } else { - getParsingFlags(config).invalidWeekday = input; - } - }); - - addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) { - week[token] = toInt(input); - }); - - // HELPERS - - function parseWeekday(input, locale) { - if (typeof input !== 'string') { - return input; - } - - if (!isNaN(input)) { - return parseInt(input, 10); - } - - input = locale.weekdaysParse(input); - if (typeof input === 'number') { - return input; - } - - return null; - } - - // LOCALES - - var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'); - function localeWeekdays (m) { - return this._weekdays[m.day()]; - } - - var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'); - function localeWeekdaysShort (m) { - return this._weekdaysShort[m.day()]; - } - - var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_'); - function localeWeekdaysMin (m) { - return this._weekdaysMin[m.day()]; - } - - function localeWeekdaysParse (weekdayName) { - var i, mom, regex; - - this._weekdaysParse = this._weekdaysParse || []; - - for (i = 0; i < 7; i++) { - // make the regex if we don't have it already - if (!this._weekdaysParse[i]) { - mom = local__createLocal([2000, 1]).day(i); - regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); - this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); - } - // test the regex - if (this._weekdaysParse[i].test(weekdayName)) { - return i; - } - } - } - - // MOMENTS - - function getSetDayOfWeek (input) { - var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); - if (input != null) { - input = parseWeekday(input, this.localeData()); - return this.add(input - day, 'd'); - } else { - return day; - } - } - - function getSetLocaleDayOfWeek (input) { - var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7; - return input == null ? weekday : this.add(input - weekday, 'd'); - } - - function getSetISODayOfWeek (input) { - // behaves the same as moment#day except - // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) - // as a setter, sunday should belong to the previous week. - return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); - } - - addFormatToken('H', ['HH', 2], 0, 'hour'); - addFormatToken('h', ['hh', 2], 0, function () { - return this.hours() % 12 || 12; - }); - - function meridiem (token, lowercase) { - addFormatToken(token, 0, 0, function () { - return this.localeData().meridiem(this.hours(), this.minutes(), lowercase); - }); - } - - meridiem('a', true); - meridiem('A', false); - - // ALIASES - - addUnitAlias('hour', 'h'); - - // PARSING - - function matchMeridiem (isStrict, locale) { - return locale._meridiemParse; - } - - addRegexToken('a', matchMeridiem); - addRegexToken('A', matchMeridiem); - addRegexToken('H', match1to2); - addRegexToken('h', match1to2); - addRegexToken('HH', match1to2, match2); - addRegexToken('hh', match1to2, match2); - - addParseToken(['H', 'HH'], HOUR); - addParseToken(['a', 'A'], function (input, array, config) { - config._isPm = config._locale.isPM(input); - config._meridiem = input; - }); - addParseToken(['h', 'hh'], function (input, array, config) { - array[HOUR] = toInt(input); - getParsingFlags(config).bigHour = true; - }); - - // LOCALES - - function localeIsPM (input) { - // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays - // Using charAt should be more compatible. - return ((input + '').toLowerCase().charAt(0) === 'p'); - } - - var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i; - function localeMeridiem (hours, minutes, isLower) { - if (hours > 11) { - return isLower ? 'pm' : 'PM'; - } else { - return isLower ? 'am' : 'AM'; - } - } - - - // MOMENTS - - // Setting the hour should keep the time, because the user explicitly - // specified which hour he wants. So trying to maintain the same hour (in - // a new timezone) makes sense. Adding/subtracting hours does not follow - // this rule. - var getSetHour = makeGetSet('Hours', true); - - addFormatToken('m', ['mm', 2], 0, 'minute'); - - // ALIASES - - addUnitAlias('minute', 'm'); - - // PARSING - - addRegexToken('m', match1to2); - addRegexToken('mm', match1to2, match2); - addParseToken(['m', 'mm'], MINUTE); - - // MOMENTS - - var getSetMinute = makeGetSet('Minutes', false); - - addFormatToken('s', ['ss', 2], 0, 'second'); - - // ALIASES - - addUnitAlias('second', 's'); - - // PARSING - - addRegexToken('s', match1to2); - addRegexToken('ss', match1to2, match2); - addParseToken(['s', 'ss'], SECOND); - - // MOMENTS - - var getSetSecond = makeGetSet('Seconds', false); - - addFormatToken('S', 0, 0, function () { - return ~~(this.millisecond() / 100); - }); - - addFormatToken(0, ['SS', 2], 0, function () { - return ~~(this.millisecond() / 10); - }); - - addFormatToken(0, ['SSS', 3], 0, 'millisecond'); - addFormatToken(0, ['SSSS', 4], 0, function () { - return this.millisecond() * 10; - }); - addFormatToken(0, ['SSSSS', 5], 0, function () { - return this.millisecond() * 100; - }); - addFormatToken(0, ['SSSSSS', 6], 0, function () { - return this.millisecond() * 1000; - }); - addFormatToken(0, ['SSSSSSS', 7], 0, function () { - return this.millisecond() * 10000; - }); - addFormatToken(0, ['SSSSSSSS', 8], 0, function () { - return this.millisecond() * 100000; - }); - addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { - return this.millisecond() * 1000000; - }); - - - // ALIASES - - addUnitAlias('millisecond', 'ms'); - - // PARSING - - addRegexToken('S', match1to3, match1); - addRegexToken('SS', match1to3, match2); - addRegexToken('SSS', match1to3, match3); - - var token; - for (token = 'SSSS'; token.length <= 9; token += 'S') { - addRegexToken(token, matchUnsigned); - } - - function parseMs(input, array) { - array[MILLISECOND] = toInt(('0.' + input) * 1000); - } - - for (token = 'S'; token.length <= 9; token += 'S') { - addParseToken(token, parseMs); - } - // MOMENTS - - var getSetMillisecond = makeGetSet('Milliseconds', false); - - addFormatToken('z', 0, 0, 'zoneAbbr'); - addFormatToken('zz', 0, 0, 'zoneName'); - - // MOMENTS - - function getZoneAbbr () { - return this._isUTC ? 'UTC' : ''; - } - - function getZoneName () { - return this._isUTC ? 'Coordinated Universal Time' : ''; - } - - var momentPrototype__proto = Moment.prototype; - - momentPrototype__proto.add = add_subtract__add; - momentPrototype__proto.calendar = moment_calendar__calendar; - momentPrototype__proto.clone = clone; - momentPrototype__proto.diff = diff; - momentPrototype__proto.endOf = endOf; - momentPrototype__proto.format = format; - momentPrototype__proto.from = from; - momentPrototype__proto.fromNow = fromNow; - momentPrototype__proto.to = to; - momentPrototype__proto.toNow = toNow; - momentPrototype__proto.get = getSet; - momentPrototype__proto.invalidAt = invalidAt; - momentPrototype__proto.isAfter = isAfter; - momentPrototype__proto.isBefore = isBefore; - momentPrototype__proto.isBetween = isBetween; - momentPrototype__proto.isSame = isSame; - momentPrototype__proto.isValid = moment_valid__isValid; - momentPrototype__proto.lang = lang; - momentPrototype__proto.locale = locale; - momentPrototype__proto.localeData = localeData; - momentPrototype__proto.max = prototypeMax; - momentPrototype__proto.min = prototypeMin; - momentPrototype__proto.parsingFlags = parsingFlags; - momentPrototype__proto.set = getSet; - momentPrototype__proto.startOf = startOf; - momentPrototype__proto.subtract = add_subtract__subtract; - momentPrototype__proto.toArray = toArray; - momentPrototype__proto.toObject = toObject; - momentPrototype__proto.toDate = toDate; - momentPrototype__proto.toISOString = moment_format__toISOString; - momentPrototype__proto.toJSON = moment_format__toISOString; - momentPrototype__proto.toString = toString; - momentPrototype__proto.unix = unix; - momentPrototype__proto.valueOf = to_type__valueOf; - - // Year - momentPrototype__proto.year = getSetYear; - momentPrototype__proto.isLeapYear = getIsLeapYear; - - // Week Year - momentPrototype__proto.weekYear = getSetWeekYear; - momentPrototype__proto.isoWeekYear = getSetISOWeekYear; - - // Quarter - momentPrototype__proto.quarter = momentPrototype__proto.quarters = getSetQuarter; - - // Month - momentPrototype__proto.month = getSetMonth; - momentPrototype__proto.daysInMonth = getDaysInMonth; - - // Week - momentPrototype__proto.week = momentPrototype__proto.weeks = getSetWeek; - momentPrototype__proto.isoWeek = momentPrototype__proto.isoWeeks = getSetISOWeek; - momentPrototype__proto.weeksInYear = getWeeksInYear; - momentPrototype__proto.isoWeeksInYear = getISOWeeksInYear; - - // Day - momentPrototype__proto.date = getSetDayOfMonth; - momentPrototype__proto.day = momentPrototype__proto.days = getSetDayOfWeek; - momentPrototype__proto.weekday = getSetLocaleDayOfWeek; - momentPrototype__proto.isoWeekday = getSetISODayOfWeek; - momentPrototype__proto.dayOfYear = getSetDayOfYear; - - // Hour - momentPrototype__proto.hour = momentPrototype__proto.hours = getSetHour; - - // Minute - momentPrototype__proto.minute = momentPrototype__proto.minutes = getSetMinute; - - // Second - momentPrototype__proto.second = momentPrototype__proto.seconds = getSetSecond; - - // Millisecond - momentPrototype__proto.millisecond = momentPrototype__proto.milliseconds = getSetMillisecond; - - // Offset - momentPrototype__proto.utcOffset = getSetOffset; - momentPrototype__proto.utc = setOffsetToUTC; - momentPrototype__proto.local = setOffsetToLocal; - momentPrototype__proto.parseZone = setOffsetToParsedOffset; - momentPrototype__proto.hasAlignedHourOffset = hasAlignedHourOffset; - momentPrototype__proto.isDST = isDaylightSavingTime; - momentPrototype__proto.isDSTShifted = isDaylightSavingTimeShifted; - momentPrototype__proto.isLocal = isLocal; - momentPrototype__proto.isUtcOffset = isUtcOffset; - momentPrototype__proto.isUtc = isUtc; - momentPrototype__proto.isUTC = isUtc; - - // Timezone - momentPrototype__proto.zoneAbbr = getZoneAbbr; - momentPrototype__proto.zoneName = getZoneName; - - // Deprecations - momentPrototype__proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth); - momentPrototype__proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth); - momentPrototype__proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear); - momentPrototype__proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779', getSetZone); - - var momentPrototype = momentPrototype__proto; - - function moment__createUnix (input) { - return local__createLocal(input * 1000); - } - - function moment__createInZone () { - return local__createLocal.apply(null, arguments).parseZone(); - } - - var defaultCalendar = { - sameDay : '[Today at] LT', - nextDay : '[Tomorrow at] LT', - nextWeek : 'dddd [at] LT', - lastDay : '[Yesterday at] LT', - lastWeek : '[Last] dddd [at] LT', - sameElse : 'L' - }; - - function locale_calendar__calendar (key, mom, now) { - var output = this._calendar[key]; - return typeof output === 'function' ? output.call(mom, now) : output; - } - - var defaultLongDateFormat = { - LTS : 'h:mm:ss A', - LT : 'h:mm A', - L : 'MM/DD/YYYY', - LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY h:mm A', - LLLL : 'dddd, MMMM D, YYYY h:mm A' - }; - - function longDateFormat (key) { - var format = this._longDateFormat[key], - formatUpper = this._longDateFormat[key.toUpperCase()]; - - if (format || !formatUpper) { - return format; - } - - this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); - - return this._longDateFormat[key]; - } - - var defaultInvalidDate = 'Invalid date'; - - function invalidDate () { - return this._invalidDate; - } - - var defaultOrdinal = '%d'; - var defaultOrdinalParse = /\d{1,2}/; - - function ordinal (number) { - return this._ordinal.replace('%d', number); - } - - function preParsePostFormat (string) { - return string; - } - - var defaultRelativeTime = { - future : 'in %s', - past : '%s ago', - s : 'a few seconds', - m : 'a minute', - mm : '%d minutes', - h : 'an hour', - hh : '%d hours', - d : 'a day', - dd : '%d days', - M : 'a month', - MM : '%d months', - y : 'a year', - yy : '%d years' - }; - - function relative__relativeTime (number, withoutSuffix, string, isFuture) { - var output = this._relativeTime[string]; - return (typeof output === 'function') ? - output(number, withoutSuffix, string, isFuture) : - output.replace(/%d/i, number); - } - - function pastFuture (diff, output) { - var format = this._relativeTime[diff > 0 ? 'future' : 'past']; - return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); - } - - function locale_set__set (config) { - var prop, i; - for (i in config) { - prop = config[i]; - if (typeof prop === 'function') { - this[i] = prop; - } else { - this['_' + i] = prop; - } - } - // Lenient ordinal parsing accepts just a number in addition to - // number + (possibly) stuff coming from _ordinalParseLenient. - this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source); - } - - var prototype__proto = Locale.prototype; - - prototype__proto._calendar = defaultCalendar; - prototype__proto.calendar = locale_calendar__calendar; - prototype__proto._longDateFormat = defaultLongDateFormat; - prototype__proto.longDateFormat = longDateFormat; - prototype__proto._invalidDate = defaultInvalidDate; - prototype__proto.invalidDate = invalidDate; - prototype__proto._ordinal = defaultOrdinal; - prototype__proto.ordinal = ordinal; - prototype__proto._ordinalParse = defaultOrdinalParse; - prototype__proto.preparse = preParsePostFormat; - prototype__proto.postformat = preParsePostFormat; - prototype__proto._relativeTime = defaultRelativeTime; - prototype__proto.relativeTime = relative__relativeTime; - prototype__proto.pastFuture = pastFuture; - prototype__proto.set = locale_set__set; - - // Month - prototype__proto.months = localeMonths; - prototype__proto._months = defaultLocaleMonths; - prototype__proto.monthsShort = localeMonthsShort; - prototype__proto._monthsShort = defaultLocaleMonthsShort; - prototype__proto.monthsParse = localeMonthsParse; - - // Week - prototype__proto.week = localeWeek; - prototype__proto._week = defaultLocaleWeek; - prototype__proto.firstDayOfYear = localeFirstDayOfYear; - prototype__proto.firstDayOfWeek = localeFirstDayOfWeek; - - // Day of Week - prototype__proto.weekdays = localeWeekdays; - prototype__proto._weekdays = defaultLocaleWeekdays; - prototype__proto.weekdaysMin = localeWeekdaysMin; - prototype__proto._weekdaysMin = defaultLocaleWeekdaysMin; - prototype__proto.weekdaysShort = localeWeekdaysShort; - prototype__proto._weekdaysShort = defaultLocaleWeekdaysShort; - prototype__proto.weekdaysParse = localeWeekdaysParse; - - // Hours - prototype__proto.isPM = localeIsPM; - prototype__proto._meridiemParse = defaultLocaleMeridiemParse; - prototype__proto.meridiem = localeMeridiem; - - function lists__get (format, index, field, setter) { - var locale = locale_locales__getLocale(); - var utc = create_utc__createUTC().set(setter, index); - return locale[field](utc, format); - } - - function list (format, index, field, count, setter) { - if (typeof format === 'number') { - index = format; - format = undefined; - } - - format = format || ''; - - if (index != null) { - return lists__get(format, index, field, setter); - } - - var i; - var out = []; - for (i = 0; i < count; i++) { - out[i] = lists__get(format, i, field, setter); - } - return out; - } - - function lists__listMonths (format, index) { - return list(format, index, 'months', 12, 'month'); - } - - function lists__listMonthsShort (format, index) { - return list(format, index, 'monthsShort', 12, 'month'); - } - - function lists__listWeekdays (format, index) { - return list(format, index, 'weekdays', 7, 'day'); - } - - function lists__listWeekdaysShort (format, index) { - return list(format, index, 'weekdaysShort', 7, 'day'); - } - - function lists__listWeekdaysMin (format, index) { - return list(format, index, 'weekdaysMin', 7, 'day'); - } - - locale_locales__getSetGlobalLocale('en', { - ordinalParse: /\d{1,2}(th|st|nd|rd)/, - ordinal : function (number) { - var b = number % 10, - output = (toInt(number % 100 / 10) === 1) ? 'th' : - (b === 1) ? 'st' : - (b === 2) ? 'nd' : - (b === 3) ? 'rd' : 'th'; - return number + output; - } - }); - - // Side effect imports - utils_hooks__hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', locale_locales__getSetGlobalLocale); - utils_hooks__hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', locale_locales__getLocale); - - var mathAbs = Math.abs; - - function duration_abs__abs () { - var data = this._data; - - this._milliseconds = mathAbs(this._milliseconds); - this._days = mathAbs(this._days); - this._months = mathAbs(this._months); - - data.milliseconds = mathAbs(data.milliseconds); - data.seconds = mathAbs(data.seconds); - data.minutes = mathAbs(data.minutes); - data.hours = mathAbs(data.hours); - data.months = mathAbs(data.months); - data.years = mathAbs(data.years); - - return this; - } - - function duration_add_subtract__addSubtract (duration, input, value, direction) { - var other = create__createDuration(input, value); - - duration._milliseconds += direction * other._milliseconds; - duration._days += direction * other._days; - duration._months += direction * other._months; - - return duration._bubble(); - } - - // supports only 2.0-style add(1, 's') or add(duration) - function duration_add_subtract__add (input, value) { - return duration_add_subtract__addSubtract(this, input, value, 1); - } - - // supports only 2.0-style subtract(1, 's') or subtract(duration) - function duration_add_subtract__subtract (input, value) { - return duration_add_subtract__addSubtract(this, input, value, -1); - } - - function absCeil (number) { - if (number < 0) { - return Math.floor(number); - } else { - return Math.ceil(number); - } - } - - function bubble () { - var milliseconds = this._milliseconds; - var days = this._days; - var months = this._months; - var data = this._data; - var seconds, minutes, hours, years, monthsFromDays; - - // if we have a mix of positive and negative values, bubble down first - // check: https://github.com/moment/moment/issues/2166 - if (!((milliseconds >= 0 && days >= 0 && months >= 0) || - (milliseconds <= 0 && days <= 0 && months <= 0))) { - milliseconds += absCeil(monthsToDays(months) + days) * 864e5; - days = 0; - months = 0; - } - - // The following code bubbles up values, see the tests for - // examples of what that means. - data.milliseconds = milliseconds % 1000; - - seconds = absFloor(milliseconds / 1000); - data.seconds = seconds % 60; - - minutes = absFloor(seconds / 60); - data.minutes = minutes % 60; - - hours = absFloor(minutes / 60); - data.hours = hours % 24; - - days += absFloor(hours / 24); - - // convert days to months - monthsFromDays = absFloor(daysToMonths(days)); - months += monthsFromDays; - days -= absCeil(monthsToDays(monthsFromDays)); - - // 12 months -> 1 year - years = absFloor(months / 12); - months %= 12; - - data.days = days; - data.months = months; - data.years = years; - - return this; - } - - function daysToMonths (days) { - // 400 years have 146097 days (taking into account leap year rules) - // 400 years have 12 months === 4800 - return days * 4800 / 146097; - } - - function monthsToDays (months) { - // the reverse of daysToMonths - return months * 146097 / 4800; - } - - function as (units) { - var days; - var months; - var milliseconds = this._milliseconds; - - units = normalizeUnits(units); - - if (units === 'month' || units === 'year') { - days = this._days + milliseconds / 864e5; - months = this._months + daysToMonths(days); - return units === 'month' ? months : months / 12; - } else { - // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(monthsToDays(this._months)); - switch (units) { - case 'week' : return days / 7 + milliseconds / 6048e5; - case 'day' : return days + milliseconds / 864e5; - case 'hour' : return days * 24 + milliseconds / 36e5; - case 'minute' : return days * 1440 + milliseconds / 6e4; - case 'second' : return days * 86400 + milliseconds / 1000; - // Math.floor prevents floating point math errors here - case 'millisecond': return Math.floor(days * 864e5) + milliseconds; - default: throw new Error('Unknown unit ' + units); - } - } - } - - // TODO: Use this.as('ms')? - function duration_as__valueOf () { - return ( - this._milliseconds + - this._days * 864e5 + - (this._months % 12) * 2592e6 + - toInt(this._months / 12) * 31536e6 - ); - } - - function makeAs (alias) { - return function () { - return this.as(alias); - }; - } - - var asMilliseconds = makeAs('ms'); - var asSeconds = makeAs('s'); - var asMinutes = makeAs('m'); - var asHours = makeAs('h'); - var asDays = makeAs('d'); - var asWeeks = makeAs('w'); - var asMonths = makeAs('M'); - var asYears = makeAs('y'); - - function duration_get__get (units) { - units = normalizeUnits(units); - return this[units + 's'](); - } - - function makeGetter(name) { - return function () { - return this._data[name]; - }; - } - - var milliseconds = makeGetter('milliseconds'); - var seconds = makeGetter('seconds'); - var minutes = makeGetter('minutes'); - var hours = makeGetter('hours'); - var days = makeGetter('days'); - var months = makeGetter('months'); - var years = makeGetter('years'); - - function weeks () { - return absFloor(this.days() / 7); - } - - var round = Math.round; - var thresholds = { - s: 45, // seconds to minute - m: 45, // minutes to hour - h: 22, // hours to day - d: 26, // days to month - M: 11 // months to year - }; - - // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize - function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) { - return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture); - } - - function duration_humanize__relativeTime (posNegDuration, withoutSuffix, locale) { - var duration = create__createDuration(posNegDuration).abs(); - var seconds = round(duration.as('s')); - var minutes = round(duration.as('m')); - var hours = round(duration.as('h')); - var days = round(duration.as('d')); - var months = round(duration.as('M')); - var years = round(duration.as('y')); - - var a = seconds < thresholds.s && ['s', seconds] || - minutes === 1 && ['m'] || - minutes < thresholds.m && ['mm', minutes] || - hours === 1 && ['h'] || - hours < thresholds.h && ['hh', hours] || - days === 1 && ['d'] || - days < thresholds.d && ['dd', days] || - months === 1 && ['M'] || - months < thresholds.M && ['MM', months] || - years === 1 && ['y'] || ['yy', years]; - - a[2] = withoutSuffix; - a[3] = +posNegDuration > 0; - a[4] = locale; - return substituteTimeAgo.apply(null, a); - } - - // This function allows you to set a threshold for relative time strings - function duration_humanize__getSetRelativeTimeThreshold (threshold, limit) { - if (thresholds[threshold] === undefined) { - return false; - } - if (limit === undefined) { - return thresholds[threshold]; - } - thresholds[threshold] = limit; - return true; - } - - function humanize (withSuffix) { - var locale = this.localeData(); - var output = duration_humanize__relativeTime(this, !withSuffix, locale); - - if (withSuffix) { - output = locale.pastFuture(+this, output); - } - - return locale.postformat(output); - } - - var iso_string__abs = Math.abs; - - function iso_string__toISOString() { - // for ISO strings we do not use the normal bubbling rules: - // * milliseconds bubble up until they become hours - // * days do not bubble at all - // * months bubble up until they become years - // This is because there is no context-free conversion between hours and days - // (think of clock changes) - // and also not between days and months (28-31 days per month) - var seconds = iso_string__abs(this._milliseconds) / 1000; - var days = iso_string__abs(this._days); - var months = iso_string__abs(this._months); - var minutes, hours, years; - - // 3600 seconds -> 60 minutes -> 1 hour - minutes = absFloor(seconds / 60); - hours = absFloor(minutes / 60); - seconds %= 60; - minutes %= 60; - - // 12 months -> 1 year - years = absFloor(months / 12); - months %= 12; - - - // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var Y = years; - var M = months; - var D = days; - var h = hours; - var m = minutes; - var s = seconds; - var total = this.asSeconds(); - - if (!total) { - // this is the same as C#'s (Noda) and python (isodate)... - // but not other JS (goog.date) - return 'P0D'; - } - - return (total < 0 ? '-' : '') + - 'P' + - (Y ? Y + 'Y' : '') + - (M ? M + 'M' : '') + - (D ? D + 'D' : '') + - ((h || m || s) ? 'T' : '') + - (h ? h + 'H' : '') + - (m ? m + 'M' : '') + - (s ? s + 'S' : ''); - } - - var duration_prototype__proto = Duration.prototype; - - duration_prototype__proto.abs = duration_abs__abs; - duration_prototype__proto.add = duration_add_subtract__add; - duration_prototype__proto.subtract = duration_add_subtract__subtract; - duration_prototype__proto.as = as; - duration_prototype__proto.asMilliseconds = asMilliseconds; - duration_prototype__proto.asSeconds = asSeconds; - duration_prototype__proto.asMinutes = asMinutes; - duration_prototype__proto.asHours = asHours; - duration_prototype__proto.asDays = asDays; - duration_prototype__proto.asWeeks = asWeeks; - duration_prototype__proto.asMonths = asMonths; - duration_prototype__proto.asYears = asYears; - duration_prototype__proto.valueOf = duration_as__valueOf; - duration_prototype__proto._bubble = bubble; - duration_prototype__proto.get = duration_get__get; - duration_prototype__proto.milliseconds = milliseconds; - duration_prototype__proto.seconds = seconds; - duration_prototype__proto.minutes = minutes; - duration_prototype__proto.hours = hours; - duration_prototype__proto.days = days; - duration_prototype__proto.weeks = weeks; - duration_prototype__proto.months = months; - duration_prototype__proto.years = years; - duration_prototype__proto.humanize = humanize; - duration_prototype__proto.toISOString = iso_string__toISOString; - duration_prototype__proto.toString = iso_string__toISOString; - duration_prototype__proto.toJSON = iso_string__toISOString; - duration_prototype__proto.locale = locale; - duration_prototype__proto.localeData = localeData; - - // Deprecations - duration_prototype__proto.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', iso_string__toISOString); - duration_prototype__proto.lang = lang; - - // Side effect imports - - addFormatToken('X', 0, 0, 'unix'); - addFormatToken('x', 0, 0, 'valueOf'); - - // PARSING - - addRegexToken('x', matchSigned); - addRegexToken('X', matchTimestamp); - addParseToken('X', function (input, array, config) { - config._d = new Date(parseFloat(input, 10) * 1000); - }); - addParseToken('x', function (input, array, config) { - config._d = new Date(toInt(input)); - }); - - // Side effect imports - - - utils_hooks__hooks.version = '2.10.6'; - - setHookCallback(local__createLocal); - - utils_hooks__hooks.fn = momentPrototype; - utils_hooks__hooks.min = min; - utils_hooks__hooks.max = max; - utils_hooks__hooks.utc = create_utc__createUTC; - utils_hooks__hooks.unix = moment__createUnix; - utils_hooks__hooks.months = lists__listMonths; - utils_hooks__hooks.isDate = isDate; - utils_hooks__hooks.locale = locale_locales__getSetGlobalLocale; - utils_hooks__hooks.invalid = valid__createInvalid; - utils_hooks__hooks.duration = create__createDuration; - utils_hooks__hooks.isMoment = isMoment; - utils_hooks__hooks.weekdays = lists__listWeekdays; - utils_hooks__hooks.parseZone = moment__createInZone; - utils_hooks__hooks.localeData = locale_locales__getLocale; - utils_hooks__hooks.isDuration = isDuration; - utils_hooks__hooks.monthsShort = lists__listMonthsShort; - utils_hooks__hooks.weekdaysMin = lists__listWeekdaysMin; - utils_hooks__hooks.defineLocale = defineLocale; - utils_hooks__hooks.weekdaysShort = lists__listWeekdaysShort; - utils_hooks__hooks.normalizeUnits = normalizeUnits; - utils_hooks__hooks.relativeTimeThreshold = duration_humanize__getSetRelativeTimeThreshold; - - var _moment = utils_hooks__hooks; - - return _moment; - -})); \ No newline at end of file diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss deleted file mode 100644 index c46caa85c..000000000 --- a/app/assets/stylesheets/application.css.scss +++ /dev/null @@ -1,67 +0,0 @@ -// From GDS's alphagov/govuk_frontend_toolkit -@import 'colours'; -@import 'font_stack'; -@import 'measurements'; -@import 'conditionals'; -@import 'device-pixels'; -@import 'typography'; -@import 'shims'; - -@import 'design-patterns/alpha-beta'; -@import 'design-patterns/buttons'; - -// From GDS's alphagov/govuk_elements - -// Helper functions and classes -@import 'elements/helpers'; - -// Generic (normalize/reset.css) -@import 'elements/reset'; - -// Base (unclassed HTML elements) -// These are predefined by govuk_template -// If you're not using govuk_template, uncomment the line below. -// HTML elements, set by the GOV.UK template -@import 'elements/layout'; - -// Components (chunks of UI) -@import 'elements/buttons'; -@import 'elements/components'; -@import 'elements/details'; -@import 'elements/elements-typography'; -@import 'elements/forms'; -@import 'elements/forms/form-date'; -@import 'elements/forms/form-multiple-choice'; -@import 'elements/forms/form-validation'; -@import 'elements/lists'; -@import 'elements/panels'; -@import 'elements/phase-banner'; -@import 'elements/tables'; - -//project specific -@import "moj/forms"; -@import "moj/navigation"; -@import "vendor/bootstrap"; -@import "moj/alert-banner"; -@import "pq"; - -.govuk-header__service-name{ - font-size: 24px; -} - -.govuk-header{ - border-bottom: none; -} - -.govuk-header__container{ - border-bottom: none; -} - -#global-header .header-wrapper { - padding-top: 0; - padding-bottom: 0; -} - -.govuk-footer { - font-size: 90%; -} diff --git a/app/assets/stylesheets/application.sass.scss b/app/assets/stylesheets/application.sass.scss new file mode 100644 index 000000000..cbdd11821 --- /dev/null +++ b/app/assets/stylesheets/application.sass.scss @@ -0,0 +1,30 @@ +$govuk-global-styles: true; +$govuk-new-link-styles: true; + +@import "govuk-frontend/dist/govuk/all"; + +$panel-colour: #DEE0E2; +$border-colour: #bfc1c3; +$text-colour: #0b0c0c; +$yellow: govuk-colour("yellow"); +$govuk-blue: govuk-colour("blue"); +$button-colour: #00703c; +$error-colour: govuk-colour("red"); +$page-colour: white; +$red: govuk-colour("red"); +$link-colour: #005ea5; +$grey-2: #bfc1c3; +$highlight-colour: #f8f8f8; +$light-blue: govuk-colour("light-blue"); +$white: govuk-colour("white"); +$grey-4: govuk-colour("mid-grey"); +$gutter-half: 15px; + +// //project specific +@import "moj/_pq"; +@import "moj/forms"; +@import "moj/navigation"; +@import "vendor/bootstrap"; +@import "vendor/jquery.datetimepicker"; +@import "vendor/pq-select2"; +@import "select2/dist/css/select2"; diff --git a/app/assets/stylesheets/moj/_alert-banner b/app/assets/stylesheets/moj/_alert-banner deleted file mode 100644 index 1bd449191..000000000 --- a/app/assets/stylesheets/moj/_alert-banner +++ /dev/null @@ -1,24 +0,0 @@ -.alert-banner { - @extend %site-width-container; - border: 4px solid $govuk-blue; - padding: 2rem 2rem 1rem; - margin-top: 2em; - margin-bottom: 2em; - box-sizing: border-box; - position: relative; - - &__icon { - color: $govuk-blue; - position: absolute; - left: 2rem; - top: 2rem; - } - - &__info { - padding: 5px 0 0 55px; - - @include media(tablet) { - font-size: 19px; - } - } -} diff --git a/app/assets/stylesheets/moj/_forms.scss b/app/assets/stylesheets/moj/_forms.scss index ad04772da..b3a97fcb1 100644 --- a/app/assets/stylesheets/moj/_forms.scss +++ b/app/assets/stylesheets/moj/_forms.scss @@ -1,18 +1,16 @@ .form-group { - .search { - label { display: inline-block; padding-right: .5em; font-size: 90%; } - + .search-box { border: 2px solid #bfc1c3; display: inline-block; } - + input, #search_button { border: none; display: inline-block; @@ -26,14 +24,116 @@ } } -.content-inner { +.content-inner { legend { border: none; display: block; + width: 100%; margin: 0; padding: 15px 0; font-weight: bold; } +} + +.button { + background-color: #00823b; + position: relative; + display: -moz-inline-stack; + display: inline-block; + padding: .526315em .789473em .263157em; + border: none; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + outline: 1px solid transparent; + outline-offset: -1px; + -webkit-appearance: none; + -webkit-box-shadow: 0 2px 0 #003618; + -moz-box-shadow: 0 2px 0 #003618; + box-shadow: 0 2px 0 #003618; + line-height: 1.25; + text-decoration: none; + -webkit-font-smoothing: antialiased; + cursor: pointer; + color: #fff; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + vertical-align: top +} + +.button:visited { + background-color: #00823b +} + +.button:hover,.button:focus { + background-color: #00692f +} + +.button:active { + top: 2px; + -webkit-box-shadow: 0 0 0 #00823b; + -moz-box-shadow: 0 0 0 #00823b; + box-shadow: 0 0 0 #00823b +} + +.button.disabled,.button[disabled="disabled"],.button[disabled] { + zoom:1;filter: alpha(opacity=50); + opacity: 0.5 +} + +.button.disabled:hover,.button[disabled="disabled"]:hover,.button[disabled]:hover { + cursor: default; + background-color: #00823b +} + +.button.disabled:active,.button[disabled="disabled"]:active,.button[disabled]:active { + top: 0; + -webkit-box-shadow: 0 2px 0 #003618; + -moz-box-shadow: 0 2px 0 #003618; + box-shadow: 0 2px 0 #003618 +} + +.button:link,.button:link:focus,.button:hover,.button:focus,.button:visited { + color: #fff +} + +.button:before { + content: ""; + height: 110%; + width: 100%; + display: block; + background: transparent; + position: absolute; + top: 0; + left: 0 +} + +.button:active:before { + top: -10%; + height: 120% +} + +@media (max-width: 640px) { + .button { + width:100%; + text-align: center + } +} + +.button::-moz-focus-inner { + border: 0; + padding: 0 +} + +.button:focus { + outline: 3px solid #ffbf47 +} + +.button[disabled="disabled"] { + background: #00823b +} - .form-label, .form-control { font-size: 1.9rem; } +.button[disabled="disabled"]:focus { + outline: none } diff --git a/app/assets/stylesheets/moj/_navigation.scss b/app/assets/stylesheets/moj/_navigation.scss index 432a88a7f..b9c694de0 100644 --- a/app/assets/stylesheets/moj/_navigation.scss +++ b/app/assets/stylesheets/moj/_navigation.scss @@ -1,3 +1,17 @@ +#nav { + margin: 0 25px; +} + +.navbar-form.navbar-right { + position: absolute; + top: 8px; + right: 0px; + + @media screen and (max-width: 1000px) { + position: inherit; + } +} + .navbar.navbar-default { background-color: $panel-colour; border-right: 1px solid $border-colour; @@ -38,29 +52,15 @@ height: 34px; } #search_button { - border-radius: 0; + position: relative; + top: -1px; width: 34px; - height: 3.4rem; + height: 2.14rem; + border-radius: 0; padding: 4px; } } -// override bootstrap -@media (max-width: 767px) { - nav.navbar-default { - .navbar-nav .open .dropdown-menu > li > a { - color: #444; - } - - #search_field.form-control { - display: inline-block; - } - .navbar-toggle .icon-bar { - background-color: #444; - } - } -} - nav.navbar-default .navbar-link { color: #444; } diff --git a/app/assets/stylesheets/pq.scss b/app/assets/stylesheets/moj/_pq.scss similarity index 90% rename from app/assets/stylesheets/pq.scss rename to app/assets/stylesheets/moj/_pq.scss index 976035f6b..4c8c7aeee 100644 --- a/app/assets/stylesheets/pq.scss +++ b/app/assets/stylesheets/moj/_pq.scss @@ -1,7 +1,3 @@ -@import "colours"; -@import "design-patterns/alpha-beta"; - -$phase:live; $success-colour: $button-colour; $notice-colour: $govuk-blue; $danger-colour: $error-colour; @@ -12,43 +8,11 @@ $danger-colour: $error-colour; body, main { - font-size: 1.9rem; - line-height: 1.3; - color: $text-colour; -} - -h1 { font-family: "nta",Arial,sans-serif; - font-size: 28px; - font-weight: bold; - margin: 0; - padding: 20px 0 10px; -} - -h2 { - font-size: 24px; - font-family: "nta",Arial,sans-serif; - font-weight: normal; - margin: 0; - padding: 15px 0; -} - -h3 { - font-size: 1.9rem; - font-weight: normal; - font-family: "nta",Arial,sans-serif; - color: #555; - margin: 10px 0 0 0; -} - -h4 { - font-size: 1.9rem; - font-weight: normal; + line-height: 1.3; color: $text-colour; } -p { margin: 0 0 6px 0; } - textarea { resize: none; } hr { @@ -73,14 +37,24 @@ label { //clear: both; } +//============================================================================== +// = Govuk Overrides =========================================================== +//============================================================================== + +.govuk-main-wrapper { + padding-top: 0; +} + +.govuk-width-container { + position: relative; +} + //============================================================================== // = GLOBAL CLASSES ============================================================ //============================================================================== #contentOuter { margin: 0 auto; max-width: 960px; } -.row { margin: 0; } - .open { color: $page-colour; } // == symbols ===== @@ -120,10 +94,6 @@ label { (https://github.com/ministryofjustice/moj_template/pull/55) we need to cancel it. */ -#global-header.with-proposition .header-wrapper .header-proposition #proposition-name:hover { - border-bottom: none; -} - // only visible on the staging server to warn that it is not the live system .staging_banner { background-color: $red; @@ -134,6 +104,11 @@ label { font-weight:bold; } +.phase-banner { + padding: 10px 0 8px; + border-bottom: 1px solid #bfc1c3; +} + // override gov.uk-template visited links colours, // as this is an application, rather than a simple web page @media screen { @@ -150,7 +125,6 @@ button.close { .flag { border: 1.5px solid; font-weight: bold; - font-size: 1.5rem; padding: 3px 5px; text-align: center; white-space: nowrap; @@ -218,6 +192,59 @@ li.select2-search-field {border-bottom: none;} } } +//= Cookies ===== + +.js-enabled #global-cookie-message { + display: none +} + +#global-cookie-message { + width: 100%; + background-color: #d5e8f3; + padding-top: 10px; + padding-bottom: 10px +} + +#global-cookie-message p { + font-family: "nta", Arial, sans-serif; + font-weight: 400; + text-transform: none; + font-size: 14px; + line-height: 1.14286; + margin-top: 0; + margin-bottom: 0 +} + +#global-cookie-message p { + max-width: 960px; + margin: 0 15px +} + +@media (min-width: 641px) { + #global-cookie-message p { + margin:0 30px + } +} + +@media (min-width: 1020px) { + #global-cookie-message p { + margin:0 auto + } +} + +@media (min-width: 641px) { + #global-cookie-message p { + font-size:16px; + line-height: 1.25 + } +} + +@media (min-width: 1020px) { + #global-cookie-message p { + margin:0 auto; + } +} + //= Forms ==== .form-group.inline { @@ -245,7 +272,7 @@ textarea.form-control { border-style: solid; border-radius: 0; width: auto; - height: auto; // override bootstrap + height: 30px; // override bootstrap } .form-control:focus, @@ -263,7 +290,7 @@ textarea.form-control { clear: left; position: relative; border: 1px solid; - padding: 18px 30px 15px 45px; + padding: 10px 30px 10px 45px; margin-top: 10px; margin-bottom: 10px; cursor: pointer; @@ -271,7 +298,7 @@ textarea.form-control { input { position: absolute; - top: 18px; + top: 10px; left: 15px; } @@ -320,7 +347,7 @@ textarea.form-control { #select-all-questions { padding: 0; label { - max-width: 655px; + width: 100%; padding: .5em 0; border-bottom: 2px solid $grey-2; input[type="checkbox"] { @@ -332,7 +359,6 @@ textarea.form-control { } #count { - max-width: 655px; background-color: $highlight-colour; padding: .7em 0 .7em .7em; border-left: 5px solid $border-colour; @@ -348,7 +374,7 @@ textarea.form-control { } .pq-question { margin: 0; - padding: 20px 0 0 0; + padding: 20px 0 10px 0; } .status-bar { line-height: 32px; @@ -387,7 +413,6 @@ textarea.form-control { } .question-proposed { color: #555; - font-size: 1.6rem; text-align: right; } .question-type { @@ -396,6 +421,16 @@ textarea.form-control { margin-left: 7px; } +.question { + .govuk-heading-s { + margin-bottom: 5px; + } + + p.deadline-date, p.replying-minister, p.policy-minister { + margin-bottom: 5px; + } +} + // = Alert messages ============================================================ @mixin alert($colour) @@ -420,7 +455,7 @@ textarea.form-control { border-radius: 0; border: 1px solid $border-colour; display: inline-block; - width: 20rem; + width: 16rem; input { display: inline; width: 86%; @@ -445,7 +480,6 @@ textarea.form-control { clear: both; thead tr th, tbody tr td { padding-left: 0; // bootstrap override - font-size: 1.6rem; } } @@ -519,6 +553,10 @@ textarea.form-control { display: block; } +.tab-content .govuk-heading-s { + margin-bottom: 5px; +} + /* = Pagination ================================================================= */ #pages { @@ -648,24 +686,6 @@ textarea.form-control { } -/* = Report pages (/reports/...) ================================================ */ - -#filter-report { - - .col-md-9 { padding: 0; } - .col-md-3 select{ width: 100%; } - .button-secondary { float: right; margin-top: 10px; } -} - -#minister-report, -#press-desk-report { - table td { - padding-top: 10px; - padding-bottom: 13px; - } - .table-cell-centered { text-align: center; } -} - /* = Settings (/admin) ====================================================== */ #settings-home { @@ -703,13 +723,7 @@ textarea.form-control { /* = Filter sidebar ========================================================== */ #dashboard, #preview { - #sidebar { - padding: 0; - h2 { - font-size: 1.9rem; - padding: 0; - } input[type="button"]{ padding-left: 0; border: none; @@ -753,7 +767,6 @@ textarea.form-control { border-bottom: 2px solid $grey-2; input[type="submit"] { margin: 0 10px 5px 0; } form { margin: 8px 0; } - .form-label { font-size: 1.9rem;} #editDates .content{ padding-bottom: 10px; @@ -786,10 +799,6 @@ textarea.form-control { legend { padding: 0; font-weight: normal; - font-size: 1.9rem; - } - input { - font-size: 1.9rem; } .notice { display: none; @@ -808,7 +817,7 @@ textarea.form-control { background-clip: border-box; background-origin: padding-box; background-size: auto auto; - background: $panel-colour image-url('option-select-toggle-sprite.png') no-repeat scroll; + background: $panel-colour url('option-select-toggle-sprite.png') no-repeat scroll; } .open { background-position: right -82px; } .closed { background-position: right 0; } @@ -847,14 +856,3 @@ textarea.form-control { } } } - -#footer .footer-meta { - .copyright { - font-size: 1.6rem; - } - .footer-meta-inner { - ul, .open-government-licence p { - font-size: 1.6rem; - } - } -} diff --git a/app/assets/stylesheets/vendor/bootstrap.scss b/app/assets/stylesheets/vendor/bootstrap.scss index 54d370939..13a033e81 100644 --- a/app/assets/stylesheets/vendor/bootstrap.scss +++ b/app/assets/stylesheets/vendor/bootstrap.scss @@ -59,15 +59,6 @@ a:hover { outline: 0; } -h1 { - margin: 0.67em 0; - font-size: 2em; -} - -abbr[title] { - border-bottom: 1px dotted; -} - b, strong { font-weight: bold; @@ -303,7 +294,7 @@ table { } html { - font-size: 62.5%; + // font-size: 62.5%; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } @@ -636,12 +627,6 @@ dd { } } -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #999999; -} - abbr.initialism { font-size: 90%; text-transform: uppercase; @@ -756,6 +741,7 @@ pre code { .container { padding-right: 15px; padding-left: 15px; + padding-top: 15px; margin-right: auto; margin-left: auto; } @@ -1636,22 +1622,8 @@ fieldset { border: 0; } -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} - label { - display: inline-block; margin-bottom: 5px; - font-weight: bold; } input[type="search"] { @@ -1715,13 +1687,10 @@ input[type="number"]::-webkit-inner-spin-button { .form-control { display: block; - width: 100%; height: 34px; padding: 6px 12px; - font-size: 14px; line-height: 1.428571429; color: #555555; - vertical-align: middle; background-color: #ffffff; border: 1px solid #cccccc; border-radius: 4px; @@ -4244,28 +4213,28 @@ textarea.input-group-sm > .input-group-btn > .btn { line-height: 20px; } -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} +// @media (max-width: 767px) { +// .navbar-nav .open .dropdown-menu { +// position: static; +// float: none; +// width: auto; +// margin-top: 0; +// background-color: transparent; +// border: 0; +// box-shadow: none; +// } +// .navbar-nav .open .dropdown-menu > li > a, +// .navbar-nav .open .dropdown-menu .dropdown-header { +// padding: 5px 15px 5px 25px; +// } +// .navbar-nav .open .dropdown-menu > li > a { +// line-height: 20px; +// } +// .navbar-nav .open .dropdown-menu > li > a:hover, +// .navbar-nav .open .dropdown-menu > li > a:focus { +// background-image: none; +// } +// } @media (min-width: 768px) { .navbar-nav { @@ -4302,33 +4271,25 @@ textarea.input-group-sm > .input-group-btn > .btn { box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); } -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - padding-left: 0; - margin-top: 0; - margin-bottom: 0; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - float: none; - margin-left: 0; - } +.navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; } - -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } +.navbar-form .form-control { + display: inline-block; +} +.navbar-form .radio, +.navbar-form .checkbox { + display: inline-block; + padding-left: 0; + margin-top: 0; + margin-bottom: 0; +} +.navbar-form .radio input[type="radio"], +.navbar-form .checkbox input[type="checkbox"] { + float: none; + margin-left: 0; } @media (min-width: 768px) { @@ -6798,4 +6759,4 @@ td.visible-print { td.hidden-print { display: none !important; } -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/vendor/pq-select2.scss b/app/assets/stylesheets/vendor/pq-select2.scss index 86a24b4db..a7ea18b11 100644 --- a/app/assets/stylesheets/vendor/pq-select2.scss +++ b/app/assets/stylesheets/vendor/pq-select2.scss @@ -1,6 +1,8 @@ /* Rules to make select2 components more like govuk elements */ -@import 'colours'; +$border-colour: grey; +$yellow: govuk-colour("yellow"); +$black: govuk-colour("black"); @mixin reset-border-radius { border-radius: 0; diff --git a/app/controllers/action_officer_reminder_controller.rb b/app/controllers/action_officer_reminder_controller.rb index 2d6d13ccc..cfdc56b1b 100644 --- a/app/controllers/action_officer_reminder_controller.rb +++ b/app/controllers/action_officer_reminder_controller.rb @@ -1,5 +1,5 @@ class ActionOfficerReminderController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def accept_reject loading_records_and_rendering do |pq, ao, ao_pq| diff --git a/app/controllers/action_officers_controller.rb b/app/controllers/action_officers_controller.rb index 61cfc46e7..530222fd1 100644 --- a/app/controllers/action_officers_controller.rb +++ b/app/controllers/action_officers_controller.rb @@ -1,5 +1,5 @@ class ActionOfficersController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def index @show_inactive = (params[:show_inactive] == "true") diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index ee09a5a22..5890a8bc6 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -1,5 +1,5 @@ class AdminController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def index update_page_title "Settings" diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d89e5c4dd..127dcd823 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,16 +6,6 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception before_action :configure_permitted_parameters, if: :devise_controller? - if Rails.env.production? || ENV["TRAP_ERRORS_IN_TEST"] == "1" - rescue_from StandardError do |exception| - if exception.is_a?(ActiveRecord::RecordNotFound) - page_not_found(exception) - else - server_error(exception) - end - end - end - def set_am_host request = self.request opts = ::ActionMailer::Base.default_url_options @@ -29,21 +19,6 @@ def after_invite_path_for(_resource) users_path end - def page_not_found(exception = nil) - update_page_title "Not found (404)" - show_error_page_and_increment_statsd(404, exception) - end - - def unauthorized - update_page_title "Unauthorized (401)" - show_error_page_and_increment_statsd(401) - end - - def server_error(exception) - update_page_title "Server Error (500)" - show_error_page_and_increment_statsd(500, exception) - end - def update_page_title(prefix, suffix = "Parliamentary Questions - Ministry of Justice") @page_title = "#{prefix} - #{suffix}" end @@ -68,15 +43,4 @@ def configure_permitted_parameters devise_parameter_sanitizer.permit(:invite, keys: %i[name roles]) devise_parameter_sanitizer.permit(:accept_invitation, keys: %i[name roles invitation_token password password_confirmation]) end - - def show_error_page_and_increment_statsd(err_number, exception = nil) - $statsd.increment("#{StatsHelper::PAGES_ERRORS}.#{err_number}") # rubocop:disable Style/GlobalVars - respond_to do |format| - format.html { render file: "public/#{err_number}.html", status: err_number, layout: nil } - format.all { head :no_content, status: err_number } - end - backtrace = exception.nil? ? nil : exception.backtrace - message = exception.nil? ? nil : exception.message - LogStuff.error(:error_page) { "status: #{err_number}, referrer:#{request.referer}, url:#{request_url}, message:#{message}, backtrace:#{backtrace}" } - end end diff --git a/app/controllers/commission_controller.rb b/app/controllers/commission_controller.rb index 4d41ded83..49693e7db 100644 --- a/app/controllers/commission_controller.rb +++ b/app/controllers/commission_controller.rb @@ -1,7 +1,7 @@ class CommissionController < ApplicationController include Validators::DateInput - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def commission pq = Pq.find(params[:commission_form][:pq_id]) diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index f3c2c27ba..82e43362e 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,10 +1,9 @@ class DashboardController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! BACKLOG = "Backlog".freeze IN_PROGRESS = "In progress".freeze NEW = "New".freeze - PER_PAGE = 15 UNASSIGNED = "unassigned".freeze def backlog @@ -69,9 +68,4 @@ def load_pq_with_counts(dashboard_state) Presenters::DashboardFilters.build(pq_counts, params) end end - - def paginate_collection(pqs) - page = params.fetch(:page, 1) - pqs.paginate(page:, per_page: PER_PAGE) - end end diff --git a/app/controllers/deputy_directors_controller.rb b/app/controllers/deputy_directors_controller.rb index c39944368..6a9d4fe06 100644 --- a/app/controllers/deputy_directors_controller.rb +++ b/app/controllers/deputy_directors_controller.rb @@ -1,5 +1,5 @@ class DeputyDirectorsController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! before_action :set_deputy_director, only: %i[show edit update destroy] before_action :prepare_divisions diff --git a/app/controllers/directorates_controller.rb b/app/controllers/directorates_controller.rb index ddf8f636f..e44af7194 100644 --- a/app/controllers/directorates_controller.rb +++ b/app/controllers/directorates_controller.rb @@ -1,5 +1,5 @@ class DirectoratesController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! before_action :set_directorate, only: %i[show edit update destroy] def index diff --git a/app/controllers/divisions_controller.rb b/app/controllers/divisions_controller.rb index 78a64c30e..56b25923a 100644 --- a/app/controllers/divisions_controller.rb +++ b/app/controllers/divisions_controller.rb @@ -1,5 +1,5 @@ class DivisionsController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! before_action :set_division, only: %i[show edit update destroy] before_action :prepare_directorates diff --git a/app/controllers/early_bird_dashboard_controller.rb b/app/controllers/early_bird_dashboard_controller.rb index 9ff74c61b..86fc85e4e 100644 --- a/app/controllers/early_bird_dashboard_controller.rb +++ b/app/controllers/early_bird_dashboard_controller.rb @@ -1,7 +1,7 @@ class EarlyBirdDashboardController < ApplicationController before_action AoTokenFilter, only: [:index] before_action :save_early_bird_credentials, only: [:index] - before_action :authenticate_user!, PqUserFilter, only: [:preview] + before_action :authenticate_user!, only: [:preview] PER_PAGE = 200 QUESTIONS_URL = "https://questions-statements.parliament.uk".freeze @@ -24,13 +24,4 @@ def save_early_bird_credentials session[:early_bird_token] = params[:token] session[:early_bird_entity] = params[:entity] end - - def load_pq_with_counts - @questions = paginate_collection(yield) if block_given? - end - - def paginate_collection(pqs) - page = params.fetch(:page, 1) - pqs.paginate(page:, per_page: PER_PAGE) - end end diff --git a/app/controllers/early_bird_members_controller.rb b/app/controllers/early_bird_members_controller.rb index 0b83ea207..0e831bef5 100644 --- a/app/controllers/early_bird_members_controller.rb +++ b/app/controllers/early_bird_members_controller.rb @@ -1,5 +1,5 @@ class EarlyBirdMembersController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def index @early_bird_members = EarlyBirdMember.active_list diff --git a/app/controllers/early_bird_send_emails_controller.rb b/app/controllers/early_bird_send_emails_controller.rb index 655b081bf..5600237ab 100644 --- a/app/controllers/early_bird_send_emails_controller.rb +++ b/app/controllers/early_bird_send_emails_controller.rb @@ -1,5 +1,5 @@ class EarlyBirdSendEmailsController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def send_emails service = EarlyBirdReportService.new diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb new file mode 100644 index 000000000..87dfd41f9 --- /dev/null +++ b/app/controllers/errors_controller.rb @@ -0,0 +1,11 @@ +class ErrorsController < ApplicationController + def not_found + update_page_title "Error - 404 - Page cannot be found" + render status: :not_found + end + + def internal_error + update_page_title "Error - 500 - Internal server error" + render status: :internal_server_error + end +end diff --git a/app/controllers/export_controller.rb b/app/controllers/export_controller.rb index 6dab900ab..74ce7060e 100644 --- a/app/controllers/export_controller.rb +++ b/app/controllers/export_controller.rb @@ -1,7 +1,7 @@ class ExportController < ApplicationController include Validators::DateInput - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def index update_page_title("Export PQs") diff --git a/app/controllers/manual_reject_commission_controller.rb b/app/controllers/manual_reject_commission_controller.rb index 7ae951d51..10a85dc3e 100644 --- a/app/controllers/manual_reject_commission_controller.rb +++ b/app/controllers/manual_reject_commission_controller.rb @@ -1,5 +1,5 @@ class ManualRejectCommissionController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def reject_manual ao_pq = ActionOfficersPq.where(id: params[:id]).first diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb deleted file mode 100644 index 4f81c5f83..000000000 --- a/app/controllers/members_controller.rb +++ /dev/null @@ -1,8 +0,0 @@ -class MembersController < ApplicationController - before_action :authenticate_user!, PqUserFilter - - def by_name - @members = ParliService.new.members_by_name(params[:name]) - render partial: "by_name" - end -end diff --git a/app/controllers/minister_contacts_controller.rb b/app/controllers/minister_contacts_controller.rb index 3135242e9..5e2bca74e 100644 --- a/app/controllers/minister_contacts_controller.rb +++ b/app/controllers/minister_contacts_controller.rb @@ -1,5 +1,5 @@ class MinisterContactsController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def new @minister_contact = MinisterContact.new(minister_id: params[:id]) diff --git a/app/controllers/ministers_controller.rb b/app/controllers/ministers_controller.rb index 3d6f96ae6..cddebdff6 100644 --- a/app/controllers/ministers_controller.rb +++ b/app/controllers/ministers_controller.rb @@ -1,5 +1,5 @@ class MinistersController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! before_action :set_minister, only: %i[show edit update destroy] def index diff --git a/app/controllers/ogds_controller.rb b/app/controllers/ogds_controller.rb index 35642e82b..636887c31 100644 --- a/app/controllers/ogds_controller.rb +++ b/app/controllers/ogds_controller.rb @@ -1,5 +1,5 @@ class OgdsController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! before_action :set_ogd, only: %i[show edit update destroy] def index diff --git a/app/controllers/pq_user_filter.rb b/app/controllers/pq_user_filter.rb deleted file mode 100644 index cdbb7bdaf..000000000 --- a/app/controllers/pq_user_filter.rb +++ /dev/null @@ -1,9 +0,0 @@ -class PqUserFilter - def self.before(controller) - controller.render file: "public/401.html", status: :unauthorized unless has_access(controller) - end - - def self.has_access(controller) - controller.current_user.pq_user? - end -end diff --git a/app/controllers/pqs_controller.rb b/app/controllers/pqs_controller.rb index b01c76272..b4e6703a8 100644 --- a/app/controllers/pqs_controller.rb +++ b/app/controllers/pqs_controller.rb @@ -1,7 +1,7 @@ class PqsController < ApplicationController include Validators::DateInput - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def index redirect_to controller: "dashboard" diff --git a/app/controllers/press_desks_controller.rb b/app/controllers/press_desks_controller.rb index d30963df9..d1db6566b 100644 --- a/app/controllers/press_desks_controller.rb +++ b/app/controllers/press_desks_controller.rb @@ -1,5 +1,5 @@ class PressDesksController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! before_action :set_press_desk, only: %i[show edit update destroy] def index diff --git a/app/controllers/press_officers_controller.rb b/app/controllers/press_officers_controller.rb index 39c054ce3..27aca9dc4 100644 --- a/app/controllers/press_officers_controller.rb +++ b/app/controllers/press_officers_controller.rb @@ -1,5 +1,5 @@ class PressOfficersController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! before_action :set_press_officer, only: %i[show edit update destroy] before_action :prepare_press_offices diff --git a/app/controllers/preview_controller.rb b/app/controllers/preview_controller.rb deleted file mode 100644 index 1b1842a19..000000000 --- a/app/controllers/preview_controller.rb +++ /dev/null @@ -1,24 +0,0 @@ -class PreviewController < ApplicationController - before_action :authenticate_user!, PqUserFilter - - NEW = "New".freeze - PER_PAGE = 200 - - def index - @preview_state = NEW - @now = Time.zone.now.strftime("%d/%m/%Y") - update_page_title "Preview" - load_pq_with_counts { Pq.new_questions.sorted_for_dashboard } - end - -private - - def load_pq_with_counts - @questions = paginate_collection(yield) if block_given? - end - - def paginate_collection(pqs) - page = params.fetch(:page, 1) - pqs.paginate(page:, per_page: PER_PAGE) - end -end diff --git a/app/controllers/quick_action_controller.rb b/app/controllers/quick_action_controller.rb index bbb947312..c4ecb9bf8 100644 --- a/app/controllers/quick_action_controller.rb +++ b/app/controllers/quick_action_controller.rb @@ -1,5 +1,5 @@ class QuickActionController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def dates params.permit(:total_pqs, :pqs_comma_separated_for_dates, :utf8, :authenticity_token, :qa_edit_deadline_date, :qa_edit_draft_date, :qa_edit_pod_date, :qa_edit_minister_date, :qa_edit_answered_date) diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb deleted file mode 100644 index 75318fdcb..000000000 --- a/app/controllers/reports_controller.rb +++ /dev/null @@ -1,34 +0,0 @@ -class ReportsController < ApplicationController - before_action :authenticate_user!, PqUserFilter - - PER_PAGE = 15 - - def ministers_by_progress - update_page_title("Minister report") - report_data = Pq.count_in_progress_by_minister - @report = Presenters::Report.ministers(report_data, Minister.active) - render "report" - end - - def press_desk_by_progress - update_page_title("Press desk report") - report_data = Pq.count_accepted_by_press_desk - @report = Presenters::Report.press_desk(report_data, PressDesk.active) - render "report" - end - - def filter_all - state = params[:state] - minister_id = params[:minister_id] - press_desk_id = params[:press_desk_id] - - @ministers = Minister.active - @action_officers = ActionOfficer.active - @press_desks = PressDesk.active - @questions = Pq.filter_for_report(state, minister_id, press_desk_id) - .paginate(page: params[:page], per_page: PER_PAGE) - @states = PqState::ALL.map { |s| [PqState.state_label(s), s] } - - render "filter_all" - end -end diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index d46058aa5..047d79dfd 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -2,8 +2,6 @@ class RootController < ApplicationController before_action :authenticate_user! def index - return redirect_to controller: "dashboard", action: "index" if current_user.pq_user? - - render file: "public/401.html", status: :unauthorized + redirect_to controller: "dashboard", action: "index" end end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 3cb5ad21a..89d83947f 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,5 +1,5 @@ class SearchController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def index uin = params[:search] diff --git a/app/controllers/transferred_controller.rb b/app/controllers/transferred_controller.rb index cc44335ed..f143a6239 100644 --- a/app/controllers/transferred_controller.rb +++ b/app/controllers/transferred_controller.rb @@ -1,7 +1,7 @@ class TransferredController < ApplicationController include Validators::DateInput - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def new update_page_title("Create a transferred PQ") diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index adb919df7..5de5d7e74 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,5 +1,5 @@ class UsersController < ApplicationController - before_action :authenticate_user!, PqUserFilter + before_action :authenticate_user! def index @users = User.active_list diff --git a/app/helpers/stats_helper.rb b/app/helpers/stats_helper.rb deleted file mode 100644 index 7e38f3a7b..000000000 --- a/app/helpers/stats_helper.rb +++ /dev/null @@ -1,18 +0,0 @@ -module StatsHelper - METRIC_DASHBOARD_HITS = "dashboard.hits".freeze - - MAIL_TIMING = "mails.timing".freeze - MAIL_SUCCESS = "mails.success".freeze - MAIL_FAILURE = "mails.failed".freeze - - PAGES_TIMING = "pages.timing".freeze - PAGES_ERRORS = "pages.errors".freeze - - TOKENS_GENERATE = "tokens.generate".freeze - TOKENS_VALID = "tokens.validation.valid".freeze - TOKENS_INVALID = "tokens.validation.invalid".freeze - - IMPORT = "questions.import".freeze - PROGRESS = "questions.progress".freeze - IMPORT_ERROR = "questions.import.fail".freeze -end diff --git a/app/javascript/application.js b/app/javascript/application.js new file mode 100644 index 000000000..fa490a2de --- /dev/null +++ b/app/javascript/application.js @@ -0,0 +1,13 @@ +import "./moj/stick-at-top-when-scrolling" +import "./moj/bootstrap" +import "./moj/jquery.datetimepicker" +import "./moj/pq" +import "./moj/stop-scrolling-at-footer" +import "./moj/validation_event" +import "./moj/early_bird_dashboard" + +import { initAll } from "govuk-frontend"; +initAll(); + +import Rails from "@rails/ujs" +Rails.start() diff --git a/app/assets/javascripts/bootstrap.js b/app/javascript/moj/bootstrap.js similarity index 100% rename from app/assets/javascripts/bootstrap.js rename to app/javascript/moj/bootstrap.js diff --git a/app/assets/javascripts/early_bird_dashboard.js b/app/javascript/moj/early_bird_dashboard.js similarity index 100% rename from app/assets/javascripts/early_bird_dashboard.js rename to app/javascript/moj/early_bird_dashboard.js diff --git a/app/assets/javascripts/jquery.datetimepicker.js b/app/javascript/moj/jquery.datetimepicker.js similarity index 99% rename from app/assets/javascripts/jquery.datetimepicker.js rename to app/javascript/moj/jquery.datetimepicker.js index 2cf8d73f5..d262f68d0 100644 --- a/app/assets/javascripts/jquery.datetimepicker.js +++ b/app/javascript/moj/jquery.datetimepicker.js @@ -1003,7 +1003,7 @@ } else if (!Date.parseDate($(this).val(), options.format)) { var splittedHours = +([$(this).val()[0], $(this).val()[1]].join('')), splittedMinutes = +([$(this).val()[2], $(this).val()[3]].join('')); - + // parse the numbers as 0312 => 03:12 if(!options.datepicker && options.timepicker && splittedHours >= 0 && splittedHours < 24 && splittedMinutes >= 0 && splittedMinutes < 60) { $(this).val([splittedHours, splittedMinutes].map(function(item) { @@ -1012,12 +1012,12 @@ } else { $(this).val((_xdsoft_datetime.now()).dateFormat(options.format)); } - + datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); } else { datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val()); } - + datetimepicker.trigger('changedatetime.xdsoft'); }); } diff --git a/app/assets/javascripts/pq.js b/app/javascript/moj/pq.js similarity index 96% rename from app/assets/javascripts/pq.js rename to app/javascript/moj/pq.js index 91707f44d..4eff104eb 100644 --- a/app/assets/javascripts/pq.js +++ b/app/javascript/moj/pq.js @@ -1,5 +1,3 @@ -var document, $, ga; - (function() { 'use strict'; @@ -43,7 +41,7 @@ var document, $, ga; var $button = $form.find('.commission-button'); if (isValidDashboardPq($form)) { $button.attr('aria-disabled', 'false'); - $button.removeAttr('disabled'); + $button.prop('disabled', false); } else { $button.attr('aria-disabled', 'true'); $button.attr('disabled', 'disabled'); @@ -70,8 +68,8 @@ var document, $, ga; if (selectionCount > '0') { var selectionMessage = ( selectionCount == 1 ) ? " PQ selected" : " PQs selected"; $('.selectionCount').html(selectionCount + selectionMessage); - $('#do-export').removeAttr("disabled", "disabled"); - $('#do-reminders').removeAttr("disabled", "disabled"); + $('#do-export').prop('disabled', false); + $('#do-reminders').prop('disabled', false); if ( ($('#qa_edit_deadline_date').val().length == 10 || $('#qa_edit_draft_date').val().length == 10 || @@ -79,7 +77,7 @@ var document, $, ga; $('#qa_edit_minister_date').val().length == 10 || $('#qa_edit_answered_date').val().length == 10) && $('#editDates .selectionCount').text().trim() != 'No PQs selected' ){ - $('#do-edit').removeAttr("disabled", "disabled"); // Enable 'Edit' button. + $('#do-edit').prop("disabled", false); // Enable 'Edit' button. } } else { @@ -318,7 +316,7 @@ var document, $, ga; // we need to avoid other unnecessary initialisations as they // break the page - $('nav').hide(); + $('.navbar').hide(); // open the reject justification form when Reject is selected // and hide it when Accept is selected @@ -408,7 +406,8 @@ var document, $, ga; // on successful reminder sent, display the data received from the // ajax call - $('.ao-reminder-link').on('ajax:success', function(e, data){ + $('.ao-reminder-link').on('ajax:success', function(e) { + var data = e.originalEvent.detail[2].response; $(this).after(data); }); } @@ -461,7 +460,7 @@ var document, $, ga; } }); - $quickActions.change(function (event) { + $quickActions.on("change", function (event) { // Quick Action: Edit Dates ~ Check at least one date and PQ selected if ( ($(event.target).is('#qa_edit_deadline_date') && $('#qa_edit_deadline_date').val().length == 16 || @@ -470,15 +469,15 @@ var document, $, ga; $(event.target).is('#qa_edit_minister_date') && $('#qa_edit_minister_date').val().length == 16 || $(event.target).is('#qa_edit_answered_date') && $('#qa_edit_answered_date').val().length == 16) && $('#editDates .selectionCount').text().trim() != 'No PQs selected' ){ - $('#do-edit').removeAttr("disabled", "disabled"); // Enable 'Edit' button. + $('#do-edit').prop("disabled", false); // Enable 'Edit' button. } else { - $('#do-edit').attr("disabled", "disabled"); // Disable 'Edit' button. + $('#do-edit').prop("disabled", true); // Disable 'Edit' button. } }); // Trigger date picker events - $dashboardFilters.change(function () { + $dashboardFilters.on("change", function () { filterQuestions(); }); @@ -499,7 +498,7 @@ var document, $, ga; } else if ( $(event.target).val() == "Clear" && $(event.target).prop('id') != "clear-keywords-filter" ){ // Uncheck radio buttons. - $('input[name="' + $(event.target).closest('.filter-box').prop('id') + '"]').removeAttr('checked'); + $('input[name="' + $(event.target).closest('.filter-box').prop('id') + '"]').prop("checked", false); // Hide the "1 selected" notice. $('#' + $(event.target).closest('.filter-box').prop('id') + ' .notice').hide(); } diff --git a/app/assets/javascripts/stick-at-top-when-scrolling.js b/app/javascript/moj/stick-at-top-when-scrolling.js similarity index 98% rename from app/assets/javascripts/stick-at-top-when-scrolling.js rename to app/javascript/moj/stick-at-top-when-scrolling.js index 6eebbf08f..1bd6f5aaf 100644 --- a/app/assets/javascripts/stick-at-top-when-scrolling.js +++ b/app/javascript/moj/stick-at-top-when-scrolling.js @@ -1,7 +1,6 @@ (function () { "use strict"; - var root = this, - $ = root.jQuery; + var root = window; if(typeof root.GOVUK === 'undefined') { root.GOVUK = {}; } // Stick elements to top of screen when you scroll past, documentation is in the README.md diff --git a/app/assets/javascripts/stop-scrolling-at-footer.js b/app/javascript/moj/stop-scrolling-at-footer.js similarity index 95% rename from app/assets/javascripts/stop-scrolling-at-footer.js rename to app/javascript/moj/stop-scrolling-at-footer.js index f5bd3f52e..bc4a6fec6 100644 --- a/app/assets/javascripts/stop-scrolling-at-footer.js +++ b/app/javascript/moj/stop-scrolling-at-footer.js @@ -13,8 +13,7 @@ (function () { "use strict"; - var root = this, - $ = root.jQuery; + var root = this; if(typeof root.GOVUK === 'undefined') { root.GOVUK = {}; } var stopScrollingAtFooter = { @@ -63,7 +62,7 @@ } }, onScroll: function(){ - if (stopScrollingAtFooter._isPolling === false) { + if (stopScrollingAtFooter._isPolling === false) { stopScrollingAtFooter.startPolling(); } }, @@ -134,5 +133,5 @@ root.GOVUK.stopScrollingAtFooter = stopScrollingAtFooter; - $(root).load(function(){ $(root).trigger('govuk.pageSizeChanged'); }); + $(root).on('load', function(){ $(root).trigger('govuk.pageSizeChanged'); }); }).call(this); diff --git a/app/assets/javascripts/validation_event.js b/app/javascript/moj/validation_event.js similarity index 84% rename from app/assets/javascripts/validation_event.js rename to app/javascript/moj/validation_event.js index 096cd9259..6a05c9ec6 100644 --- a/app/assets/javascripts/validation_event.js +++ b/app/javascript/moj/validation_event.js @@ -2,7 +2,7 @@ window.onload = function() { window.dataLayer = window.dataLayer || []; - if($('.pq-msg-warning, .pq-msg-error').size() > 0) { + if($('.pq-msg-warning, .pq-msg-error').length > 0) { $('.pq-msg-warning, .pq-msg-error').each( function(_, obj) { window.dataLayer.push({ event: "validation_error", diff --git a/app/models/email.rb b/app/models/email.rb index 5efc7bc24..94c4efdc4 100644 --- a/app/models/email.rb +++ b/app/models/email.rb @@ -1,68 +1,2 @@ -# == Schema Information -# -# Table name: emails -# -# id :integer not null, primary key -# mailer :string -# method :string -# params :text -# from :text -# to :text -# cc :text -# reply_to :text -# send_attempted_at :datetime -# sent_at :datetime -# num_send_attempts :integer default(0) -# status :string default("new") -# created_at :datetime not null -# updated_at :datetime not null -# - -class Email < ApplicationRecord - EMAIL_REGEXP = /\A(.*<)?[^@\s]+@([^@\s]+\.)+[^@\s]+>?\z/ - EMAIL_DELIMITERS = [";", ":"].freeze - - validates :method, presence: true - validates :from, presence: true - validates :to, presence: true - validates :reply_to, presence: true - - # validates_format_of :from, with: EMAIL_REGEXP - # validates_format_of :reply_to, with: EMAIL_REGEXP - validates :from, format: { with: EMAIL_REGEXP, format: true } - validates :reply_to, format: { with: EMAIL_REGEXP, format: true } - - validate :concatenated_email_to_format - validate :concatenated_email_cc_format - - validates :mailer, inclusion: { in: %w[DbSyncMailer ImportMailer PqMailer] } - validates :status, inclusion: { in: %w[new sending sent failed abandoned] } - - serialize :params - - scope :new_only, -> { where(status: "new").order(:id) } - scope :waiting, -> { where(status: %w[new failed]).order(:id) } - scope :abandoned, -> { where(status: "abandoned").order(:id) } - -private - - def concatenated_email_to_format - errors.add(:to, "invalid") unless concatenated_email_format(:to) - end - - def concatenated_email_cc_format - errors.add(:cc, "invalid") unless concatenated_email_format(:cc) - end - - def concatenated_email_format(field) - return true unless attribute(field) - - EMAIL_DELIMITERS.any? do |delimiter| - all_valid_emails?(attribute(field).split(delimiter)) - end - end - - def all_valid_emails?(emails) - emails.all? { |email| email.strip =~ EMAIL_REGEXP } - end -end +# legacy model +class Email < ApplicationRecord; end diff --git a/app/models/pq.rb b/app/models/pq.rb index c62d485d2..8e0c76c68 100644 --- a/app/models/pq.rb +++ b/app/models/pq.rb @@ -2,48 +2,48 @@ # # Table name: pqs # -# id :integer not null, primary key -# house_id :integer -# raising_member_id :integer -# tabled_date :datetime -# response_due :datetime -# question :text -# answer :string -# created_at :datetime not null -# updated_at :datetime not null -# uin :string -# member_name :string -# member_constituency :string -# house_name :string -# date_for_answer :date -# registered_interest :boolean -# internal_deadline :datetime -# question_type :string -# minister_id :integer -# policy_minister_id :integer -# progress_id :integer -# draft_answer_received :datetime -# holding_reply :datetime -# preview_url :string -# pod_waiting :datetime -# pod_clearance :datetime -# transferred :boolean -# question_status :string -# sent_to_policy_minister :datetime -# cleared_by_policy_minister :datetime -# sent_to_answering_minister :datetime -# cleared_by_answering_minister :datetime -# answer_submitted :datetime -# final_response_info_released :string -# transfer_out_ogd_id :integer -# transfer_out_date :datetime -# directorate_id :integer -# original_division_id :integer -# transfer_in_ogd_id :integer -# transfer_in_date :datetime -# state :string default("unassigned") -# state_weight :integer default(0) -# archived :boolean default(FALSE) +# id :integer not null, primary key +# house_id :integer +# raising_member_id :integer +# tabled_date :datetime +# response_due :datetime +# question :text +# answer :string +# created_at :datetime not null +# updated_at :datetime not null +# uin :string +# member_name :string +# member_constituency :string +# house_name :string +# date_for_answer :date +# registered_interest :boolean +# internal_deadline :datetime +# question_type :string +# minister_id :integer +# policy_minister_id :integer +# progress_id :integer +# draft_answer_received :datetime +# holding_reply :datetime +# preview_url :string +# pod_waiting :datetime +# pod_clearance :datetime +# transferred :boolean +# question_status :string +# sent_to_policy_minister :datetime +# cleared_by_policy_minister :datetime +# sent_to_answering_minister :datetime +# cleared_by_answering_minister :datetime +# answer_submitted :datetime +# final_response_info_released :string +# transfer_out_ogd_id :integer +# transfer_out_date :datetime +# directorate_id :integer +# original_division_id :integer +# transfer_in_ogd_id :integer +# transfer_in_date :datetime +# state :string default("unassigned") +# state_weight :integer default(0) +# archived :boolean default(FALSE) # class Pq < ApplicationRecord diff --git a/app/models/pq_counts.rb b/app/models/pq_counts.rb index c95f7f2e8..ee9be324c 100644 --- a/app/models/pq_counts.rb +++ b/app/models/pq_counts.rb @@ -1,54 +1,4 @@ module PqCounts - # Returns a hash with the PQ state as key and count by minister_id as value. - # - # It includes only PQs that are in the in progress states (PqState::IN_PROGRESS), - # and ministers that are active. - # - # @returns [Hash[String, Hash]] - # - # Example: - # - # { - # "no_response" => { - # 43 => 1, - # 44 => 10 - # 48 => 2 - # }, - # "with_pod" => { - # 44 => 2 - # } - # } - def count_in_progress_by_minister - select("minister_id, state, count(*)") - .joins(:minister) - .where(state: PqState::IN_PROGRESS) - .where("ministers.deleted = false") - .group(:state, :minister_id) - .reduce({}) do |acc, r| - h = { r.minister_id => r.count } - acc.merge(r.state => h) { |_, old_v, new_v| old_v.merge(new_v) } - end - end - - # Returns a hash with the PQ state as key and count by press_desk_id as value. - # - # It will filter out PQs that are below the PqState::DRAFT_PENDING state, - # and press desks that are not active. - # - # @returns [Hash[String, Hash]] - # - def count_accepted_by_press_desk - join_press_desks - .select("pqs.state, ao.press_desk_id, count(distinct pqs.id)") - .where.not(state: PqState::UNASSIGNED) - .where("aopq.response = 'accepted' AND pd.deleted = false") - .group("state, ao.press_desk_id") - .reduce({}) do |acc, r| - h = { r.press_desk_id => r.count } - acc.merge(r.state => h) { |_, old_v, new_v| old_v.merge(new_v) } - end - end - # Returns a hash of PQ counts by state # # @returns [Hash[String, Fixnum]] diff --git a/app/models/pq_scopes.rb b/app/models/pq_scopes.rb index 13180f9c8..f874f643f 100644 --- a/app/models/pq_scopes.rb +++ b/app/models/pq_scopes.rb @@ -22,14 +22,6 @@ def draft_pending by_status(PqState::DRAFT_PENDING) end - def filter_for_report(state, minister_id, press_desk_id) - q = order(:internal_deadline) - q = join_press_desks.where("pd.id = ?", press_desk_id).distinct("pqs.uin") if press_desk_id.present? - q = q.where(state:) if state.present? - q = q.where(minister_id:) if minister_id.present? - q - end - def imported_since_last_weekday end_of_last_weekday = Time.zone.today.last_weekday.end_of_day end_of_today = Time.zone.today.end_of_day @@ -40,13 +32,6 @@ def in_progress where("date_for_answer >= CURRENT_DATE and state IN (?)", PqState::IN_PROGRESS) end - def join_press_desks - joins("JOIN action_officers_pqs aopq ON aopq.pq_id = pqs.id") - .joins("JOIN action_officers ao ON ao.id = aopq.action_officer_id") - .joins("JOIN press_desks pd ON pd.id = ao.press_desk_id") - .where("aopq.response = 'accepted' AND pd.deleted = false") - end - def minister_cleared by_status(PqState::MINISTER_CLEARED) end diff --git a/app/models/pqa_import_run.rb b/app/models/pqa_import_run.rb index a3db5419e..03e63a98a 100644 --- a/app/models/pqa_import_run.rb +++ b/app/models/pqa_import_run.rb @@ -18,8 +18,6 @@ class PQAImportRun < ApplicationRecord validates :status, inclusion: { in: %w[OK Failure OK_with_errors], message: "Status must be 'OK', 'Failure' or 'OK_with_errors': was '%{value}'" } - serialize :error_messages - def self.last_import_time_utc rec = successful.order(:start_time).last if rec.nil? diff --git a/app/services/commissioning_service.rb b/app/services/commissioning_service.rb index 2d9335166..207b9fa09 100644 --- a/app/services/commissioning_service.rb +++ b/app/services/commissioning_service.rb @@ -48,8 +48,6 @@ def notify_assignment(ao_pq) expires = @current_time.end_of_day + AO_TOKEN_LIFETIME.days token = @token_service.generate_token(path, entity, expires) - $statsd.increment "#{StatsHelper::TOKENS_GENERATE}.commission" # rubocop:disable Style/GlobalVars - LogStuff.tag(:mailer_commission) do NotifyPqMailer.commission_email(pq:, action_officer: ao, token:, entity:, email: ao.email).deliver_later if ao.group_email.present? diff --git a/app/services/early_bird_report_service.rb b/app/services/early_bird_report_service.rb index 7edb88952..9c64fbac3 100644 --- a/app/services/early_bird_report_service.rb +++ b/app/services/early_bird_report_service.rb @@ -15,7 +15,6 @@ def notify_early_bird token = @token_service.generate_token(early_bird_dashboard_path, entity, end_of_day) recipients = EarlyBirdMember.active.pluck(:email) << "pqtest@digital.justice.gov.uk" - $statsd.increment "#{StatsHelper::TOKENS_GENERATE}.earlybird" # rubocop:disable Style/GlobalVars recipients.each do |recipient| LogStuff.tag(:mailer_early_bird) do LogStuff.info { "Early bird email to pqtest@digital.justice.gov.uk} (name early_bird) [CCd to #{recipients.join(';')}]" } diff --git a/app/services/pqa_service.rb b/app/services/pqa_service.rb index 44b085b3f..fc7cc476a 100644 --- a/app/services/pqa_service.rb +++ b/app/services/pqa_service.rb @@ -8,17 +8,13 @@ def initialize(client) end def questions(date_from, date_to = nil, status = nil) - $statsd.time("#{StatsHelper::IMPORT}.qa.response_time") do # rubocop:disable Style/GlobalVars - response = @client.questions(date_from, date_to, status) - PQA::XmlDecoder.decode_questions(response.body) - end + response = @client.questions(date_from, date_to, status) + PQA::XmlDecoder.decode_questions(response.body) end def question(uin) - $statsd.time("#{StatsHelper::IMPORT}.qa.response_time") do # rubocop:disable Style/GlobalVars - response = @client.question(uin) - PQA::XmlDecoder.decode_questions(response.body) - end + response = @client.question(uin) + PQA::XmlDecoder.decode_questions(response.body) end def answer_response(uin, member_id, text, is_holding_answer) diff --git a/app/views/action_officers/_form.html.slim b/app/views/action_officers/_form.html.slim index 1094a79a6..ec090f988 100644 --- a/app/views/action_officers/_form.html.slim +++ b/app/views/action_officers/_form.html.slim @@ -20,7 +20,7 @@ .form-group label.form-label for="action_officer_press_desk_id" Press Desk (required) = f.collection_select(:press_desk_id, @press_desks, :id, :name, :include_blank => "Please select") - p Status + p.govuk-body Status .form-group label.block-label for="action_officer_deleted" = f.check_box :deleted diff --git a/app/views/action_officers/edit.html.slim b/app/views/action_officers/edit.html.slim index 803d93fa8..7bb636b9f 100644 --- a/app/views/action_officers/edit.html.slim +++ b/app/views/action_officers/edit.html.slim @@ -1,4 +1,4 @@ -h1 Edit action officer +h1.govuk-heading-l Edit action officer = render partial: "shared/flash_messages", flash: flash = render 'form' = link_to 'Return to action officer list', action_officers_path, {class: 'admin-link'} diff --git a/app/views/action_officers/index.html.slim b/app/views/action_officers/index.html.slim index 10c5309a5..a5cfa79d3 100644 --- a/app/views/action_officers/index.html.slim +++ b/app/views/action_officers/index.html.slim @@ -1,4 +1,4 @@ -h1 Action officers +h1.govuk-heading-l Action officers = render partial: "shared/flash_messages", flash: flash #admin-ao-list .row @@ -24,5 +24,5 @@ h1 Action officers td.edit = link_to edit_action_officer_path(action_officer) do 'Edit - span.visually-hidden + span.govuk-visually-hidden = action_officer.name diff --git a/app/views/action_officers/new.html.slim b/app/views/action_officers/new.html.slim index 50c235e50..77cc933dc 100644 --- a/app/views/action_officers/new.html.slim +++ b/app/views/action_officers/new.html.slim @@ -1,4 +1,4 @@ -h1 Add action officer +h1.govuk-heading-l Add action officer = render partial: "shared/flash_messages", flash: flash = render 'form' = link_to 'Return to action officer list', action_officers_path, {class: 'admin-link'} diff --git a/app/views/action_officers/show.html.slim b/app/views/action_officers/show.html.slim index 99a409d3c..139f9c550 100644 --- a/app/views/action_officers/show.html.slim +++ b/app/views/action_officers/show.html.slim @@ -1,24 +1,24 @@ #admin-ao-details - h1 Action officer details + h1.govuk-heading-l Action officer details = render partial: "shared/flash_messages", flash: flash - h2 Name - p= @action_officer.name - h2 Email - p= @action_officer.email - h2 Group email - p= @action_officer.group_email - h2 Phone - p= @action_officer.phone - h2 Deputy director - p= @action_officer.deputy_director.name - h2 Division - p= @action_officer.deputy_director.division.name - h2 Press desk - p= @action_officer.press_desk.name - h2 Status + h2.govuk-heading-m Name + p.govuk-body= @action_officer.name + h2.govuk-heading-m Email + p.govuk-body= @action_officer.email + h2.govuk-heading-m Group email + p.govuk-body= @action_officer.group_email + h2.govuk-heading-m Phone + p.govuk-body= @action_officer.phone + h2.govuk-heading-m Deputy director + p.govuk-body= @action_officer.deputy_director.name + h2.govuk-heading-m Division + p.govuk-body= @action_officer.deputy_director.division.name + h2.govuk-heading-m Press desk + p.govuk-body= @action_officer.press_desk.name + h2.govuk-heading-m Status = render partial: 'shared/status', object: @action_officer - p + p.govuk-body = link_to 'Edit', edit_action_officer_path(@action_officer), {class: 'admin-link'} - span.visually-hidden + span.govuk-visually-hidden = ' | ' = link_to 'Return to action officer list', action_officers_path, {class: 'admin-link'} diff --git a/app/views/admin/index.html.slim b/app/views/admin/index.html.slim index a8b6a8ef8..8da13aa9c 100644 --- a/app/views/admin/index.html.slim +++ b/app/views/admin/index.html.slim @@ -1,11 +1,11 @@ #settings-home - h1 Settings + h1.govuk-heading-l Settings - if flash[:success] .pq-msg-success = flash[:success] - .row + div .col-md-4 - h2 Address books + h2.govuk-heading-m Address books nav ul li= link_to 'Action officers', action_officers_path @@ -14,7 +14,7 @@ li= link_to 'Press officers', press_officers_path li= link_to 'Early bird list', early_bird_members_path .col-md-4 - h2 Tasks + h2.govuk-heading-m Tasks nav ul li= link_to 'Create a transferred PQ', transferred_new_path @@ -25,7 +25,7 @@ li = link_to 'Archive PQ Session', new_archive_path if current_user.admin? .col-md-4 - h2 User admin + h2.govuk-heading-m User admin nav ul li= link_to 'Users', users_path diff --git a/app/views/archives/new.html.slim b/app/views/archives/new.html.slim index da42f84f3..f164519a6 100644 --- a/app/views/archives/new.html.slim +++ b/app/views/archives/new.html.slim @@ -2,9 +2,9 @@ .pq-msg-success = flash[:success] -h1 Archive PQ Session +h1.govuk-heading-l Archive PQ Session -h2 There are #{@count} unarchived questions -h3 Prefixes previously used, or reserved: #{@all_prefixes} +h2.govuk-heading-m There are #{@count} unarchived questions +h3.govuk-heading-s Prefixes previously used, or reserved: #{@all_prefixes} = render 'form' unless @count.zero? diff --git a/app/views/assignment/confirmation.html.slim b/app/views/assignment/confirmation.html.slim index b618d00db..62f39c84e 100644 --- a/app/views/assignment/confirmation.html.slim +++ b/app/views/assignment/confirmation.html.slim @@ -1,23 +1,23 @@ = render partial: 'shared/flash_messages' - if @assignment.rejected? - p.space-before-20 + p.govuk-body.space-before-20 strong Thank you for your response - p.space-before + p.govuk-body.space-before | You rejected PQ strong= " #{@question.uin} " = raw @assignment.updated_at.to_formatted_s(:wordy) = ' stating: ' - p.space-before + p.govuk-body.space-before = "#{@assignment.reason_option} - #{@assignment.reason}" - p.space-before Your message has been sent to the PQ team. You don't need to take any further action. + p.govuk-body.space-before Your message has been sent to the PQ team. You don't need to take any further action. - elsif @question.action_officers_pqs.accepted.action_officer_id == @assignment.action_officer_id - p.space-before-20 + p.govuk-body.space-before-20 strong Thank you for your response - p.space-before + p.govuk-body.space-before | You accepted PQ strong= " #{@question.uin} " = raw @question.action_officers_pqs.accepted.updated_at.to_formatted_s(:wordy) - p.space-before You will receive a further email which explains how and when to return your draft response, and provides a link to PQ guidance on the intranet. + p.govuk-body.space-before You will receive a further email which explains how and when to return your draft response, and provides a link to PQ guidance on the intranet. - else p strong This PQ has already been accepted @@ -25,6 +25,6 @@ = ' accepted PQ' strong= " #{@question.uin} " = raw @question.action_officers_pqs.accepted.updated_at.to_formatted_s(:wordy) -p.space-before +p.govuk-body.space-before | If you have any queries please a href="mailto:#{Settings.mail_reply_to}" contact the PQ team diff --git a/app/views/assignment/show.html.slim b/app/views/assignment/show.html.slim index 126862665..dfdd0b5e7 100644 --- a/app/views/assignment/show.html.slim +++ b/app/views/assignment/show.html.slim @@ -1,19 +1,19 @@ #assignment - h1 PQ Assignment + h1.govuk-heading-l PQ Assignment = render partial: 'shared/flash_messages' - .row.space-before-20 - p.strong = @ao.name - p.space-before + div.space-before-20 + p.govuk-body.strong = @ao.name + p.govuk-body.space-before | You have been allocated the PQ strong= @question.uin blockquote = @question.question - p Question asked by #{@question.member_name} (#{@question.house_name}). - p To be answered on behalf of #{@question.minister.name}. + p.govuk-body Question asked by #{@question.member_name} (#{@question.house_name}). + p.govuk-body To be answered on behalf of #{@question.minister.name}. - p.strong.space-before-20 Please accept or reject this allocation as soon as possible to avoid the hold up for the allocation of this PQ. + p.govuk-body.strong.space-before-20 Please accept or reject this allocation as soon as possible to avoid the hold up for the allocation of this PQ. - if @question.action_officers_pqs.size > 1 - p.space-before This question has also been allocated to the following action officers, but only one person may accept responsibility. + p.govuk-body.space-before This question has also been allocated to the following action officers, but only one person may accept responsibility. ul.question-allocation-list data-pqid=@question.id - @question.action_officers_pqs.each do |ao_pq| - if !ao_pq.action_officer.nil? && ao_pq.action_officer_id!=@ao.id @@ -58,6 +58,6 @@ | If you have any queries please a href="mailto:#{Settings.mail_reply_to}" contact the PQ Team - else - .row + div .col-md-10 - p.strong The question has been accepted and an email has been sent to the accepting officer with drafting instructions, please contact Parliamentary branch if you have any questions + p.govuk-body.strong The question has been accepted and an email has been sent to the accepting officer with drafting instructions, please contact Parliamentary branch if you have any questions diff --git a/app/views/dashboard/_dashboard_filter.html.slim b/app/views/dashboard/_dashboard_filter.html.slim index 9cbfb5321..debaff7b0 100644 --- a/app/views/dashboard/_dashboard_filter.html.slim +++ b/app/views/dashboard/_dashboard_filter.html.slim @@ -1,20 +1,12 @@ -- if @dashboard_state == "New" - h1 Parliamentary questions - New -- if @dashboard_state == "In progress" - h1 Parliamentary questions - In progress -- if @dashboard_state == "Backlog" - h1 Parliamentary questions - Backlog - -#sidebar.col-md-3.col-md-push-9 - +#sidebar.govuk-grid-column-one-quarter #quick-links - h2 Quick actions + h2.govuk-heading-m Quick actions = render partial: "dashboard/quick_action_draft_reminders", locals: {total_pqs: @total_pqs} = render partial: "dashboard/quick_action_edit_dates", locals: {total_pqs: @total_pqs} = render partial: "dashboard/quick_action_export", locals: {total_pqs: @total_pqs, pqs_comma_separated: @pqs_comma_separated} #filters - h2 Filter + h2.govuk-heading-m Filter - if @dashboard_state == "In progress" || @dashboard_state == "Backlog" #date-for-answer.filter-box diff --git a/app/views/dashboard/_question_data.html.slim b/app/views/dashboard/_question_data.html.slim index 5f441432e..c78dd6ef0 100644 --- a/app/views/dashboard/_question_data.html.slim +++ b/app/views/dashboard/_question_data.html.slim @@ -1,6 +1,6 @@ -.pq-header.row - .col-md-5 - h2 +.pq-header.govuk-grid-row + .govuk-grid-column-one-half + h2.govuk-heading-m = link_to(question.uin, { controller: 'pqs', action: 'show', id: question.uin}, :class=>"question-uin", aria: { label: "Click to view and edit question #{question.uin}, question status #{question.state.humanize}, internal deadline #{question.internal_deadline || 'not set'}" }) span.question-type = question.question_type_header @@ -8,16 +8,18 @@ input id="#{question.uin}" class="pq-select" type="checkbox" name="uin-#{question.id}" value="#{question.id}" span Select #{question.uin} - .col-md-4 + .govuk-grid-column-one-quarter - if question.proposed? div.question-proposed Action Officer Proposed - - if !question.date_for_answer.nil? and !question.is_new? + - elsif !question.date_for_answer.nil? and !question.is_new? | Date for answer span.answer-date = question.date_for_answer.to_formatted_s(:date) = render partial: 'shared/answer_time', locals: {date_for_answer: question.date_for_answer, is_closed: question.closed?} + - else + span - .col-md-3 + .govuk-grid-column-one-quarter span class=("flag #{state_classname question.state}") = state_label question.state p.pq-question diff --git a/app/views/dashboard/_question_data_commissioned.html.slim b/app/views/dashboard/_question_data_commissioned.html.slim index dc724de29..9ba6d6e9e 100644 --- a/app/views/dashboard/_question_data_commissioned.html.slim +++ b/app/views/dashboard/_question_data_commissioned.html.slim @@ -1,21 +1,21 @@ - if question.internal_deadline - h3 Internal deadline: - span.deadline-date.text + h3.govuk-heading-s Internal deadline: + p.govuk-body.deadline-date.text = question.internal_deadline = render partial: 'shared/deadline_time', locals: {internal_deadline: question.internal_deadline, is_closed: question.closed?, draft_reply: question.draft_answer_received} - h3 Replying minister: - span.replying-minister + h3.govuk-heading-s Replying minister: + p.govuk-body.replying-minister = question.minister.name - if question.policy_minister - h3 Policy minister: - span.policy-minister + h3.govuk-heading-s Policy minister: + p.govuk-body.policy-minister = question.policy_minister.name - if question.action_officer_accepted.nil? - h3 Action officer(s) + h3.govuk-heading-s Action officer(s) - question.action_officers_pqs.not_rejected.each_with_index do | ao_pq, ind | - if ind > 0 = ', ' = link_to ao_pq.action_officer.name, action_officer_path(ao_pq.action_officer) - question.action_officers_pqs.each do | ao_pq | - p.space-before + p.govuk-body.space-before = render partial: 'shared/ao_reminder_link', locals: {ao_pq: ao_pq, question: question} diff --git a/app/views/dashboard/_question_data_uncommissioned.html.slim b/app/views/dashboard/_question_data_uncommissioned.html.slim index d7c7cf6ae..632004ee1 100644 --- a/app/views/dashboard/_question_data_uncommissioned.html.slim +++ b/app/views/dashboard/_question_data_uncommissioned.html.slim @@ -1,5 +1,4 @@ -.pq-columns.row - +.pq-columns.govuk-grid-row = form_for CommissionForm.new(minister_id: question.minister_id, policy_minister_id: question.policy_minister_id), remote: true, authenticity_token: true, @@ -7,17 +6,17 @@ url: commission_path do |f| /! start of 'sub column 1' - .col-md-7 + .govuk-grid-column-one-half = render partial: 'shared/minister_selection', locals: {question: question, form: f} = render partial: 'shared/action_officer_selection', locals: {action_officers: action_officers, question: question, form: f, reassign: false} .form-group = f.hidden_field(:pq_id , :value => question.id) /! start of sub column 2 - .col-md-5 + .govuk-grid-column-one-half - if !question.house_name.nil? label.form-label= question.house_name - p= question.member_name + p.govuk-body= question.member_name label.form-label for="pq_date_for_answer-#{question.id}" Date for answer .datepicker.form-group @@ -30,5 +29,5 @@ input.deadline-date.form-control.required-for-commission id="pq_internal_deadline-#{question.id}" name="commission_form[internal_deadline]" type="text" value=(question.internal_deadline.to_s) span.fa.fa-calendar title="select a date" = render partial: 'shared/deadline_time', locals: {internal_deadline: question.internal_deadline, is_closed: question.closed?, draft_reply: question.draft_answer_received} - .form-group + .govuk-grid-column-full.form-group = f.submit 'Commission', :class => 'button commission-button' diff --git a/app/views/dashboard/_questions_list.html.slim b/app/views/dashboard/_questions_list.html.slim index 186cf5c29..4a2e7475c 100644 --- a/app/views/dashboard/_questions_list.html.slim +++ b/app/views/dashboard/_questions_list.html.slim @@ -1,4 +1,4 @@ -.col-md-9.col-md-pull-3 +.govuk-grid-column-three-quarters = render partial: "shared/flash_messages", flash: flash #count aria-live='polite' strong #{@questions.length} @@ -14,4 +14,4 @@ - questions.each do |question| li id="pq-frame-#{question.id}" data-pquin=question.uin class="question" = render partial: 'dashboard/question_data', locals: {question: question, action_officers: action_officers} - #pages.row + #pages diff --git a/app/views/dashboard/index.html.slim b/app/views/dashboard/index.html.slim index 9b60af2da..54006b929 100644 --- a/app/views/dashboard/index.html.slim +++ b/app/views/dashboard/index.html.slim @@ -1,3 +1,9 @@ -#dashboard - = render partial: "dashboard_filter" +#dashboard.govuk-grid-row.content-inner + - if @dashboard_state == "New" + h1.govuk-heading-l Parliamentary questions - New + - if @dashboard_state == "In progress" + h1.govuk-heading-l Parliamentary questions - In progress + - if @dashboard_state == "Backlog" + h1.govuk-heading-l Parliamentary questions - Backlog = render partial: "dashboard/questions_list", locals: {questions: @questions, action_officers: @action_officers } + = render partial: "dashboard_filter" diff --git a/app/views/deputy_directors/_form.html.slim b/app/views/deputy_directors/_form.html.slim index b26af04dc..14b10a48d 100644 --- a/app/views/deputy_directors/_form.html.slim +++ b/app/views/deputy_directors/_form.html.slim @@ -12,7 +12,7 @@ label.form-label for="deputy_director_division_id" Division (required) = f.collection_select(:division_id, @divisions, :id, :name, :include_blank => "Please select") .form-group - p Status + p.govuk-body Status label.block-label for="deputy_director_deleted" = f.check_box :deleted | Inactive diff --git a/app/views/deputy_directors/edit.html.slim b/app/views/deputy_directors/edit.html.slim index 6db8aae5a..33bb4eca7 100644 --- a/app/views/deputy_directors/edit.html.slim +++ b/app/views/deputy_directors/edit.html.slim @@ -1,3 +1,3 @@ -h1 Edit deputy director +h1.govuk-heading-l Edit deputy director = render 'form' = link_to 'Return to deputy director list', deputy_directors_path, {class: 'admin-link'} diff --git a/app/views/deputy_directors/index.html.slim b/app/views/deputy_directors/index.html.slim index e4c2db85f..b0054b50e 100644 --- a/app/views/deputy_directors/index.html.slim +++ b/app/views/deputy_directors/index.html.slim @@ -1,6 +1,6 @@ -h1 Deputy directors +h1.govuk-heading-l Deputy directors #admin-deputy-list - .row + div ul#admin-button-bar li= link_to ('Add deputy director'), new_deputy_director_path, {class: 'button-secondary'} table.table aria-label="deputy directors" diff --git a/app/views/deputy_directors/new.html.slim b/app/views/deputy_directors/new.html.slim index 6e40ca837..da60943cf 100644 --- a/app/views/deputy_directors/new.html.slim +++ b/app/views/deputy_directors/new.html.slim @@ -1,3 +1,3 @@ -h1 Add deputy director +h1.govuk-heading-l Add deputy director = render 'form' = link_to 'Return to deputy director list', deputy_directors_path, {class: 'admin-link'} diff --git a/app/views/deputy_directors/show.html.slim b/app/views/deputy_directors/show.html.slim index 3f9593833..4cb69fefe 100644 --- a/app/views/deputy_directors/show.html.slim +++ b/app/views/deputy_directors/show.html.slim @@ -1,16 +1,16 @@ #admin-deputy-details - h1 Deputy director details - = render partial: "shared/flash_messages", flash: flash - h2 Name - = @deputy_director.name - h2 Email - = @deputy_director.email - h2 Division - = @deputy_director.division.name - h2 Status - = render partial: 'shared/status', object: @deputy_director - p + h1.govuk-heading-l Deputy director details + p.govuk-body= render partial: "shared/flash_messages", flash: flash + h2.govuk-heading-m Name + p.govuk-body= @deputy_director.name + h2.govuk-heading-m Email + p.govuk-body= @deputy_director.email + h2.govuk-heading-m Division + p.govuk-body= @deputy_director.division.name + h2.govuk-heading-m Status + p.govuk-body= render partial: 'shared/status', object: @deputy_director + p.govuk-body = link_to 'Edit', edit_deputy_director_path(@deputy_director), {class: 'admin-link'} - span.visually-hidden + span.govuk-visually-hidden = ' | ' = link_to 'Return to deputy director list', deputy_directors_path, {class: 'admin-link'} diff --git a/app/views/devise/confirmations/new.html.slim b/app/views/devise/confirmations/new.html.slim index 7153affb9..9af06f9de 100644 --- a/app/views/devise/confirmations/new.html.slim +++ b/app/views/devise/confirmations/new.html.slim @@ -1,4 +1,4 @@ -h1 Resend confirmation instructions +h1.govuk-heading-l.govuk-main-wrapper--auto-spacing Resend confirmation instructions = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| = devise_error_messages! div diff --git a/app/views/devise/passwords/edit.html.slim b/app/views/devise/passwords/edit.html.slim index c337da176..e04cc2ae8 100644 --- a/app/views/devise/passwords/edit.html.slim +++ b/app/views/devise/passwords/edit.html.slim @@ -1,4 +1,4 @@ -h1 Change your password +h1.govuk-heading-l.govuk-main-wrapper--auto-spacing Change your password = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| = devise_error_messages! = f.hidden_field :reset_password_token diff --git a/app/views/devise/passwords/new.html.slim b/app/views/devise/passwords/new.html.slim index 6dbc793b4..c2c908e1c 100644 --- a/app/views/devise/passwords/new.html.slim +++ b/app/views/devise/passwords/new.html.slim @@ -1,4 +1,4 @@ -h1.heading-large Forgot your password? +h1.govuk-heading-l.govuk-main-wrapper--auto-spacing Forgot your password? = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| = devise_error_messages! fieldset diff --git a/app/views/devise/registrations/edit.html.slim b/app/views/devise/registrations/edit.html.slim index 28448801d..12733234d 100644 --- a/app/views/devise/registrations/edit.html.slim +++ b/app/views/devise/registrations/edit.html.slim @@ -1,4 +1,4 @@ -h1 +h1.govuk-heading-l.govuk-main-wrapper--auto-spacing | Edit #{resource_name.to_s.humanize} = form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| = devise_error_messages! @@ -11,7 +11,7 @@ h1 | Currently waiting confirmation for: #{resource.unconfirmed_email} div = f.label :password - p leave blank if you don't want to change it + p.govuk-body leave blank if you don't want to change it br/ = f.password_field :password, autocomplete: "off" div @@ -20,11 +20,11 @@ h1 = f.password_field :password_confirmation, autocomplete: "off" div = f.label :current_password - p we need your current password to confirm your changes + p.govuk-body we need your current password to confirm your changes br/ = f.password_field :current_password, autocomplete: "off" div= f.submit "Update" -h2 Cancel my account +h2.govuk-heading-m Cancel my account p | Unhappy? #{button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete} = link_to "Back", :back diff --git a/app/views/devise/registrations/new.html.slim b/app/views/devise/registrations/new.html.slim index 6df4a6f08..2ac05cf2a 100644 --- a/app/views/devise/registrations/new.html.slim +++ b/app/views/devise/registrations/new.html.slim @@ -1,4 +1,4 @@ -h1 Sign up +h1.govuk-heading-l.govuk-main-wrapper--auto-spacing Sign up = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| = devise_error_messages! div diff --git a/app/views/devise/sessions/new.html.slim b/app/views/devise/sessions/new.html.slim index 95ce4305a..0791477b3 100644 --- a/app/views/devise/sessions/new.html.slim +++ b/app/views/devise/sessions/new.html.slim @@ -1,7 +1,7 @@ - content_for :page_title do = 'Sign in - ' -h1.heading-large Sign in +h1.govuk-heading-l.govuk-main-wrapper--auto-spacing Sign in = form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| = render partial: "shared/flash_messages", flash: flash = devise_error_messages! diff --git a/app/views/devise/shared/_links.slim b/app/views/devise/shared/_links.slim index ccc6aa102..80220e22f 100644 --- a/app/views/devise/shared/_links.slim +++ b/app/views/devise/shared/_links.slim @@ -1,15 +1,15 @@ br br - if controller_name != 'sessions' - p= link_to "Sign in", new_session_path(resource_name) + p.govuk-body= link_to "Sign in", new_session_path(resource_name) - if devise_mapping.registerable? && controller_name != 'registrations' - p= link_to "Sign up", new_registration_path(resource_name) + p.govuk-body= link_to "Sign up", new_registration_path(resource_name) - if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' - p= link_to "Forgot your password?", new_password_path(resource_name) + p.govuk-body= link_to "Forgot your password?", new_password_path(resource_name) - if devise_mapping.confirmable? && controller_name != 'confirmations' - p= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) + p.govuk-body= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) - if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' - p= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) + p.govuk-body= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) - if devise_mapping.omniauthable? - resource_class.omniauth_providers.each do |provider| - p= link_to "Sign in with # provider.to_s.titleize ", omniauth_authorize_path(resource_name, provider) + p.govuk-body= link_to "Sign in with # provider.to_s.titleize ", omniauth_authorize_path(resource_name, provider) diff --git a/app/views/devise/unlocks/new.html.slim b/app/views/devise/unlocks/new.html.slim index 1f15055ec..a7c2fa86d 100644 --- a/app/views/devise/unlocks/new.html.slim +++ b/app/views/devise/unlocks/new.html.slim @@ -1,4 +1,4 @@ -h1.heading-large Resend unlock instructions +h1.govuk-heading-l.govuk-main-wrapper--auto-spacing Resend unlock instructions = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| = devise_error_messages! .form-group diff --git a/app/views/directorates/_form.html.slim b/app/views/directorates/_form.html.slim index 10d8b4e81..71f952c4e 100644 --- a/app/views/directorates/_form.html.slim +++ b/app/views/directorates/_form.html.slim @@ -6,7 +6,7 @@ label.form-label for="directorate_name" Name (required) = f.text_field :name, {class: 'form-control'} .form-group - p Status + p.govuk-body Status label.block-label for="directorate_deleted" = f.check_box :deleted | Inactive diff --git a/app/views/directorates/edit.html.slim b/app/views/directorates/edit.html.slim index ce49cfbd2..f696cd830 100644 --- a/app/views/directorates/edit.html.slim +++ b/app/views/directorates/edit.html.slim @@ -1,3 +1,3 @@ -h1 Edit directorate +h1.govuk-heading-l Edit directorate = render 'form' = link_to 'Return to directorate list', directorates_path, {class: 'admin-link'} diff --git a/app/views/directorates/index.html.slim b/app/views/directorates/index.html.slim index e65c8e698..8710c4c2b 100644 --- a/app/views/directorates/index.html.slim +++ b/app/views/directorates/index.html.slim @@ -1,6 +1,6 @@ -h1 Directorates +h1.govuk-heading-l Directorates #admin-directorate-list - .row + div ul#admin-button-bar li= link_to 'Add directorate', new_directorate_path, {class: 'button-secondary'} table.table aria-label="directorates" diff --git a/app/views/directorates/new.html.slim b/app/views/directorates/new.html.slim index 372748895..7d8b31c47 100644 --- a/app/views/directorates/new.html.slim +++ b/app/views/directorates/new.html.slim @@ -1,3 +1,3 @@ -h1 New directorate +h1.govuk-heading-l New directorate = render 'form' = link_to 'Return to directorate list', directorates_path, {class: 'admin-link'} diff --git a/app/views/directorates/show.html.slim b/app/views/directorates/show.html.slim index c602a721e..304f6f08d 100644 --- a/app/views/directorates/show.html.slim +++ b/app/views/directorates/show.html.slim @@ -1,12 +1,12 @@ #admin-directorate-details - h1 Directorate details + h1.govuk-heading-l Directorate details = render partial: "shared/flash_messages", flash: flash - h2 Name - p= @directorate.name - h2 Status + h2.govuk-heading-m Name + p.govuk-body= @directorate.name + h2.govuk-heading-m Status = render partial: 'shared/status', object: @directorate - p + p.govuk-body = link_to 'Edit', edit_directorate_path(@directorate), {class: 'admin-link'} - span.visually-hidden + span.govuk-visually-hidden = ' | ' = link_to 'Return to directorate list', directorates_path, {class: 'admin-link'} diff --git a/app/views/divisions/_form.html.slim b/app/views/divisions/_form.html.slim index b0ec83b6a..0e13d143b 100644 --- a/app/views/divisions/_form.html.slim +++ b/app/views/divisions/_form.html.slim @@ -9,7 +9,7 @@ label.form-label for="division_directorate_id" Directorate (required) = f.collection_select(:directorate_id, @directorates, :id, :name, :include_blank => "Please select") .form-group - p Status + p.govuk-body Status label.block-label for="division_deleted" = f.check_box :deleted | Inactive diff --git a/app/views/divisions/edit.html.slim b/app/views/divisions/edit.html.slim index 1818b8aa3..033fd91ee 100644 --- a/app/views/divisions/edit.html.slim +++ b/app/views/divisions/edit.html.slim @@ -1,3 +1,3 @@ -h1 Edit division +h1.govuk-heading-l Edit division = render 'form' = link_to 'Return to Divisions list', divisions_path, {class: 'admin-link'} diff --git a/app/views/divisions/index.html.slim b/app/views/divisions/index.html.slim index ac26af987..0e45c8895 100644 --- a/app/views/divisions/index.html.slim +++ b/app/views/divisions/index.html.slim @@ -1,6 +1,6 @@ -h1 Divisions +h1.govuk-heading-l Divisions #admin-division-list - .row + div ul#admin-button-bar li= link_to ('Add division'), new_division_path, {class: 'button-secondary'} table.table aria-label="divisions" diff --git a/app/views/divisions/new.html.slim b/app/views/divisions/new.html.slim index 0721a1e58..979c867fd 100644 --- a/app/views/divisions/new.html.slim +++ b/app/views/divisions/new.html.slim @@ -1,3 +1,3 @@ -h1 New Division +h1.govuk-heading-l New Division = render 'form' = link_to 'Return to divisions', divisions_path, {class: 'admin-link'} diff --git a/app/views/divisions/show.html.slim b/app/views/divisions/show.html.slim index ea0492160..d95a5c846 100644 --- a/app/views/divisions/show.html.slim +++ b/app/views/divisions/show.html.slim @@ -1,14 +1,14 @@ #admin-division-details - h1 Division details + h1.govuk-heading-l Division details = render partial: "shared/flash_messages", flash: flash - h2 Name - p= @division.name - h2 Directorate - p= @division.directorate.name - h2 Status + h2.govuk-heading-m Name + p.govuk-body= @division.name + h2.govuk-heading-m Directorate + p.govuk-body= @division.directorate.name + h2.govuk-heading-m Status = render partial: 'shared/status', object: @division - p + p.govuk-body = link_to 'Edit', edit_division_path(@division), {class: 'admin-link'} - span.visually-hidden + span.govuk-visually-hidden = ' | ' = link_to 'Return to divisions list', divisions_path, {class: 'admin-link'} diff --git a/app/views/early_bird_dashboard/index.html.slim b/app/views/early_bird_dashboard/index.html.slim index 5298b38bc..f4869b531 100644 --- a/app/views/early_bird_dashboard/index.html.slim +++ b/app/views/early_bird_dashboard/index.html.slim @@ -1,12 +1,12 @@ -#preview +#preview.content-inner #main - h1 New parliamentary questions to be allocated today (#{@now}) + h1.govuk-heading-l New parliamentary questions to be allocated today (#{@now}) = render partial: 'shared/flash_messages' #sidebar.col-md-3.col-md-push-9 #filters.js-stick-at-top-when-scrolling - h2 Filter + h2.govuk-heading-m Filter #question-type.filter-box fieldset legend @@ -44,7 +44,7 @@ ul.questions-list - @questions.each do |question| li.question - h2 + h2.govuk-heading-m = link_to(question.uin, {controller: 'pqs', action: 'show', id: question.uin}, :class => "question-uin") - if question.question_type == 'NamedDay' span.question-type | Named Day @@ -54,24 +54,24 @@ span.question-type | Ordinary span.pq-question #{question.question} - unless question.member_name.empty? - h3 Asked by: + h3.govuk-heading-s Asked by: span.asked-by #{question.member_name} br - h3 Constituency: + h3.govuk-heading-s Constituency: - if question.member_constituency.nil? | N/A br - else span.constituency #{question.member_constituency} - if question.proposed? - h3 Deputy Director(s) proposed to answer: + h3.govuk-heading-s Deputy Director(s) proposed to answer: - if question.commissioned? - h3 Question assigned to Deputy Director(s): + h3.govuk-heading-s Question assigned to Deputy Director(s): - p.action_officers-list + p.govuk-body.action_officers-list = question.action_officers.not_rejected.pluck(:name).join(', ') - p.propose-links + p.govuk-body.propose-links = link_to('Email PQ team about this question', "mailto:#{Settings.mail_reply_to}?subject=Question #{question.uin}") - unless question.commissioned? = ' | ' diff --git a/app/views/early_bird_members/_form.html.slim b/app/views/early_bird_members/_form.html.slim index 16ae309f9..1175d78ad 100644 --- a/app/views/early_bird_members/_form.html.slim +++ b/app/views/early_bird_members/_form.html.slim @@ -9,7 +9,7 @@ .form-group label.form-label for="early_bird_member_email" Email (required) = f.email_field :email , {class: 'form-control'} - p Status + p.govuk-body Status .form-group label.block-label for="early_bird_member_deleted" = f.check_box :deleted diff --git a/app/views/early_bird_members/edit.html.slim b/app/views/early_bird_members/edit.html.slim index 998ad0165..6b1e9ed63 100644 --- a/app/views/early_bird_members/edit.html.slim +++ b/app/views/early_bird_members/edit.html.slim @@ -1,3 +1,3 @@ -h1 Edit early bird member +h1.govuk-heading-l Edit early bird member = render 'form' = link_to 'Return to early bird member list', early_bird_members_path, {class: 'admin-link'} diff --git a/app/views/early_bird_members/index.html.slim b/app/views/early_bird_members/index.html.slim index dd71d9acf..ae33837bd 100644 --- a/app/views/early_bird_members/index.html.slim +++ b/app/views/early_bird_members/index.html.slim @@ -1,9 +1,9 @@ -h1 Early bird members +h1.govuk-heading-l Early bird members = render partial: "shared/flash_messages", flash: flash #admin-wm-list - .row + div ul#admin-button-bar - li= link_to ('Send early bird info'), {controller: 'early_bird_send_emails', action: 'send_emails'}, {class: 'button', :onclick=> "ga('send', 'event', 'settings', 'earlybird', 'send early bird info');" } + li= link_to ('Send early bird info'), {controller: 'early_bird_send_emails', action: 'send_emails'}, {class: 'button' } li= link_to ('Add early bird member'), new_early_bird_member_path, {class: 'button-secondary'} li= link_to ('Early bird preview'), {controller: 'early_bird_dashboard', action: 'preview'}, {class: 'button-secondary'} table.table aria-label="early bird members" diff --git a/app/views/early_bird_members/new.html.slim b/app/views/early_bird_members/new.html.slim index 39b20bb3e..0aff9f551 100644 --- a/app/views/early_bird_members/new.html.slim +++ b/app/views/early_bird_members/new.html.slim @@ -1,3 +1,3 @@ -h1 Add early bird member +h1.govuk-heading-l Add early bird member = render 'form' = link_to 'Return to early bird member list', early_bird_members_path, {class: 'admin-link'} diff --git a/app/views/early_bird_members/show.html.slim b/app/views/early_bird_members/show.html.slim index 5059b0233..849ea43d9 100644 --- a/app/views/early_bird_members/show.html.slim +++ b/app/views/early_bird_members/show.html.slim @@ -1,14 +1,14 @@ -h1 Early bird member details +h1.govuk-heading-l Early bird member details #admin-wl-details = render partial: "shared/flash_messages", flash: flash - h2 Name - p= @early_bird_member.name - h2 Email - p= @early_bird_member.email - h2 Status + h2.govuk-heading-m Name + p.govuk-body= @early_bird_member.name + h2.govuk-heading-m Email + p.govuk-body= @early_bird_member.email + h2.govuk-heading-m Status = render partial: 'shared/status', object: @early_bird_member - p + p.govuk-body = link_to 'Edit', edit_early_bird_member_path(@early_bird_member), {class: 'admin-link'} - span.visually-hidden + span.govuk-visually-hidden = ' | ' - = link_to 'Return to early bird member list', early_bird_members_path, {class: 'admin-link'} \ No newline at end of file + = link_to 'Return to early bird member list', early_bird_members_path, {class: 'admin-link'} diff --git a/app/views/early_bird_organiser/new.html.slim b/app/views/early_bird_organiser/new.html.slim index 663529eca..5f1b4ae15 100644 --- a/app/views/early_bird_organiser/new.html.slim +++ b/app/views/early_bird_organiser/new.html.slim @@ -1,5 +1,5 @@ - if flash[:message] .pq-msg-notice = flash[:message] -h1 Suspend early bird email +h1.govuk-heading-l Suspend early bird email = render 'form' diff --git a/app/views/errors/internal_error.html.slim b/app/views/errors/internal_error.html.slim new file mode 100644 index 000000000..22b8c1d57 --- /dev/null +++ b/app/views/errors/internal_error.html.slim @@ -0,0 +1,8 @@ +.govuk-width-container + main#main-content.govuk-main-wrapper[role="main"] + h1.govuk-heading-xl.govuk-main-wrapper--auto-spacing + | Sorry, there is a problem with the service + p.govuk-body + = "Please contact " + = link_to("pqsupport@digital.justice.gov.uk", "mailto:pqsupport@digital.justice.gov.uk") + = " with the details to report this issue." diff --git a/app/views/errors/not_found.html.slim b/app/views/errors/not_found.html.slim new file mode 100644 index 000000000..d287b2180 --- /dev/null +++ b/app/views/errors/not_found.html.slim @@ -0,0 +1,12 @@ +.govuk-width-container + main#main-content.govuk-main-wrapper[role="main"] + h1.govuk-heading-xl.govuk-main-wrapper--auto-spacing + | This page cannot be found + p.govuk-body + | If you typed the web address, check it is correct. + p.govuk-body + | If you pasted the web address, check you copied the entire address. + p.govuk-body + = "If the web address is correct or you selected a link or button, please contact " + = link_to("pqsupport@digital.justice.gov.uk", "mailto:pqsupport@digital.justice.gov.uk") + = " with the details to report this issue." diff --git a/app/views/export/index.html.slim b/app/views/export/index.html.slim index 23c687d3d..713431f40 100644 --- a/app/views/export/index.html.slim +++ b/app/views/export/index.html.slim @@ -1,5 +1,5 @@ #csv-export - h1 + h1.govuk-heading-l ' Export PQs to abbr title="Comma Separated Values" CSV = render partial: 'shared/flash_messages' @@ -17,4 +17,4 @@ input#date_to.form-control name="date_to" type="text" value=Date.today.to_formatted_s(:date) / span.fa.fa-calendar title="select a date" .form-group - = submit_tag 'Download CSV' , class: 'button' , :onclick=> "ga('send', 'event', 'tasks', 'download', 'pq csv');" + = submit_tag 'Download CSV' , class: 'button' diff --git a/app/views/layouts/_banner.html.slim b/app/views/layouts/_banner.html.slim deleted file mode 100644 index 2bd0f5257..000000000 --- a/app/views/layouts/_banner.html.slim +++ /dev/null @@ -1,5 +0,0 @@ -.alert-banner - svg.alert-banner__icon fill="currentColor" role="presentation" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 25" height="40" width="40" - path d="M13.7,18.5h-2.4v-2.4h2.4V18.5z M12.5,13.7c-0.7,0-1.2-0.5-1.2-1.2V7.7c0-0.7,0.5-1.2,1.2-1.2s1.2,0.5,1.2,1.2v4.8 -C13.7,13.2,13.2,13.7,12.5,13.7z M12.5,0.5c-6.6,0-12,5.4-12,12s5.4,12,12,12s12-5.4,12-12S19.1,0.5,12.5,0.5z" - p.alert-banner__info This service will be down for essential maintenance between 12pm and 2pm on Monday 15th February. You will not have access during this period. Please contact pqsupport@digital.justice.gov.uk if you have any queries. Apologies for the inconvenience. diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index bf6234e31..c69b7ad10 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -1,58 +1,21 @@ - content_for :page_title do = @page_title -- content_for :head do - - = stylesheet_link_tag('font-awesome.css', media: 'all') - = stylesheet_link_tag('select2.css', media: 'all') - = stylesheet_link_tag('select2-bootstrap.css', media: 'all') - = stylesheet_link_tag('vendor/jquery.datetimepicker.css', media: 'all') - = stylesheet_link_tag('vendor/pq-select2.css', media: 'all') - = stylesheet_link_tag('/assets/govuk-frontend.min.css', media: 'all') - = stylesheet_link_tag('application.css', media: 'all') - - javascript: - (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-5RRTCM3'); - -- content_for :body_start do - - content_for :body_start do - noscript - iframe src="https://www.googletagmanager.com/ns.html?id=GTM-5RRTCM3" height="0" width="0" style="display:none;visibility:hidden" - - content_for :content do - main#contentOuter(role="main") - .phase-banner - / p.phase-tag = config_item(:phase).upcase )(this line is kept incase the moj elements change and this line needs to be reinstated!) - Help make this service better - your - - if ! user_signed_in? - feedback - - else - feedback - | will help us to improve it. - - - - if HostEnv.is_staging? - .staging_banner - | This is not the live Parliamentary Questions Tracker environment - - = render partial: "shared/navigation" - .container - #content.content-inner - = yield - -- content_for :footer_support_links do - h4.govuk-visually-hidden - | Support links - ul.govuk-footer__inline-list - li.govuk-footer__inline-list-item - = link_to "Accessibility", "/accessibility", class: "govuk-footer__link", aria: { label: "click to visit accessibility statement"} - li.govuk-footer__inline-list-item - = link_to "Contact Parliamentary Branch", "mailto:pqs@justice.gov.uk?Subject=PQ%20Tracker", class: "govuk-footer__link", aria: { label: "click to email parliamentary branch"} - -- content_for :body_end - - - = javascript_include_tag('application.js') - + .phase-banner + / p.phase-tag = config_item(:phase).upcase )(this line is kept incase the moj elements change and this line needs to be reinstated!) + span Help make this service better - your + - if ! user_signed_in? + feedback + - else + feedback + | will help us to improve it. + + - if HostEnv.is_staging? + .staging_banner + | This is not the live Parliamentary Questions Tracker environment + + = render partial: "shared/navigation" + div.container= yield = render template: "layouts/govuk_template" diff --git a/app/views/layouts/govuk_template.html.erb b/app/views/layouts/govuk_template.html.erb index 65f2d9fb8..8b52a28e5 100644 --- a/app/views/layouts/govuk_template.html.erb +++ b/app/views/layouts/govuk_template.html.erb @@ -1,131 +1,55 @@ -<%= yield :top_of_page %> - -"> +