diff --git a/.travis.yml b/.travis.yml index 62fea3bfb..9ea2b6da2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ before_install: install: - npm install script: -- "./node_modules/grunt-cli/bin/grunt test" +- npm test notifications: email: false before_deploy: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7e19f27c3..94bec39e1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,6 +5,7 @@ guidelines: ## Indentation and whitespace +Your JavaScript code should pass [linting](https://github.com/alphagov/govuk_prototype_kit/blob/0d9835b7ad8d7bc06aa288204eb3ce9a07116be2/docs/linting.md). 2-space, soft-tabs only. No trailing whitespace. ## Versioning diff --git a/Gruntfile.js b/Gruntfile.js index b9c436544..cd6057c2c 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,5 +1,4 @@ module.exports = function (grunt) { - grunt.initConfig({ // Builds Sass @@ -14,7 +13,7 @@ module.exports = function (grunt) { 'public/stylesheets/elements-page-ie6.css': 'public/sass/elements-page-ie6.scss', 'public/stylesheets/elements-page-ie7.css': 'public/sass/elements-page-ie7.scss', 'public/stylesheets/elements-page-ie8.css': 'public/sass/elements-page-ie8.scss', - 'public/stylesheets/prism.css': 'public/sass/prism.scss', + 'public/stylesheets/prism.css': 'public/sass/prism.scss' }, options: { includePaths: [ @@ -29,7 +28,7 @@ module.exports = function (grunt) { // Empty encoded snippets folder clean: { - contents: ['app/views/snippets/encoded/*'], + contents: ['app/views/snippets/encoded/*'] }, // Copies templates and assets from external modules and dirs @@ -59,11 +58,11 @@ module.exports = function (grunt) { govuk_template_jinja: { files: [{ expand: true, - cwd: 'govuk_modules/govuk_template/views/layouts/', - src: '**', - dest: 'lib/' - }] - }, + cwd: 'govuk_modules/govuk_template/views/layouts/', + src: '**', + dest: 'lib/' + }] + } }, // Encode HTML snippets @@ -71,7 +70,7 @@ module.exports = function (grunt) { files: { src: ['app/views/snippets/*.html'], dest: 'app/views/snippets/encoded/' - }, + } }, // workaround for libsass @@ -124,9 +123,9 @@ module.exports = function (grunt) { } } - }); + }) - [ + ;[ 'grunt-contrib-clean', 'grunt-contrib-copy', 'grunt-contrib-watch', @@ -136,8 +135,8 @@ module.exports = function (grunt) { 'grunt-shell', 'grunt-htmlentities' ].forEach(function (task) { - grunt.loadNpmTasks(task); - }); + grunt.loadNpmTasks(task) + }) grunt.registerTask('default', [ 'clean', @@ -145,7 +144,7 @@ module.exports = function (grunt) { 'encode_snippets', 'sass', 'concurrent:target' - ]); + ]) grunt.registerTask( 'test_default', @@ -154,45 +153,45 @@ module.exports = function (grunt) { 'copy', 'sass' ] - ); + ) grunt.registerTask( 'encode_snippets', 'Encode HTML snippets', - function() { - grunt.task.run('htmlentities'); + function () { + grunt.task.run('htmlentities') } - ); + ) grunt.registerTask( 'lint', 'Use govuk-scss-lint to lint the sass files', - function() { - grunt.task.run('shell', 'lint_message'); + function () { + grunt.task.run('shell', 'lint_message') } - ); + ) grunt.registerTask( 'lint_message', 'Output a message once linting is complete', - function() { - grunt.log.write("scss lint is complete, without errors."); + function () { + grunt.log.write('scss lint is complete, without errors.') } - ); + ) grunt.registerTask( 'test', 'Lint the Sass files, then check the app runs', - function() { - grunt.task.run('lint', 'test_default', 'test_message'); + function () { + grunt.task.run('lint', 'test_default', 'test_message') } - ); + ) grunt.registerTask( 'test_message', 'Output a message once the tests are complete', - function() { - grunt.log.write("scss lint is complete and the app runs, without errors."); + function () { + grunt.log.write('scss lint is complete and the app runs, without errors.') } - ); -}; + ) +} diff --git a/README.md b/README.md index fd47374c8..7416c284d 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,22 @@ Run the app Go to [localhost:3000](http://localhost:3000) in your browser. +## Linting + +### GOV.UK lint +[GOV.UK elements uses govuk-lint](https://github.com/alphagov/govuk-lint#sass), which uses [scss-lint](https://github.com/brigade/scss-lint) as its scss linter. + +### Standard JavaScript +GOV.UK elements uses [standardjs](http://standardjs.com/), an opinionated JavaScript linter. All JavaScript files follow its conventions. + +Both linters run on CI to ensure that new pull requests are in line with them. + +## Linting + +To check the whole codebase, run: + + npm test + ## Running Wraith to compare changes GOV.UK elements uses Wraith so that regressions can be easily spotted. diff --git a/app/config.js b/app/config.js index 4f41d2ea2..5ad868b04 100644 --- a/app/config.js +++ b/app/config.js @@ -7,4 +7,4 @@ module.exports = { // Cookie warning cookieText: 'GOV.UK uses cookies to make the site simpler. Find out more about cookies' -}; +} diff --git a/app/routes.js b/app/routes.js index ceef8ece1..3f0bb0ead 100755 --- a/app/routes.js +++ b/app/routes.js @@ -1,221 +1,219 @@ module.exports = { - bind : function (app, asset_path) { - + bind: function (app, assetPath) { app.get('/', function (req, res) { - res.render('index', {'asset_path' : asset_path }); - }); + res.render('index', { 'asset_path': assetPath }) + }) // Redirect snippets page to the index page app.get('/snippets', function (req, res) { - res.redirect('/'); - }); + res.redirect('/') + }) app.get('/style-guide', function (req, res) { - var page_name = "Front-end style guide"; - res.render('guide_style_guide', { 'page_name' : page_name }); - }); + var pageName = 'Front-end style guide' + res.render('guide_style_guide', { 'page_name': pageName }) + }) // Layout app.get('/layout', function (req, res) { - var page_name = "Layout"; - res.render('guide_layout', { 'page_name' : page_name }); - }); + var pageName = 'Layout' + res.render('guide_layout', { 'page_name': pageName }) + }) // Example page: Grid layout app.get('/layout/example-grid-layout', function (req, res) { - var section = "layout"; - var section_name = "Layout"; - var page_name = "Example: Grid layout"; - res.render('examples/example_grid_layout', { 'section': section, 'section_name' : section_name, 'page_name' : page_name }); - }); + var section = 'layout' + var sectionName = 'Layout' + var pageName = 'Example: Grid layout' + res.render('examples/example_grid_layout', { 'section': section, 'section_name': sectionName, 'page_name': pageName }) + }) // Redirect examples from /examples/ to /section/example-name-of-example app.get('/examples/grid-layout', function (req, res) { - res.redirect('/layout/example-grid-layout'); - }); + res.redirect('/layout/example-grid-layout') + }) // Typography app.get('/typography', function (req, res) { - var page_name = "Typography"; - res.render('guide_typography', { 'page_name' : page_name }); - }); + var pageName = 'Typography' + res.render('guide_typography', { 'page_name': pageName }) + }) // Example page: Typography app.get('/typography/example-typography', function (req, res) { - var section = "typography"; - var section_name = "Typography"; - var page_name = "Example: Typography"; - res.render('examples/example_typography', { 'section': section, 'section_name' : section_name, 'page_name' : page_name }); - }); + var section = 'typography' + var sectionName = 'Typography' + var pageName = 'Example: Typography' + res.render('examples/example_typography', { 'section': section, 'section_name': sectionName, 'page_name': pageName }) + }) // Redirect examples from /examples/ to /section/example-name-of-example app.get('/examples/typography', function (req, res) { - res.redirect('/typography/example-typography'); - }); + res.redirect('/typography/example-typography') + }) // Example page: Progressive disclosure app.get('/typography/example-details-summary', function (req, res) { - var section = "typography"; - var section_name = "Typography"; - var page_name = "Example: Details summary"; - res.render('examples/example_details_summary', { 'section': section, 'section_name' : section_name, 'page_name' : page_name }); - }); + var section = 'typography' + var sectionName = 'Typography' + var pageName = 'Example: Details summary' + res.render('examples/example_details_summary', { 'section': section, 'section_name': sectionName, 'page_name': pageName }) + }) // Redirect examples from /patterns/ to /section/example-name-of-example app.get('/patterns/details-summary', function (req, res) { - res.redirect('/typography/example-details-summary'); - }); + res.redirect('/typography/example-details-summary') + }) // Colour app.get('/colour', function (req, res) { - var page_name = "Colour"; - res.render('guide_colour', { 'page_name' : page_name }); - }); + var pageName = 'Colour' + res.render('guide_colour', { 'page_name': pageName }) + }) // Icons and images app.get('/icons-images', function (req, res) { - var page_name = "Icons and images"; - res.render('guide_icons_images', { 'page_name' : page_name }); - }); + var pageName = 'Icons and images' + res.render('guide_icons_images', { 'page_name': pageName }) + }) // Example page: Icons app.get('/icons-images/example-icons', function (req, res) { - var section = "icons-images"; - var section_name = "Icons and images"; - var page_name = "Example: Icons"; - res.render('examples/example_icons', { 'section': section, 'section_name' : section_name, 'page_name' : page_name }); - }); + var section = 'icons-images' + var sectionName = 'Icons and images' + var pageName = 'Example: Icons' + res.render('examples/example_icons', { 'section': section, 'section_name': sectionName, 'page_name': pageName }) + }) // Data app.get('/data', function (req, res) { - var page_name = "Data"; - res.render('guide_data', { 'page_name' : page_name }); - }); + var pageName = 'Data' + res.render('guide_data', { 'page_name': pageName }) + }) // Buttons app.get('/buttons', function (req, res) { - var page_name = "Buttons"; - res.render('guide_buttons', { 'page_name' : page_name }); - }); + var pageName = 'Buttons' + res.render('guide_buttons', { 'page_name': pageName }) + }) // Forms app.get('/form-elements', function (req, res) { - var page_name = "Form elements"; - res.render('guide_form_elements', { 'page_name' : page_name }); - }); + var pageName = 'Form elements' + res.render('guide_form_elements', { 'page_name': pageName }) + }) // Example page: Basic form app.get('/form-elements/example-forms', function (req, res) { - var section = "form-elements"; - var section_name = "Form elements"; - var page_name = "Example: Form"; - res.render('examples/example_forms', { 'section': section, 'section_name' : section_name, 'page_name' : page_name }); - }); + var section = 'form-elements' + var sectionName = 'Form elements' + var pageName = 'Example: Form' + res.render('examples/example_forms', { 'section': section, 'section_name': sectionName, 'page_name': pageName }) + }) // Redirect examples from /examples/ to /section/example-name-of-example app.get('/examples/forms', function (req, res) { - res.redirect('/form-elements/example-forms'); - }); + res.redirect('/form-elements/example-forms') + }) // Example page: Date pattern app.get('/form-elements/example-date', function (req, res) { - var section = "form-elements"; - var section_name = "Form elements"; - var page_name = "Example: Date"; - res.render('examples/example_date', { 'section': section, 'section_name' : section_name, 'page_name' : page_name }); - }); + var section = 'form-elements' + var sectionName = 'Form elements' + var pageName = 'Example: Date' + res.render('examples/example_date', { 'section': section, 'section_name': sectionName, 'page_name': pageName }) + }) // Redirect examples from /patterns/ to /section/example-name-of-example app.get('/patterns/date', function (req, res) { - res.redirect('/form-elements/example-date'); - }); + res.redirect('/form-elements/example-date') + }) // Example page: Radio buttons and checkboxes app.get('/form-elements/example-radios-checkboxes', function (req, res) { - var section = "form-elements"; - var section_name = "Form elements"; - var page_name = "Example: Radio buttons and checkboxes"; - res.render('examples/example_radios_checkboxes', { 'section': section, 'section_name' : section_name, 'page_name' : page_name }); - }); + var section = 'form-elements' + var sectionName = 'Form elements' + var pageName = 'Example: Radio buttons and checkboxes' + res.render('examples/example_radios_checkboxes', { 'section': section, 'section_name': sectionName, 'page_name': pageName }) + }) // Redirect examples from /patterns/ to /section/example-name-of-example app.get('/patterns/radios-checkboxes', function (req, res) { - res.redirect('/form-elements/example-radios-checkboxes'); - }); + res.redirect('/form-elements/example-radios-checkboxes') + }) // Example page: Form elements app.get('/form-elements/example-form-elements', function (req, res) { - var section = "form-elements"; - var section_name = "Form elements"; - var page_name = "Example: Form elements"; - res.render('examples/example_form_elements', { 'section': section, 'section_name' : section_name, 'page_name' : page_name }); - }); + var section = 'form-elements' + var sectionName = 'Form elements' + var pageName = 'Example: Form elements' + res.render('examples/example_form_elements', { 'section': section, 'section_name': sectionName, 'page_name': pageName }) + }) // Errors and validation app.get('/errors', function (req, res) { - var page_name = "Errors and validation"; - res.render('guide_errors', { 'page_name' : page_name }); - }); + var pageName = 'Errors and validation' + res.render('guide_errors', { 'page_name': pageName }) + }) // Example page: Form validation app.get('/errors/example-form-validation-single-question-radio', function (req, res) { - var section = "errors"; - var section_name = "Errors and validation"; - var page_name = "Example: Form validation - single question"; - res.render('examples/example_form_validation_single_question_radio', { 'section': section, 'section_name' : section_name, 'page_name' : page_name }); - }); + var section = 'errors' + var sectionName = 'Errors and validation' + var pageName = 'Example: Form validation - single question' + res.render('examples/example_form_validation_single_question_radio', { 'section': section, 'section_name': sectionName, 'page_name': pageName }) + }) app.post('/errors/example-form-validation-single-question-radio', function (req, res) { - var section = "errors"; - var section_name = "Errors and validation"; - var page_name = "Example: Form validation - single question"; - var personal_details = req.body.personal_details; - var error = false; - if (!personal_details) { - error = true; + var section = 'errors' + var sectionName = 'Errors and validation' + var pageName = 'Example: Form validation - single question' + var personalDetails = req.body.personalDetails + var error = false + if (!personalDetails) { + error = true } else { - error = false; + error = false } - res.render('examples/example_form_validation_single_question_radio', { 'section': section, 'section_name' : section_name, 'page_name' : page_name, 'personal_details': personal_details, 'error': error}); - }); + res.render('examples/example_form_validation_single_question_radio', { 'section': section, 'section_name': sectionName, 'page_name': pageName, 'personal_details': personalDetails, 'error': error }) + }) // Redirect examples from /examples/ to /section/example-name-of-example app.get('/examples/form-validation-single-question-radio', function (req, res) { - res.redirect('/errors/example-form-validation-single-question-radio'); - }); + res.redirect('/errors/example-form-validation-single-question-radio') + }) app.get('/errors/example-form-validation-multiple-questions', function (req, res) { - var section = "errors"; - var section_name = "Errors and validation"; - var page_name = "Example: Form validation - multiple questions"; - res.render('examples/example_form_validation_multiple_questions', { 'section': section, 'section_name' : section_name, 'page_name' : page_name }); - }); + var section = 'errors' + var sectionName = 'Errors and validation' + var pageName = 'Example: Form validation - multiple questions' + res.render('examples/example_form_validation_multiple_questions', { 'section': section, 'section_name': sectionName, 'page_name': pageName }) + }) app.post('/errors/example-form-validation-multiple-questions', function (req, res) { - var section = "errors"; - var section_name = "Errors and validation"; - var page_name = "Example: Form validation - multiple questions"; - var fullName = req.body.fullName; - var niNo = req.body.niNo; - var error = false; + var section = 'errors' + var sectionName = 'Errors and validation' + var pageName = 'Example: Form validation - multiple questions' + var fullName = req.body.fullName + var niNo = req.body.niNo + var error = false if (!fullName || !niNo) { - error = true; + error = true } else { - error = false; + error = false } - res.render('examples/example_form_validation_multiple_questions', { 'section': section, 'section_name' : section_name, 'page_name' : page_name, 'fullName': fullName, 'niNo': niNo, 'error': error}); - }); + res.render('examples/example_form_validation_multiple_questions', { 'section': section, 'section_name': sectionName, 'page_name': pageName, 'fullName': fullName, 'niNo': niNo, 'error': error }) + }) // Redirect examples from /examples/ to /section/example-name-of-example app.get('/examples/form-validation-multiple-questions', function (req, res) { - res.redirect('/errors/example-form-validation-multiple-questions'); - }); + res.redirect('/errors/example-form-validation-multiple-questions') + }) // Alpha and beta banners app.get('/alpha-beta-banners', function (req, res) { - var page_name = "Alpha and beta banners"; - res.render('guide_alpha_beta', { 'page_name' : page_name }); - }); - + var pageName = 'Alpha and beta banners' + res.render('guide_alpha_beta', { 'page_name': pageName }) + }) } -}; +} diff --git a/app/views/includes/scripts.html b/app/views/includes/scripts.html index 434f496f9..da56e7a26 100644 --- a/app/views/includes/scripts.html +++ b/app/views/includes/scripts.html @@ -6,5 +6,5 @@ - + diff --git a/app/views/snippets/form_error_radio.html b/app/views/snippets/form_error_radio.html index 72b02affc..f702987d8 100644 --- a/app/views/snippets/form_error_radio.html +++ b/app/views/snippets/form_error_radio.html @@ -42,12 +42,12 @@

