dss_tech_docs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +29 -0
- data/.gitignore +37 -0
- data/.rspec +2 -0
- data/.rubocop.yml +11 -0
- data/.ruby-version +1 -0
- data/.travis.yml +9 -0
- data/CHANGELOG.md +274 -0
- data/CONTRIBUTING.md +9 -0
- data/Gemfile +4 -0
- data/LICENCE +21 -0
- data/README.md +89 -0
- data/Rakefile +13 -0
- data/docs/configuration.md +205 -0
- data/docs/core-layout-without-sidebar.png +0 -0
- data/docs/core-layout.png +0 -0
- data/docs/expired-page.png +0 -0
- data/docs/frontmatter.md +145 -0
- data/docs/last-reviewed-only.png +0 -0
- data/docs/last-reviewed-only.svg +1 -0
- data/docs/layout-layout.png +0 -0
- data/docs/not-expired-page.png +0 -0
- data/docs/page-expiry.md +85 -0
- data/dss_tech_docs.gemspec +46 -0
- data/example/.ruby-version +1 -0
- data/example/Gemfile +3 -0
- data/example/config.rb +9 -0
- data/example/config/hide-expiry.yml +51 -0
- data/example/config/tech-docs.yml +50 -0
- data/example/source/api-path.html.md +7 -0
- data/example/source/api-reference.html.md +5 -0
- data/example/source/child-of-expired-page.html.md +8 -0
- data/example/source/core-layout-without-sidebar.html.md.erb +7 -0
- data/example/source/core-layout.html.md.erb +12 -0
- data/example/source/expired-page-with-owner.html.md +10 -0
- data/example/source/expired-page.html.md +9 -0
- data/example/source/headings.html.md +11 -0
- data/example/source/index.html.md.erb +19 -0
- data/example/source/javascripts/application.js +1 -0
- data/example/source/not-expired-page.html.md +9 -0
- data/example/source/pets.yml +109 -0
- data/example/source/stylesheets/print.css.scss +3 -0
- data/example/source/stylesheets/screen-old-ie.css.scss +4 -0
- data/example/source/stylesheets/screen.css.scss +1 -0
- data/example/source/templates/proxy_template.html.md +8 -0
- data/lib/assets/javascripts/_analytics.js +58 -0
- data/lib/assets/javascripts/_govuk/modules.js +57 -0
- data/lib/assets/javascripts/_modules/anchored-headings.js +18 -0
- data/lib/assets/javascripts/_modules/collapsible-navigation.js +95 -0
- data/lib/assets/javascripts/_modules/in-page-navigation.js +132 -0
- data/lib/assets/javascripts/_modules/navigation.js +34 -0
- data/lib/assets/javascripts/_modules/page-expiry.js +15 -0
- data/lib/assets/javascripts/_modules/search.js +367 -0
- data/lib/assets/javascripts/_modules/table-of-contents.js +111 -0
- data/lib/assets/javascripts/_start-modules.js +13 -0
- data/lib/assets/javascripts/_vendor/fixedsticky.js +194 -0
- data/lib/assets/javascripts/_vendor/jquery.js +5 -0
- data/lib/assets/javascripts/_vendor/jquery.mark.js +1081 -0
- data/lib/assets/javascripts/_vendor/lodash.js +613 -0
- data/lib/assets/javascripts/_vendor/modernizr.js +3 -0
- data/lib/assets/javascripts/govuk_tech_docs.js +10 -0
- data/lib/assets/stylesheets/_accessibility.scss +9 -0
- data/lib/assets/stylesheets/_core.scss +71 -0
- data/lib/assets/stylesheets/_fonts.scss +29 -0
- data/lib/assets/stylesheets/_govuk_tech_docs.scss +2 -0
- data/lib/assets/stylesheets/_syntax-highlighting.scss +196 -0
- data/lib/assets/stylesheets/_variables.scss +12 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_colours.scss +2 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_conditionals.scss +81 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_css3.scss +90 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_device-pixels.scss +10 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_font_stack.scss +19 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_grid_layout.scss +136 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_helpers.scss +16 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_measurements.scss +14 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_shims.scss +55 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_typography.scss +249 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/_url-helpers.scss +16 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/colours/_organisation.scss +103 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/colours/_palette.scss +77 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/design-patterns/_alpha-beta.scss +66 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/design-patterns/_breadcrumbs.scss +53 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/design-patterns/_buttons.scss +141 -0
- data/lib/assets/stylesheets/govuk_frontend_toolkit/design-patterns/_media-player.scss +242 -0
- data/lib/assets/stylesheets/modules/_anchored-heading.scss +54 -0
- data/lib/assets/stylesheets/modules/_app-pane.scss +64 -0
- data/lib/assets/stylesheets/modules/_collapsible.scss +52 -0
- data/lib/assets/stylesheets/modules/_contribution-banner.scss +22 -0
- data/lib/assets/stylesheets/modules/_footer.scss +130 -0
- data/lib/assets/stylesheets/modules/_govuk-logo.scss +47 -0
- data/lib/assets/stylesheets/modules/_header.scss +290 -0
- data/lib/assets/stylesheets/modules/_page-review.scss +35 -0
- data/lib/assets/stylesheets/modules/_phase-banner.scss +22 -0
- data/lib/assets/stylesheets/modules/_search.scss +137 -0
- data/lib/assets/stylesheets/modules/_skip-link.scss +31 -0
- data/lib/assets/stylesheets/modules/_technical-documentation.scss +241 -0
- data/lib/assets/stylesheets/modules/_toc.scss +216 -0
- data/lib/assets/stylesheets/modules/_warning-text.scss +73 -0
- data/lib/assets/stylesheets/palette/_syntax-highlighting.scss +23 -0
- data/lib/assets/stylesheets/utilities/_fonts.scss +29 -0
- data/lib/assets/stylesheets/utilities/_printable.scss +13 -0
- data/lib/assets/stylesheets/vendor/_fixedsticky.scss +22 -0
- data/lib/dss_tech_docs.rb +121 -0
- data/lib/govuk_tech_docs/api_reference/api_reference_extension.rb +101 -0
- data/lib/govuk_tech_docs/api_reference/api_reference_renderer.rb +279 -0
- data/lib/govuk_tech_docs/api_reference/templates/api_reference_full.html.erb +19 -0
- data/lib/govuk_tech_docs/api_reference/templates/operation.html.erb +11 -0
- data/lib/govuk_tech_docs/api_reference/templates/parameters.html.erb +28 -0
- data/lib/govuk_tech_docs/api_reference/templates/path.html.erb +4 -0
- data/lib/govuk_tech_docs/api_reference/templates/responses.html.erb +33 -0
- data/lib/govuk_tech_docs/api_reference/templates/schema.html.erb +29 -0
- data/lib/govuk_tech_docs/contribution_banner.rb +62 -0
- data/lib/govuk_tech_docs/meta_tags.rb +67 -0
- data/lib/govuk_tech_docs/page_review.rb +52 -0
- data/lib/govuk_tech_docs/pages.rb +32 -0
- data/lib/govuk_tech_docs/redirects.rb +39 -0
- data/lib/govuk_tech_docs/table_of_contents/heading.rb +30 -0
- data/lib/govuk_tech_docs/table_of_contents/heading_tree.rb +27 -0
- data/lib/govuk_tech_docs/table_of_contents/heading_tree_builder.rb +41 -0
- data/lib/govuk_tech_docs/table_of_contents/heading_tree_renderer.rb +46 -0
- data/lib/govuk_tech_docs/table_of_contents/headings_builder.rb +39 -0
- data/lib/govuk_tech_docs/table_of_contents/helpers.rb +79 -0
- data/lib/govuk_tech_docs/tech_docs_html_renderer.rb +34 -0
- data/lib/govuk_tech_docs/unique_identifier_extension.rb +13 -0
- data/lib/govuk_tech_docs/unique_identifier_generator.rb +72 -0
- data/lib/govuk_tech_docs/version.rb +3 -0
- data/lib/govuk_tech_docs/warning_text_extension.rb +23 -0
- data/lib/source/api/pages.json.erb +1 -0
- data/lib/source/favicon.ico +0 -0
- data/lib/source/images/anchored-heading-icon-2x.png +0 -0
- data/lib/source/images/anchored-heading-icon.png +0 -0
- data/lib/source/images/gov.uk_logotype_crown-2x.png +0 -0
- data/lib/source/images/gov.uk_logotype_crown.png +0 -0
- data/lib/source/images/gov.uk_logotype_crown_invert_trans.png +0 -0
- data/lib/source/images/govuk-crest-2x.png +0 -0
- data/lib/source/images/govuk-crest.png +0 -0
- data/lib/source/images/govuk-icn-close.png +0 -0
- data/lib/source/images/govuk-icn-close@2x.png +0 -0
- data/lib/source/images/govuk-icn-numbered-list.png +0 -0
- data/lib/source/images/govuk-icn-numbered-list@2x.png +0 -0
- data/lib/source/images/open-government-licence.png +0 -0
- data/lib/source/images/open-government-licence_2x.png +0 -0
- data/lib/source/images/search-result-caret.svg +13 -0
- data/lib/source/layouts/_analytics.erb +15 -0
- data/lib/source/layouts/_footer.erb +10 -0
- data/lib/source/layouts/_header.erb +44 -0
- data/lib/source/layouts/_page_review.erb +22 -0
- data/lib/source/layouts/_search.erb +16 -0
- data/lib/source/layouts/core.erb +82 -0
- data/lib/source/layouts/layout.erb +18 -0
- metadata +474 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
(function($, root) {
|
|
2
|
+
"use strict";
|
|
3
|
+
root.GOVUK = root.GOVUK || {};
|
|
4
|
+
GOVUK.Modules = GOVUK.Modules || {};
|
|
5
|
+
|
|
6
|
+
GOVUK.modules = {
|
|
7
|
+
find: function(container) {
|
|
8
|
+
var modules,
|
|
9
|
+
moduleSelector = '[data-module]',
|
|
10
|
+
container = container || $('body');
|
|
11
|
+
|
|
12
|
+
modules = container.find(moduleSelector);
|
|
13
|
+
|
|
14
|
+
// Container could be a module too
|
|
15
|
+
if (container.is(moduleSelector)) {
|
|
16
|
+
modules = modules.add(container);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return modules;
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
start: function(container) {
|
|
23
|
+
var modules = this.find(container);
|
|
24
|
+
|
|
25
|
+
for (var i = 0, l = modules.length; i < l; i++) {
|
|
26
|
+
var module,
|
|
27
|
+
element = $(modules[i]),
|
|
28
|
+
type = camelCaseAndCapitalise(element.data('module')),
|
|
29
|
+
started = element.data('module-started');
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if (typeof GOVUK.Modules[type] === "function" && !started) {
|
|
33
|
+
module = new GOVUK.Modules[type]();
|
|
34
|
+
module.start(element);
|
|
35
|
+
element.data('module-started', true);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// eg selectable-table to SelectableTable
|
|
40
|
+
function camelCaseAndCapitalise(string) {
|
|
41
|
+
return capitaliseFirstLetter(camelCase(string));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// http://stackoverflow.com/questions/6660977/convert-hyphens-to-camel-case-camelcase
|
|
45
|
+
function camelCase(string) {
|
|
46
|
+
return string.replace(/-([a-z])/g, function (g) {
|
|
47
|
+
return g[1].toUpperCase();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// http://stackoverflow.com/questions/1026069/capitalize-the-first-letter-of-string-in-javascript
|
|
52
|
+
function capitaliseFirstLetter(string) {
|
|
53
|
+
return string.charAt(0).toUpperCase() + string.slice(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
})(jQuery, window);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
(function($, Modules) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Modules.AnchoredHeadings = function() {
|
|
5
|
+
this.start = function($element) {
|
|
6
|
+
var headings = $element.find('h1, h2, h3, h4, h5, h6');
|
|
7
|
+
headings.each(injectAnchor);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
function injectAnchor() {
|
|
11
|
+
var $this = $(this);
|
|
12
|
+
$this.addClass('anchored-heading');
|
|
13
|
+
$this.prepend(
|
|
14
|
+
'<a href="#' + $this.attr('id') + '" class="anchored-heading__icon" aria-hidden="true"></a>'
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
})(jQuery, window.GOVUK.Modules);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
(function($, Modules) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Modules.CollapsibleNavigation = function () {
|
|
5
|
+
|
|
6
|
+
var $contentPane;
|
|
7
|
+
var $nav;
|
|
8
|
+
var $topLevelItems;
|
|
9
|
+
var $headings;
|
|
10
|
+
var $listings;
|
|
11
|
+
|
|
12
|
+
var $openLink;
|
|
13
|
+
var $closeLink;
|
|
14
|
+
|
|
15
|
+
this.start = function ($element) {
|
|
16
|
+
$contentPane = $('.app-pane__content');
|
|
17
|
+
$nav = $element;
|
|
18
|
+
$topLevelItems = $nav.find('> ul > li');
|
|
19
|
+
$headings = $topLevelItems.find('> a');
|
|
20
|
+
$listings = $topLevelItems.find('> ul');
|
|
21
|
+
|
|
22
|
+
// Attach collapsible heading functionality,on mobile and desktop
|
|
23
|
+
collapsibleHeadings();
|
|
24
|
+
openActiveHeading();
|
|
25
|
+
$contentPane.on('scroll', _.debounce(openActiveHeading, 100, { maxWait: 100 }));
|
|
26
|
+
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function collapsibleHeadings() {
|
|
30
|
+
for (var i = $topLevelItems.length - 1; i >= 0; i--) {
|
|
31
|
+
var $topLevelItem = $($topLevelItems[i]);
|
|
32
|
+
var $heading = $topLevelItem.find('> a');
|
|
33
|
+
var $listing = $topLevelItem.find('> ul');
|
|
34
|
+
var id = 'toc-' + $heading.text().toLowerCase().replace(' ', '-');
|
|
35
|
+
// Only add collapsible functionality if there are children.
|
|
36
|
+
if ($listing.length == 0) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
$topLevelItem.addClass('collapsible');
|
|
40
|
+
$listing.addClass('collapsible__body')
|
|
41
|
+
.attr('id', id)
|
|
42
|
+
.attr('aria-expanded', 'false');
|
|
43
|
+
$heading.addClass('collapsible__heading')
|
|
44
|
+
.after('<button class="collapsible__toggle" aria-controls="' + id +'"><span class="collapsible__toggle-label">Expand ' + $heading.text() + '</span><span class="collapsible__toggle-icon" aria-hidden="true"></button>')
|
|
45
|
+
$topLevelItem.on('click', '.collapsible__toggle', function(e) {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
var $parent = $(this).parent();
|
|
48
|
+
toggleHeading($parent);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function toggleHeading($topLevelItem) {
|
|
54
|
+
var isOpen = $topLevelItem.hasClass('is-open');
|
|
55
|
+
var $heading = $topLevelItem.find('> a');
|
|
56
|
+
var $body = $topLevelItem.find('.collapsible__body');
|
|
57
|
+
var $toggleLabel = $topLevelItem.find('.collapsible__toggle-label');
|
|
58
|
+
|
|
59
|
+
$topLevelItem.toggleClass('is-open', !isOpen);
|
|
60
|
+
$body.attr('aria-expanded', isOpen ? 'false' : 'true');
|
|
61
|
+
$toggleLabel.text(isOpen ? 'Expand ' + $heading.text() : 'Collapse ' + $heading.text());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function openActiveHeading() {
|
|
65
|
+
var $activeElement;
|
|
66
|
+
var currentPath = window.location.pathname;
|
|
67
|
+
var isActiveTrail = '[href*="' + currentPath + '"]';
|
|
68
|
+
// Add an exception for the root page, as every href includes /
|
|
69
|
+
if(currentPath == '/') {
|
|
70
|
+
isActiveTrail = '[href="' + currentPath + window.location.hash + '"]'
|
|
71
|
+
}
|
|
72
|
+
for (var i = $topLevelItems.length - 1; i >= 0; i--) {
|
|
73
|
+
var $element = $($topLevelItems[i]);
|
|
74
|
+
var $heading = $element.find('> a');
|
|
75
|
+
// Check if this item href matches
|
|
76
|
+
if($heading.is(isActiveTrail)) {
|
|
77
|
+
$activeElement = $element;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
// Otherwise check the children
|
|
81
|
+
var $children = $element.find('li > a');
|
|
82
|
+
var $matchingChildren = $children.filter(isActiveTrail);
|
|
83
|
+
if ($matchingChildren.length) {
|
|
84
|
+
$activeElement = $element;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if($activeElement && !$activeElement.hasClass('is-open')) {
|
|
89
|
+
toggleHeading($activeElement);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
};
|
|
95
|
+
})(jQuery, window.GOVUK.Modules);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
(function($, Modules) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Modules.InPageNavigation = function InPageNavigation() {
|
|
5
|
+
var $tocPane;
|
|
6
|
+
var $contentPane;
|
|
7
|
+
var $tocItems;
|
|
8
|
+
var $targets;
|
|
9
|
+
|
|
10
|
+
this.start = function start($element) {
|
|
11
|
+
$tocPane = $element.find('.app-pane__toc');
|
|
12
|
+
$contentPane = $element.find('.app-pane__content');
|
|
13
|
+
$tocItems = $('.js-toc-list').find('a');
|
|
14
|
+
$targets = $contentPane.find('[id]');
|
|
15
|
+
|
|
16
|
+
$contentPane.on('scroll', _.debounce(handleScrollEvent, 100, { maxWait: 100 }));
|
|
17
|
+
|
|
18
|
+
if (Modernizr.history) {
|
|
19
|
+
// Popstate is triggered when using the back button to navigate 'within'
|
|
20
|
+
// the page, i.e. changing the anchor part of the URL.
|
|
21
|
+
$(window).on('popstate', function (event) {
|
|
22
|
+
restoreScrollPosition(event.originalEvent.state);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (history.state && history.state.scrollTop) {
|
|
26
|
+
// Restore existing state when e.g. using the back button to return to
|
|
27
|
+
// this page
|
|
28
|
+
restoreScrollPosition(history.state);
|
|
29
|
+
} else {
|
|
30
|
+
// Store the initial position so that we can restore it even if we
|
|
31
|
+
// never scroll.
|
|
32
|
+
handleInitialLoadEvent();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function restoreScrollPosition(state) {
|
|
38
|
+
if (state && typeof state.scrollTop !== 'undefined') {
|
|
39
|
+
$contentPane.scrollTop(state.scrollTop);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function handleInitialLoadEvent() {
|
|
44
|
+
var fragment = fragmentForTargetElement();
|
|
45
|
+
|
|
46
|
+
if (!fragment) {
|
|
47
|
+
fragment = fragmentForFirstElementInView();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
handleChangeInActiveItem(fragment);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function handleScrollEvent() {
|
|
54
|
+
handleChangeInActiveItem(fragmentForFirstElementInView());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function handleChangeInActiveItem(fragment) {
|
|
58
|
+
storeCurrentPositionInHistoryApi(fragment);
|
|
59
|
+
highlightActiveItemInToc(fragment);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function storeCurrentPositionInHistoryApi(fragment) {
|
|
63
|
+
if (Modernizr.history && fragment) {
|
|
64
|
+
history.replaceState(
|
|
65
|
+
{ scrollTop: $contentPane.scrollTop() },
|
|
66
|
+
"",
|
|
67
|
+
fragment
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function highlightActiveItemInToc(fragment) {
|
|
73
|
+
// Navigation items for single page navigation don't necessarily include the path name, but
|
|
74
|
+
// navigation items for multipage navigation items do include it. This checks for either case.
|
|
75
|
+
var $activeTocItem = $tocItems.filter(
|
|
76
|
+
'[href="' + window.location.pathname + fragment + '"],[href="' + fragment + '"]'
|
|
77
|
+
);
|
|
78
|
+
// Navigation items with children don't contain fragments in their url
|
|
79
|
+
// Check to see if any nav items contain just the path name.
|
|
80
|
+
if(!$activeTocItem.get(0)) {
|
|
81
|
+
$activeTocItem = $tocItems.filter('[href="' + window.location.pathname + '"]');
|
|
82
|
+
}
|
|
83
|
+
if ($activeTocItem.get(0)) {
|
|
84
|
+
$tocItems.removeClass('toc-link--in-view');
|
|
85
|
+
$activeTocItem.addClass('toc-link--in-view');
|
|
86
|
+
scrollTocToActiveItem($activeTocItem);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function scrollTocToActiveItem($activeTocItem) {
|
|
91
|
+
var paneHeight = $tocPane.height();
|
|
92
|
+
var linkTop = $activeTocItem.position().top;
|
|
93
|
+
var linkBottom = linkTop + $activeTocItem.outerHeight();
|
|
94
|
+
|
|
95
|
+
var offset = null;
|
|
96
|
+
|
|
97
|
+
if (linkTop < 0) {
|
|
98
|
+
offset = linkTop;
|
|
99
|
+
} else if (linkBottom >= paneHeight) {
|
|
100
|
+
offset = -(paneHeight - linkBottom);
|
|
101
|
+
} else {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var newScrollTop = $tocPane.scrollTop() + offset;
|
|
106
|
+
|
|
107
|
+
$tocPane.scrollTop(newScrollTop);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function fragmentForTargetElement() {
|
|
111
|
+
return window.location.hash;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function fragmentForFirstElementInView() {
|
|
115
|
+
var result = null;
|
|
116
|
+
|
|
117
|
+
$($targets.get().reverse()).each(function checkIfInView(index) {
|
|
118
|
+
if (result) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
var $this = $(this);
|
|
123
|
+
|
|
124
|
+
if (Math.floor($this.position().top) <= 0) {
|
|
125
|
+
result = $this;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return result ? '#' + result.attr('id') : false;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
})(jQuery, window.GOVUK.Modules);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
(function($, Modules) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Modules.Navigation = function () {
|
|
5
|
+
var $html = $('html');
|
|
6
|
+
|
|
7
|
+
var $navToggle;
|
|
8
|
+
var $nav;
|
|
9
|
+
|
|
10
|
+
this.start = function ($element) {
|
|
11
|
+
$navToggle = $('.js-nav-toggle', $element);
|
|
12
|
+
$nav = $('.js-nav', $element);
|
|
13
|
+
|
|
14
|
+
updateAriaAttributes();
|
|
15
|
+
|
|
16
|
+
$navToggle.on('click', toggleNavigation);
|
|
17
|
+
$(window).on('resize', updateAriaAttributes);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function updateAriaAttributes() {
|
|
21
|
+
var navIsVisible = $nav.is(':visible');
|
|
22
|
+
|
|
23
|
+
$navToggle.attr('aria-expanded', navIsVisible ? 'true' : 'false');
|
|
24
|
+
$nav.attr('aria-hidden', navIsVisible ? 'false' : 'true');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function toggleNavigation() {
|
|
28
|
+
var navIsVisible = !$html.hasClass('nav-open');
|
|
29
|
+
|
|
30
|
+
$html.toggleClass('nav-open', navIsVisible);
|
|
31
|
+
updateAriaAttributes();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
})(jQuery, window.GOVUK.Modules);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
(function($, Modules) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
Modules.PageExpiry = function PageExpiry() {
|
|
5
|
+
this.start = function start($element) {
|
|
6
|
+
var rawDate = $element.data('last-reviewed-on');
|
|
7
|
+
var isExpired = Date.parse(rawDate) < new Date();
|
|
8
|
+
|
|
9
|
+
if (isExpired) {
|
|
10
|
+
$element.find('.page-expiry--not-expired').hide(0);
|
|
11
|
+
$element.find('.page-expiry--expired').show(0);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
})(jQuery, window.GOVUK.Modules);
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
//= require lunr.min
|
|
2
|
+
//= require _vendor/jquery.mark.js
|
|
3
|
+
(function($, Modules) {
|
|
4
|
+
'use strict';
|
|
5
|
+
|
|
6
|
+
Modules.Search = function Search() {
|
|
7
|
+
var s = this;
|
|
8
|
+
var $html = $('html');
|
|
9
|
+
s.lunrIndex;
|
|
10
|
+
s.lunrData;
|
|
11
|
+
var $searchForm;
|
|
12
|
+
var $searchLabel;
|
|
13
|
+
var $searchInput;
|
|
14
|
+
var $searchResults;
|
|
15
|
+
var $searchResultsTitle;
|
|
16
|
+
var $searchResultsWrapper;
|
|
17
|
+
var $searchResultsClose;
|
|
18
|
+
var results;
|
|
19
|
+
var query;
|
|
20
|
+
var queryTimer;
|
|
21
|
+
var maxSearchEntries = 20;
|
|
22
|
+
|
|
23
|
+
this.start = function start($element) {
|
|
24
|
+
$searchForm = $element.find('form');
|
|
25
|
+
$searchInput = $element.find('#search');
|
|
26
|
+
$searchLabel = $element.find('.search__label');
|
|
27
|
+
$searchResultsWrapper = $element.find('.search-results')
|
|
28
|
+
$searchResults = $searchResultsWrapper.find('.search-results__content');
|
|
29
|
+
$searchResultsTitle = $searchResultsWrapper.find('.search-results__title');
|
|
30
|
+
$searchResultsClose = $searchResultsWrapper.find('.search-results__close');
|
|
31
|
+
s.downloadSearchIndex();
|
|
32
|
+
attach();
|
|
33
|
+
changeSearchLabel();
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
this.downloadSearchIndex = function downloadSearchIndex() {
|
|
37
|
+
updateTitle('Loading search index')
|
|
38
|
+
$.ajax({
|
|
39
|
+
url: '/search.json',
|
|
40
|
+
cache: true,
|
|
41
|
+
method: 'GET',
|
|
42
|
+
success: function(data) {
|
|
43
|
+
s.lunrData = data;
|
|
44
|
+
s.lunrIndex = lunr.Index.load(s.lunrData.index);
|
|
45
|
+
replaceStopWordFilter();
|
|
46
|
+
$(document).trigger('lunrIndexLoaded');
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function attach() {
|
|
52
|
+
// Search functionality on search text input
|
|
53
|
+
$searchInput.on('input', function (e) {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
query = $(this).val();
|
|
56
|
+
s.search(query, function(r) {
|
|
57
|
+
results = r;
|
|
58
|
+
renderResults(query);
|
|
59
|
+
updateTitle();
|
|
60
|
+
});
|
|
61
|
+
if(window.ga) {
|
|
62
|
+
window.clearTimeout(queryTimer);
|
|
63
|
+
queryTimer = window.setTimeout(sendQueryToAnalytics, 1000);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Set focus on the first search result instead of submiting the search
|
|
68
|
+
// form to Google
|
|
69
|
+
$searchForm.on('submit', function(e) {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
showResults();
|
|
72
|
+
$searchResults.find('.search-result__title a').first().focus();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Closing the search results, move focus back to the search input
|
|
76
|
+
$searchResultsClose.on('click', function(e) {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
$searchInput.focus();
|
|
79
|
+
hideResults();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Attach analytics events to search result clicks
|
|
83
|
+
if(window.ga) {
|
|
84
|
+
$searchResults.on('click', '.search-result__title a', function() {
|
|
85
|
+
var href = $(this).attr('href');
|
|
86
|
+
ga('send', {
|
|
87
|
+
hitType: 'event',
|
|
88
|
+
eventCategory: 'Search result',
|
|
89
|
+
eventAction: 'click',
|
|
90
|
+
eventLabel: href,
|
|
91
|
+
transport: 'beacon'
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// When selecting navigation link, close the search results.
|
|
97
|
+
$('.toc').on('click','a', function(e) {
|
|
98
|
+
hideResults();
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function changeSearchLabel() {
|
|
103
|
+
$searchLabel.text('Search');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getResults(query) {
|
|
107
|
+
var results = [];
|
|
108
|
+
s.lunrIndex.search(query).forEach( function (item, index) {
|
|
109
|
+
if ( index < maxSearchEntries ) {
|
|
110
|
+
results.push(s.lunrData.docs[item.ref]);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.search = function search(query, callback) {
|
|
117
|
+
if(query === '') {
|
|
118
|
+
hideResults();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
showResults();
|
|
122
|
+
// The index has not been downloaded yet, exit early and wait.
|
|
123
|
+
if(!s.lunrIndex) {
|
|
124
|
+
$(document).on('lunrIndexLoaded', function() {
|
|
125
|
+
s.search(query, callback);
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
callback(getResults(query));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function renderResults(query) {
|
|
133
|
+
var output = '';
|
|
134
|
+
if (results.length == 0) {
|
|
135
|
+
output += '<p>Nothing found</p>';
|
|
136
|
+
}
|
|
137
|
+
output += '<ul>';
|
|
138
|
+
for(var index in results) {
|
|
139
|
+
var result = results[index];
|
|
140
|
+
var content = s.processContent(result.content, query);
|
|
141
|
+
output += '<li class="search-result">';
|
|
142
|
+
output += '<h3 class="search-result__title">';
|
|
143
|
+
output += '<a href="' + result.url + '">';
|
|
144
|
+
output += result.title;
|
|
145
|
+
output += '</a>';
|
|
146
|
+
output += '</h3>';
|
|
147
|
+
if(typeof content !== 'undefined') {
|
|
148
|
+
output += '<p>' + content + '</p>';
|
|
149
|
+
}
|
|
150
|
+
output += '</li>';
|
|
151
|
+
}
|
|
152
|
+
output += '</ul>';
|
|
153
|
+
|
|
154
|
+
$searchResults.html( output );
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.processContent = function processContent(content, query) {
|
|
158
|
+
var output;
|
|
159
|
+
content = '<div>'+ content + '</div>';
|
|
160
|
+
content = $(content).mark(query);
|
|
161
|
+
|
|
162
|
+
// Split content by sentence.
|
|
163
|
+
var sentences = content.html().replace(/(\.+|\:|\!|\?|\r|\n)(\"*|\'*|\)*|}*|]*)/gm, "|").split("|");
|
|
164
|
+
|
|
165
|
+
// Select the first five sentences that contain a <mark>
|
|
166
|
+
var selectedSentences = [];
|
|
167
|
+
for (var i = 0; i < sentences.length; i++) {
|
|
168
|
+
if(selectedSentences.length === 5) {
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
var sentence = sentences[i].trim();
|
|
173
|
+
var containsMark = sentence.includes('mark>');
|
|
174
|
+
if (containsMark && (selectedSentences.indexOf(sentence) == -1)) {
|
|
175
|
+
selectedSentences.push(sentence);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if(selectedSentences.length > 0) {
|
|
179
|
+
output = ' … ' + selectedSentences.join(' … ') + ' … ';
|
|
180
|
+
}
|
|
181
|
+
return output;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Default text is to display the number of search results
|
|
185
|
+
function updateTitle(text) {
|
|
186
|
+
if(typeof text == "undefined") {
|
|
187
|
+
var count = results.length;
|
|
188
|
+
var text = count + ' results';
|
|
189
|
+
}
|
|
190
|
+
$searchResultsTitle.text(text);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function showResults() {
|
|
194
|
+
$searchResultsWrapper.addClass('is-open')
|
|
195
|
+
.attr('aria-hidden', 'false');
|
|
196
|
+
$html.addClass('has-search-results-open');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function hideResults() {
|
|
200
|
+
$searchResultsWrapper.removeClass('is-open')
|
|
201
|
+
.attr('aria-hidden', 'true');
|
|
202
|
+
$html.removeClass('has-search-results-open');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function sendQueryToAnalytics() {
|
|
206
|
+
if(query === '') {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
var stripped = window.stripPIIFromString(query)
|
|
210
|
+
ga('send', {
|
|
211
|
+
hitType: 'event',
|
|
212
|
+
eventCategory: 'Search query',
|
|
213
|
+
eventAction: 'type',
|
|
214
|
+
eventLabel: stripped,
|
|
215
|
+
transport: 'beacon'
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function replaceStopWordFilter() {
|
|
220
|
+
// Replace the default stopWordFilter as it excludes useful words like
|
|
221
|
+
// 'get'
|
|
222
|
+
// See: https://lunrjs.com/docs/stop_word_filter.js.html#line43
|
|
223
|
+
s.lunrIndex.pipeline.remove(lunr.stopWordFilter);
|
|
224
|
+
s.lunrIndex.pipeline.add(s.govukStopWorldFilter);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
this.govukStopWorldFilter = lunr.generateStopWordFilter([
|
|
228
|
+
'a',
|
|
229
|
+
'able',
|
|
230
|
+
'about',
|
|
231
|
+
'across',
|
|
232
|
+
'after',
|
|
233
|
+
'all',
|
|
234
|
+
'almost',
|
|
235
|
+
'also',
|
|
236
|
+
'am',
|
|
237
|
+
'among',
|
|
238
|
+
'an',
|
|
239
|
+
'and',
|
|
240
|
+
'any',
|
|
241
|
+
'are',
|
|
242
|
+
'as',
|
|
243
|
+
'at',
|
|
244
|
+
'be',
|
|
245
|
+
'because',
|
|
246
|
+
'been',
|
|
247
|
+
'but',
|
|
248
|
+
'by',
|
|
249
|
+
'can',
|
|
250
|
+
'cannot',
|
|
251
|
+
'could',
|
|
252
|
+
'dear',
|
|
253
|
+
'did',
|
|
254
|
+
'do',
|
|
255
|
+
'does',
|
|
256
|
+
'either',
|
|
257
|
+
'else',
|
|
258
|
+
'ever',
|
|
259
|
+
'every',
|
|
260
|
+
'for',
|
|
261
|
+
'from',
|
|
262
|
+
'got',
|
|
263
|
+
'had',
|
|
264
|
+
'has',
|
|
265
|
+
'have',
|
|
266
|
+
'he',
|
|
267
|
+
'her',
|
|
268
|
+
'hers',
|
|
269
|
+
'him',
|
|
270
|
+
'his',
|
|
271
|
+
'how',
|
|
272
|
+
'however',
|
|
273
|
+
'i',
|
|
274
|
+
'if',
|
|
275
|
+
'in',
|
|
276
|
+
'into',
|
|
277
|
+
'is',
|
|
278
|
+
'it',
|
|
279
|
+
'its',
|
|
280
|
+
'just',
|
|
281
|
+
'least',
|
|
282
|
+
'let',
|
|
283
|
+
'like',
|
|
284
|
+
'likely',
|
|
285
|
+
'may',
|
|
286
|
+
'me',
|
|
287
|
+
'might',
|
|
288
|
+
'most',
|
|
289
|
+
'must',
|
|
290
|
+
'my',
|
|
291
|
+
'neither',
|
|
292
|
+
'no',
|
|
293
|
+
'nor',
|
|
294
|
+
'not',
|
|
295
|
+
'of',
|
|
296
|
+
'off',
|
|
297
|
+
'often',
|
|
298
|
+
'on',
|
|
299
|
+
'only',
|
|
300
|
+
'or',
|
|
301
|
+
'other',
|
|
302
|
+
'our',
|
|
303
|
+
'own',
|
|
304
|
+
'rather',
|
|
305
|
+
'said',
|
|
306
|
+
'say',
|
|
307
|
+
'says',
|
|
308
|
+
'she',
|
|
309
|
+
'should',
|
|
310
|
+
'since',
|
|
311
|
+
'so',
|
|
312
|
+
'some',
|
|
313
|
+
'than',
|
|
314
|
+
'that',
|
|
315
|
+
'the',
|
|
316
|
+
'their',
|
|
317
|
+
'them',
|
|
318
|
+
'then',
|
|
319
|
+
'there',
|
|
320
|
+
'these',
|
|
321
|
+
'they',
|
|
322
|
+
'this',
|
|
323
|
+
'tis',
|
|
324
|
+
'to',
|
|
325
|
+
'too',
|
|
326
|
+
'twas',
|
|
327
|
+
'us',
|
|
328
|
+
'wants',
|
|
329
|
+
'was',
|
|
330
|
+
'we',
|
|
331
|
+
'were',
|
|
332
|
+
'what',
|
|
333
|
+
'when',
|
|
334
|
+
'where',
|
|
335
|
+
'which',
|
|
336
|
+
'while',
|
|
337
|
+
'who',
|
|
338
|
+
'whom',
|
|
339
|
+
'why',
|
|
340
|
+
'will',
|
|
341
|
+
'with',
|
|
342
|
+
'would',
|
|
343
|
+
'yet',
|
|
344
|
+
'you',
|
|
345
|
+
'your'
|
|
346
|
+
])
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// Polyfill includes
|
|
350
|
+
if (!String.prototype.includes) {
|
|
351
|
+
String.prototype.includes = function(search, start) {
|
|
352
|
+
'use strict';
|
|
353
|
+
if (typeof start !== 'number') {
|
|
354
|
+
start = 0;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (start + search.length > this.length) {
|
|
358
|
+
return false;
|
|
359
|
+
} else {
|
|
360
|
+
return this.indexOf(search, start) !== -1;
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
})(jQuery, window.GOVUK.Modules);
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|