From 661776a7eae86912e7904533e43597ab0ee11a47 Mon Sep 17 00:00:00 2001 From: Theodor Vararu Date: Wed, 13 Jul 2016 15:06:28 +0100 Subject: [PATCH] Lint codebase using standard --- Gruntfile.js | 39 ++-- app/assets/javascripts/application.js | 128 +++++------ app/assets/javascripts/details.polyfill.js | 139 ++++++------ .../javascripts/govuk/selection-buttons.js | 106 +++++----- app/assets/javascripts/jquery-1.11.3.js | 1 + app/config.js | 6 +- app/filters.js | 16 +- app/routes.js | 35 ++- lib/core_filters.js | 17 +- lib/utils.js | 109 +++++----- server.js | 199 +++++++++--------- start.js | 38 ++-- 12 files changed, 395 insertions(+), 438 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 376e5635a9..a508944a8d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,11 +1,10 @@ -module.exports = function(grunt){ +module.exports = function (grunt) { grunt.initConfig({ - // Builds Sass sass: { dev: { options: { - style: "expanded", + style: 'expanded', sourcemap: true, includePaths: [ 'govuk_modules/govuk_template/assets/stylesheets', @@ -16,10 +15,10 @@ module.exports = function(grunt){ }, files: [{ expand: true, - cwd: "app/assets/sass", - src: ["*.scss"], - dest: "public/stylesheets/", - ext: ".css" + cwd: 'app/assets/sass', + src: ['*.scss'], + dest: 'public/stylesheets/', + ext: '.css' }] } }, @@ -33,7 +32,7 @@ module.exports = function(grunt){ src: ['**/*', '!sass/**'], dest: 'public/' }], - ignoreInDest: "**/stylesheets/**", + ignoreInDest: '**/stylesheets/**', updateAndDelete: true }, govuk: { @@ -73,14 +72,14 @@ module.exports = function(grunt){ files: ['app/assets/sass/**/*.scss'], tasks: ['sass'], options: { - spawn: false, + spawn: false } }, - assets:{ + assets: { files: ['app/assets/**/*', '!app/assets/sass/**'], tasks: ['sync:assets'], options: { - spawn: false, + spawn: false } } }, @@ -105,33 +104,33 @@ module.exports = function(grunt){ } } } - }); + }) - [ + ;[ 'grunt-sync', 'grunt-contrib-watch', 'grunt-sass', 'grunt-nodemon', 'grunt-concurrent' ].forEach(function (task) { - grunt.loadNpmTasks(task); - }); + grunt.loadNpmTasks(task) + }) grunt.registerTask('generate-assets', [ 'sync', 'sass' - ]); + ]) grunt.registerTask('default', [ 'generate-assets', 'concurrent:target' - ]); + ]) grunt.registerTask( 'test', 'default', function () { - grunt.log.writeln('Test that the app runs'); + grunt.log.writeln('Test that the app runs') } - ); -}; + ) +} diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index e21481001d..33d64954c9 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -1,127 +1,113 @@ -function ShowHideContent() { - var self = this; +/* global $ */ +/* global GOVUK */ - self.escapeElementName = function(str) { - result = str.replace('[', '\\[').replace(']', '\\]') - return(result); - }; +function ShowHideContent () { + var self = this + + 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 () { // 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 // Show and hide toggled content // Where .block-label uses the data-target attribute - var toggleContent = new ShowHideContent(); - toggleContent.showHideRadioToggledContent(); - toggleContent.showHideCheckboxToggledContent(); - -}); + var toggleContent = new ShowHideContent() + toggleContent.showHideRadioToggledContent() + toggleContent.showHideCheckboxToggledContent() +}) diff --git a/app/assets/javascripts/details.polyfill.js b/app/assets/javascripts/details.polyfill.js index e0a9c4073a..37ed22d2c2 100644 --- a/app/assets/javascripts/details.polyfill.js +++ b/app/assets/javascripts/details.polyfill.js @@ -7,187 +7,184 @@ // http://www.sitepoint.com/fixing-the-details-element/ (function () { - 'use strict'; + '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/app/assets/javascripts/govuk/selection-buttons.js b/app/assets/javascripts/govuk/selection-buttons.js index 02955c25ec..282e86ef45 100644 --- a/app/assets/javascripts/govuk/selection-buttons.js +++ b/app/assets/javascripts/govuk/selection-buttons.js @@ -1,109 +1,109 @@ (function () { - "use strict"; - var root = this, - $ = root.jQuery; + 'use strict' + var root = this + var $ = root.jQuery - if (typeof GOVUK === 'undefined') { root.GOVUK = {}; } + if (typeof GOVUK === 'undefined') { root.GOVUK = {} } var SelectionButtons = function (elmsOrSelector, opts) { - this.selectedClass = 'selected'; - this.focusedClass = 'focused'; + this.selectedClass = 'selected' + this.focusedClass = 'focused' if (opts !== undefined) { $.each(opts, function (optionName, optionObj) { - this[optionName] = optionObj; - }.bind(this)); + this[optionName] = optionObj + }.bind(this)) } if (typeof elmsOrSelector === 'string') { - this.$elms = $(elmsOrSelector); - this.selector = elmsOrSelector; - this.setInitialState($(this.selector)); + this.$elms = $(elmsOrSelector) + this.selector = elmsOrSelector + this.setInitialState($(this.selector)) } else if (elmsOrSelector !== undefined) { - this.$elms = elmsOrSelector; - this.setInitialState(this.$elms); + this.$elms = elmsOrSelector + this.setInitialState(this.$elms) } - this.addEvents(); - }; + this.addEvents() + } SelectionButtons.prototype.addEvents = function () { if (typeof this.$elms !== 'undefined') { - this.addElementLevelEvents(); + this.addElementLevelEvents() } else { - this.addDocumentLevelEvents(); + this.addDocumentLevelEvents() } - }; + } SelectionButtons.prototype.setInitialState = function ($elms) { $elms.each(function (idx, elm) { - var $elm = $(elm); + var $elm = $(elm) if ($elm.is(':checked')) { - this.markSelected($elm); + this.markSelected($elm) } - }.bind(this)); - }; + }.bind(this)) + } SelectionButtons.prototype.markFocused = function ($elm, state) { if (state === 'focused') { - $elm.parent('label').addClass(this.focusedClass); + $elm.parent('label').addClass(this.focusedClass) } else { - $elm.parent('label').removeClass(this.focusedClass); + $elm.parent('label').removeClass(this.focusedClass) } - }; + } SelectionButtons.prototype.markSelected = function ($elm) { - var radioName; + var radioName if ($elm.attr('type') === 'radio') { - radioName = $elm.attr('name'); + radioName = $elm.attr('name') $($elm[0].form).find('input[name="' + radioName + '"]') .parent('label') - .removeClass(this.selectedClass); - $elm.parent('label').addClass(this.selectedClass); + .removeClass(this.selectedClass) + $elm.parent('label').addClass(this.selectedClass) } else { // checkbox if ($elm.is(':checked')) { - $elm.parent('label').addClass(this.selectedClass); + $elm.parent('label').addClass(this.selectedClass) } else { - $elm.parent('label').removeClass(this.selectedClass); + $elm.parent('label').removeClass(this.selectedClass) } } - }; + } SelectionButtons.prototype.addElementLevelEvents = function () { - this.clickHandler = this.getClickHandler(); - this.focusHandler = this.getFocusHandler({ 'level' : 'element' }); + this.clickHandler = this.getClickHandler() + this.focusHandler = this.getFocusHandler({ 'level': 'element' }) this.$elms .on('click', this.clickHandler) - .on('focus blur', this.focusHandler); - }; + .on('focus blur', this.focusHandler) + } SelectionButtons.prototype.addDocumentLevelEvents = function () { - this.clickHandler = this.getClickHandler(); - this.focusHandler = this.getFocusHandler({ 'level' : 'document' }); + this.clickHandler = this.getClickHandler() + this.focusHandler = this.getFocusHandler({ 'level': 'document' }) $(document) .on('click', this.selector, this.clickHandler) - .on('focus blur', this.selector, this.focusHandler); - }; + .on('focus blur', this.selector, this.focusHandler) + } SelectionButtons.prototype.getClickHandler = function () { return function (e) { - this.markSelected($(e.target)); - }.bind(this); - }; + this.markSelected($(e.target)) + }.bind(this) + } SelectionButtons.prototype.getFocusHandler = function (opts) { - var focusEvent = (opts.level === 'document') ? 'focusin' : 'focus'; + var focusEvent = (opts.level === 'document') ? 'focusin' : 'focus' return function (e) { - var state = (e.type === focusEvent) ? 'focused' : 'blurred'; + var state = (e.type === focusEvent) ? 'focused' : 'blurred' - this.markFocused($(e.target), state); - }.bind(this); - }; + this.markFocused($(e.target), state) + }.bind(this) + } SelectionButtons.prototype.destroy = function () { if (typeof this.selector !== 'undefined') { $(document) .off('click', this.selector, this.clickHandler) - .off('focus blur', this.selector, this.focusHandler); + .off('focus blur', this.selector, this.focusHandler) } else { this.$elms .off('click', this.clickHandler) - .off('focus blur', this.focusHandler); + .off('focus blur', this.focusHandler) } - }; + } - root.GOVUK.SelectionButtons = SelectionButtons; -}).call(this); + root.GOVUK.SelectionButtons = SelectionButtons +}).call(this) diff --git a/app/assets/javascripts/jquery-1.11.3.js b/app/assets/javascripts/jquery-1.11.3.js index 6feb11086f..fe4a0ecd7f 100644 --- a/app/assets/javascripts/jquery-1.11.3.js +++ b/app/assets/javascripts/jquery-1.11.3.js @@ -1,3 +1,4 @@ +/* eslint-disable */ /*! * jQuery JavaScript Library v1.11.3 * http://jquery.com/ diff --git a/app/config.js b/app/config.js index e9e66b4eb8..539193f26a 100644 --- a/app/config.js +++ b/app/config.js @@ -3,9 +3,8 @@ // Note: prototype config can be overridden using environment variables (eg on heroku) module.exports = { - // Service name used in header. Eg: 'Renew your passport' - serviceName: "Service name goes here", + serviceName: 'Service name goes here', // Default port that prototype runs on port: '3000', @@ -19,5 +18,4 @@ module.exports = { // Cookie warning - update link to service's cookie page. cookieText: 'GOV.UK uses cookies to make the site simpler. Find out more about cookies' -}; - \ No newline at end of file +} diff --git a/app/filters.js b/app/filters.js index 746d716472..47ab9ed0ec 100644 --- a/app/filters.js +++ b/app/filters.js @@ -1,19 +1,18 @@ -module.exports = function(env) { - +module.exports = function (env) { /** * Instantiate object used to store the methods registered as a * 'filter' (of the same name) within nunjucks. You can override * gov.uk core filters by creating filter methods of the same name. * @type {Object} */ - var filters = {}; + var filters = {} /* ------------------------------------------------------------------ add your methods to the filters obj below this comment block: @example: filters.sayHi = function(name) { - return 'Hi ' + name + '!'; + return 'Hi ' + name + '!' } Which in your templates would be used as: @@ -26,7 +25,7 @@ module.exports = function(env) { Filters can take additional arguments, for example: filters.sayHi = function(name,tone) { - return (tone == 'formal' ? 'Greetings' : 'Hi') + ' ' + name + '!'; + return (tone == 'formal' ? 'Greetings' : 'Hi') + ' ' + name + '!' } Which would be used like this: @@ -39,11 +38,8 @@ module.exports = function(env) { ------------------------------------------------------------------ */ - - /* ------------------------------------------------------------------ keep the following line to return your filters to the app ------------------------------------------------------------------ */ - return filters; - -}; + return filters +} diff --git a/app/routes.js b/app/routes.js index ee26b199f3..0a37e53793 100644 --- a/app/routes.js +++ b/app/routes.js @@ -1,44 +1,33 @@ -var express = require('express'); -var router = express.Router(); +var express = require('express') +var router = express.Router() router.get('/', function (req, res) { - - res.render('index'); - -}); - + res.render('index') +}) // Example routes - feel free to delete these // Passing data into a page router.get('/examples/template-data', function (req, res) { - - res.render('examples/template-data', { 'name' : 'Foo' }); - -}); + res.render('examples/template-data', { 'name': 'Foo' }) +}) // Branching router.get('/examples/over-18', function (req, res) { - // get the answer from the query string (eg. ?over18=false) - var over18 = req.query.over18; - - if (over18 == "false"){ + var over18 = req.query.over18 + if (over18 === 'false') { // redirect to the relevant page - res.redirect("/examples/under-18"); - + res.redirect('/examples/under-18') } else { - // if over18 is any other value (or is missing) render the page requested - res.render('examples/over-18'); - + res.render('examples/over-18') } - -}); +}) // add your routes here -module.exports = router; +module.exports = router diff --git a/lib/core_filters.js b/lib/core_filters.js index 5fb5cacaca..c26330a9b1 100644 --- a/lib/core_filters.js +++ b/lib/core_filters.js @@ -1,15 +1,14 @@ -module.exports = function(env) { - +module.exports = function (env) { // if you need accss to the internal nunjucks filter you can just env // see the example below for 'safe' which is used in 'filters.log' - var nunjucksSafe = env.getFilter('safe'); + var nunjucksSafe = env.getFilter('safe') /** * object used store the methods registered as a 'filter' (of the same name) within nunjucks * filters.foo("input") here, becomes {{ "input" | foo }} within nunjucks templates * @type {Object} */ - var filters = {}; + var filters = {} /** * logs an object in the template to the console on the client. @@ -18,9 +17,9 @@ module.exports = function(env) { * @example {{ "hello world" | log }} * @example {{ "hello world" | log | safe }} [for environments with autoescaping turned on] */ - filters.log = function log(a) { - return nunjucksSafe(''); - }; + filters.log = function log (a) { + return nunjucksSafe('') + } - return filters; -} \ No newline at end of file + return filters +} diff --git a/lib/utils.js b/lib/utils.js index 9cc25f029f..13eae21797 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,8 +1,9 @@ -var basicAuth = require('basic-auth'), - config = require(__dirname + '/../app/config.js'), - fs = require('fs'), - portScanner = require('portscanner'), - prompt = require('prompt'); +var path = require('path') +var basicAuth = require('basic-auth') +var config = require(path.resolve(__dirname, '/../app/config.js')) +var fs = require('fs') +var portScanner = require('portscanner') +var prompt = require('prompt') /** * Simple basic auth middleware for use with Express 4.x. @@ -10,57 +11,54 @@ var basicAuth = require('basic-auth'), * Based on template found at: http://www.danielstjules.com/2014/08/03/basic-auth-with-express-4/ * * @example - * app.use('/api-requiring-auth', utils.basicAuth('username', 'password')); + * app.use('/api-requiring-auth', utils.basicAuth('username', 'password')) * * @param {string} username Expected username * @param {string} password Expected password * @returns {function} Express 4 middleware requiring the given credentials */ -exports.basicAuth = function(username, password) { - return function(req, res, next) { - - if (!username || !password) { - console.log('Username or password is not set.'); - return res.send('

Error:

Username or password not set. See guidance for setting these.

'); - } - - var user = basicAuth(req); +exports.basicAuth = function (username, password) { + return function (req, res, next) { + if (!username || !password) { + console.log('Username or password is not set.') + return res.send('

Error:

Username or password not set. See guidance for setting these.

') + } - if (!user || user.name !== username || user.pass !== password) { - res.set('WWW-Authenticate', 'Basic realm=Authorization Required'); - return res.sendStatus(401); - } + var user = basicAuth(req) - next(); - }; -}; + if (!user || user.name !== username || user.pass !== password) { + res.set('WWW-Authenticate', 'Basic realm=Authorization Required') + return res.sendStatus(401) + } -exports.findAvailablePort = function(app, callback){ + next() + } +} - var port = null; +exports.findAvailablePort = function (app, callback) { + var port = null try { - port = Number(fs.readFileSync(__dirname+'/../.port.tmp')); - } catch (e){ - port = Number(process.env.PORT || config.port); + port = Number(fs.readFileSync(path.join(__dirname, '/../.port.tmp'))) + } catch (e) { + port = Number(process.env.PORT || config.port) } - console.log(''); + console.log('') // Check that default port is free, else offer to change - portScanner.findAPortNotInUse(port, port+50, '127.0.0.1', function(error, availablePort) { - - if (port == availablePort){ - callback(port); + portScanner.findAPortNotInUse(port, port + 50, '127.0.0.1', function (error, availablePort) { + if (error) { throw error } + if (port === availablePort) { + callback(port) } else { - // Default port in use - offer to change to available port - console.error("ERROR: Port " + port + " in use - you may have another prototype running.\n"); + console.error('ERROR: Port ' + port + ' in use - you may have another prototype running.\n') // Set up prompt settings - prompt.colors = false; - prompt.start(); - prompt.message = ""; - prompt.delimiter = ""; + prompt.colors = false + prompt.start() + prompt.message = '' + prompt.delimiter = '' // Ask user if they want to change port prompt.get([{ @@ -71,33 +69,30 @@ exports.findAvailablePort = function(app, callback){ pattern: /y(es)?|no?/i, message: 'Please enter y or n' }], function (err, result) { - - if (result.answer.match(/y(es)?/i) ) { + if (err) { throw err } + if (result.answer.match(/y(es)?/i)) { // User answers yes - port = availablePort; - fs.writeFileSync(__dirname+'/../.port.tmp', port); - console.log('Changed to port ' + port); + port = availablePort + fs.writeFileSync(path.join(__dirname, '/../.port.tmp'), port) + console.log('Changed to port ' + port) - callback(port); + callback(port) } else { - // User answers no - exit - console.log('\nYou can set a new default port in server.js, or by running the server with PORT=XXXX'); - console.log("\nExit by pressing 'ctrl + c'"); - process.exit(0); - + console.log('\nYou can set a new default port in server.js, or by running the server with PORT=XXXX') + console.log("\nExit by pressing 'ctrl + c'") + process.exit(0) } - }); + }) } - }); - + }) } -exports.forceHttps = function(req, res, next) { +exports.forceHttps = function (req, res, next) { if (req.headers['x-forwarded-proto'] !== 'https') { - console.log('Redirecting request to https'); + console.log('Redirecting request to https') // 302 temporary - this is a feature that can be disabled - return res.redirect(302, 'https://' + req.get('Host') + req.url); + return res.redirect(302, 'https://' + req.get('Host') + req.url) } - next(); -}; + next() +} diff --git a/server.js b/server.js index e8665da512..3a442e60bb 100644 --- a/server.js +++ b/server.js @@ -1,170 +1,167 @@ -var path = require('path'), - express = require('express'), - session = require('express-session'), - nunjucks = require('express-nunjucks'), - routes = require(__dirname + '/app/routes.js'), - favicon = require('serve-favicon'), - app = express(), - bodyParser = require('body-parser'), - browserSync = require('browser-sync'), - config = require(__dirname + '/app/config.js'), - utils = require(__dirname + '/lib/utils.js'), - packageJson = require(__dirname + '/package.json'), +var path = require('path') +var express = require('express') +var session = require('express-session') +var nunjucks = require('express-nunjucks') +var routes = require('./app/routes.js') +var favicon = require('serve-favicon') +var app = express() +var bodyParser = require('body-parser') +var browserSync = require('browser-sync') +var config = require('./app/config.js') +var utils = require('./lib/utils.js') +var packageJson = require(path.join(__dirname, '/package.json')) // Grab environment variables specified in Procfile or as Heroku config vars - releaseVersion = packageJson.version, - username = process.env.USERNAME, - password = process.env.PASSWORD, - env = process.env.NODE_ENV || 'development', - useAuth = process.env.USE_AUTH || config.useAuth, - useHttps = process.env.USE_HTTPS || config.useHttps; +var releaseVersion = packageJson.version +var username = process.env.USERNAME +var password = process.env.PASSWORD +var env = process.env.NODE_ENV || 'development' +var useAuth = process.env.USE_AUTH || config.useAuth +var useHttps = process.env.USE_HTTPS || config.useHttps - env = env.toLowerCase(); - useAuth = useAuth.toLowerCase(); - useHttps = useHttps.toLowerCase(); +env = env.toLowerCase() +useAuth = useAuth.toLowerCase() +useHttps = useHttps.toLowerCase() // Authenticate against the environment-provided credentials, if running // the app in production (Heroku, effectively) -if (env === 'production' && useAuth === 'true'){ - app.use(utils.basicAuth(username, password)); +if (env === 'production' && useAuth === 'true') { + app.use(utils.basicAuth(username, password)) } // 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) // require core and custom filters, merges to one object // and then add the methods to nunjucks env obj -nunjucks.ready(function(nj) { - var coreFilters = require(__dirname + '/lib/core_filters.js')(nj), - customFilters = require(__dirname + '/app/filters.js')(nj), - filters = Object.assign(coreFilters, customFilters); - Object.keys(filters).forEach(function(filterName) { - nj.addFilter(filterName, filters[filterName]); - }); -}); +nunjucks.ready(function (nj) { + var coreFilters = require(path.join(__dirname, '/lib/core_filters.js'))(nj) + var customFilters = require(path.join(__dirname, '/app/filters.js'))(nj) + var filters = Object.assign(coreFilters, customFilters) + Object.keys(filters).forEach(function (filterName) { + nj.addFilter(filterName, filters[filterName]) + }) +}) // Middleware to serve static assets -app.use('/public', express.static(__dirname + '/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/images/icons', express.static(__dirname + '/govuk_modules/govuk_frontend_toolkit/images')); +app.use('/public', express.static(path.join(__dirname, '/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'))) +app.use('/public/images/icons', express.static(path.join(__dirname, '/govuk_modules/govuk_frontend_toolkit/images'))) // Elements refers to icon folder instead of images folder -app.use(favicon(path.join(__dirname, 'govuk_modules', 'govuk_template', 'assets', 'images','favicon.ico'))); +app.use(favicon(path.join(__dirname, 'govuk_modules', 'govuk_template', 'assets', 'images', 'favicon.ico'))) // Support for parsing data in POSTs -app.use(bodyParser.json()); +app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: true -})); +})) // Support session data app.use(session({ resave: false, saveUninitialized: false, - secret: Math.round(Math.random()*100000).toString() -})); + secret: Math.round(Math.random() * 100000).toString() +})) // 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.serviceName=config.serviceName; - res.locals.cookieText=config.cookieText; - res.locals.releaseVersion="v" + releaseVersion; - next(); -}); + res.locals.serviceName = config.serviceName + res.locals.cookieText = config.cookieText + res.locals.releaseVersion = 'v' + releaseVersion + next() +}) // Force HTTPs on production connections -if (env === 'production' && useHttps === 'true'){ - app.use(utils.forceHttps); +if (env === 'production' && useHttps === 'true') { + app.use(utils.forceHttps) } // Disallow search index idexing app.use(function (req, res, next) { // Setting headers stops pages being indexed even if indexed pages link to them. - res.setHeader('X-Robots-Tag', 'noindex'); - next(); -}); + res.setHeader('X-Robots-Tag', 'noindex') + next() +}) app.get('/robots.txt', function (req, res) { - res.type('text/plain'); - res.send("User-agent: *\nDisallow: /"); -}); + res.type('text/plain') + res.send('User-agent: *\nDisallow: /') +}) // routes (found in app/routes.js) -if (typeof(routes) != "function"){ - console.log(routes.bind); - console.log("Warning: the use of bind in routes is deprecated - please check the prototype kit documentation for writing routes.") - routes.bind(app); +if (typeof (routes) !== 'function') { + console.log(routes.bind) + console.log('Warning: the use of bind in routes is deprecated - please check the prototype kit documentation for writing routes.') + routes.bind(app) } else { - app.use("/", routes); + app.use('/', routes) } // Strip .html and .htm if provided -app.get(/\.html?$/i, function (req, res){ - var path = req.path; - var parts = path.split('.'); - parts.pop(); - path = parts.join('.'); - res.redirect(path); -}); +app.get(/\.html?$/i, function (req, res) { + var path = req.path + var parts = path.split('.') + parts.pop() + path = parts.join('.') + res.redirect(path) +}) // auto render any view that exists app.get(/^\/([^.]+)$/, function (req, res) { + var path = (req.params[0]) - var path = (req.params[0]); - - res.render(path, function(err, html) { + res.render(path, function (err, html) { if (err) { - res.render(path + "/index", function(err2, html) { + res.render(path + '/index', function (err2, html) { if (err2) { - console.log(err); - res.status(404).send(err + "
" + err2); + console.log(err) + res.status(404).send(err + '
' + err2) } else { - res.end(html); + res.end(html) } - }); + }) } else { - res.end(html); + res.end(html) } - }); - -}); + }) +}) -console.log("\nGOV.UK Prototype kit v" + releaseVersion); +console.log('\nGOV.UK Prototype kit v' + releaseVersion) // Display warning not to use kit for production services. -console.log("\nNOTICE: the kit is for building prototypes, do not use it for production services."); +console.log('\nNOTICE: the kit is for building prototypes, do not use it for production services.') // start the app -utils.findAvailablePort(app, function(port) { - console.log('Listening on port ' + port + ' url: http://localhost:' + port); +utils.findAvailablePort(app, function (port) { + console.log('Listening on port ' + port + ' url: http://localhost:' + port) if (env === 'production') { - app.listen(port); + app.listen(port) } else { - app.listen(port-50,function() - { + app.listen(port - 50, function () { browserSync({ - proxy:'localhost:'+(port-50), - port:port, - ui:false, - files:['public/**/*.*','app/views/**/*.*'], - ghostmode:false, - open:false, - notify:false, - logLevel: "error" - }); - }); + proxy: 'localhost:' + (port - 50), + port: port, + ui: false, + files: ['public/**/*.*', 'app/views/**/*.*'], + ghostmode: false, + open: false, + notify: false, + logLevel: 'error' + }) + }) } -}); +}) diff --git a/start.js b/start.js index d91ad66283..134f416ed0 100644 --- a/start.js +++ b/start.js @@ -1,29 +1,29 @@ +var path = require('path') + // Check for `node_modules` folder and warn if missing -var fs = require('fs'); +var fs = require('fs') -if (!fs.existsSync(__dirname + '/node_modules')) { - console.error('ERROR: Node module folder missing. Try running `npm install`'); - process.exit(0); +if (!fs.existsSync(path.join(__dirname, '/node_modules'))) { + console.error('ERROR: Node module folder missing. Try running `npm install`') + process.exit(0) } -// remove .port.tmp if it exists +// remove .port.tmp if it exists try { - fs.unlinkSync(__dirname + '/.port.tmp'); -} catch(e){} - -var gruntfile = __dirname + '/Gruntfile.js'; + fs.unlinkSync(path.join(__dirname, '/.port.tmp')) +} catch (e) {} -require(__dirname + '/node_modules/grunt/lib/grunt.js').cli({ - 'gruntfile' : gruntfile -}); +var gruntfile = path.join(__dirname, '/Gruntfile.js') -process.on('SIGINT', function() { +require('./node_modules/grunt/lib/grunt.js').cli({ + 'gruntfile': gruntfile +}) - // remove .port.tmp if it exists +process.on('SIGINT', function () { + // remove .port.tmp if it exists try { - fs.unlinkSync(__dirname + '/.port.tmp'); - } catch(e){} - - process.exit(0); + fs.unlinkSync(path.join(__dirname, '/.port.tmp')) + } catch (e) {} -}); + process.exit(0) +})