govuk_tech_docs 1.5.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/CHANGELOG.md +90 -0
  4. data/docs/configuration.md +15 -0
  5. data/docs/frontmatter.md +2 -14
  6. data/docs/page-expiry.md +69 -0
  7. data/example/Gemfile +1 -0
  8. data/example/config/tech-docs.yml +6 -0
  9. data/example/source/api-path.html.md +7 -0
  10. data/example/source/api-reference.html.md +5 -0
  11. data/example/source/pets.yml +106 -0
  12. data/govuk_tech_docs.gemspec +2 -0
  13. data/lib/assets/javascripts/_analytics.js +12 -0
  14. data/lib/assets/javascripts/_modules/collapsible-navigation.js +5 -3
  15. data/lib/assets/javascripts/_modules/search.js +175 -6
  16. data/lib/assets/stylesheets/modules/_collapsible.scss +12 -5
  17. data/lib/assets/stylesheets/modules/_technical-documentation.scss +16 -11
  18. data/lib/assets/stylesheets/modules/_toc.scss +1 -1
  19. data/lib/govuk_tech_docs.rb +13 -2
  20. data/lib/govuk_tech_docs/api_reference/api_reference_extension.rb +100 -0
  21. data/lib/govuk_tech_docs/api_reference/api_reference_renderer.rb +279 -0
  22. data/lib/govuk_tech_docs/api_reference/templates/api_reference_full.html.erb +9 -0
  23. data/lib/govuk_tech_docs/api_reference/templates/operation.html.erb +11 -0
  24. data/lib/govuk_tech_docs/api_reference/templates/parameters.html.erb +28 -0
  25. data/lib/govuk_tech_docs/api_reference/templates/path.html.erb +4 -0
  26. data/lib/govuk_tech_docs/api_reference/templates/responses.html.erb +33 -0
  27. data/lib/govuk_tech_docs/api_reference/templates/schema.html.erb +29 -0
  28. data/lib/govuk_tech_docs/page_review.rb +15 -3
  29. data/lib/govuk_tech_docs/pages.rb +3 -2
  30. data/lib/govuk_tech_docs/tech_docs_html_renderer.rb +10 -0
  31. data/lib/govuk_tech_docs/version.rb +1 -1
  32. data/lib/source/layouts/_header.erb +2 -4
  33. metadata +42 -4
  34. data/lib/source/images/arrow-down.svg +0 -9
  35. data/lib/source/images/arrow-up.svg +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a0b627893fa39ba6cc6484a977b7b9c00639ad28
4
- data.tar.gz: a45ebcff76937b949bb5c7388359bbd26cbe1d53
3
+ metadata.gz: 7e106d8464ee73119270e939801832a1e49118a7
4
+ data.tar.gz: 334c5c672945bd52df62772578ccb862356cafd1
5
5
  SHA512:
6
- metadata.gz: fe4b821226d40bff2fd3d2eff34887862e74eda39c00ef058f512b448f995d1d0181aff90e3448c5493108dbae2eb76d5ee34746a5c7077adb4fede3107c3553
7
- data.tar.gz: b9e34c88cfc72c46f4cab5e73bd3946e4475e6a1a3a42f2140ef8fb024f48f57ef6f8e57a8f1dc5b47aef41a9ccf7f07bb7ce080d6cb7f81a806da52d5ee8881
6
+ metadata.gz: b507269b3da54e98e28db73fb89a0d4f2f2e1032f0829f2c3a7acc2401eb4f24b37e6776c16b6fdd0bfe49693a3df270ca8a68fcc162a78bbc380f5ec68d68f1
7
+ data.tar.gz: d825189334f2ea5612ca72836d2259a7d983c0b20ea5a559f7c1c9e61d47647700964ab20dca97d2f66bd3e2cf187681725becab2cb089e2343a7dfa49b89cf0
@@ -6,3 +6,6 @@ Naming/HeredocDelimiterNaming:
6
6
 
7
7
  Lint/NestedMethodDefinition:
8
8
  Enabled: false
