jekyll-geolexica 1.0.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/Gemfile +6 -0
- data/README.adoc +102 -0
- data/_config.yml +88 -0
- data/_data/lang.yaml +96 -0
- data/_includes/_title.html +5 -0
- data/_includes/head.html +48 -0
- data/_includes/localized-concept.html +99 -0
- data/_includes/newsroll-entry.html +17 -0
- data/_includes/page-header.html +31 -0
- data/_includes/resource-tree-item.html +49 -0
- data/_includes/script.html +0 -0
- data/_layouts/base-page.html +7 -0
- data/_layouts/concept.html +154 -0
- data/_layouts/concept.jsonld.html +152 -0
- data/_layouts/concept.ttl.html +83 -0
- data/_layouts/custom-home.html +33 -0
- data/_layouts/custom-post.html +7 -0
- data/_layouts/default.html +176 -0
- data/_layouts/home.html +6 -0
- data/_layouts/page.html +6 -0
- data/_layouts/post.html +13 -0
- data/_layouts/posts.html +10 -0
- data/_layouts/resource-index.html +14 -0
- data/_layouts/resource-page.html +25 -0
- data/_pages/404.adoc +12 -0
- data/_pages/api/rdf-profile.ttl +225 -0
- data/_pages/concepts-index-list.json +24 -0
- data/_pages/concepts-index.json +14 -0
- data/_pages/concepts.adoc +38 -0
- data/_pages/index.adoc +8 -0
- data/_pages/posts.adoc +6 -0
- data/_pages/stats.adoc +18 -0
- data/_pages/stats.json +5 -0
- data/_sass/adoc-markup.scss +197 -0
- data/_sass/concept.scss +171 -0
- data/_sass/concepts.scss +18 -0
- data/_sass/expandable-nav.scss +187 -0
- data/_sass/geolexica_home.scss +174 -0
- data/_sass/home.scss +87 -0
- data/_sass/jekyll-theme-isotc211.scss +146 -0
- data/_sass/legacy-crossbrowser.scss +67 -0
- data/_sass/main.scss +413 -0
- data/_sass/mixins.scss +39 -0
- data/_sass/normalize.scss +424 -0
- data/_sass/offsets.scss +59 -0
- data/_sass/post.scss +16 -0
- data/_sass/posts.scss +18 -0
- data/assets/algolia-search.js +28 -0
- data/assets/js/concept-search-worker.js +103 -0
- data/assets/js/concept-search.js +293 -0
- data/assets/js/ga.js +15 -0
- data/assets/js/nav.js +125 -0
- data/assets/js/resource-browser.js +79 -0
- data/assets/logo-ribose.svg +1 -0
- data/assets/resource-viewer-placeholder.html +11 -0
- data/assets/style.scss +11 -0
- data/babel.config.js +16 -0
- data/browserconfig.xml +12 -0
- data/fonts/MetaWebPro-Normal.woff +0 -0
- data/fonts/MetaWebPro-Thin.woff +0 -0
- data/jekyll-geolexica.gemspec +45 -0
- data/lib/jekyll-geolexica.rb +5 -0
- data/lib/jekyll/geolexica.rb +19 -0
- data/lib/jekyll/geolexica/concept_page.rb +169 -0
- data/lib/jekyll/geolexica/concept_serializer.rb +44 -0
- data/lib/jekyll/geolexica/concepts_generator.rb +64 -0
- data/lib/jekyll/geolexica/configuration.rb +47 -0
- data/lib/jekyll/geolexica/glossary.rb +95 -0
- data/lib/jekyll/geolexica/hooks.rb +33 -0
- data/lib/jekyll/geolexica/meta_pages_generator.rb +58 -0
- data/lib/jekyll/geolexica/version.rb +8 -0
- data/package-lock.json +2921 -0
- data/package.json +10 -0
- metadata +209 -0
data/_sass/offsets.scss
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
// Offsets
|
2
|
+
// =======
|
3
|
+
|
4
|
+
$sideOffsetBase: 15vw;
|
5
|
+
|
6
|
+
body > header {
|
7
|
+
padding: 0 $sideOffsetBase 0 $sideOffsetBase;
|
8
|
+
|
9
|
+
// Hanging logo on the left
|
10
|
+
@media screen and (min-width: $bigscreenBreakpoint) {
|
11
|
+
padding: 0 $sideOffsetBase 0 calc(#{$sideOffsetBase} - #{$logoOffset});
|
12
|
+
}
|
13
|
+
}
|
14
|
+
body > footer {
|
15
|
+
padding: 0 $sideOffsetBase 0 $sideOffsetBase;
|
16
|
+
|
17
|
+
// Hanging logo on the right
|
18
|
+
@media screen and (min-width: $bigscreenBreakpoint) {
|
19
|
+
padding: 0 calc(#{$sideOffsetBase} - #{$logoOffset}) 0 $sideOffsetBase;
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
body.home > main {
|
24
|
+
> section .section-title,
|
25
|
+
> .section > h2,
|
26
|
+
> .section > .sectionbody {
|
27
|
+
margin-left: $sideOffsetBase;
|
28
|
+
margin-right: $sideOffsetBase;
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
body.home > main > .news {
|
33
|
+
.items {
|
34
|
+
margin-left: calc(#{$sideOffsetBase} - #{$homeSectionItemSidePadding});
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
// Basic body
|
39
|
+
.pad-all-main-contents {
|
40
|
+
> main > * {
|
41
|
+
padding-left: $sideOffsetBase;
|
42
|
+
padding-right: $sideOffsetBase / 2;
|
43
|
+
|
44
|
+
@media screen and (min-width: $bigscreenBreakpoint) {
|
45
|
+
padding-right: $sideOffsetBase;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
body.post, body.page, body.post-index, body.resource-index {
|
51
|
+
@extend .pad-all-main-contents;
|
52
|
+
}
|
53
|
+
|
54
|
+
body.resource {
|
55
|
+
> main > * {
|
56
|
+
padding-left: $stripeWidth * 2;
|
57
|
+
padding-right: 0;
|
58
|
+
}
|
59
|
+
}
|
data/_sass/post.scss
ADDED
data/_sass/posts.scss
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
---
|
2
|
+
---
|
3
|
+
|
4
|
+
{% if site.algolia %}
|
5
|
+
// Instanciating InstantSearch.js with Algolia credentials
|
6
|
+
var search = instantsearch({
|
7
|
+
appId: '{{ site.algolia.application_id }}',
|
8
|
+
indexName: '{{ site.algolia.index_name }}',
|
9
|
+
apiKey: '{{ site.algolia.search_only_api_key }}'
|
10
|
+
});
|
11
|
+
|
12
|
+
// Adding searchbar and results widgets
|
13
|
+
search.addWidget(
|
14
|
+
instantsearch.widgets.searchBox({
|
15
|
+
container: '#search-searchbar',
|
16
|
+
placeholder: 'Search into posts...',
|
17
|
+
poweredBy: true // This is required if you're on the free Community plan
|
18
|
+
})
|
19
|
+
);
|
20
|
+
search.addWidget(
|
21
|
+
instantsearch.widgets.hits({
|
22
|
+
container: '#search-hits'
|
23
|
+
})
|
24
|
+
);
|
25
|
+
|
26
|
+
// Starting the search
|
27
|
+
search.start();
|
28
|
+
{% endif %}
|
@@ -0,0 +1,103 @@
|
|
1
|
+
importScripts('/assets/js/babel-polyfill.js');
|
2
|
+
|
3
|
+
const CONCEPTS_URL = '/api/concepts-index-list.json';
|
4
|
+
|
5
|
+
const LANGUAGES = [
|
6
|
+
'eng',
|
7
|
+
'ara',
|
8
|
+
'spa',
|
9
|
+
'swe',
|
10
|
+
'kor',
|
11
|
+
'rus',
|
12
|
+
'ger',
|
13
|
+
'fre',
|
14
|
+
'fin',
|
15
|
+
'jpn',
|
16
|
+
'dan',
|
17
|
+
'chi',
|
18
|
+
];
|
19
|
+
|
20
|
+
var concepts = null;
|
21
|
+
var latestQuery = null;
|
22
|
+
|
23
|
+
function fetchConcepts() {
|
24
|
+
if (concepts === null) {
|
25
|
+
concepts = fetch(CONCEPTS_URL).then((resp) => resp.json());
|
26
|
+
}
|
27
|
+
return concepts;
|
28
|
+
}
|
29
|
+
|
30
|
+
async function filterAndSort(params) {
|
31
|
+
var concepts = await fetchConcepts();
|
32
|
+
|
33
|
+
if (params.string !== '') {
|
34
|
+
concepts = concepts.map((_item) => {
|
35
|
+
// Search all localized term names for the presence of given search string
|
36
|
+
|
37
|
+
const item = Object.assign({}, _item);
|
38
|
+
const queryString = params.string.toLowerCase();
|
39
|
+
const matchingLanguages = LANGUAGES.
|
40
|
+
filter((lang) => {
|
41
|
+
const term = (item[lang] || {}).term;
|
42
|
+
return term && term.toLowerCase().indexOf(params.string) >= 0;
|
43
|
+
});
|
44
|
+
|
45
|
+
if (matchingLanguages.length > 0) {
|
46
|
+
for (let lang of LANGUAGES) {
|
47
|
+
if (matchingLanguages.indexOf(lang) < 0) {
|
48
|
+
delete item[lang];
|
49
|
+
}
|
50
|
+
}
|
51
|
+
return item;
|
52
|
+
} else {
|
53
|
+
return null;
|
54
|
+
}
|
55
|
+
}).filter((item) => item !== null);
|
56
|
+
}
|
57
|
+
|
58
|
+
if (params.valid !== undefined) {
|
59
|
+
concepts = concepts.
|
60
|
+
filter((item) => {
|
61
|
+
// Only select concepts with at least one localized version matching given validity query
|
62
|
+
const validLocalizedItems = LANGUAGES.
|
63
|
+
filter((lang) => item.hasOwnProperty(lang)).
|
64
|
+
filter((lang) => item[lang].entry_status === params.valid);
|
65
|
+
return validLocalizedItems.length > 0;
|
66
|
+
}).
|
67
|
+
map((_item) => {
|
68
|
+
// Delete localized versions that don’t match given validity query
|
69
|
+
|
70
|
+
const item = Object.assign({}, _item);
|
71
|
+
for (let lang of LANGUAGES) {
|
72
|
+
if (item[lang] && item[lang].entry_status !== params.valid) {
|
73
|
+
delete item[lang];
|
74
|
+
}
|
75
|
+
}
|
76
|
+
return item;
|
77
|
+
});
|
78
|
+
}
|
79
|
+
|
80
|
+
return concepts.sort((item1, item2) => item1.termid - item2.termid);
|
81
|
+
}
|
82
|
+
|
83
|
+
onmessage = async function(msg) {
|
84
|
+
latestQuery = msg.data;
|
85
|
+
|
86
|
+
let concepts;
|
87
|
+
try {
|
88
|
+
concepts = await filterAndSort(msg.data);
|
89
|
+
} catch (e) {
|
90
|
+
console.error(e);
|
91
|
+
postMessage({ error: "Failed to fetch concepts, please <a href='javascript:window.location.reload();'>reload</a> & try again!" });
|
92
|
+
throw e;
|
93
|
+
return;
|
94
|
+
}
|
95
|
+
|
96
|
+
// Check if we the query changed while concepts were being fetched,
|
97
|
+
// in that case skip posting back the message
|
98
|
+
// NOTE: if more query parameters are supported, update the condition to ensure
|
99
|
+
// full comparison
|
100
|
+
if (latestQuery.string === msg.data.string) {
|
101
|
+
postMessage(concepts);
|
102
|
+
}
|
103
|
+
};
|
@@ -0,0 +1,293 @@
|
|
1
|
+
(function () {
|
2
|
+
|
3
|
+
const searchWorker = new Worker('/assets/js/concept-search-worker.js');
|
4
|
+
|
5
|
+
// TODO: Move to a shared module
|
6
|
+
const LANGUAGES = [
|
7
|
+
'eng',
|
8
|
+
'ara',
|
9
|
+
'spa',
|
10
|
+
'swe',
|
11
|
+
'kor',
|
12
|
+
'rus',
|
13
|
+
'ger',
|
14
|
+
'fre',
|
15
|
+
'fin',
|
16
|
+
'jpn',
|
17
|
+
'dan',
|
18
|
+
'chi',
|
19
|
+
];
|
20
|
+
|
21
|
+
|
22
|
+
// React-based concept browser
|
23
|
+
// ===========================
|
24
|
+
|
25
|
+
let el = React.createElement;
|
26
|
+
|
27
|
+
function maybeConceptLinkForField(fieldName) {
|
28
|
+
return (concept) => {
|
29
|
+
const link = getConceptPermalink(concept);
|
30
|
+
if (link) {
|
31
|
+
return el('a', { href: link, target: '_blank', }, concept[fieldName]);
|
32
|
+
} else {
|
33
|
+
return el('span', null, concept[fieldName]);
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
let fieldConfig = {
|
39
|
+
termid: { title: 'Term ID', view: maybeConceptLinkForField('termid'), },
|
40
|
+
term: { title: 'Term', view: maybeConceptLinkForField('term'), },
|
41
|
+
language_code: { title: 'Lang' },
|
42
|
+
entry_status: { title: 'Validity' },
|
43
|
+
review_decision: { title: 'Review' },
|
44
|
+
};
|
45
|
+
|
46
|
+
let fields = ['termid', 'language_code', 'term', 'entry_status', 'review_decision'].map((f) => {
|
47
|
+
return { name: f, ...fieldConfig[f] };
|
48
|
+
});
|
49
|
+
|
50
|
+
class SearchControls extends React.Component {
|
51
|
+
constructor(props) {
|
52
|
+
super();
|
53
|
+
|
54
|
+
this.handleSearchStringChange = this.handleSearchStringChange.bind(this);
|
55
|
+
this.handleValiditySelectionChange = this.handleValiditySelectionChange.bind(this);
|
56
|
+
|
57
|
+
this.stringInputRef = React.createRef();
|
58
|
+
|
59
|
+
this.state = {
|
60
|
+
valid: 'valid', // Required value of the entry_status field, or undefined
|
61
|
+
string: '',
|
62
|
+
};
|
63
|
+
}
|
64
|
+
componentDidMount() {
|
65
|
+
this.stringInputRef.current.focus();
|
66
|
+
}
|
67
|
+
render() {
|
68
|
+
var searchControls = [
|
69
|
+
el('input', {
|
70
|
+
key: 'search-string',
|
71
|
+
ref: this.stringInputRef,
|
72
|
+
className: 'search-string',
|
73
|
+
type: 'text',
|
74
|
+
placeholder: 'Start typing…',
|
75
|
+
onChange: this.handleSearchStringChange}),
|
76
|
+
];
|
77
|
+
|
78
|
+
if (this.state.string.length > 1 && (this.props.refineControls || []).length > 0) {
|
79
|
+
var refineControls = [];
|
80
|
+
|
81
|
+
if (this.props.refineControls.indexOf('validity') >= 0) {
|
82
|
+
refineControls.push(
|
83
|
+
el('div', { key: 'validity', className: 'validity' }, [
|
84
|
+
el('input', {
|
85
|
+
key: 'validity-checkbox',
|
86
|
+
id: 'conceptSearchValidity',
|
87
|
+
type: 'checkbox',
|
88
|
+
checked: this.state.valid === 'valid' || false,
|
89
|
+
onChange: this.handleValiditySelectionChange}),
|
90
|
+
el('label', {
|
91
|
+
key: 'validity-label',
|
92
|
+
htmlFor: 'conceptSearchValidity' }, 'valid only'),
|
93
|
+
]),
|
94
|
+
)
|
95
|
+
}
|
96
|
+
|
97
|
+
searchControls.push(el('div', { key: 'refine', className: 'refine' }, refineControls));
|
98
|
+
}
|
99
|
+
|
100
|
+
return el(React.Fragment, null, searchControls);
|
101
|
+
}
|
102
|
+
|
103
|
+
emitSearchChange() {
|
104
|
+
this.props.onSearchChange({
|
105
|
+
valid: this.state.valid,
|
106
|
+
string: this.state.string,
|
107
|
+
});
|
108
|
+
}
|
109
|
+
|
110
|
+
handleSearchStringChange(evt) {
|
111
|
+
this.setState({ string: evt.target.value }, () => { this.emitSearchChange() });
|
112
|
+
}
|
113
|
+
|
114
|
+
handleValiditySelectionChange(evt) {
|
115
|
+
this.setState(({ valid, string }) => {
|
116
|
+
if (valid === 'valid') {
|
117
|
+
return { valid: undefined, string };
|
118
|
+
} else {
|
119
|
+
return { valid: 'valid', string };
|
120
|
+
}
|
121
|
+
}, () => { this.emitSearchChange() });
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
class ConceptList extends React.Component {
|
126
|
+
render() {
|
127
|
+
return el('table', null, [
|
128
|
+
|
129
|
+
el('thead', { key: 'thead' }, el('tr', null, this.props.fields.map((field) => {
|
130
|
+
return el('th', {
|
131
|
+
className: `field-${field.name}`,
|
132
|
+
key: field.name,
|
133
|
+
}, field.title);
|
134
|
+
}))),
|
135
|
+
|
136
|
+
el('tbody', { key: 'tbody' }, this.props.items.map((item) => {
|
137
|
+
const localizedItems = LANGUAGES.
|
138
|
+
filter((lang) => Object.keys(item).indexOf(lang) >= 0).
|
139
|
+
map((lang) => item[lang]);
|
140
|
+
|
141
|
+
return [item, ...localizedItems].map((item) => {
|
142
|
+
const isLocalized = item.hasOwnProperty('language_code');
|
143
|
+
const conceptId = isLocalized ? item.id : item.termid;
|
144
|
+
|
145
|
+
return el(
|
146
|
+
'tr', {
|
147
|
+
key: `${conceptId}-${item.language_code}`,
|
148
|
+
className: `${isLocalized ? 'localized' : 'main'}`,
|
149
|
+
},
|
150
|
+
this.props.fields.map((field) => {
|
151
|
+
const view = field.view;
|
152
|
+
const defaultView = (item) => { return item[field.name]; };
|
153
|
+
return el(
|
154
|
+
'td', {
|
155
|
+
className: `lang-${item.language_code} field-${field.name}`,
|
156
|
+
key: `${conceptId}-${item.language_code}-${field.name}`,
|
157
|
+
},
|
158
|
+
(view || defaultView)(item));
|
159
|
+
})
|
160
|
+
);
|
161
|
+
});
|
162
|
+
}).reduce((a, b) => a.concat(b), [])),
|
163
|
+
|
164
|
+
]);
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
class ConceptBrowser extends React.Component {
|
169
|
+
constructor(props) {
|
170
|
+
super();
|
171
|
+
|
172
|
+
this.state = {
|
173
|
+
items: [],
|
174
|
+
searchQuery: {}, // string, (in future) valid
|
175
|
+
expanded: false,
|
176
|
+
error: false,
|
177
|
+
loading: false,
|
178
|
+
};
|
179
|
+
|
180
|
+
this.handleSearchQuery = this.handleSearchQuery.bind(this);
|
181
|
+
this.handleToggleBrowser = this.handleToggleBrowser.bind(this);
|
182
|
+
}
|
183
|
+
|
184
|
+
componentDidMount() {
|
185
|
+
searchWorker.onmessage = (msg) => {
|
186
|
+
if (msg.data.error) {
|
187
|
+
this.setState({ loading: false, error: msg.data.error });
|
188
|
+
} else {
|
189
|
+
this.setState({ loading: false, error: null, items: msg.data });
|
190
|
+
}
|
191
|
+
};
|
192
|
+
}
|
193
|
+
|
194
|
+
componentWillUnmount() {
|
195
|
+
searchWorker.onmessage = undefined;
|
196
|
+
}
|
197
|
+
|
198
|
+
render() {
|
199
|
+
var headerEls = [];
|
200
|
+
var searchString = this.state.searchQuery.string;
|
201
|
+
|
202
|
+
if (searchString && searchString.length > 1) {
|
203
|
+
let buttonLabel = this.state.expanded ? '×' : '+';
|
204
|
+
headerEls.push(
|
205
|
+
el('button', {
|
206
|
+
key: 'toggle',
|
207
|
+
ref: this.toggleSwitchRef,
|
208
|
+
className: 'toggle',
|
209
|
+
onClick: this.handleToggleBrowser,
|
210
|
+
}, buttonLabel)
|
211
|
+
);
|
212
|
+
}
|
213
|
+
headerEls.push(el('span', { key: 'title' }, 'Find a concept'));
|
214
|
+
headerEls.push(el('a', { key: 'link', href: '/concepts' }, '(browse all)'));
|
215
|
+
|
216
|
+
var els = [
|
217
|
+
el('h2', { key: 'section-title', className: 'section-title' }, headerEls),
|
218
|
+
el('div', { key: 'search-controls', className: 'search-controls' },
|
219
|
+
el(SearchControls, {
|
220
|
+
onSearchChange: this.handleSearchQuery,
|
221
|
+
refineControls: ['validity'],
|
222
|
+
})
|
223
|
+
),
|
224
|
+
];
|
225
|
+
|
226
|
+
if (this.state.error) {
|
227
|
+
els.push(el('div', {
|
228
|
+
key: 'search-results',
|
229
|
+
className: 'search-results status-message error',
|
230
|
+
dangerouslySetInnerHTML: { __html: this.state.error },
|
231
|
+
}));
|
232
|
+
} else if (this.state.loading) {
|
233
|
+
els.push(el('div', {
|
234
|
+
key: 'search-results',
|
235
|
+
className: 'search-results status-message loading',
|
236
|
+
}, 'Loading…'));
|
237
|
+
} else if (this.state.expanded) {
|
238
|
+
els.push(el('div', {
|
239
|
+
key: 'search-results',
|
240
|
+
className: 'search-results',
|
241
|
+
}, el(ConceptList, { items: this.state.items, fields })));
|
242
|
+
}
|
243
|
+
|
244
|
+
return el(React.Fragment, null, els);
|
245
|
+
}
|
246
|
+
|
247
|
+
handleSearchQuery(query) {
|
248
|
+
var hasQuery = query.string.length > 1;
|
249
|
+
if (hasQuery) {
|
250
|
+
window.setTimeout(() => { searchWorker.postMessage(query) }, 100);
|
251
|
+
}
|
252
|
+
this.setState({ loading: hasQuery, searchQuery: query, expanded: hasQuery });
|
253
|
+
updateBodyClass({ searchQuery: query, expanded: hasQuery });
|
254
|
+
}
|
255
|
+
|
256
|
+
handleToggleBrowser() {
|
257
|
+
this.setState((state) => {
|
258
|
+
state.expanded = !state.expanded;
|
259
|
+
updateBodyClass({ expanded: state.expanded });
|
260
|
+
return state;
|
261
|
+
});
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
ReactDOM.render(el(ConceptBrowser, null), document.querySelector('.browse-concepts'))
|
266
|
+
|
267
|
+
function getConceptPermalink(concept) {
|
268
|
+
if (concept.termid) {
|
269
|
+
return `/concepts/${concept.termid}/`;
|
270
|
+
} else if (concept.id && concept.language_code) {
|
271
|
+
return `/concepts/${concept.id}/#entry-lang-${concept.language_code}`;
|
272
|
+
} else {
|
273
|
+
return null;
|
274
|
+
}
|
275
|
+
}
|
276
|
+
|
277
|
+
function updateBodyClass({ searchQuery, expanded }) {
|
278
|
+
if (searchQuery) {
|
279
|
+
if (searchQuery.string.length > 1) {
|
280
|
+
document.querySelector('body').classList.add('browser-expandable');
|
281
|
+
} else {
|
282
|
+
document.querySelector('body').classList.remove('browser-expandable');
|
283
|
+
}
|
284
|
+
}
|
285
|
+
|
286
|
+
if (expanded === true) {
|
287
|
+
document.querySelector('body').classList.add('browser-expanded');
|
288
|
+
} else if (expanded === false) {
|
289
|
+
document.querySelector('body').classList.remove('browser-expanded');
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
}());
|