umberto 2.1.5 → 2.1.6

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.
package/hexo-config.json CHANGED
@@ -7,6 +7,7 @@
7
7
  "public_dir": "build/docs",
8
8
  "theme": "umberto",
9
9
  "external_link": {
10
+ "enable": true,
10
11
  "exclude": [
11
12
  "ckeditor.com"
12
13
  ]
@@ -24,6 +25,7 @@
24
25
  "highlight": false,
25
26
  "html": true,
26
27
  "breaks": true,
28
+ "linkify": false,
27
29
  "plugins": [
28
30
  {
29
31
  "plugin": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "umberto",
3
- "version": "2.1.5",
3
+ "version": "2.1.6",
4
4
  "description": "CKSource Documentation builder",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -13,58 +13,58 @@
13
13
  "hexo-shim.js"
14
14
  ],
15
15
  "dependencies": {
16
- "@babel/core": "^7.10.3",
17
- "@babel/polyfill": "^7.10.1",
18
- "@babel/preset-env": "^7.10.3",
19
- "@ckeditor/jsdoc-plugins": "^30.1.4",
20
- "babel-loader": "^8.1.0",
16
+ "@babel/core": "^7.18.10",
17
+ "@babel/polyfill": "^7.12.1",
18
+ "@babel/preset-env": "^7.18.10",
19
+ "@ckeditor/jsdoc-plugins": "^30.3.5",
20
+ "babel-loader": "^8.2.5",
21
21
  "chalk": "^4.1.0",
22
- "cheerio": "^0.22.0",
22
+ "cheerio": "^1.0.0-rc.12",
23
23
  "cpx": "^1.5.0",
24
- "del": "^5.1.0",
24
+ "del": "^6.1.1",
25
25
  "element-closest": "^3.0.2",
26
26
  "escape-string-regexp": "^4.0.0",
27
- "fs-extra": "^9.0.1",
28
- "fuse.js": "^6.2.1",
29
- "hexo": "^4.2.1",
27
+ "fs-extra": "^10.1.0",
28
+ "fuse.js": "^6.6.2",
29
+ "hexo": "^6.2.0",
30
30
  "hexo-generator-archive": "^1.0.0",
31
31
  "hexo-generator-category": "^1.0.0",
32
32
  "hexo-generator-index": "^1.0.0",
33
33
  "hexo-generator-tag": "^1.0.0",
34
34
  "hexo-render-pug": "^2.1.4",
35
- "hexo-renderer-markdown-it-plus": "^1.0.4",
36
- "javascript-stringify": "^2.0.1",
37
- "jquery": "~3.5.1",
38
- "js-beautify": "^1.11.0",
39
- "lodash": "^4.17.15",
35
+ "hexo-renderer-markdown-it-plus": "^1.0.5",
36
+ "javascript-stringify": "^2.1.0",
37
+ "jquery": "~3.6.0",
38
+ "js-beautify": "^1.14.4",
39
+ "lodash": "^4.17.21",
40
40
  "markdown-it-expand-tabs": "^1.0.13",
41
41
  "markdown-it-toc-and-anchor": "^4.2.0",
42
- "medium-zoom": "^1.0.5",
42
+ "medium-zoom": "^1.0.6",
43
+ "minimatch": "^5.1.0",
43
44
  "mkdirp": "^1.0.4",
44
- "moment": "^2.27.0",
45
- "minimatch": "^3.1.2",
46
- "nyc": "15.1.0",
47
- "pug": "^3.0.0",
48
- "sass": "^1.43.4",
49
- "sitemap": "^6.1.6",
50
- "tippy.js": "^6.3.2",
45
+ "moment": "^2.29.4",
46
+ "nyc": "^15.1.0",
47
+ "pug": "^3.0.2",
48
+ "sass": "^1.54.0",
49
+ "sitemap": "^7.1.1",
50
+ "tippy.js": "^6.3.7",
51
51
  "tree-model": "^1.0.7",
52
- "upath": "^2.0.0",
53
- "vnu-jar": "^20.5.29",
54
- "webpack": "^5.58.1"
52
+ "upath": "^2.0.1",
53
+ "vnu-jar": "^21.10.12",
54
+ "webpack": "^5.74.0"
55
55
  },
56
56
  "devDependencies": {
57
- "@ckeditor/ckeditor5-dev-env": "^30.1.4",
58
- "browser-sync": "^2.26.7",
59
- "chai": "^4.3.4",
60
- "chokidar": "^3.4.0",
61
- "eslint": "^7.3.0",
62
- "eslint-config-ckeditor5": "^4.0.0",
63
- "husky": "^4.2.5",
64
- "lint-staged": "^10.2.11",
65
- "mocha": "^8.3.2",
57
+ "@ckeditor/ckeditor5-dev-env": "^30.3.5",
58
+ "browser-sync": "^2.27.10",
59
+ "chai": "^4.3.6",
60
+ "chokidar": "^3.5.3",
61
+ "eslint": "^8.21.0",
62
+ "eslint-config-ckeditor5": "^4.0.1",
63
+ "husky": "^8.0.0",
64
+ "lint-staged": "^13.0.3",
65
+ "mocha": "^10.0.0",
66
66
  "mockery": "^2.1.0",
67
- "sinon": "^10.0.0"
67
+ "sinon": "^14.0.0"
68
68
  },
69
69
  "engines": {
70
70
  "node": ">=14.0.0",
@@ -79,7 +79,8 @@
79
79
  "coverage": "nyc --reporter=lcov --reporter=text-summary --cache false npm run test",
80
80
  "changelog": "node ./scripts-dev/changelog.js",
81
81
  "release:bump-version": "node ./scripts-dev/bump-version.js",
82
- "release:publish": "node ./scripts-dev/publish.js"
82
+ "release:publish": "node ./scripts-dev/publish.js",
83
+ "prepare": "husky install"
83
84
  },
84
85
  "author": "CKSource (http://cksource.com/)",
85
86
  "license": "MIT",
@@ -99,12 +100,7 @@
99
100
  "coverage/**",
100
101
  "packages/**"
101
102
  ],
102
- "husky": {
103
- "hooks": {
104
- "pre-commit": "lint-staged"
105
- }
106
- },
107
103
  "hexo": {
108
- "version": "4.2.1"
104
+ "version": "6.2.0"
109
105
  }
110
106
  }