9
+
10
+ Performance/HashEachMethods:
11
+ Enabled: false
@@ -1,5 +1,95 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 1.6.0
6
+
7
+ Version 1.6.0 adds API reference generation, and improves the search function.
8
+
9
+ ### New feature: API reference
10
+
11
+ Specify an OpenAPI spec file using `api_path`, for example: `api_path: source/pets.yml`.
12
+
13
+ To output the generated content in the page of your choice, write `api>` in any markdown parsed file (.md, .html.md, .html.md.erb). You can also specify to just print a specific path: `api> /pets`
14
+
15
+ This generates request and response schemas and examples for each API operation.
16
+
17
+ More info:
18
+
19
+ - https://github.com/alphagov/tech-docs-gem/pull/40
20
+ - https://github.com/alphagov/tech-docs-gem/pull/48
21
+
22
+ ### Search result improvements
23
+
24
+ The search indexing pipeline has been tweaked to provide expected results. The display of the search results has also been
25
+
26
+ If you're using search then you'll need to add this line to your project `Gemfile`:
27
+
28
+ ```
29
+ gem 'middleman-search', git: 'git://github.com/alphagov/middleman-search.git'
30
+ ```
31
+
32
+ More info:
33
+ - https://github.com/alphagov/tech-docs-gem/pull/37
34
+ - https://github.com/alphagov/tech-docs-gem/pull/38
35
+ - https://github.com/alphagov/tech-docs-gem/pull/41
36
+ - https://github.com/alphagov/tech-docs-gem/pull/42
37
+ - https://github.com/alphagov/tech-docs-gem/pull/43
38
+
39
+ ### Correct fork of middleman in example project
40
+
41
+ The example gem project now uses alphagov/middleman-search. This prevents `bundle exec middleman serve` from crashing.
42
+
43
+ More info:
44
+ - https://github.com/alphagov/tech-docs-gem/pull/35
45
+
46
+ ### Accessibility improvements
47
+
48
+ Accessibility improvements to search, collapsible navigation, and the logo.
49
+
50
+ More info:
51
+ - https://github.com/alphagov/tech-docs-gem/pull/36
52
+ - https://github.com/alphagov/tech-docs-gem/pull/33
53
+
54
+ ### Additional configuration options for review system
55
+
56
+ `default_owner_slack` and `owner_slack_workspace` are now configurable for use within the review system.
57
+
58
+ More info:
59
+ - https://github.com/alphagov/tech-docs-gem/pull/47
60
+
61
+ ### Correct fork of middleman in example project
62
+
63
+ The example gem project now uses alphagov/middleman-search. This prevents `bundle exec middleman serve` from crashing.
64
+
65
+ More info:
66
+ - https://github.com/alphagov/tech-docs-gem/pull/35
67
+
68
+ ### Accessibility improvements
69
+
70
+ Accessibility improvements to search, collapsible navigation, and the logo.
71
+
72
+ More info:
73
+ - https://github.com/alphagov/tech-docs-gem/pull/36
74
+ - https://github.com/alphagov/tech-docs-gem/pull/33
75
+
76
+ ### Search result improvements
77
+
78
+ The search indexing pipeline has been tweaked to provide expected results. The display of the search results has also been improved.
79
+
80
+ If you're using search then you'll need to add this line to your project `Gemfile`:
81
+
82
+ ```
83
+ gem 'middleman-search', git: 'git://github.com/alphagov/middleman-search.git'
84
+ ```
85
+
86
+ More info:
87
+ - https://github.com/alphagov/tech-docs-gem/pull/37
88
+ - https://github.com/alphagov/tech-docs-gem/pull/38
89
+ - https://github.com/alphagov/tech-docs-gem/pull/41
90
+ - https://github.com/alphagov/tech-docs-gem/pull/42
91
+ - https://github.com/alphagov/tech-docs-gem/pull/43
92
+
3
93
  ## 1.5.0
4
94
 
5
95
  ### New feature: Search
@@ -148,3 +148,18 @@ default: `true`
148
148
  ```yaml
149
149
  show_govuk_logo: true
150
150
  ```
151
+
152
+ ## `api_path`
153
+
154
+ Define a path to an Open API V3 spec file. This can be a relative file path or a URI to a raw file.
155
+
156
+ ```yaml
157
+ api_path: ./source/pets.yml
158
+ ```
159
+
160
+ ## `owner_slack_workspace` and `default_owner_slack`
161
+
162
+ These attributes are used to specify the owner of a page. See the separate
163
+ [documentation for page expiry][expiry] for more details.
164
+
165
+ [expiry]: https://alphagov.github.io/tech-docs-manual/#page-expiry-and-review-notices
@@ -5,24 +5,12 @@
5
5
  ## `last_reviewed_on` and `review_in`