diff --git a/app/views/snippets/form_error_radio_show_errors.html b/app/views/snippets/form_error_radio_show_errors.html index a9c7453de..7054488e5 100644 --- a/app/views/snippets/form_error_radio_show_errors.html +++ b/app/views/snippets/form_error_radio_show_errors.html @@ -38,12 +38,12 @@

diff --git a/package.json b/package.json index 32a4871b4..42391ca10 100644 --- a/package.json +++ b/package.json @@ -28,10 +28,18 @@ "grunt-sass": "^1.0.0", "grunt-shell": "^1.3.0", "node-sass": "^3.2.0", - "readdir": "0.0.6" + "readdir": "0.0.6", + "standard": "^7.1.2" }, - "scripts": { - "test": "./node_modules/grunt-cli/bin/grunt test" + "scripts":{ + "test": "standard && ./node_modules/grunt-cli/bin/grunt test" + }, + "standard": { + "ignore": [ + "lib", + "public/javascripts/vendor/", + "tests" + ] }, "bugs": { "url": "https://github.com/alphagov/govuk_elements/issues" diff --git a/public/javascripts/application.js b/public/javascripts/application.js index f4540ac75..d3b7d86f4 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -1,161 +1,142 @@ -function ShowHideContent() { - var self = this; +/* global $ */ +/* global jQuery */ +/* global GOVUK */ +function ShowHideContent () { + var self = this - self.escapeElementName = function(str) { - result = str.replace('[', '\\[').replace(']', '\\]') - return(result); - }; + self.escapeElementName = function (str) { + var result = str.replace('[', '\\[').replace(']', '\\]') + return (result) + } self.showHideRadioToggledContent = function () { $(".block-label input[type='radio']").each(function () { + var $radio = $(this) + var $radioGroupName = $radio.attr('name') + var $radioLabel = $radio.parent('label') - var $radio = $(this); - var $radioGroupName = $radio.attr('name'); - var $radioLabel = $radio.parent('label'); - - var dataTarget = $radioLabel.attr('data-target'); + var dataTarget = $radioLabel.attr('data-target') // Add ARIA attributes // If the data-target attribute is defined if (dataTarget) { - // Set aria-controls - $radio.attr('aria-controls', dataTarget); + $radio.attr('aria-controls', dataTarget) $radio.on('click', function () { - // Select radio buttons in the same group - $radio.closest('form').find(".block-label input[name=" + self.escapeElementName($radioGroupName) + "]").each(function () { - var $this = $(this); + $radio.closest('form').find('.block-label input[name=' + self.escapeElementName($radioGroupName) + ']').each(function () { + var $this = $(this) - var groupDataTarget = $this.parent('label').attr('data-target'); - var $groupDataTarget = $('#' + groupDataTarget); + var groupDataTarget = $this.parent('label').attr('data-target') + var $groupDataTarget = $('#' + groupDataTarget) // Hide toggled content - $groupDataTarget.addClass('js-hidden'); + $groupDataTarget.addClass('js-hidden') // Set aria-expanded and aria-hidden for hidden content - $this.attr('aria-expanded', 'false'); - $groupDataTarget.attr('aria-hidden', 'true'); - }); + $this.attr('aria-expanded', 'false') + $groupDataTarget.attr('aria-hidden', 'true') + }) - var $dataTarget = $('#' + dataTarget); - $dataTarget.removeClass('js-hidden'); + var $dataTarget = $('#' + dataTarget) + $dataTarget.removeClass('js-hidden') // Set aria-expanded and aria-hidden for clicked radio - $radio.attr('aria-expanded', 'true'); - $dataTarget.attr('aria-hidden', 'false'); - - }); - + $radio.attr('aria-expanded', 'true') + $dataTarget.attr('aria-hidden', 'false') + }) } else { // If the data-target attribute is undefined for a radio button, // hide visible data-target content for radio buttons in the same group $radio.on('click', function () { - // Select radio buttons in the same group - $(".block-label input[name=" + self.escapeElementName($radioGroupName) + "]").each(function () { - - var groupDataTarget = $(this).parent('label').attr('data-target'); - var $groupDataTarget = $('#' + groupDataTarget); + $('.block-label input[name=' + self.escapeElementName($radioGroupName) + ']').each(function () { + var groupDataTarget = $(this).parent('label').attr('data-target') + var $groupDataTarget = $('#' + groupDataTarget) // Hide toggled content - $groupDataTarget.addClass('js-hidden'); + $groupDataTarget.addClass('js-hidden') // Set aria-expanded and aria-hidden for hidden content - $(this).attr('aria-expanded', 'false'); - $groupDataTarget.attr('aria-hidden', 'true'); - }); - - }); + $(this).attr('aria-expanded', 'false') + $groupDataTarget.attr('aria-hidden', 'true') + }) + }) } - - }); + }) } self.showHideCheckboxToggledContent = function () { + $(".block-label input[type='checkbox']").each(function () { + var $checkbox = $(this) + var $checkboxLabel = $(this).parent() - $(".block-label input[type='checkbox']").each(function() { - - var $checkbox = $(this); - var $checkboxLabel = $(this).parent(); - - var $dataTarget = $checkboxLabel.attr('data-target'); + var $dataTarget = $checkboxLabel.attr('data-target') // Add ARIA attributes // If the data-target attribute is defined if (typeof $dataTarget !== 'undefined' && $dataTarget !== false) { - // Set aria-controls - $checkbox.attr('aria-controls', $dataTarget); + $checkbox.attr('aria-controls', $dataTarget) // Set aria-expanded and aria-hidden - $checkbox.attr('aria-expanded', 'false'); - $('#'+$dataTarget).attr('aria-hidden', 'true'); + $checkbox.attr('aria-expanded', 'false') + $('#' + $dataTarget).attr('aria-hidden', 'true') // For checkboxes revealing hidden content - $checkbox.on('click', function() { - - var state = $(this).attr('aria-expanded') === 'false' ? true : false; + $checkbox.on('click', function () { + var state = $(this).attr('aria-expanded') === 'false' // Toggle hidden content - $('#'+$dataTarget).toggleClass('js-hidden'); + $('#' + $dataTarget).toggleClass('js-hidden') // Update aria-expanded and aria-hidden attributes - $(this).attr('aria-expanded', state); - $('#'+$dataTarget).attr('aria-hidden', !state); - - }); + $(this).attr('aria-expanded', state) + $('#' + $dataTarget).attr('aria-hidden', !state) + }) } - - }); + }) } } -$(document).ready(function() { - +$(document).ready(function () { // Turn off jQuery animation - jQuery.fx.off = true; + jQuery.fx.off = true // Use GOV.UK selection-buttons.js to set selected // and focused states for block labels - var $blockLabels = $(".block-label input[type='radio'], .block-label input[type='checkbox']"); - new GOVUK.SelectionButtons($blockLabels); + var $blockLabels = $(".block-label input[type='radio'], .block-label input[type='checkbox']") + new GOVUK.SelectionButtons($blockLabels) // eslint-disable-line // Use GOV.UK shim-links-with-button-role.js to trigger a link styled to look like a button, // with role="button" when the space key is pressed. - GOVUK.shimLinksWithButtonRole.init(); + GOVUK.shimLinksWithButtonRole.init() // Details/summary polyfill // See /javascripts/vendor/details.polyfill.js // Where .block-label uses the data-target attribute // to toggle hidden content - var toggleContent = new ShowHideContent(); - toggleContent.showHideRadioToggledContent(); - toggleContent.showHideCheckboxToggledContent(); - -}); - -$(window).load(function() { + var toggleContent = new ShowHideContent() + toggleContent.showHideRadioToggledContent() + toggleContent.showHideCheckboxToggledContent() +}) +$(window).load(function () { // Only set focus for the error example pages - if ($(".js-error-example").length) { - + if ($('.js-error-example').length) { // If there is an error summary, set focus to the summary - if ($(".error-summary").length) { - $(".error-summary").focus(); - $(".error-summary a").click(function(e) { - e.preventDefault(); - var href = $(this).attr("href"); - $(href).focus(); - }); - } - // Otherwise, set focus to the field with the error - else { - $(".error input:first").focus(); + if ($('.error-summary').length) { + $('.error-summary').focus() + $('.error-summary a').click(function (e) { + e.preventDefault() + var href = $(this).attr('href') + $(href).focus() + }) + } else { + // Otherwise, set focus to the field with the error + $('.error input:first').focus() } } - -}); - +}) diff --git a/public/javascripts/vendor/details.polyfill.js b/public/javascripts/govuk/details.polyfill.js similarity index 67% rename from public/javascripts/vendor/details.polyfill.js rename to public/javascripts/govuk/details.polyfill.js index e0a9c4073..43cba922b 100644 --- a/public/javascripts/vendor/details.polyfill.js +++ b/public/javascripts/govuk/details.polyfill.js @@ -6,188 +6,185 @@ // http://www.sitepoint.com/fixing-the-details-element/ -(function () { - 'use strict'; +;(function () { + 'use strict' - var NATIVE_DETAILS = typeof document.createElement('details').open === 'boolean'; + var NATIVE_DETAILS = typeof document.createElement('details').open === 'boolean' // Add event construct for modern browsers or IE // which fires the callback with a pre-converted target reference - function addEvent(node, type, callback) { + function addEvent (node, type, callback) { if (node.addEventListener) { node.addEventListener(type, function (e) { - callback(e, e.target); - }, false); + callback(e, e.target) + }, false) } else if (node.attachEvent) { node.attachEvent('on' + type, function (e) { - callback(e, e.srcElement); - }); + callback(e, e.srcElement) + }) } } // Handle cross-modal click events - function addClickEvent(node, callback) { + function addClickEvent (node, callback) { // Prevent space(32) from scrolling the page addEvent(node, 'keypress', function (e, target) { if (target.nodeName === 'SUMMARY') { if (e.keyCode === 32) { if (e.preventDefault) { - e.preventDefault(); + e.preventDefault() } else { - e.returnValue = false; + e.returnValue = false } } } - }); + }) // When the key comes up - check if it is enter(13) or space(32) addEvent(node, 'keyup', function (e, target) { - if (e.keyCode === 13 || e.keyCode === 32) { callback(e, target); } - }); + if (e.keyCode === 13 || e.keyCode === 32) { callback(e, target) } + }) addEvent(node, 'mouseup', function (e, target) { - callback(e, target); - }); + callback(e, target) + }) } // Get the nearest ancestor element of a node that matches a given tag name - function getAncestor(node, match) { + function getAncestor (node, match) { do { if (!node || node.nodeName.toLowerCase() === match) { - break; + break } - } while (node = node.parentNode); + node = node.parentNode + } while (node) - return node; + return node } // Create a started flag so we can prevent the initialisation // function firing from both DOMContentLoaded and window.onload - var started = false; + var started = false // Initialisation function - function addDetailsPolyfill(list) { - + function addDetailsPolyfill (list) { // If this has already happened, just return // else set the flag so it doesn't happen again if (started) { - return; + return } - started = true; + started = true // Get the collection of details elements, but if that's empty // then we don't need to bother with the rest of the scripting if ((list = document.getElementsByTagName('details')).length === 0) { - return; + return } // else iterate through them to apply their initial state - var n = list.length, i = 0; + var n = list.length + var i = 0 for (i; i < n; i++) { - var details = list[i]; + var details = list[i] // Save shortcuts to the inner summary and content elements - details.__summary = details.getElementsByTagName('summary').item(0); - details.__content = details.getElementsByTagName('div').item(0); + details.__summary = details.getElementsByTagName('summary').item(0) + details.__content = details.getElementsByTagName('div').item(0) // If the content doesn't have an ID, assign it one now // which we'll need for the summary's aria-controls assignment if (!details.__content.id) { - details.__content.id = 'details-content-' + i; + details.__content.id = 'details-content-' + i } // Add ARIA role="group" to details - details.setAttribute('role', 'group'); + details.setAttribute('role', 'group') // Add role=button to summary - details.__summary.setAttribute('role', 'button'); + details.__summary.setAttribute('role', 'button') // Add aria-controls - details.__summary.setAttribute('aria-controls', details.__content.id); + details.__summary.setAttribute('aria-controls', details.__content.id) // Set tabIndex so the summary is keyboard accessible for non-native elements // http://www.saliences.com/browserBugs/tabIndex.html if (!NATIVE_DETAILS) { - details.__summary.tabIndex = 0; + details.__summary.tabIndex = 0 } // Detect initial open state - var openAttr = details.getAttribute('open') !== null; + var openAttr = details.getAttribute('open') !== null if (openAttr === true) { - details.__summary.setAttribute('aria-expanded', 'true'); - details.__content.setAttribute('aria-hidden', 'false'); + details.__summary.setAttribute('aria-expanded', 'true') + details.__content.setAttribute('aria-hidden', 'false') } else { - details.__summary.setAttribute('aria-expanded', 'false'); - details.__content.setAttribute('aria-hidden', 'true'); + details.__summary.setAttribute('aria-expanded', 'false') + details.__content.setAttribute('aria-hidden', 'true') if (!NATIVE_DETAILS) { - details.__content.style.display = 'none'; + details.__content.style.display = 'none' } } // Create a circular reference from the summary back to its // parent details element, for convenience in the click handler - details.__summary.__details = details; + details.__summary.__details = details // If this is not a native implementation, create an arrow // inside the summary if (!NATIVE_DETAILS) { - - var twisty = document.createElement('i'); + var twisty = document.createElement('i') if (openAttr === true) { - twisty.className = 'arrow arrow-open'; - twisty.appendChild(document.createTextNode('\u25bc')); + twisty.className = 'arrow arrow-open' + twisty.appendChild(document.createTextNode('\u25bc')) } else { - twisty.className = 'arrow arrow-closed'; - twisty.appendChild(document.createTextNode('\u25ba')); + twisty.className = 'arrow arrow-closed' + twisty.appendChild(document.createTextNode('\u25ba')) } - details.__summary.__twisty = details.__summary.insertBefore(twisty, details.__summary.firstChild); - details.__summary.__twisty.setAttribute('aria-hidden', 'true'); - + details.__summary.__twisty = details.__summary.insertBefore(twisty, details.__summary.firstChild) + details.__summary.__twisty.setAttribute('aria-hidden', 'true') } } // Define a statechange function that updates aria-expanded and style.display // Also update the arrow position - function statechange(summary) { + function statechange (summary) { + var expanded = summary.__details.__summary.getAttribute('aria-expanded') === 'true' + var hidden = summary.__details.__content.getAttribute('aria-hidden') === 'true' - var expanded = summary.__details.__summary.getAttribute('aria-expanded') === 'true'; - var hidden = summary.__details.__content.getAttribute('aria-hidden') === 'true'; - - summary.__details.__summary.setAttribute('aria-expanded', (expanded ? 'false' : 'true')); - summary.__details.__content.setAttribute('aria-hidden', (hidden ? 'false' : 'true')); + summary.__details.__summary.setAttribute('aria-expanded', (expanded ? 'false' : 'true')) + summary.__details.__content.setAttribute('aria-hidden', (hidden ? 'false' : 'true')) if (!NATIVE_DETAILS) { - summary.__details.__content.style.display = (expanded ? 'none' : ''); + summary.__details.__content.style.display = (expanded ? 'none' : '') - var hasOpenAttr = summary.__details.getAttribute('open') !== null; + var hasOpenAttr = summary.__details.getAttribute('open') !== null if (!hasOpenAttr) { - summary.__details.setAttribute('open', 'open'); + summary.__details.setAttribute('open', 'open') } else { - summary.__details.removeAttribute('open'); + summary.__details.removeAttribute('open') } } if (summary.__twisty) { - summary.__twisty.firstChild.nodeValue = (expanded ? '\u25ba' : '\u25bc'); - summary.__twisty.setAttribute('class', (expanded ? 'arrow arrow-closed' : 'arrow arrow-open')); + summary.__twisty.firstChild.nodeValue = (expanded ? '\u25ba' : '\u25bc') + summary.__twisty.setAttribute('class', (expanded ? 'arrow arrow-closed' : 'arrow arrow-open')) } - return true; + return true } // Bind a click event to handle summary elements - addClickEvent(document, function(e, summary) { + addClickEvent(document, function (e, summary) { if (!(summary = getAncestor(summary, 'summary'))) { - return true; + return true } - return statechange(summary); - }); + return statechange(summary) + }) } // Bind two load events for modern and older browsers // If the first one fires it will set a flag to block the second one // but if it's not supported then the second one will fire - addEvent(document, 'DOMContentLoaded', addDetailsPolyfill); - addEvent(window, 'load', addDetailsPolyfill); - -})(); + addEvent(document, 'DOMContentLoaded', addDetailsPolyfill) + addEvent(window, 'load', addDetailsPolyfill) +})() diff --git a/public/javascripts/redirect.js b/public/javascripts/redirect.js index 24507d621..952b2d83e 100644 --- a/public/javascripts/redirect.js +++ b/public/javascripts/redirect.js @@ -1,10 +1,9 @@ // Redirect all the existing URLs with fragment identifiers in to the new section -(function () { - 'use strict'; - +;(function () { + 'use strict' var getNewRouteFor = function (fragment) { - var baseURL = window.location.protocol + '//' +window.location.host; + var baseURL = window.location.protocol + '//' + window.location.host var newRoutes = { '#guide-layout': '/layout', '#layout-spacing': '/layout/#layout-spacing', @@ -50,25 +49,24 @@ '#guide-alpha-beta': '/alpha-beta-banners', '#guide-alpha-beta-govuk': '/alpha-beta-banners/#alpha-banner', '#alpha-beta-creating-banners': '/alpha-beta-banners/#creating-phase-banners' - }; + } if (newRoutes[fragment] !== 'undefined') { - return baseURL + newRoutes[fragment]; + return baseURL + newRoutes[fragment] } - return false; - }; + return false + } - var fragment = window.location.hash; - var newRoute; + var fragment = window.location.hash + var newRoute if (fragment === '') { - return; + return } - newRoute = getNewRouteFor(fragment); + newRoute = getNewRouteFor(fragment) if (newRoute) { - window.location.href = newRoute; + window.location.href = newRoute } - -}()); +}()) diff --git a/server.js b/server.js index 28694dc6e..2925de5c5 100755 --- a/server.js +++ b/server.js @@ -1,53 +1,53 @@ -var bodyParser = require('body-parser'), - config = require(__dirname + '/app/config.js'), - express = require('express'), - nunjucks = require('express-nunjucks'), - path = require('path'), - routes = require(__dirname + '/app/routes.js'), - app = express(), - port = (process.env.PORT || 3000); +var path = require('path') +var express = require('express') +var nunjucks = require('express-nunjucks') +var routes = require('./app/routes.js') +var app = express() +var bodyParser = require('body-parser') +var config = require('./app/config.js') +var port = (process.env.PORT || 3000) // Application settings -app.set('view engine', 'html'); -app.set('views', [__dirname + '/app/views', __dirname + '/lib/']); +app.set('view engine', 'html') +app.set('views', [path.join(__dirname, '/app/views'), path.join(__dirname, '/lib/')]) nunjucks.setup({ autoescape: true, watch: true, noCache: true -}, app); +}, app) // Middleware to serve static assets -app.use('/public', express.static(__dirname + '/public')); -app.use('/public', express.static(__dirname + '/govuk_modules/public')); -app.use('/public', express.static(__dirname + '/govuk_modules/govuk_template/assets')); -app.use('/public', express.static(__dirname + '/govuk_modules/govuk_frontend_toolkit')); +app.use('/public', express.static(path.join(__dirname, '/public'))) +app.use('/public', express.static(path.join(__dirname, '/govuk_modules/public'))) +app.use('/public', express.static(path.join(__dirname, '/govuk_modules/govuk_template/assets'))) +app.use('/public', express.static(path.join(__dirname, '/govuk_modules/govuk_frontend_toolkit'))) // Support for parsing data in POSTs -app.use(bodyParser.json()); +app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ - extended: true -})); + extended: true +})) // send assetPath to all views app.use(function (req, res, next) { - res.locals.asset_path="/public/"; - next(); -}); + res.locals.asset_path = '/public/' + next() +}) // Add variables that are available in all views app.use(function (req, res, next) { - res.locals.cookieText=config.cookieText; - next(); -}); + res.locals.cookieText = config.cookieText + next() +}) // routes (found in routes.js) -routes.bind(app, '/public/'); +routes.bind(app, '/public/') // start the app -app.listen(port); -console.log(''); -console.log('Listening on port ' + port); -console.log(''); +app.listen(port) +console.log('') +console.log('Listening on port ' + port) +console.log('') diff --git a/start.js b/start.js index 0a45aa8bb..1a136488f 100755 --- a/start.js +++ b/start.js @@ -1,15 +1,19 @@ -var fs = require('fs'), - pidFile = __dirname + '/.start.pid', - fileOptions = { encoding : 'utf-8' }; +var path = require('path') +var fs = require('fs') +var pidFile = path.join(__dirname, '/.start.pid') +var fileOptions = { encoding: 'utf-8' } // start grunt +var gruntfile = path.join(__dirname, '/Gruntfile.js') +require('./node_modules/grunt/lib/grunt.js').cli({ + 'gruntfile': gruntfile +}) -require(__dirname + '/node_modules/grunt/lib/grunt.js').cli(); -fs.writeFileSync(pidFile, process.pid, fileOptions); -process.on('SIGINT', function() { - var pid = fs.readFileSync(pidFile, fileOptions); +fs.writeFileSync(pidFile, process.pid, fileOptions) +process.on('SIGINT', function () { + var pid = fs.readFileSync(pidFile, fileOptions) - fs.unlink(pidFile); - process.kill(pid, 'SIGTERM'); - process.exit(); + fs.unlink(pidFile) + process.kill(pid, 'SIGTERM') + process.exit() })