reveal.js-appearance 1.2.1 → 1.3.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.
- package/CHANGELOG.md +10 -0
- package/README.md +45 -12
- package/package.json +64 -20
- package/plugin/appearance/appearance.css +102 -202
- package/plugin/appearance/appearance.esm.js +620 -414
- package/plugin/appearance/appearance.js +620 -414
- package/css/demo.css +0 -187
- package/demo-markdown.html +0 -62
- package/demo.html +0 -391
- package/img/1.jpg +0 -0
- package/img/2.jpg +0 -0
- package/img/3.jpg +0 -0
- package/img/4.jpg +0 -0
- package/img/5.jpg +0 -0
- package/markdown.md +0 -156
- package/plugin/appearance/plugin-src.js +0 -458
- package/screenshot.png +0 -0
|
@@ -1,444 +1,688 @@
|
|
|
1
1
|
|
|
2
2
|
/*****************************************************************
|
|
3
|
-
* @author: Martijn De Jongh (Martino), martijn.de.jongh@gmail.com
|
|
4
|
-
* https://github.com/Martinomagnifico
|
|
5
3
|
*
|
|
6
|
-
* Appearance
|
|
7
|
-
* Version 1.
|
|
4
|
+
* Appearance for Reveal.js
|
|
5
|
+
* Version 1.3.0
|
|
8
6
|
*
|
|
7
|
+
* @author: Martijn De Jongh (Martino), martijn.de.jongh@gmail.com
|
|
8
|
+
* https://github.com/martinomagnifico
|
|
9
|
+
*
|
|
9
10
|
* @license
|
|
10
11
|
* MIT licensed
|
|
12
|
+
*
|
|
13
|
+
* Copyright (C) 2023 Martijn De Jongh (Martino)
|
|
11
14
|
*
|
|
12
|
-
* Thanks to:
|
|
13
|
-
* - Hakim El Hattab, Reveal.js
|
|
14
|
-
* - Daniel Eden, Animate.css
|
|
15
15
|
******************************************************************/
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
17
|
(function (global, factory) {
|
|
20
18
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
21
19
|
typeof define === 'function' && define.amd ? define(factory) :
|
|
22
20
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Appearance = factory());
|
|
23
21
|
})(this, (function () { 'use strict';
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Check if a given string is valid JSON.
|
|
25
|
+
* @param {string} str - The string to be checked.
|
|
26
|
+
* @returns {boolean} `true` if the string is valid JSON, otherwise `false`.
|
|
27
|
+
*/
|
|
28
|
+
const isJSON = str => {
|
|
27
29
|
try {
|
|
28
|
-
|
|
29
|
-
} catch (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
t.querySelector = function (t) {
|
|
35
|
-
return r.apply(this, arguments);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
let c = u(t.querySelectorAll);
|
|
39
|
-
|
|
40
|
-
if (t.querySelectorAll = function (t) {
|
|
41
|
-
return c.apply(this, arguments);
|
|
42
|
-
}, t.matches) {
|
|
43
|
-
let n = u(t.matches);
|
|
44
|
-
|
|
45
|
-
t.matches = function (t) {
|
|
46
|
-
return n.apply(this, arguments);
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (t.closest) {
|
|
51
|
-
let o = u(t.closest);
|
|
52
|
-
|
|
53
|
-
t.closest = function (t) {
|
|
54
|
-
return o.apply(this, arguments);
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function u(t) {
|
|
59
|
-
return function (r) {
|
|
60
|
-
if (r && e.test(r)) {
|
|
61
|
-
let c = "q" + Math.floor(9e6 * Math.random()) + 1e6;
|
|
62
|
-
arguments[0] = r.replace(e, "[" + c + "]"), this.setAttribute(c, "");
|
|
63
|
-
let n = t.apply(this, arguments);
|
|
64
|
-
return this.removeAttribute(c), n;
|
|
65
|
-
}
|
|
30
|
+
return JSON.parse(str) && !!str;
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
66
35
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Check if an element has child nodes that are `SECTION` elements.
|
|
38
|
+
* @param {Object} param0 - Object with childNodes property.
|
|
39
|
+
* @param {NodeListOf<ChildNode>} param0.childNodes - List of child nodes of the element.
|
|
40
|
+
* @returns {boolean} `true` if the element contains `SECTION` child nodes, otherwise `false`.
|
|
41
|
+
*/
|
|
42
|
+
const isStack = _ref => {
|
|
43
|
+
let {
|
|
44
|
+
childNodes
|
|
45
|
+
} = _ref;
|
|
46
|
+
let isStack = false;
|
|
47
|
+
for (let i = 0; i < childNodes.length; i++) {
|
|
48
|
+
if (childNodes[i].tagName == "SECTION") {
|
|
49
|
+
isStack = true;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
71
52
|
}
|
|
53
|
+
return isStack;
|
|
54
|
+
};
|
|
72
55
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Copy data attributes from a source element to a target element with an optional exception.
|
|
58
|
+
* @param {Object} param0 - Object with attributes property.
|
|
59
|
+
* @param {NamedNodeMap} param0.attributes - Map of attributes of the source element.
|
|
60
|
+
* @param {Element} target - Target element to copy attributes to.
|
|
61
|
+
* @param {string} [not] - Optional attribute name to exclude from copying.
|
|
62
|
+
*/
|
|
63
|
+
const copyDataAttributes = (_ref2, target, not) => {
|
|
64
|
+
let {
|
|
65
|
+
attributes
|
|
66
|
+
} = _ref2;
|
|
67
|
+
[...attributes].filter(_ref3 => {
|
|
68
|
+
let {
|
|
69
|
+
nodeName
|
|
70
|
+
} = _ref3;
|
|
71
|
+
return nodeName.includes('data');
|
|
72
|
+
}).forEach(_ref4 => {
|
|
73
|
+
let {
|
|
74
|
+
nodeName,
|
|
75
|
+
nodeValue
|
|
76
|
+
} = _ref4;
|
|
77
|
+
if (not && nodeName !== not || !not) {
|
|
78
|
+
target.setAttribute(nodeName, nodeValue);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
};
|
|
86
82
|
|
|
87
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Check if the given item is an object and not an array.
|
|
85
|
+
* @param {*} item - The item to be checked.
|
|
86
|
+
* @returns {boolean} `true` if the item is an object and not an array, otherwise `false`.
|
|
87
|
+
*/
|
|
88
|
+
const isObject = item => {
|
|
89
|
+
return item && typeof item === 'object' && !Array.isArray(item);
|
|
90
|
+
};
|
|
88
91
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Deep merge multiple objects into a target object.
|
|
94
|
+
* @param {Object} target - Target object to merge values into.
|
|
95
|
+
* @param {...Object} sources - Source objects to merge from.
|
|
96
|
+
* @returns {Object} The merged object.
|
|
97
|
+
*/
|
|
98
|
+
const mergeDeep = function (target) {
|
|
99
|
+
for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
100
|
+
sources[_key - 1] = arguments[_key];
|
|
101
|
+
}
|
|
102
|
+
if (!sources.length) return target;
|
|
103
|
+
const source = sources.shift();
|
|
104
|
+
if (isObject(target) && isObject(source)) {
|
|
105
|
+
for (const key in source) {
|
|
106
|
+
if (isObject(source[key])) {
|
|
107
|
+
if (!target[key]) Object.assign(target, {
|
|
108
|
+
[key]: {}
|
|
109
|
+
});
|
|
110
|
+
mergeDeep(target[key], source[key]);
|
|
111
|
+
} else {
|
|
112
|
+
Object.assign(target, {
|
|
113
|
+
[key]: source[key]
|
|
114
|
+
});
|
|
92
115
|
}
|
|
93
|
-
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return mergeDeep(target, ...sources);
|
|
119
|
+
};
|
|
94
120
|
|
|
95
|
-
|
|
96
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Resolves the given Promise immediately using setTimeout.
|
|
123
|
+
* @param {Function} resolve - The resolve function of a Promise.
|
|
124
|
+
* @returns {number} The ID value of the timer that is set.
|
|
125
|
+
*/
|
|
126
|
+
const doneLoading = resolve => {
|
|
127
|
+
return setTimeout(resolve, 0);
|
|
128
|
+
};
|
|
97
129
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Converts a JavaScript object or a JSON-formatted string to a JSON string.
|
|
132
|
+
*
|
|
133
|
+
* @param {Object|string} str - The input string or object to be converted to a JSON string.
|
|
134
|
+
* @returns {string} The JSON string.
|
|
135
|
+
*/
|
|
136
|
+
const toJSONString = str => {
|
|
137
|
+
let JSONString = '';
|
|
138
|
+
if (typeof str === "string") str = str.replace(/[“”]/g, '"').replace(/[‘’]/g, "'");
|
|
139
|
+
if (isJSON(str)) {
|
|
140
|
+
JSONString = str;
|
|
141
|
+
} else {
|
|
142
|
+
if (typeof str === "object") {
|
|
143
|
+
JSONString = JSON.stringify(str, null, 2);
|
|
144
|
+
} else {
|
|
145
|
+
JSONString = str.trim().replace(/'/g, '"').charAt(0) === "{" ? str.trim().replace(/'/g, '"') : `{${str.trim().replace(/'/g, '"')}}`;
|
|
103
146
|
}
|
|
104
|
-
}
|
|
147
|
+
}
|
|
148
|
+
return JSONString;
|
|
149
|
+
};
|
|
105
150
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Dynamically loads a stylesheet from the specified URL and calls a callback function when it's loaded.
|
|
153
|
+
*
|
|
154
|
+
* @param {string} url - The URL of the stylesheet to load.
|
|
155
|
+
* @param {Function} callback - A callback function to be called when the stylesheet is loaded.
|
|
156
|
+
*/
|
|
157
|
+
const loadStyle = (url, callback) => {
|
|
158
|
+
const head = document.querySelector('head');
|
|
159
|
+
const style = document.createElement('link');
|
|
160
|
+
style.rel = 'stylesheet';
|
|
161
|
+
style.href = url;
|
|
162
|
+
style.onload = () => {
|
|
163
|
+
if (typeof callback === 'function') {
|
|
164
|
+
callback.call();
|
|
165
|
+
callback = null;
|
|
166
|
+
}
|
|
110
167
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
for (let i = 0; i < section.childNodes.length; i++) {
|
|
116
|
-
if (section.childNodes[i].tagName == "SECTION") {
|
|
117
|
-
isStack = true;
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
168
|
+
style.onreadystatechange = () => {
|
|
169
|
+
if (style.readyState === 'loaded') {
|
|
170
|
+
style.onload();
|
|
120
171
|
}
|
|
121
|
-
|
|
122
|
-
return isStack;
|
|
123
172
|
};
|
|
173
|
+
head.appendChild(style);
|
|
174
|
+
};
|
|
124
175
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
176
|
+
/**
|
|
177
|
+
* Retrieves the path of a JavaScript file based on its filename.
|
|
178
|
+
*
|
|
179
|
+
* @param {string} fileName - The filename of the script.
|
|
180
|
+
* @returns {string} The path to the plugin's location.
|
|
181
|
+
*/
|
|
182
|
+
const pluginPath = fileName => {
|
|
183
|
+
let path;
|
|
184
|
+
let pluginScript = document.querySelector(`script[src$="${fileName}"]`);
|
|
185
|
+
if (pluginScript) {
|
|
186
|
+
path = pluginScript.getAttribute("src").slice(0, -1 * fileName.length);
|
|
187
|
+
} else {
|
|
188
|
+
path = (typeof document === 'undefined' && typeof location === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : typeof document === 'undefined' ? location.href : (document.currentScript && document.currentScript.src || new URL('appearance.js', document.baseURI).href)).slice(0, (typeof document === 'undefined' && typeof location === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : typeof document === 'undefined' ? location.href : (document.currentScript && document.currentScript.src || new URL('appearance.js', document.baseURI).href)).lastIndexOf('/') + 1);
|
|
131
189
|
}
|
|
190
|
+
return path;
|
|
191
|
+
};
|
|
192
|
+
const debugLog = (options, text) => {
|
|
193
|
+
if (options.debug) console.log(text);
|
|
194
|
+
};
|
|
132
195
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Retrieves and loads CSS stylesheets based on the provided options and ES5 filename.
|
|
198
|
+
*
|
|
199
|
+
* @param {Object} options - Configuration options for loading CSS.
|
|
200
|
+
* @param {string} fileName - The filename of the script.
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
const getAndLoadCSS = (options, fileName) => {
|
|
204
|
+
let thePath = pluginPath(fileName);
|
|
205
|
+
let pluginBaseName = fileName.replace(/\.[^/.]+$/, "");
|
|
206
|
+
let AppearanceStylePath = options.csspath.appearance ? options.csspath.appearance : `${thePath}${pluginBaseName}.css` || `plugin/${pluginBaseName}/${pluginBaseName}.css`;
|
|
207
|
+
let AnimateCSSPath = !options.compatibility ? options.animatecsspath.link : options.animatecsspath.compat;
|
|
208
|
+
if (options.debug) {
|
|
209
|
+
console.log(`Paths:`);
|
|
210
|
+
console.log(` - Plugin path = ${thePath}`);
|
|
211
|
+
console.log(` - Appearance CSS path = ${AppearanceStylePath}`);
|
|
212
|
+
console.log(` - AnimateCSS CSS path = ${AnimateCSSPath}`);
|
|
213
|
+
}
|
|
214
|
+
loadStyle(AnimateCSSPath, function () {
|
|
215
|
+
loadStyle(AppearanceStylePath);
|
|
216
|
+
});
|
|
217
|
+
};
|
|
152
218
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
219
|
+
/**
|
|
220
|
+
* Adds automatic animations to elements within a section based on specified criteria.
|
|
221
|
+
*
|
|
222
|
+
* This function examines the provided section for attributes and options to determine
|
|
223
|
+
* which classes should be added to its elements to enable automatic animations.
|
|
224
|
+
*
|
|
225
|
+
* @param {HTMLElement} section - The section element to which automatic animations will be applied.
|
|
226
|
+
* @param {Object} options - The existing user options object
|
|
227
|
+
* @param {Object} vars - The existing vars object
|
|
228
|
+
* @returns {undefined}
|
|
229
|
+
*/
|
|
230
|
+
const addAutoAnimation = (section, options, vars) => {
|
|
231
|
+
let sectionAutoSelectors = null;
|
|
232
|
+
if (section.hasAttribute("data-autoappear")) {
|
|
233
|
+
let sectDataAppear = section.dataset.autoappear;
|
|
234
|
+
if (sectDataAppear == "auto" || sectDataAppear == "" || sectDataAppear.length < 1 || sectDataAppear == "true") {
|
|
235
|
+
// This section should get the global autoappear classes on its objects
|
|
236
|
+
sectionAutoSelectors = options.autoelements ? options.autoelements : null;
|
|
237
|
+
} else {
|
|
238
|
+
// This section should get the local autoappear classes on its objects
|
|
239
|
+
sectionAutoSelectors = sectDataAppear;
|
|
156
240
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
let
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
speedClasses.forEach(speed => {
|
|
186
|
-
if (elementInSection.classList.contains(speed)) {
|
|
187
|
-
speedClass = speed;
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
let classesToRemove = [];
|
|
191
|
-
elementInSection.classList.forEach(currentClass => {
|
|
192
|
-
if (String(currentClass).includes("animate__")) {
|
|
193
|
-
classesToRemove.push(currentClass);
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
classesToRemove.forEach(currentClass => {
|
|
197
|
-
elementInSection.classList.remove(currentClass);
|
|
198
|
-
});
|
|
199
|
-
newClasses.forEach(newClass => {
|
|
200
|
-
if (speedClasses.includes(newClass)) {
|
|
201
|
-
// There is a speed class from JSON to be assigned
|
|
202
|
-
if (speedClass) {
|
|
203
|
-
speedClass = newClass;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
newClasses.forEach(newClass => {
|
|
208
|
-
elementInSection.classList.add(newClass);
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
if (speedClass) {
|
|
212
|
-
elementInSection.classList.add(speedClass);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (newDelay) {
|
|
216
|
-
elementInSection.dataset.delay = newDelay;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
elementInSection.classList.add(baseclass);
|
|
241
|
+
} else if (options.autoappear && options.autoelements) {
|
|
242
|
+
// This section should get the global autoappear classes on its objects
|
|
243
|
+
sectionAutoSelectors = options.autoelements;
|
|
244
|
+
}
|
|
245
|
+
if (sectionAutoSelectors) {
|
|
246
|
+
let elementsToAnimate = JSON.parse(toJSONString(sectionAutoSelectors));
|
|
247
|
+
Object.entries(elementsToAnimate).forEach(_ref => {
|
|
248
|
+
let [selector, assignables] = _ref;
|
|
249
|
+
// Exclude the elements from vars.appearances
|
|
250
|
+
let elements = Array.from(section.querySelectorAll(selector)).filter(element => !vars.appearances.includes(element));
|
|
251
|
+
if (elements.length) {
|
|
252
|
+
elements.forEach(element => {
|
|
253
|
+
vars.appearances.push(element);
|
|
254
|
+
let newClasses = [],
|
|
255
|
+
newDelay = null,
|
|
256
|
+
speedClass = false,
|
|
257
|
+
elementSplit = null,
|
|
258
|
+
containerDelay = null;
|
|
259
|
+
if (Array.isArray(assignables)) {
|
|
260
|
+
newClasses = assignables[0].split(/[ ,]+/);
|
|
261
|
+
newDelay = assignables[1];
|
|
262
|
+
} else if (typeof assignables == "string") {
|
|
263
|
+
newClasses = assignables.split(/[ ,]+/);
|
|
264
|
+
} else if (assignables.constructor === Object) {
|
|
265
|
+
if (assignables.class || assignables.animation) {
|
|
266
|
+
let animationClass = assignables.animation ? assignables.animation : assignables.class;
|
|
267
|
+
newClasses = animationClass.split(/[ ,]+/);
|
|
220
268
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const findAppearancesIn = function (container, includeClass, excludeClass) {
|
|
227
|
-
if (!isStack(container)) {
|
|
228
|
-
let appearances = selectionArray(container, `:scope ${includeClass}`);
|
|
229
|
-
appearances.forEach(appearance => {
|
|
230
|
-
let convertListItem = appearance => {
|
|
231
|
-
let from = appearance,
|
|
232
|
-
to = appearance.parentNode;
|
|
233
|
-
if (!to) return;
|
|
234
|
-
|
|
235
|
-
for (let sibling of to.children) {
|
|
236
|
-
if (sibling !== appearance) {
|
|
237
|
-
if (sibling.dataset.appearParent) return;
|
|
269
|
+
if (assignables.speed) {
|
|
270
|
+
speedClass = String(assignables.speed);
|
|
271
|
+
if (!speedClass.includes("animate__")) {
|
|
272
|
+
speedClass = `animate__${speedClass}`;
|
|
238
273
|
}
|
|
239
274
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
copyDataAttributes(from, to, "data-appear-parent");
|
|
243
|
-
to.innerHTML = from.innerHTML;
|
|
244
|
-
}; // Conversion of list items with Appearance classes to the parent, needs manual attribute
|
|
245
|
-
// Relates to Quarto wrapping list content in a span.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (appearance.hasAttribute("data-appear-parent")) {
|
|
249
|
-
convertListItem(appearance);
|
|
250
|
-
} // Automatic conversion of list items which directly contain spans.
|
|
251
|
-
// Relates to Quarto wrapping list content in a span.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (options.appearparents) {
|
|
255
|
-
if (appearance.parentNode && appearance.parentNode.tagName) {
|
|
256
|
-
if (appearance.tagName == "SPAN" && appearance.parentNode.tagName == "LI") {
|
|
257
|
-
let spanLength = String(appearance.outerHTML).length;
|
|
258
|
-
let liContentLength = String(appearance.parentNode.innerHTML).length;
|
|
259
|
-
|
|
260
|
-
if (spanLength == liContentLength) {
|
|
261
|
-
convertListItem(appearance);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
275
|
+
if (assignables.delay) {
|
|
276
|
+
newDelay = String(assignables.delay);
|
|
264
277
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
appearances = selectionArray(container, `:scope ${includeClass}`);
|
|
268
|
-
let excludes = selectionArray(container, `:scope ${excludeClass} ${includeClass}`);
|
|
269
|
-
let delay = 0;
|
|
270
|
-
appearances.filter(function (appearance, index) {
|
|
271
|
-
if (!(excludes.indexOf(appearance) > -1)) {
|
|
272
|
-
if (index == 0 && appearance.dataset.delay || index != 0) {
|
|
273
|
-
let elementDelay = options.delay;
|
|
274
|
-
|
|
275
|
-
if (appearance.dataset && appearance.dataset.delay) {
|
|
276
|
-
elementDelay = parseInt(appearance.dataset.delay);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
delay = delay + elementDelay; // Allow fragments to be Appearance items
|
|
280
|
-
|
|
281
|
-
if (appearance.classList.contains("fragment")) {
|
|
282
|
-
delay = 0;
|
|
283
|
-
|
|
284
|
-
if (appearance.querySelectorAll(`.${baseclass}`)) {
|
|
285
|
-
let firstNestedAppearance = appearance.querySelectorAll(`.${baseclass}`)[0];
|
|
286
|
-
|
|
287
|
-
if (firstNestedAppearance) {
|
|
288
|
-
let elementDelay = options.delay;
|
|
289
|
-
|
|
290
|
-
if (firstNestedAppearance.dataset && firstNestedAppearance.dataset.delay) {
|
|
291
|
-
elementDelay = parseInt(firstNestedAppearance.dataset.delay);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
firstNestedAppearance.dataset.delay = elementDelay;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
appearance.style.setProperty('animation-delay', delay + "ms");
|
|
278
|
+
if (assignables.split) {
|
|
279
|
+
elementSplit = String(assignables.split);
|
|
300
280
|
}
|
|
281
|
+
if (assignables["container-delay"]) {
|
|
282
|
+
containerDelay = String(assignables["container-delay"]);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
element.classList.add(...newClasses);
|
|
286
|
+
if (speedClass) {
|
|
287
|
+
element.classList.add(speedClass);
|
|
288
|
+
}
|
|
289
|
+
if (newDelay) {
|
|
290
|
+
element.dataset.delay = newDelay;
|
|
291
|
+
}
|
|
292
|
+
if (elementSplit) {
|
|
293
|
+
element.dataset.split = elementSplit;
|
|
294
|
+
}
|
|
295
|
+
if (containerDelay) {
|
|
296
|
+
element.dataset.containerDelay = containerDelay;
|
|
301
297
|
}
|
|
302
298
|
});
|
|
303
299
|
}
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
regularSections.forEach(section => {
|
|
308
|
-
if (section.hasAttribute("data-autoappear")) {
|
|
309
|
-
let sectDataAppear = section.dataset.autoappear;
|
|
310
|
-
|
|
311
|
-
if (sectDataAppear == "auto" || sectDataAppear == "" || sectDataAppear.length < 1 || sectDataAppear == "true") {
|
|
312
|
-
// This section should get the global autoappear classes on its objects
|
|
313
|
-
if (options.autoelements) {
|
|
314
|
-
if (!options.autoelements) {
|
|
315
|
-
return console.log(`Please add some elements in the option "autoelements"`);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
assignAutoClass(section, options.autoelements, 'global');
|
|
319
|
-
}
|
|
320
|
-
} else if (sectDataAppear.length > 0) {
|
|
321
|
-
// This section should get the local data-autoappear classes on its objects
|
|
322
|
-
assignAutoClass(section, sectDataAppear, 'local'); //section.removeAttribute("data-autoappear");
|
|
323
|
-
}
|
|
324
|
-
} else {
|
|
325
|
-
if (options.autoappear) {
|
|
326
|
-
if (!options.autoelements) {
|
|
327
|
-
return console.log(`Please add some elements in the option "autoelements"`);
|
|
328
|
-
} // This section should get the global autoappear classes on its objects
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
};
|
|
329
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Hoist a list item's appearance to its parent element's appearance.
|
|
306
|
+
*
|
|
307
|
+
* @param {HTMLElement} from - The list item element.
|
|
308
|
+
* @returns {undefined}
|
|
309
|
+
*/
|
|
310
|
+
const hoistAppearance = (from, baseclass) => {
|
|
311
|
+
let to = from.parentNode;
|
|
312
|
+
if (!to) return;
|
|
313
|
+
for (const sibling of to.children) {
|
|
314
|
+
if (sibling !== from && sibling.dataset.appearParent) return;
|
|
315
|
+
}
|
|
316
|
+
to.classList = from.classList;
|
|
317
|
+
copyDataAttributes(from, to, "data-appear-parent");
|
|
318
|
+
to.innerHTML = from.innerHTML;
|
|
319
|
+
to.classList.add(baseclass);
|
|
320
|
+
};
|
|
330
321
|
|
|
331
|
-
|
|
332
|
-
|
|
322
|
+
/**
|
|
323
|
+
* Fix list items that were changed by Quarto.
|
|
324
|
+
*
|
|
325
|
+
* This function is designed for use with Quarto and handles the conversion of list items
|
|
326
|
+
* with Appearance classes to their parent elements when a manual attribute is present.
|
|
327
|
+
* It also provides automatic conversion for list items that directly contain spans, which
|
|
328
|
+
* is related to Quarto's wrapping of list content in a span.
|
|
329
|
+
*
|
|
330
|
+
* @param {HTMLElement} appearance - The list item element whose appearance will be converted.
|
|
331
|
+
* @param {Object} options - An options object that controls the conversion behavior.
|
|
332
|
+
* @param {boolean} options.appearparents - If `true`, automatic conversion of list items with spans is enabled.
|
|
333
|
+
* @returns {undefined}
|
|
334
|
+
*/
|
|
335
|
+
const fixListItem = (appearance, options, names) => {
|
|
336
|
+
let baseclass = names.baseclass;
|
|
337
|
+
if (appearance.hasAttribute("data-appear-parent")) {
|
|
338
|
+
hoistAppearance(appearance, baseclass);
|
|
339
|
+
}
|
|
340
|
+
if (options.appearparents) {
|
|
341
|
+
if (appearance.parentNode && appearance.parentNode.tagName) {
|
|
342
|
+
if (appearance.tagName == "SPAN" && appearance.parentNode.tagName == "LI") {
|
|
343
|
+
let spanLength = String(appearance.outerHTML).length;
|
|
344
|
+
let liContentLength = String(appearance.parentNode.innerHTML).length;
|
|
345
|
+
if (spanLength == liContentLength) {
|
|
346
|
+
hoistAppearance(appearance);
|
|
333
347
|
}
|
|
334
|
-
});
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
if (options.compatibility) {
|
|
338
|
-
animatecss = '.backInDown, .backInLeft, .backInRight, .backInUp, .bounceIn, .bounceInDown, .bounceInLeft, .bounceInRight, .bounceInUp, .fadeIn, .fadeInDown, .fadeInDownBig, .fadeInLeft, .fadeInLeftBig, .fadeInRight, .fadeInRightBig, .fadeInUp, .fadeInUpBig, .fadeInTopLeft, .fadeInTopRight, .fadeInBottomLeft, .fadeInBottomRight, .flipInX, .flipInY, .lightSpeedInRight, .lightSpeedInLeft, .rotateIn, .rotateInDownLeft, .rotateInDownRight, .rotateInUpLeft, .rotateInUpRight, .jackInTheBox, .rollIn, .zoomIn, .zoomInDown, .zoomInLeft, .zoomInRight, .zoomInUp, .slideInDown, .slideInLeft, .slideInRight, .slideInUp, .skidLeft, .skidLeftBig, .skidRight, .skidRightBig, .shrinkIn, .shrinkInBlur';
|
|
339
|
-
baseclass = options.compatibilitybaseclass;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
let allappearances = deck.getRevealElement().querySelectorAll(animatecss);
|
|
343
|
-
allappearances.forEach(appearance => {
|
|
344
|
-
if (!appearance.classList.contains(baseclass)) {
|
|
345
|
-
appearance.classList.add(baseclass);
|
|
346
348
|
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
findAppearancesIn(section, appearanceSelector, fragmentSelector);
|
|
351
|
-
});
|
|
352
|
-
fragments.forEach(fragment => {
|
|
353
|
-
findAppearancesIn(fragment, appearanceSelector, fragmentSelector);
|
|
354
|
-
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
355
352
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
353
|
+
/**
|
|
354
|
+
* Adds a base class to an HTML element if it doesn't already have it.
|
|
355
|
+
*
|
|
356
|
+
* This function checks if the specified HTML element has the specified base class,
|
|
357
|
+
* and if not, it adds the base class to the element's class list.
|
|
358
|
+
*
|
|
359
|
+
* @param {HTMLElement} appearance - The HTML element to which the base class should be added.
|
|
360
|
+
* @param {Object} names - The existing 'names' object
|
|
361
|
+
* @returns {undefined}
|
|
362
|
+
*/
|
|
363
|
+
|
|
364
|
+
const addBaseClass = (appearance, names) => {
|
|
365
|
+
if (!appearance.classList.contains(names.baseclass)) {
|
|
366
|
+
appearance.classList.add(names.baseclass);
|
|
367
|
+
}
|
|
368
|
+
if (appearance.classList.contains(names.fragmentClass)) {
|
|
369
|
+
appearance.classList.add('custom');
|
|
370
|
+
}
|
|
371
|
+
};
|
|
362
372
|
|
|
363
|
-
|
|
364
|
-
|
|
373
|
+
const addDelay = (appearanceArray, options, names) => {
|
|
374
|
+
let delay = 0;
|
|
375
|
+
appearanceArray.forEach((appearance, index) => {
|
|
376
|
+
if (index == 0 && appearance.dataset.delay || index != 0) {
|
|
377
|
+
let elementDelay = options.delay;
|
|
378
|
+
if (appearance.dataset && appearance.dataset.delay) {
|
|
379
|
+
elementDelay = parseInt(appearance.dataset.delay);
|
|
380
|
+
}
|
|
381
|
+
delay = delay + elementDelay;
|
|
382
|
+
appearance.style.setProperty('animation-delay', delay + "ms");
|
|
383
|
+
appearance.removeAttribute('data-delay');
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
};
|
|
365
387
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
388
|
+
/**
|
|
389
|
+
* Selects elements with a specified class that are not nested inside an element with another specified class.
|
|
390
|
+
* @param {string} targetClass - The class name to select elements.
|
|
391
|
+
* @param {string} excludeClass - The class name to exclude elements nested inside it.
|
|
392
|
+
* @param {Element} el - The element to find the target elements in.
|
|
393
|
+
* @returns {Element[]} - Array of selected elements.
|
|
394
|
+
*/
|
|
395
|
+
const elemsNotInClass = (targetClass, excludeClass, el) => Array.from(el.querySelectorAll(`.${targetClass}`)).filter(s => !s.closest(`.${excludeClass}`));
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Selects elements with a specified class that are nested inside an element with another specified class.
|
|
399
|
+
* @param {string} targetClass - The class name to select elements.
|
|
400
|
+
* @param {string} parentClass - The class name of the parent to find elements in.
|
|
401
|
+
* @param {Element} el - The element to find the target elements in.
|
|
402
|
+
* @returns {Element[]} - Array of selected elements.
|
|
403
|
+
*/
|
|
404
|
+
const elemsInClass = (targetClass, parentClass, el) => Array.from(el.querySelectorAll(`.${targetClass}`)).filter(s => s.closest(`.${parentClass}`) === el);
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Extracts groups of elements with a specified class from the provided section element.
|
|
408
|
+
* Groups are formed based on nesting inside elements with another specified class.
|
|
409
|
+
* @param {Element} section - The section to extract data from.
|
|
410
|
+
* @returns {Element[][]} - Nested arrays of selected elements.
|
|
411
|
+
*/
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Extracts groups of elements with a specified class from the provided section element.
|
|
415
|
+
* Groups are formed based on nesting inside elements with another specified class.
|
|
416
|
+
* @param {Element} section - The section to extract data from.
|
|
417
|
+
* @param {string} targetClass - The class name to select elements.
|
|
418
|
+
* @param {string} groupClass - The class name of the parent to find elements in.
|
|
419
|
+
* @returns {Element[][]} - Nested arrays of selected elements.
|
|
420
|
+
*/
|
|
421
|
+
const getAppearanceArrays = (section, targetClass, groupClass) => {
|
|
422
|
+
const result = [elemsNotInClass(targetClass, groupClass, section), ...Array.from(section.querySelectorAll(`.${groupClass}`)).map(frag => elemsInClass(targetClass, groupClass, frag))];
|
|
423
|
+
if (result.some(group => group.length > 0)) {
|
|
424
|
+
return result;
|
|
425
|
+
} else {
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
369
429
|
|
|
370
|
-
|
|
371
|
-
|
|
430
|
+
const convertToSpans = (parent, kind) => {
|
|
431
|
+
let splitElements = false;
|
|
432
|
+
let joinChar = ' ';
|
|
433
|
+
if (kind == "words") {
|
|
434
|
+
splitElements = parent.textContent.trim().split(/\s+/);
|
|
435
|
+
} else if (kind == "letters") {
|
|
436
|
+
splitElements = parent.textContent.trim().split('');
|
|
437
|
+
joinChar = '';
|
|
438
|
+
}
|
|
439
|
+
if (splitElements) {
|
|
440
|
+
const parentAnimateClasses = Array.from(parent.classList).filter(className => className.startsWith('animate__'));
|
|
441
|
+
const newHtml = splitElements.map((element, index) => {
|
|
442
|
+
const span = document.createElement('span');
|
|
443
|
+
span.textContent = element;
|
|
444
|
+
if (element == " ") {
|
|
445
|
+
span.textContent = "\u00A0";
|
|
372
446
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
options.appearevent = "autoanimate";
|
|
447
|
+
if (parent.dataset.delay && index !== 0) {
|
|
448
|
+
span.dataset.delay = parent.dataset.delay;
|
|
376
449
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
slides.to.dataset.appearanceCanStart = true;
|
|
450
|
+
if (parent.dataset.containerDelay && index === 0) {
|
|
451
|
+
span.dataset.delay = parent.dataset.containerDelay;
|
|
380
452
|
}
|
|
453
|
+
parent.classList.forEach(className => className.startsWith('animate__') && span.classList.add(className));
|
|
454
|
+
return span.outerHTML;
|
|
455
|
+
}).join(joinChar);
|
|
456
|
+
parentAnimateClasses.forEach(className => parent.classList.remove(className));
|
|
457
|
+
parent.removeAttribute('data-delay');
|
|
458
|
+
parent.removeAttribute('data-split');
|
|
459
|
+
parent.removeAttribute('data-container-delay');
|
|
460
|
+
parent.innerHTML = newHtml;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
381
463
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
let fromFragments = slides.from.querySelectorAll(`.fragment.visible`);
|
|
464
|
+
/**
|
|
465
|
+
* Derives slide from and to from the event object.
|
|
466
|
+
*
|
|
467
|
+
* @param {Object} event - The event object containing slide transition details.
|
|
468
|
+
* @returns {Object} - An object containing references to the "from" and "to" slides.
|
|
469
|
+
*/
|
|
470
|
+
const fromTo = event => {
|
|
471
|
+
let slides = {};
|
|
472
|
+
slides.from = event.fromSlide || event.previousSlide || null;
|
|
473
|
+
slides.to = event.toSlide || event.currentSlide || null;
|
|
474
|
+
return slides;
|
|
475
|
+
};
|
|
397
476
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
477
|
+
/**
|
|
478
|
+
* A function that determines the appearance event for a given slide.
|
|
479
|
+
*
|
|
480
|
+
* This function checks the `data-appearevent` attribute of the slide and the `options.appearevent` parameter.
|
|
481
|
+
* If `data-appearevent` is set to "auto", it is converted to "autoanimate". If `options.appearevent` is "auto", it is also converted to "autoanimate".
|
|
482
|
+
* The function returns the appearance event, prioritizing `data-appearevent` over `options.appearevent`.
|
|
483
|
+
*
|
|
484
|
+
* @param {HTMLElement} toSlide - The slide for which the appearance event is determined.
|
|
485
|
+
* @param {Object} options - An object containing options for the appearance event.
|
|
486
|
+
* @param {string} options.appearevent - The appearance event option provided in the `options` object.
|
|
487
|
+
*
|
|
488
|
+
* @returns {string} - The determined appearance event for the slide, either from `data-appearevent` or `options.appearevent`.
|
|
489
|
+
*/
|
|
490
|
+
const slideAppearevent = (toSlide, options) => {
|
|
491
|
+
if (toSlide.dataset.appearevent && toSlide.dataset.appearevent === "auto") {
|
|
492
|
+
toSlide.dataset.appearevent = "autoanimate";
|
|
493
|
+
}
|
|
494
|
+
if (options.appearevent == "auto") {
|
|
495
|
+
options.appearevent = "autoanimate";
|
|
496
|
+
}
|
|
497
|
+
return toSlide.dataset.appearevent ? toSlide.dataset.appearevent : options.appearevent;
|
|
498
|
+
};
|
|
406
499
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
500
|
+
/**
|
|
501
|
+
* Remove the 'data-appearance-can-start' attribute from the 'from' slide if the 'hideagain' option is enabled.
|
|
502
|
+
*
|
|
503
|
+
* @param {HTMLElement} slides - The container element for the slides.
|
|
504
|
+
* @param {Object} options - An object containing configuration options.
|
|
505
|
+
* @param {boolean} options.hideagain - A flag indicating whether to remove the attribute when 'hideagain' is true.
|
|
506
|
+
*/
|
|
507
|
+
const removeStartAttribute = (slides, options) => {
|
|
508
|
+
if (options.hideagain) {
|
|
509
|
+
if (slides.from && slides.from.dataset.appearanceCanStart) {
|
|
510
|
+
slides.from.removeAttribute('data-appearance-can-start');
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
};
|
|
410
514
|
|
|
411
|
-
|
|
412
|
-
|
|
515
|
+
/**
|
|
516
|
+
* Turn off slide appearances when transitioning from one slide to another if the 'hideagain' option is enabled.
|
|
517
|
+
*
|
|
518
|
+
* @param {HTMLElement} slides - The container element for the slides.
|
|
519
|
+
* @param {Object} options - An object containing configuration options.
|
|
520
|
+
* @param {string} names.animatecss - The CSS selector for animated elements.
|
|
521
|
+
*/
|
|
522
|
+
const turnOffSlideAppearances = (slides, options, names) => {
|
|
523
|
+
if (options.hideagain) {
|
|
524
|
+
let fromAppearances = slides.from.querySelectorAll(names.animatecss);
|
|
525
|
+
fromAppearances.forEach(appearance => {
|
|
526
|
+
appearance.classList.remove('animationended');
|
|
527
|
+
});
|
|
528
|
+
// Remove visible class from fragments when moving away from that slide
|
|
529
|
+
let fromFragments = slides.from.querySelectorAll(`.fragment.visible`);
|
|
530
|
+
if (fromFragments) {
|
|
531
|
+
fromFragments.forEach(fragment => {
|
|
532
|
+
fragment.classList.remove('visible');
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
};
|
|
413
537
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
538
|
+
/**
|
|
539
|
+
* Handles the showing and hiding of slides based on the provided event and options.
|
|
540
|
+
*
|
|
541
|
+
* @param {Object} event - The event object containing slide transition details.
|
|
542
|
+
* @param {Object} options - An object containing configurations for slide appearance management.
|
|
543
|
+
*/
|
|
544
|
+
const showHideSlide = (event, options, names, vars) => {
|
|
545
|
+
let view = vars.deck.getConfig().view;
|
|
546
|
+
let etype = event.type;
|
|
547
|
+
let slides = fromTo(event);
|
|
548
|
+
if (slides.to) {
|
|
549
|
+
if (etype == "ready") {
|
|
550
|
+
slides.to.dataset.appearanceCanStart = true;
|
|
551
|
+
}
|
|
552
|
+
let appearevent = slideAppearevent(slides.to, options);
|
|
553
|
+
if (etype == appearevent || etype == "slidetransitionend" && appearevent == "autoanimate") {
|
|
554
|
+
slides.to.dataset.appearanceCanStart = true;
|
|
555
|
+
}
|
|
420
556
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
557
|
+
// Add experimental Reader mode compatibility, does not have a slidetransitionend event yet
|
|
558
|
+
if (view == "scroll" && etype == 'slidechanged') {
|
|
559
|
+
removeStartAttribute(slides, options);
|
|
560
|
+
turnOffSlideAppearances(slides, options, names);
|
|
424
561
|
|
|
425
|
-
|
|
426
|
-
|
|
562
|
+
// Add delay to allow for scroll animation to finish
|
|
563
|
+
setTimeout(() => {
|
|
564
|
+
slides.to.dataset.appearanceCanStart = true;
|
|
565
|
+
}, options.delay);
|
|
566
|
+
}
|
|
567
|
+
if (etype == "slidetransitionend") {
|
|
568
|
+
removeStartAttribute(slides, options);
|
|
569
|
+
turnOffSlideAppearances(slides, options, names);
|
|
570
|
+
}
|
|
571
|
+
if (etype == 'slidechanged' && document.body.dataset.exitoverview) {
|
|
572
|
+
removeStartAttribute(slides, options);
|
|
573
|
+
slides.to.dataset.appearanceCanStart = true;
|
|
574
|
+
} else if (etype == 'overviewhidden') {
|
|
575
|
+
document.body.dataset.exitoverview = true;
|
|
576
|
+
setTimeout(() => {
|
|
577
|
+
document.body.removeAttribute('data-exitoverview');
|
|
578
|
+
}, 500);
|
|
579
|
+
if (event.currentSlide) {
|
|
580
|
+
removeStartAttribute(slides, options);
|
|
581
|
+
slides.to.dataset.appearanceCanStart = true;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
};
|
|
427
586
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
587
|
+
const Plugin = () => {
|
|
588
|
+
const vars = {};
|
|
589
|
+
vars.names = {};
|
|
590
|
+
let options = {};
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Prepare the plugin to find Appearance elements
|
|
594
|
+
* @param {Object} vars - The variables to be prepared.
|
|
595
|
+
* @param {Object} names - The names to be prepared.
|
|
596
|
+
* @param {Function} resolve - The callback function to be called when preparation is complete.
|
|
597
|
+
* @throws {Error} Throws an error if the 'options' object is not defined.
|
|
598
|
+
*/
|
|
599
|
+
const prepare = (options, vars, resolve) => {
|
|
600
|
+
debugLog(options, "------------- Preloading -------------");
|
|
601
|
+
let names = vars.names;
|
|
602
|
+
getAndLoadCSS(options, names.es5Filename);
|
|
603
|
+
if (options.compatibility) {
|
|
604
|
+
names.animatecss = '.backInDown, .backInLeft, .backInRight, .backInUp, .bounceIn, .bounceInDown, .bounceInLeft, .bounceInRight, .bounceInUp, .fadeIn, .fadeInDown, .fadeInDownBig, .fadeInLeft, .fadeInLeftBig, .fadeInRight, .fadeInRightBig, .fadeInUp, .fadeInUpBig, .fadeInTopLeft, .fadeInTopRight, .fadeInBottomLeft, .fadeInBottomRight, .flipInX, .flipInY, .lightSpeedInRight, .lightSpeedInLeft, .rotateIn, .rotateInDownLeft, .rotateInDownRight, .rotateInUpLeft, .rotateInUpRight, .jackInTheBox, .rollIn, .zoomIn, .zoomInDown, .zoomInLeft, .zoomInRight, .zoomInUp, .slideInDown, .slideInLeft, .slideInRight, .slideInUp, .skidLeft, .skidLeftBig, .skidRight, .skidRightBig, .shrinkIn, .shrinkInBlur';
|
|
605
|
+
names.baseclass = options.compatibilitybaseclass;
|
|
606
|
+
}
|
|
607
|
+
vars.appearances = Array.from(vars.slides.querySelectorAll(names.animatecss));
|
|
608
|
+
|
|
609
|
+
// Go through each section to see if there are any (auto) selectors that need animation classes
|
|
610
|
+
vars.regularSections.forEach(theSection => addAutoAnimation(theSection, options, vars));
|
|
611
|
+
vars.appearances.forEach((theAppearance, index) => {
|
|
612
|
+
// Fix any list item where the Appearance classes were moved to the span (Quarto does this)
|
|
613
|
+
fixListItem(theAppearance, options, names);
|
|
614
|
+
|
|
615
|
+
// Go through each appearance element and add the baseclass if it doesn't have it
|
|
616
|
+
addBaseClass(theAppearance, names);
|
|
617
|
+
if (theAppearance.hasAttribute('data-split')) {
|
|
618
|
+
convertToSpans(theAppearance, theAppearance.dataset.split);
|
|
431
619
|
}
|
|
432
|
-
};
|
|
620
|
+
});
|
|
621
|
+
vars.regularSections.forEach((theSection, index) => {
|
|
622
|
+
// Get all the Appearances in the section as separate arrays per delay loop
|
|
623
|
+
let appearanceArrays = getAppearanceArrays(theSection, names.baseclass, names.fragmentClass);
|
|
624
|
+
if (appearanceArrays) {
|
|
625
|
+
appearanceArrays.forEach(appearanceArray => {
|
|
626
|
+
// Add the delays to each appearance in the array
|
|
627
|
+
addDelay(appearanceArray, options);
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
doneLoading(resolve);
|
|
632
|
+
};
|
|
433
633
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
634
|
+
/**
|
|
635
|
+
* The main function of the plugin
|
|
636
|
+
* @param {object} deck - The deck object
|
|
637
|
+
* @param {object} options - The options object
|
|
638
|
+
* @param {string} es5Filename - The name of the file that will be used
|
|
639
|
+
*/
|
|
640
|
+
const Appear = function (deck, options, es5Filename) {
|
|
641
|
+
let names = vars.names;
|
|
642
|
+
|
|
643
|
+
// Set up names
|
|
644
|
+
names.baseclass = options.baseclass;
|
|
645
|
+
names.compatibilitybaseclass = options.compatibilitybaseclass;
|
|
646
|
+
names.fragmentSelector = ".fragment";
|
|
647
|
+
names.fragmentClass = "fragment";
|
|
648
|
+
names.speedClasses = ['slower', 'slow', 'fast', 'faster'];
|
|
649
|
+
names.speedClasses.push(...names.speedClasses.map(speed => `animate__${speed}`));
|
|
650
|
+
names.animatecss = '[class^="animate__"],[class*=" animate__"]';
|
|
651
|
+
names.es5Filename = es5Filename;
|
|
652
|
+
names.eventnames = ['ready', 'slidechanged', 'slidetransitionend', 'autoanimate', 'overviewhidden', 'scrolle'];
|
|
653
|
+
|
|
654
|
+
// Set up variables
|
|
655
|
+
vars.deck = deck;
|
|
656
|
+
vars.dom = deck.getRevealElement();
|
|
657
|
+
vars.viewport = deck.getViewportElement();
|
|
658
|
+
vars.slides = deck.getSlidesElement();
|
|
659
|
+
vars.sections = vars.slides.querySelectorAll('section');
|
|
660
|
+
vars.fragments = vars.slides.querySelectorAll(names.fragmentSelector);
|
|
661
|
+
vars.regularSections = Array.from(vars.sections).filter(section => !isStack(section));
|
|
662
|
+
if (/receiver/i.test(window.location.search)) vars.viewport.classList.add('sv');
|
|
663
|
+
names.eventnames.forEach(eventname => deck.on(eventname, event => {
|
|
664
|
+
showHideSlide(event, options, names, vars);
|
|
437
665
|
}));
|
|
666
|
+
vars.viewport.addEventListener("animationend", event => {
|
|
667
|
+
event.target.classList.add('animationended');
|
|
668
|
+
});
|
|
669
|
+
vars.viewport.addEventListener("fragmenthidden", event => {
|
|
670
|
+
event.fragment.classList.remove('animationended');
|
|
671
|
+
event.fragment.querySelectorAll('.animationended').forEach(el => {
|
|
672
|
+
el.classList.remove('animationended');
|
|
673
|
+
});
|
|
674
|
+
});
|
|
675
|
+
return new Promise(resolve => {
|
|
676
|
+
prepare(options, vars, resolve);
|
|
677
|
+
debugLog(options, "---------- Done preloading ----------");
|
|
678
|
+
});
|
|
438
679
|
};
|
|
439
680
|
|
|
681
|
+
/**
|
|
682
|
+
* Initialize the plugin
|
|
683
|
+
* @param {object} deck - The deck object
|
|
684
|
+
*/
|
|
440
685
|
const init = function (deck) {
|
|
441
|
-
let es5Filename = "appearance.js";
|
|
442
686
|
let defaultOptions = {
|
|
443
687
|
baseclass: 'animate__animated',
|
|
444
688
|
hideagain: true,
|
|
@@ -456,47 +700,9 @@
|
|
|
456
700
|
compatibility: false,
|
|
457
701
|
compatibilitybaseclass: 'animated'
|
|
458
702
|
};
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
for (let i in defaultOptions) {
|
|
462
|
-
if (!options.hasOwnProperty(i)) {
|
|
463
|
-
options[i] = defaultOptions[i];
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
};
|
|
467
|
-
|
|
468
|
-
let options = deck.getConfig().appearance || {};
|
|
469
|
-
defaults(options, defaultOptions);
|
|
470
|
-
|
|
471
|
-
function pluginPath() {
|
|
472
|
-
let path;
|
|
473
|
-
let pluginScript = document.querySelector(`script[src$="${es5Filename}"]`);
|
|
474
|
-
|
|
475
|
-
if (pluginScript) {
|
|
476
|
-
path = pluginScript.getAttribute("src").slice(0, -1 * es5Filename.length);
|
|
477
|
-
} else {
|
|
478
|
-
path = (typeof document === 'undefined' && typeof location === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : typeof document === 'undefined' ? location.href : (document.currentScript && document.currentScript.src || new URL('appearance.js', document.baseURI).href)).slice(0, (typeof document === 'undefined' && typeof location === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : typeof document === 'undefined' ? location.href : (document.currentScript && document.currentScript.src || new URL('appearance.js', document.baseURI).href)).lastIndexOf('/') + 1);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
return path;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
let AppearanceStylePath = options.csspath.appearance ? options.csspath.appearance : `${pluginPath()}appearance.css` || 'plugin/appearance/appearance.css';
|
|
485
|
-
let AnimateCSSPath = !options.compatibility ? options.animatecsspath.link : options.animatecsspath.compat;
|
|
486
|
-
|
|
487
|
-
if (options.debug) {
|
|
488
|
-
console.log(`Plugin path = ${pluginPath()}`);
|
|
489
|
-
console.log(`Compatibility mode: ${options.compatibility}`);
|
|
490
|
-
console.log(`Appearance CSS path = ${AppearanceStylePath}`);
|
|
491
|
-
console.log(`AnimateCSS CSS path = ${AnimateCSSPath}`);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
loadStyle(AnimateCSSPath, 'stylesheet', function () {
|
|
495
|
-
loadStyle(AppearanceStylePath, 'stylesheet');
|
|
496
|
-
});
|
|
497
|
-
appear(deck, options);
|
|
703
|
+
options = mergeDeep(defaultOptions, deck.getConfig().appearance || {});
|
|
704
|
+
return Appear(deck, options, "appearance.js");
|
|
498
705
|
};
|
|
499
|
-
|
|
500
706
|
return {
|
|
501
707
|
id: 'appearance',
|
|
502
708
|
init: init
|