@@ -5,13 +5,13 @@
5
5
 
6
6
  'use strict';
7
7
 
8
- const cheerio = require( 'cheerio' );
8
+ const { default: cheerio } = require( 'cheerio' );
9
9
 
10
10
  /**
11
11
  * Fixes some codeblocks issues.
12
12
  */
13
13
  hexo.extend.filter.register( 'after_post_render', page => {
14
- const $ = cheerio.load( page.content );
14
+ const $ = cheerio.load( page.content, null, false );
15
15
 
16
16
  // This is an attempt to automatically discover code blocks of HTML and add 'html' class if needed.
17
17
  // It is needed for triple backtick ``` codeblocks in markdown which contain HTML but don't use ```html notation.
@@ -5,7 +5,7 @@
5
5
 
6
6
  'use strict';
7
7
 
8
- const cheerio = require( 'cheerio' );
8
+ const { default: cheerio } = require( 'cheerio' );
9
9
  const upath = require( 'upath' );
10
10
 
11
11
  /**
@@ -23,7 +23,7 @@ hexo.extend.filter.register( 'after_post_render', page => {
23
23
  }
24
24
 
25
25
  const errorsHtml = hexo.projectGlobals[ page.projectName ].errorsHtml;
26
- const $ = cheerio.load( errorsHtml );
26
+ const $ = cheerio.load( errorsHtml, null, false );
27
27
 
28
28
  // Errors are used in guides so links must be converted to have proper paths. Converting links to API here.
29
29
  $( 'a[href]' ).each( function() {
@@ -5,7 +5,7 @@
5
5
 
6
6
  'use strict';
7
7
 
8
- const cheerio = require( 'cheerio' );
8
+ const { default: cheerio } = require( 'cheerio' );
9
9
 
10
10
  /**
11
11
  * Make sure each page has a title.
@@ -19,7 +19,7 @@ hexo.extend.filter.register( 'after_post_render', page => {
19
19
  return;
20
20
  }
21
21
 
22
- const $ = cheerio.load( page.content );
22
+ const $ = cheerio.load( page.content, null, false );
23
23
  const title = $( 'h1:not( .live-snippet h1 )' ).first();
24
24
 
25
25
  if ( !title.length && !page.title ) {
@@ -5,10 +5,10 @@
5
5
 
6
6
  'use strict';
7
7
 
8
- const cheerio = require( 'cheerio' );
8
+ const { default: cheerio } = require( 'cheerio' );
9
9
 
10
10
  hexo.extend.helper.register( 'uSplitToTitleAndContent', data => {
11
- const $ = cheerio.load( data );
11
+ const $ = cheerio.load( data, null, false );
12
12
  const title = $( 'h1:not( .live-snippet h1 )' ).first().remove();
13
13
 
14
14
  return {
@@ -5,10 +5,10 @@
5
5
 
6
6
  'use strict';
7
7
 
8
- const cheerio = require( 'cheerio' );
8
+ const { default: cheerio } = require( 'cheerio' );
9
9
 
10
10
  hexo.extend.helper.register( 'uToc', ( data, options = {} ) => {
11
- const $ = cheerio.load( data );
11
+ const $ = cheerio.load( data, null, false );
12
12
 
13
13
  let usedHeadings = [ 'h2', 'h3', 'h4', 'h5', 'h6' ];
14
14
 
@@ -28,7 +28,7 @@ hexo.extend.helper.register( 'uToc', ( data, options = {} ) => {
28
28
  return '';
29
29
  }
30
30
 
31
- const $r = cheerio.load( `<nav class=${ className }><h3>Table of contents</h3></nav>` ); // result object
31
+ const $r = cheerio.load( `<nav class=${ className }><h3>Table of contents</h3></nav>`, null, false ); // result object
32
32
  const tocLastLevels = [ $r( 'nav' ), 0, 0, 0, 0, 0, 0 ];
33
33
 
34
34
  headings.each( function() {
@@ -7,7 +7,7 @@
7
7
 
8
8
  const upath = require( 'upath' );
9
9
  const fs = require( 'fs-extra' );
10
- const cheerio = require( 'cheerio' );
10
+ const { default: cheerio } = require( 'cheerio' );
11
11
  const FileNameManager = require( './classes/file-name-manager' );
12
12
  const HtmlFile = require( './classes/html-file' );
13
13
  const DocDataFactory = require( './classes/doc-data-factory' );
@@ -397,7 +397,7 @@ module.exports = class ApiBuilder {
397
397
  title = `${ capitalize( data.kind ) } ${ capitalize( split.name ) } (${ data.longname.replace( 'module:', '' ) })`;
398
398
  }
399
399
 
400
- const $ = data.description ? cheerio.load( data.description.content ) : cheerio.load( '' );
400
+ const $ = data.description ? cheerio.load( data.description.content, null, false ) : cheerio.load( '', null, false );
401
401
  const html = $.root();
402
402
  html.find( 'pre' ).remove();
403
403
  const desc = html.text().trim().replace( /\n/g, ' ' ).replace( / +/g, ' ' );
@@ -5,7 +5,7 @@
5
5
 
6
6
  'use strict';
7
7
 
8
- const cheerio = require( 'cheerio' );
8
+ const { default: cheerio } = require( 'cheerio' );
9
9
  const splitLongname = require( '../../helpers/split-longname' );
10
10
  const chalk = require( 'chalk' );
11
11
  const macroReplacer = require( '../../tasks/macro-replacer' );
@@ -52,7 +52,7 @@ module.exports = class DescriptionParser {
52
52
  };
53
53
  }
54
54
 
55
- const $ = cheerio.load( str );
55
+ const $ = cheerio.load( str, null, false );
56
56
  const excerpt = $( 'p' ).first().remove().html();
57
57
  const content = $.html();
58
58
 
@@ -222,7 +222,7 @@ module.exports = class DescriptionParser {
222
222
  return data;
223
223
  }
224
224
 
225
- const $ = cheerio.load( data );
225
+ const $ = cheerio.load( data, null, false );
226
226
 
227
227
  $( ':header' ).replaceWith( ( i, item ) => {
228
228
  const headerLevel = Number( item.name.slice( -1 ) );
@@ -244,7 +244,7 @@ module.exports = class DescriptionParser {
244
244
  return str;
245
245
  }
246
246
 
247
- const $ = cheerio.load( str );
247
+ const $ = cheerio.load( str, null, false );
248
248
 
249
249
  $( 'pre[class~="source"]' ).each( function() {
250
250
  let code = $( this ).html();
@@ -91,9 +91,9 @@ module.exports = class DocDataFactory {
91
91
  const functions = moduleMembersCollection.get( 'function' );
92
92
  const members = moduleMembersCollection.get( 'member' );
93
93
  const constants = moduleMembersCollection.get( 'constant' );
94
- const functionsProcessed = this._getMembersWithExcerpt( functions, { parentDoclet: doclet } ) || [];
95
- const membersProcessed = this._getMembersWithExcerpt( members, { parentDoclet: doclet } ) || [];
96
- const constantsProcessed = this._getMembersWithExcerpt( constants, { parentDoclet: doclet } ) || [];
94
+ const functionsProcessed = this._getMembers( functions, { parentDoclet: doclet } ) || [];
95
+ const membersProcessed = this._getMembers( members, { parentDoclet: doclet } ) || [];
96
+ const constantsProcessed = this._getMembers( constants, { parentDoclet: doclet } ) || [];
97
97
 
98
98
  return Object.assign( base, {
99
99
  classes: classes.map( el => this.getClassShort( el, true ) ),
@@ -118,7 +118,6 @@ module.exports = class DocDataFactory {
118
118
  const description = this._descriptionParser.parseDescription( doclet.description, {
119
119
  doclet,
120
120
  asMember,
121
- withExcerpt: true,
122
121
  buildInfo: this._options.buildInfo
123
122
  } );
124
123
 
@@ -167,7 +166,7 @@ module.exports = class DocDataFactory {
167
166
  const augmentsNested = doclet.augmentsNested ? doclet.augmentsNested : [];
168
167
  augmentsNested.unshift( doclet.longname );
169
168
  augmentsNested.reverse();
170
- const properties = this._getMembersWithExcerpt( doclet.properties, { parentDoclet: doclet } ) || [];
169
+ const properties = this._getMembers( doclet.properties, { parentDoclet: doclet } ) || [];
171
170
 
172
171
  /* eslint-disable max-len */
173
172
  // For all "typedef" doclets - copy property name as its id. Thanks to that we will be able to make a directly link to specified property. See #796.
@@ -243,10 +242,10 @@ module.exports = class DocDataFactory {
243
242
  const properties = classMembersCollection.get( 'member' );
244
243
  const methods = classMembersCollection.get( 'function' );
245
244
  const events = classMembersCollection.get( 'event' );
246
- const configOptionsProcessed = this._getMembersWithExcerpt( configOptions, { parentDoclet: doclet } );
245
+ const configOptionsProcessed = this._getMembers( configOptions, { parentDoclet: doclet } );
247
246
  // Sorting properties alphabetically and also public > protected > private.
248
247
  const propertiesProcessed = sortPartsAlphabetically(
249
- this._getMembersWithExcerpt( properties, { parentDoclet: doclet } ),
248
+ this._getMembers( properties, { parentDoclet: doclet } ),
250
249
  [
251
250
  {
252
251
  key: 'access',
@@ -266,7 +265,7 @@ module.exports = class DocDataFactory {
266
265
  const staticProperties = Array.isArray( propertiesProcessed ) ? propertiesProcessed.filter( p => p.scope === 'static' ) : [];
267
266
  const instanceProperties = Array.isArray( propertiesProcessed ) ? propertiesProcessed.filter( p => p.scope !== 'static' ) : [];
268
267
  const methodsProcessed = sortPartsAlphabetically(
269
- this._getMembersWithExcerpt( methods, { parentDoclet: doclet } ),
268
+ this._getMembers( methods, { parentDoclet: doclet } ),
270
269
  [
271
270
  {
272
271
  key: 'name',
@@ -301,7 +300,7 @@ module.exports = class DocDataFactory {
301
300
  methods: methodsProcessed,
302
301
  staticMethods,
303
302
  instanceMethods,
304
- events: this._getMembersWithExcerpt( events, {
303
+ events: this._getMembers( events, {
305
304
  callback: item => {
306
305
  item.name = item.name.replace( 'event:', '' );
307
306
 
@@ -324,7 +323,7 @@ module.exports = class DocDataFactory {
324
323
  getErrors() {
325
324
  const errors = this._dataCollection.get( 'error' ).getAll();
326
325
 
327
- return this._getMembersWithExcerpt( errors );
326
+ return this._getMembers( errors );
328
327
  }
329
328
 
330
329
  /**
@@ -5,7 +5,7 @@
5
5
 
6
6
  'use strict';
7
7
 
8
- const cheerio = require( 'cheerio' );
8
+ const { default: cheerio } = require( 'cheerio' );
9
9
  const keyNameMap = {
10
10
  tagname: {
11
11
  name: 'kind',
@@ -161,7 +161,7 @@ function tagnameAdapter( item ) {
161
161
 
162
162
  function docAdapter( item ) {
163
163
  const description = item.doc;
164
- const $ = cheerio.load( description );
164
+ const $ = cheerio.load( description, null, false );
165
165
 
166
166
  $( 'a' ).each( function() {
167
167
  const href = $( this ).attr( 'href' );
@@ -263,7 +263,7 @@ function overridesAdapter( item ) {
263
263
  const overrides = item.overrides || [];
264
264
 
265
265
  return overrides.map( o => {
266
- const $ = cheerio.load( o.link );
266
+ const $ = cheerio.load( o.link, null, false );
267
267
 
268
268
  $( 'a' ).each( function() {
269
269
  const href = $( this ).attr( 'href' );
@@ -8,7 +8,7 @@
8
8
  const fs = require( 'fs' );
9
9
  const upath = require( 'upath' );
10
10
  const glob = require( 'glob' );
11
- const cheerio = require( 'cheerio' );
11
+ const { default: cheerio } = require( 'cheerio' );
12
12
 
13
13
  const SDK_SELECTOR = 'meta[name="sdk-samples"]';
14
14
 
@@ -16,7 +16,8 @@ module.exports = sourcePath => {
16
16
  const files = [];
17
17
  const filePaths = glob.sync( upath.join( sourcePath, '*.html' ) );
18
18
  for ( const filePath of filePaths ) {
19
- const $ = cheerio.load( fs.readFileSync( filePath, { encoding: 'utf8' } ) );
19
+ const content = fs.readFileSync( filePath, { encoding: 'utf8' } );
20
+ const $ = cheerio.load( content, null, false );
20
21
  const samplesNames = $( SDK_SELECTOR ).attr( 'content' ) ? $( SDK_SELECTOR ).attr( 'content' ).split( '|' ) : null;
21
22
  const samples = samplesNames ? [] : null;
22
23
  const meta = {};
@@ -96,7 +96,7 @@ module.exports = class TemplateCollection {
96
96
  extractLongname,
97
97
  getApiInfoboxTooltip,
98
98
  capitalize,
99
- relative_url
99
+ relative_url // eslint-disable-line camelcase
100
100
  },
101
101
  this._helpers,
102
102
  this._locals
@@ -22,8 +22,8 @@ mixin classItem( item )
22
22
  p.badges
23
23
  each badge in item.badges
24
24
  +badge( badge )
25
- div.collapsing-list__description
26
- if ( item.description )
25
+ if ( item.description && item.description.excerpt )
26
+ div.collapsing-list__description
27
27
  p( class="collapsing-list__excerpt collapsing-list__excerpt--short" )
28
28
  | !{ item.description.excerpt }
29
29
  +seeSource( item )
@@ -10,8 +10,8 @@ mixin error( err )
10
10
  a( class="member-name" href="#" + err.id ) #{ err.name }
11
11
 
12
12
  div.collapsing-list__description.collapsing-list__description--error-page
13
- p.collapsing-list__excerpt
14
- if ( err.description )
13
+ if ( err.description && err.description.excerpt )
14
+ p.collapsing-list__excerpt
15
15
  | !{ err.description.excerpt }
16
16
 
17
17
  div.collapsing-list__content
@@ -50,8 +50,8 @@ mixin method( met )
50
50
  +badge( badge )
51
51
 
52
52
  div.collapsing-list__description
53
- div.collapsing-list__excerpt
54
- if ( met.description )
53
+ if ( met.description && met.description.excerpt )
54
+ div.collapsing-list__excerpt
55
55
  p !{ met.description.excerpt }
56
56
  +seeSource( met )
57
57
 
@@ -27,7 +27,7 @@ mixin property( prop )
27
27
  +badge( badge )
28
28
 
29
29
  div.collapsing-list__description
30
- if ( prop.description )
30
+ if ( prop.description && prop.description.excerpt )
31
31
  div.collapsing-list__excerpt
32
32
  p !{ prop.description.excerpt }
33
33
  +seeSource( prop )
@@ -53,6 +53,10 @@ ul.collapsing-list {
53
53
  display: flex;
54
54
  border-top: 1px solid u-color( 'separator' );
55
55
 
56
+ &--no-excerpt {
57
+ padding: u-spacing( -3 ) 0;
58
+ }
59
+
56
60
  &.toggler {
57
61
  &--collapsed,
58
62
  &--expanded {
@@ -173,7 +177,6 @@ ul.collapsing-list {
173
177
  }
174
178
 
175
179
  &__description {
176
- min-height: u-spacing( 1 );
177
180
  //padding: 0 0 0 u-spacing( 3 );
178
181
  overflow: hidden;
179
182
 
@@ -64,43 +64,72 @@ function enableCollapsable( options ) {
64
64
 
65
65
  export function hideTogglers() {
66
66
  $( '.collapsing-list__item' ).each( function() {
67
- const excerpt = $( this ).find( '.collapsing-list__excerpt' );
68
- const term = $( this ).children( '.collapsing-list__term' );
69
-
67
+ const item = $( this );
68
+ const excerpt = item.find( '.collapsing-list__excerpt' );
70
69
  const hasExcerpt = excerpt.length > 0;
71
70
 
72
- // If there is no excerpt it means that API item has no description at all so we can hide the toggler.
73
- if ( !hasExcerpt ) {
74
- term.addClass( 'hidden' );
75
-
76
- return;
71
+ if ( hasExcerpt ) {
72
+ hideTogglersWithExcerpts( item );
73
+ } else {
74
+ hideTogglersWithoutExcerpts( item );
77
75
  }
76
+ } );
77
+ }
78
78
 
79
- const isExcerptShort = $( this ).find( '.collapsing-list__excerpt--short' ).length > 0;
79
+ function hideTogglersWithoutExcerpts( item ) {
80
+ const hasDescription = item.find( '.collapsing-list__description' ).length > 0;
80
81
 
81
- // Short excerpts have no togglers in the first place.
82
- if ( isExcerptShort ) {
83
- return;
84
- }
82
+ // Items without description have no togglers in the first place.
83
+ if ( !hasDescription ) {
84
+ const term = item.children( '.collapsing-list__term' );
85
+ term.addClass( 'collapsing-list__term--no-excerpt' );
85
86
 
86
- // Check whether short description is cut off by an ellipsis due to available space being too narrow.
87
- const excerptParagraph = $( this ).find( '.collapsing-list__excerpt p' );
88
- // Some elements have no short description, eg.:
89
- // api/module_basic-styles_attributecommand-AttributeCommand.html#function-constructor
90
- const excerptHasEllipsis = !excerptParagraph.length ? false :
91
- Math.abs( excerptParagraph[ 0 ].clientWidth - excerptParagraph[ 0 ].scrollWidth ) > 0;
87
+ return;
88
+ }
92
89
 
93
- // Check whether there is any content in the extended description.
94
- const hasContent = $( this ).find( '.collapsing-list__content' ).text().trim().length > 0;
90
+ // Check whether there is any content in the extended description.
91
+ const hasContent = item.find( '.collapsing-list__content' ).text().trim().length > 0;
95
92
 
96
- // Check whether there is a link to the source code.
97
- const hasSourceLink = $( this ).find( '.see-source' ).length > 0;
93
+ // Check whether there is a link to the source code.
94
+ const hasSourceLink = item.find( '.see-source' ).length > 0;
98
95
 
99
- // If none of the criteria above is met, toggler is not needed and can be hidden away.
100
- if ( !excerptHasEllipsis && !hasContent && !hasSourceLink ) {
101
- term.removeClass( classCol );
102
- term.addClass( classExp );
103
- term.addClass( 'hidden' );
104
- }
105
- } );
96
+ // If none of the criteria above is met, toggler is not needed and can be hidden away.
97
+ if ( !hasContent && !hasSourceLink ) {
98
+ hideTogglerForItem( item );
99
+ }
100
+ }
101
+
102
+ function hideTogglersWithExcerpts( item ) {
103
+ const isExcerptShort = item.find( '.collapsing-list__excerpt--short' ).length > 0;
104
+
105
+ // Short excerpts have no togglers in the first place.
106
+ if ( isExcerptShort ) {
107
+ return;
108
+ }
109
+
110
+ // Check whether short description is cut off by an ellipsis due to available space being too narrow.
111
+ const excerptParagraph = item.find( '.collapsing-list__excerpt p' );
112
+ // Some elements have no short description, eg.:
113
+ // api/module_basic-styles_attributecommand-AttributeCommand.html#function-constructor
114
+ const excerptHasEllipsis = !excerptParagraph.length ? false :
115
+ Math.abs( excerptParagraph[ 0 ].clientWidth - excerptParagraph[ 0 ].scrollWidth ) > 0;
116
+
117
+ // Check whether there is any content in the extended description.
118
+ const hasContent = item.find( '.collapsing-list__content' ).text().trim().length > 0;
119
+
120
+ // Check whether there is a link to the source code.
121
+ const hasSourceLink = item.find( '.see-source' ).length > 0;
122
+
123
+ // If none of the criteria above is met, toggler is not needed and can be hidden away.
124
+ if ( !excerptHasEllipsis && !hasContent && !hasSourceLink ) {
125
+ hideTogglerForItem( item );
126
+ }
127
+ }
128
+
129
+ function hideTogglerForItem( item ) {
130
+ const term = item.children( '.collapsing-list__term' );
131
+
132
+ term.removeClass( classCol );
133
+ term.addClass( classExp );
134
+ term.addClass( 'hidden' );
106
135
  }