6
6
 
7
7
  These attributes determine the date when the page needs to be reviewed next.
8
+ See the separate [documentation for page expiry][expiry] for more details.
8
9
 
9
- If the page doesn't need to be reviewed, we show a blue box with the last-reviewed date, when it needs review again, and the owner.
10
-
11
- ![](not-expired-page.png)
12
-
13
- If the page needs to be reviewed, we show a red box saying the page might not be accurate.
14
-
15
- ![](expired-page.png)
10
+ [expiry]: https://alphagov.github.io/tech-docs-manual/#page-expiry-and-review-notices
16
11
 
17
12
  Example:
18
13
 
19
- ```yaml
20
- ---
21
- last_reviewed_on: 2018-01-18
22
- review_in: 6 weeks
23
- ---
24
- ```
25
-
26
14
  You can use this in combination with [owner_slack](#owner-slack) to set an owner for the page.
27
15
 
28
16
  ## `layout`
@@ -0,0 +1,69 @@
1
+ # Page Expiry and Review Notices
2
+
3
+ It's possible to include frontmatter configuration for a page to set an
4
+ expiration date for a piece of content.
5
+
6
+ If the page doesn't need to be reviewed, we show a blue box with the
7
+ last-reviewed date, when it needs review again, and the owner.
8
+
9
+ ![](not-expired-page.png)
10
+
11
+ If the page needs to be reviewed, we show a red box saying the page might not
12
+ be accurate.
13
+
14
+ ![](expired-page.png)
15
+
16
+ This feature relies on JavaScript being enabled on the user's browser to
17
+ display the relevant notices.
18
+
19
+
20
+ ## Frontmatter configuration
21
+
22
+ ### `last_reviewed_on` and `review_in`
23
+
24
+ These attributes determine the date when the page needs to be reviewed next.
25
+
26
+ ```yaml
27
+ ---
28
+ last_reviewed_on: 2018-01-18
29
+ review_in: 6 weeks
30
+ ---
31
+ ```
32
+
33
+ You can use this in combination with `owner_slack` or `default_owner_slack` to
34
+ set an owner for the page.
35
+
36
+ ### `owner_slack`
37
+
38
+ The Slack username or channel of the page owner. This can be used to appoint an
39
+ individual or team as responsible for keeping the page up to date.
40
+
41
+
42
+ ## Global configuration
43
+
44
+ ### `owner_slack_workspace`
45
+
46
+ The Slack workspace name used when linking to the Slack owner of a piece of
47
+ content. If not provided, the owner of a piece of content (channel or user)
48
+ won't be linked.
49
+
50
+ ```yaml
51
+ owner_slack_workspace: gds
52
+ ```
53
+
54
+ ### `default_owner_slack`
55
+
56
+ The default Slack user or channel name to show as the owner of a piece of
57
+ content. Can be overridden using the `owner_slack` frontmatter config option.
58
+
59
+ ```yaml
60
+ default_owner_slack: '#owner'
61
+ ```
62
+
63
+
64
+ ## Page API
65
+
66
+ The expiry date for each page is also shown in the `/api/pages.json`
67
+ representation of all pages. This is used by the
68
+ [tech-docs-notifier](https://github.com/alphagov/tech-docs-notifier) to post
69
+ messages to Slack when pages have expired.
@@ -1,3 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'govuk_tech_docs', path: '..'
4
+ gem 'middleman-search', git: 'git://github.com/alphagov/middleman-search.git'
@@ -41,3 +41,9 @@ github_repo: alphagov/example-repo
41
41
 
42
42
  redirects:
43
43
  /something/old.html: /index.html
44
+
45
+ api_path: source/pets.yml
46
+
47
+ # Optional global settings for the page review process
48
+ owner_slack_workspace: gds
49
+ default_owner_slack: '#2nd-line'
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: API /Pets
3
+ ---
4
+
5
+ # API /Pets
6
+
7
+ api> /pets
@@ -0,0 +1,5 @@
1
+ ---
2
+ title: Example API Petstore
3
+ ---
4
+
5
+ api>
@@ -0,0 +1,106 @@
1
+ openapi: "3.0.0"
2
+ info:
3
+ version: 1.0.0
4
+ title: Swagger Petstore
5
+ description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification
6
+ license:
7
+ name: MIT
8
+ servers:
9
+ - url: http://petstore.swagger.io/v1
10
+ paths:
11
+ /pets:
12
+ get:
13
+ summary: List all pets
14
+ operationId: listPets
15
+ tags:
16
+ - pets
17
+ parameters:
18
+ - name: limit
19
+ in: query
20
+ description: How many items to return at one time (max 100)
21
+ required: false
22
+ schema:
23
+ type: integer
24
+ format: int32
25
+ responses:
26
+ '200':
27
+ description: A paged array of pets
28
+ headers:
29
+ content:
30
+ application/json:
31
+ schema:
32
+ $ref: "#/components/schemas/Pets"
33
+ default:
34
+ description: unexpected error
35
+ content:
36
+ application/json:
37
+ schema:
38
+ $ref: "#/components/schemas/Error"
39
+ post:
40
+ summary: Create a pet
41
+ operationId: createPets
42
+ tags:
43
+ - pets
44
+ responses:
45
+ '201':
46
+ description: Null response
47
+ default:
48
+ description: unexpected error
49
+ content:
50
+ application/json:
51
+ schema:
52
+ $ref: "#/components/schemas/Error"
53
+ /pets/{petId}:
54
+ get:
55
+ summary: Info for a specific pet
56
+ operationId: showPetById
57
+ tags:
58
+ - pets
59
+ parameters:
60
+ - name: petId
61
+ in: path
62
+ required: true
63
+ description: The id of the pet to retrieve
64
+ schema:
65
+ type: string
66
+ responses:
67
+ '200':
68
+ description: Expected response to a valid request
69
+ content:
70
+ application/json:
71
+ schema:
72
+ $ref: "#/components/schemas/Pets"
73
+ default:
74
+ description: unexpected error
75
+ content:
76
+ application/json:
77
+ schema:
78
+ $ref: "#/components/schemas/Error"
79
+ components:
80
+ schemas:
81
+ Pet:
82
+ required:
83
+ - id
84
+ - name
85
+ properties:
86
+ id:
87
+ type: integer
88
+ format: int64
89
+ name:
90
+ type: string
91
+ tag:
92
+ type: string
93
+ Pets:
94
+ type: array
95
+ items:
96
+ $ref: "#/components/schemas/Pet"
97
+ Error:
98
+ required:
99
+ - code
100
+ - message
101
+ properties:
102
+ code:
103
+ type: integer
104
+ format: int32
105
+ message:
106
+ type: string
@@ -32,6 +32,8 @@ Gem::Specification.new do |spec|
32
32
  spec.add_dependency "middleman-search"
33
33
  spec.add_dependency "nokogiri"
34
34
  spec.add_dependency "redcarpet", "~> 3.3.2"
35
+ spec.add_dependency "openapi3_parser"
36
+ spec.add_dependency "pry"
35
37
 
36
38
 
37
39
  spec.add_development_dependency "bundler", "~> 1.15"
@@ -42,5 +42,17 @@
42
42
  $('.header a').on('click', linkTrackingEventHandler('topNavigationClick'));
43
43
  $('.toc a').on('click', linkTrackingEventHandler('tableOfContentsNavigationClick'));
44
44
  catchBrokenFragmentLinks();
45
+
46
+ // Borrowed from:
47
+ // https://github.com/alphagov/govuk_frontend_toolkit/blob/master/javascripts/govuk/analytics/analytics.js
48
+ window.stripPIIFromString = function (string) {
49
+ var EMAIL_PATTERN = /[^\s=/?&]+(?:@|%40)[^\s=/?&]+/g
50
+ var POSTCODE_PATTERN = /[A-PR-UWYZ][A-HJ-Z]?[0-9][0-9A-HJKMNPR-Y]?(?:[\s+]|%20)*[0-9][ABD-HJLNPQ-Z]{2}/gi
51
+ var DATE_PATTERN = /\d{4}(-?)\d{2}(-?)\d{2}/g
52
+ var stripped = string.replace(EMAIL_PATTERN, '[email]')
53
+ .replace(DATE_PATTERN, '[date]')
54
+ .replace(POSTCODE_PATTERN, '[postcode]');
55
+ return stripped
56
+ }
45
57
  });
