vidply 1.0.5 → 1.0.7

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.
@@ -1,154 +1,154 @@
1
- /**
2
- * DOM manipulation utilities
3
- */
4
-
5
- export const DOMUtils = {
6
- createElement(tag, options = {}) {
7
- const element = document.createElement(tag);
8
-
9
- if (options.className) {
10
- element.className = options.className;
11
- }
12
-
13
- if (options.attributes) {
14
- Object.entries(options.attributes).forEach(([key, value]) => {
15
- element.setAttribute(key, value);
16
- });
17
- }
18
-
19
- if (options.innerHTML) {
20
- element.innerHTML = options.innerHTML;
21
- }
22
-
23
- if (options.textContent) {
24
- element.textContent = options.textContent;
25
- }
26
-
27
- if (options.style) {
28
- Object.assign(element.style, options.style);
29
- }
30
-
31
- if (options.children) {
32
- options.children.forEach(child => {
33
- if (child) element.appendChild(child);
34
- });
35
- }
36
-
37
- return element;
38
- },
39
-
40
- addClass(element, className) {
41
- if (element && className) {
42
- element.classList.add(className);
43
- }
44
- },
45
-
46
- removeClass(element, className) {
47
- if (element && className) {
48
- element.classList.remove(className);
49
- }
50
- },
51
-
52
- toggleClass(element, className) {
53
- if (element && className) {
54
- element.classList.toggle(className);
55
- }
56
- },
57
-
58
- hasClass(element, className) {
59
- return element && element.classList.contains(className);
60
- },
61
-
62
- show(element) {
63
- if (element) {
64
- element.style.display = '';
65
- }
66
- },
67
-
68
- hide(element) {
69
- if (element) {
70
- element.style.display = 'none';
71
- }
72
- },
73
-
74
- fadeIn(element, duration = 300) {
75
- if (!element) return;
76
-
77
- element.style.opacity = '0';
78
- element.style.display = '';
79
-
80
- let start = null;
81
- const animate = (timestamp) => {
82
- if (!start) start = timestamp;
83
- const progress = timestamp - start;
84
- const opacity = Math.min(progress / duration, 1);
85
-
86
- element.style.opacity = opacity;
87
-
88
- if (progress < duration) {
89
- requestAnimationFrame(animate);
90
- }
91
- };
92
-
93
- requestAnimationFrame(animate);
94
- },
95
-
96
- fadeOut(element, duration = 300) {
97
- if (!element) return;
98
-
99
- const startOpacity = parseFloat(getComputedStyle(element).opacity) || 1;
100
- let start = null;
101
-
102
- const animate = (timestamp) => {
103
- if (!start) start = timestamp;
104
- const progress = timestamp - start;
105
- const opacity = Math.max(startOpacity - (progress / duration), 0);
106
-
107
- element.style.opacity = opacity;
108
-
109
- if (progress < duration) {
110
- requestAnimationFrame(animate);
111
- } else {
112
- element.style.display = 'none';
113
- }
114
- };
115
-
116
- requestAnimationFrame(animate);
117
- },
118
-
119
- offset(element) {
120
- if (!element) return { top: 0, left: 0 };
121
-
122
- const rect = element.getBoundingClientRect();
123
- return {
124
- top: rect.top + window.pageYOffset,
125
- left: rect.left + window.pageXOffset,
126
- width: rect.width,
127
- height: rect.height
128
- };
129
- },
130
-
131
- escapeHTML(str) {
132
- const div = document.createElement('div');
133
- div.textContent = str;
134
- return div.innerHTML;
135
- },
136
-
137
- sanitizeHTML(html) {
138
- // Basic HTML sanitization - allow safe tags for VTT captions
139
- // Since we control the HTML (from VTT parsing), we can safely allow these tags
140
- const temp = document.createElement('div');
141
-
142
- // Strip out any potentially dangerous tags/attributes
143
- // Allow: strong, em, u, span, b, i with class and data-voice attributes
144
- const safeHtml = html
145
- .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
146
- .replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '')
147
- .replace(/on\w+\s*=/gi, '') // Remove event handlers
148
- .replace(/javascript:/gi, ''); // Remove javascript: protocol
149
-
150
- temp.innerHTML = safeHtml;
151
- return temp.innerHTML;
152
- }
153
- };
154
-
1
+ /**
2
+ * DOM manipulation utilities
3
+ */
4
+
5
+ export const DOMUtils = {
6
+ createElement(tag, options = {}) {
7
+ const element = document.createElement(tag);
8
+
9
+ if (options.className) {
10
+ element.className = options.className;
11
+ }
12
+
13
+ if (options.attributes) {
14
+ Object.entries(options.attributes).forEach(([key, value]) => {
15
+ element.setAttribute(key, value);
16
+ });
17
+ }
18
+
19
+ if (options.innerHTML) {
20
+ element.innerHTML = options.innerHTML;
21
+ }
22
+
23
+ if (options.textContent) {
24
+ element.textContent = options.textContent;
25
+ }
26
+
27
+ if (options.style) {
28
+ Object.assign(element.style, options.style);
29
+ }
30
+
31
+ if (options.children) {
32
+ options.children.forEach(child => {
33
+ if (child) element.appendChild(child);
34
+ });
35
+ }
36
+
37
+ return element;
38
+ },
39
+
40
+ addClass(element, className) {
41
+ if (element && className) {
42
+ element.classList.add(className);
43
+ }
44
+ },
45
+
46
+ removeClass(element, className) {
47
+ if (element && className) {
48
+ element.classList.remove(className);
49
+ }
50
+ },
51
+
52
+ toggleClass(element, className) {
53
+ if (element && className) {
54
+ element.classList.toggle(className);
55
+ }
56
+ },
57
+
58
+ hasClass(element, className) {
59
+ return element && element.classList.contains(className);
60
+ },
61
+
62
+ show(element) {
63
+ if (element) {
64
+ element.style.display = '';
65
+ }
66
+ },
67
+
68
+ hide(element) {
69
+ if (element) {
70
+ element.style.display = 'none';
71
+ }
72
+ },
73
+
74
+ fadeIn(element, duration = 300) {
75
+ if (!element) return;
76
+
77
+ element.style.opacity = '0';
78
+ element.style.display = '';
79
+
80
+ let start = null;
81
+ const animate = (timestamp) => {
82
+ if (!start) start = timestamp;
83
+ const progress = timestamp - start;
84
+ const opacity = Math.min(progress / duration, 1);
85
+
86
+ element.style.opacity = opacity;
87
+
88
+ if (progress < duration) {
89
+ requestAnimationFrame(animate);
90
+ }
91
+ };
92
+
93
+ requestAnimationFrame(animate);
94
+ },
95
+
96
+ fadeOut(element, duration = 300) {
97
+ if (!element) return;
98
+
99
+ const startOpacity = parseFloat(getComputedStyle(element).opacity) || 1;
100
+ let start = null;
101
+
102
+ const animate = (timestamp) => {
103
+ if (!start) start = timestamp;
104
+ const progress = timestamp - start;
105
+ const opacity = Math.max(startOpacity - (progress / duration), 0);
106
+
107
+ element.style.opacity = opacity;
108
+
109
+ if (progress < duration) {
110
+ requestAnimationFrame(animate);
111
+ } else {
112
+ element.style.display = 'none';
113
+ }
114
+ };
115
+
116
+ requestAnimationFrame(animate);
117
+ },
118
+
119
+ offset(element) {
120
+ if (!element) return { top: 0, left: 0 };
121
+
122
+ const rect = element.getBoundingClientRect();
123
+ return {
124
+ top: rect.top + window.pageYOffset,
125
+ left: rect.left + window.pageXOffset,
126
+ width: rect.width,
127
+ height: rect.height
128
+ };
129
+ },
130
+
131
+ escapeHTML(str) {
132
+ const div = document.createElement('div');
133
+ div.textContent = str;
134
+ return div.innerHTML;
135
+ },
136
+
137
+ sanitizeHTML(html) {
138
+ // Basic HTML sanitization - allow safe tags for VTT captions
139
+ // Since we control the HTML (from VTT parsing), we can safely allow these tags
140
+ const temp = document.createElement('div');
141
+
142
+ // Strip out any potentially dangerous tags/attributes
143
+ // Allow: strong, em, u, span, b, i with class and data-voice attributes
144
+ const safeHtml = html
145
+ .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
146
+ .replace(/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi, '')
147
+ .replace(/on\w+\s*=/gi, '') // Remove event handlers
148
+ .replace(/javascript:/gi, ''); // Remove javascript: protocol
149
+
150
+ temp.innerHTML = safeHtml;
151
+ return temp.innerHTML;
152
+ }
153
+ };
154
+
@@ -1,53 +1,53 @@
1
- /**
2
- * Simple EventEmitter implementation
3
- */
4
-
5
- export class EventEmitter {
6
- constructor() {
7
- this.events = {};
8
- }
9
-
10
- on(event, listener) {
11
- if (!this.events[event]) {
12
- this.events[event] = [];
13
- }
14
- this.events[event].push(listener);
15
- return this;
16
- }
17
-
18
- once(event, listener) {
19
- const onceListener = (...args) => {
20
- listener(...args);
21
- this.off(event, onceListener);
22
- };
23
- return this.on(event, onceListener);
24
- }
25
-
26
- off(event, listener) {
27
- if (!this.events[event]) return this;
28
-
29
- if (!listener) {
30
- delete this.events[event];
31
- } else {
32
- this.events[event] = this.events[event].filter(l => l !== listener);
33
- }
34
-
35
- return this;
36
- }
37
-
38
- emit(event, ...args) {
39
- if (!this.events[event]) return this;
40
-
41
- this.events[event].forEach(listener => {
42
- listener(...args);
43
- });
44
-
45
- return this;
46
- }
47
-
48
- removeAllListeners() {
49
- this.events = {};
50
- return this;
51
- }
52
- }
53
-
1
+ /**
2
+ * Simple EventEmitter implementation
3
+ */
4
+
5
+ export class EventEmitter {
6
+ constructor() {
7
+ this.events = {};
8
+ }
9
+
10
+ on(event, listener) {
11
+ if (!this.events[event]) {
12
+ this.events[event] = [];
13
+ }
14
+ this.events[event].push(listener);
15
+ return this;
16
+ }
17
+
18
+ once(event, listener) {
19
+ const onceListener = (...args) => {
20
+ listener(...args);
21
+ this.off(event, onceListener);
22
+ };
23
+ return this.on(event, onceListener);
24
+ }
25
+
26
+ off(event, listener) {
27
+ if (!this.events[event]) return this;
28
+
29
+ if (!listener) {
30
+ delete this.events[event];
31
+ } else {
32
+ this.events[event] = this.events[event].filter(l => l !== listener);
33
+ }
34
+
35
+ return this;
36
+ }
37
+
38
+ emit(event, ...args) {
39
+ if (!this.events[event]) return this;
40
+
41
+ this.events[event].forEach(listener => {
42
+ listener(...args);
43
+ });
44
+
45
+ return this;
46
+ }
47
+
48
+ removeAllListeners() {
49
+ this.events = {};
50
+ return this;
51
+ }
52
+ }
53
+
@@ -0,0 +1,156 @@
1
+ /**
2
+ * StorageManager - Handles persistent storage of user preferences
3
+ */
4
+
5
+ export class StorageManager {
6
+ constructor(namespace = 'vidply') {
7
+ this.namespace = namespace;
8
+ this.storage = this.isStorageAvailable() ? localStorage : null;
9
+ }
10
+
11
+ /**
12
+ * Check if localStorage is available
13
+ */
14
+ isStorageAvailable() {
15
+ try {
16
+ const test = '__storage_test__';
17
+ localStorage.setItem(test, test);
18
+ localStorage.removeItem(test);
19
+ return true;
20
+ } catch (e) {
21
+ return false;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Get a namespaced key
27
+ */
28
+ getKey(key) {
29
+ return `${this.namespace}_${key}`;
30
+ }
31
+
32
+ /**
33
+ * Save a value to storage
34
+ */
35
+ set(key, value) {
36
+ if (!this.storage) return false;
37
+
38
+ try {
39
+ const namespacedKey = this.getKey(key);
40
+ this.storage.setItem(namespacedKey, JSON.stringify(value));
41
+ return true;
42
+ } catch (e) {
43
+ console.warn('Failed to save to localStorage:', e);
44
+ return false;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Get a value from storage
50
+ */
51
+ get(key, defaultValue = null) {
52
+ if (!this.storage) return defaultValue;
53
+
54
+ try {
55
+ const namespacedKey = this.getKey(key);
56
+ const value = this.storage.getItem(namespacedKey);
57
+ return value ? JSON.parse(value) : defaultValue;
58
+ } catch (e) {
59
+ console.warn('Failed to read from localStorage:', e);
60
+ return defaultValue;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Remove a value from storage
66
+ */
67
+ remove(key) {
68
+ if (!this.storage) return false;
69
+
70
+ try {
71
+ const namespacedKey = this.getKey(key);
72
+ this.storage.removeItem(namespacedKey);
73
+ return true;
74
+ } catch (e) {
75
+ console.warn('Failed to remove from localStorage:', e);
76
+ return false;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Clear all namespaced values
82
+ */
83
+ clear() {
84
+ if (!this.storage) return false;
85
+
86
+ try {
87
+ const keys = Object.keys(this.storage);
88
+ keys.forEach(key => {
89
+ if (key.startsWith(this.namespace)) {
90
+ this.storage.removeItem(key);
91
+ }
92
+ });
93
+ return true;
94
+ } catch (e) {
95
+ console.warn('Failed to clear localStorage:', e);
96
+ return false;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Save transcript preferences
102
+ */
103
+ saveTranscriptPreferences(preferences) {
104
+ return this.set('transcript_preferences', preferences);
105
+ }
106
+
107
+ /**
108
+ * Get transcript preferences
109
+ */
110
+ getTranscriptPreferences() {
111
+ return this.get('transcript_preferences', null);
112
+ }
113
+
114
+ /**
115
+ * Save caption preferences
116
+ */
117
+ saveCaptionPreferences(preferences) {
118
+ return this.set('caption_preferences', preferences);
119
+ }
120
+
121
+ /**
122
+ * Get caption preferences
123
+ */
124
+ getCaptionPreferences() {
125
+ return this.get('caption_preferences', null);
126
+ }
127
+
128
+ /**
129
+ * Save player preferences (volume, speed, etc.)
130
+ */
131
+ savePlayerPreferences(preferences) {
132
+ return this.set('player_preferences', preferences);
133
+ }
134
+
135
+ /**
136
+ * Get player preferences
137
+ */
138
+ getPlayerPreferences() {
139
+ return this.get('player_preferences', null);
140
+ }
141
+
142
+ /**
143
+ * Save sign language preferences (position and size)
144
+ */
145
+ saveSignLanguagePreferences(preferences) {
146
+ return this.set('sign_language_preferences', preferences);
147
+ }
148
+
149
+ /**
150
+ * Get sign language preferences
151
+ */
152
+ getSignLanguagePreferences() {
153
+ return this.get('sign_language_preferences', null);
154
+ }
155
+ }
156
+