46
58
  })(jQuery);
@@ -31,15 +31,17 @@
31
31
  var $topLevelItem = $($topLevelItems[i]);
32
32
  var $heading = $topLevelItem.find('> a');
33
33
  var $listing = $topLevelItem.find('> ul');
34
+ var id = 'toc-' + $heading.text().toLowerCase().replace(' ', '-');
34
35
  // Only add collapsible functionality if there are children.
35
36
  if ($listing.length == 0) {
36
37
  continue;
37
38
  }
38
39
  $topLevelItem.addClass('collapsible');
39
40
  $listing.addClass('collapsible__body')
41
+ .attr('id', id)
40
42
  .attr('aria-expanded', 'false');
41
43
  $heading.addClass('collapsible__heading')
42
- .after('<button class="collapsible__toggle"><span class="collapsible__toggle-label">Expand ' + $heading.text() + '</span><span class="collapsible__toggle-icon" aria-hidden="true"></button>')
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>')
43
45
  $topLevelItem.on('click', '.collapsible__toggle', function(e) {
44
46
  e.preventDefault();
45
47
  var $parent = $(this).parent();
@@ -51,11 +53,11 @@
51
53
  function toggleHeading($topLevelItem) {
52
54
  var isOpen = $topLevelItem.hasClass('is-open');
53
55
  var $heading = $topLevelItem.find('> a');
54
- var $body = $topLevelItem.find('collapsible__body');
56
+ var $body = $topLevelItem.find('.collapsible__body');
55
57
  var $toggleLabel = $topLevelItem.find('.collapsible__toggle-label');
56
58
 
57
59
  $topLevelItem.toggleClass('is-open', !isOpen);
58
- $body.attr('aria-expanded', isOpen ? 'true' : 'false');
60
+ $body.attr('aria-expanded', isOpen ? 'false' : 'true');
59
61
  $toggleLabel.text(isOpen ? 'Expand ' + $heading.text() : 'Collapse ' + $heading.text());
60
62
  }
61
63
 
@@ -16,7 +16,9 @@
16
16
  var $searchResultsWrapper;
17
17
  var $searchResultsClose;
18
18
  var results;
19
- var maxSearchEntries = 10;
19
+ var query;
20
+ var queryTimer;
21
+ var maxSearchEntries = 20;
20
22
 
21
23
  this.start = function start($element) {
22
24
  $searchForm = $element.find('form');
@@ -40,6 +42,7 @@
40
42
  success: function(data) {
41
43
  s.lunrData = data;
42
44
  s.lunrIndex = lunr.Index.load(s.lunrData.index);
45
+ replaceStopWordFilter();
43
46
  $(document).trigger('lunrIndexLoaded');
44
47
  }
45
48
  });
@@ -49,12 +52,16 @@
49
52
  // Search functionality on search text input
50
53
  $searchInput.on('input', function (e) {
51
54
  e.preventDefault();
52
- var query = $(this).val();
55
+ query = $(this).val();
53
56
  s.search(query, function(r) {
54
57
  results = r;
55
58
  renderResults(query);
56
59
  updateTitle();
57
60
  });
61
+ if(window.ga) {
62
+ window.clearTimeout(queryTimer);
63
+ queryTimer = window.setTimeout(sendQueryToAnalytics, 1000);
64
+ }
58
65
  });
59
66
 
60
67
  // Set focus on the first search result instead of submiting the search
@@ -71,6 +78,25 @@
71
78
  $searchInput.focus();
72
79
  hideResults();
73
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
+ })
74
100
  }
75
101
 
76
102
  function changeSearchLabel() {
@@ -84,7 +110,6 @@
84
110
  results.push(s.lunrData.docs[item.ref]);
85
111
  }
86
112
  });
87
-
88
113
  return results;
89
114
  }
90
115
 
@@ -144,9 +169,10 @@
144
169
  break;
145
170
  }
146
171
 
147
- var containsMark = sentences[i].includes('mark>');
148
- if (containsMark) {
149
- selectedSentences.push(sentences[i].trim());
172
+ var sentence = sentences[i].trim();
173
+ var containsMark = sentence.includes('mark>');
174
+ if (containsMark && (selectedSentences.indexOf(sentence) == -1)) {
175
+ selectedSentences.push(sentence);
150
176
  }
151
177
  }
152
178
  if(selectedSentences.length > 0) {
@@ -175,6 +201,149 @@
175
201
  .attr('aria-hidden', 'true');
176
202
  $html.removeClass('has-search-results-open');
177
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
+ ])
178
347
  };
179
348
 
180
349
  // Polyfill includes