@myxo-victor/chexjs 6.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.
- package/ChexJs/.htaccess +9 -0
- package/ChexJs/Chex.js +712 -0
- package/ChexJs/Chex.php +354 -0
- package/ChexJs/Chex_notify.php +68 -0
- package/ChexJs/LICENSE +22 -0
- package/ChexJs/README.md +121 -0
- package/ChexJs/api/read.txt +4 -0
- package/ChexJs/app/api/api.txt +4 -0
- package/ChexJs/app/components/main.js +4 -0
- package/ChexJs/app/index.css +12 -0
- package/ChexJs/app/index.html +28 -0
- package/ChexJs/app/logic/app.js +4 -0
- package/ChexJs/components/demo.js +45 -0
- package/ChexJs/components/main.js +609 -0
- package/ChexJs/images/logo.png +0 -0
- package/ChexJs/index.css +118 -0
- package/ChexJs/index.html +44 -0
- package/ChexJs/libs/modal.js +617 -0
- package/ChexJs/libs/orbit.js +217 -0
- package/ChexJs/libs/racket.js +374 -0
- package/ChexJs/libs/rinx.js +88 -0
- package/ChexJs/libs/scrollEcho.js +212 -0
- package/ChexJs/libs/skeleton.js +341 -0
- package/ChexJs/libs/smooth.js +647 -0
- package/ChexJs/logic/app.js +36 -0
- package/ChexJs/notification_handler.txt +1 -0
- package/ChexJs/sw.js +67 -0
- package/package.json +12 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OrbitJS Engine V3.1.0 - Professional Grade
|
|
3
|
+
* Fixed Visibility & Dynamic Rendering Support
|
|
4
|
+
* Features: Seamless Loop, Touch, Hover-Pause, Auto-Dots, Chex-Ready.
|
|
5
|
+
* @author Myxo victor
|
|
6
|
+
* Single Image slider (Allows us to display slide through bunch of images 1 by 1)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const orbit = {
|
|
10
|
+
_sliders: [],
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Initialize the sliding logic
|
|
14
|
+
* @param {Object} config - { IDs: ['id1', 'id2'], interval: 4000, dots: true }
|
|
15
|
+
*/
|
|
16
|
+
slides: function(config) {
|
|
17
|
+
// 1. Initial Check & Delay for Dynamic Content (Chex support)
|
|
18
|
+
const init = () => {
|
|
19
|
+
const ids = config.IDs || [];
|
|
20
|
+
const elements = ids.map(id => document.getElementById(id)).filter(el => el !== null);
|
|
21
|
+
|
|
22
|
+
// If elements aren't in the DOM yet, retry in 100ms
|
|
23
|
+
if (elements.length === 0) {
|
|
24
|
+
setTimeout(init, 100);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this._build(config, elements);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
init();
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
_build: function(config, elements) {
|
|
35
|
+
const interval = config.interval || 4000;
|
|
36
|
+
const showDots = config.dots !== false;
|
|
37
|
+
const pauseOnHover = config.pauseOnHover !== false;
|
|
38
|
+
const wrapper = elements[0].parentElement;
|
|
39
|
+
const originalCount = elements.length;
|
|
40
|
+
|
|
41
|
+
// Prevent nested/duplicate initialization (can blank out content)
|
|
42
|
+
if (wrapper && wrapper.classList && wrapper.classList.contains('orbit-track')) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (wrapper && wrapper.querySelector && wrapper.querySelector('.orbit-track')) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 2. Setup Wrapper Layout
|
|
50
|
+
// If height is 0, images will be invisible. We force a min-height.
|
|
51
|
+
if (wrapper.offsetHeight === 0) {
|
|
52
|
+
wrapper.style.minHeight = "300px";
|
|
53
|
+
}
|
|
54
|
+
wrapper.style.position = 'relative';
|
|
55
|
+
wrapper.style.overflow = 'hidden';
|
|
56
|
+
wrapper.style.display = 'block';
|
|
57
|
+
wrapper.style.touchAction = 'pan-y';
|
|
58
|
+
|
|
59
|
+
// 3. Create Seamless Track
|
|
60
|
+
const track = document.createElement('div');
|
|
61
|
+
track.className = 'orbit-track';
|
|
62
|
+
track.style.cssText = `
|
|
63
|
+
display: flex;
|
|
64
|
+
width: 100%;
|
|
65
|
+
height: 100%;
|
|
66
|
+
z-index: 5;
|
|
67
|
+
transition: transform 0.8s cubic-bezier(0.645, 0.045, 0.355, 1);
|
|
68
|
+
will-change: transform;
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
// 4. Handle Clones for Seamless Loop
|
|
72
|
+
const firstClone = elements[0].cloneNode(true);
|
|
73
|
+
const lastClone = elements[elements.length - 1].cloneNode(true);
|
|
74
|
+
firstClone.id += '-clone';
|
|
75
|
+
lastClone.id += '-clone';
|
|
76
|
+
|
|
77
|
+
const trackElements = [lastClone, ...elements, firstClone];
|
|
78
|
+
|
|
79
|
+
trackElements.forEach(el => {
|
|
80
|
+
el.style.flex = '0 0 100%';
|
|
81
|
+
el.style.width = '100%';
|
|
82
|
+
el.style.height = '100%';
|
|
83
|
+
el.style.objectFit = 'cover';
|
|
84
|
+
el.style.display = 'block'; // Force visibility
|
|
85
|
+
el.style.visibility = 'visible';
|
|
86
|
+
el.style.opacity = '1';
|
|
87
|
+
track.appendChild(el);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// 5. Replace contents and start
|
|
91
|
+
wrapper.innerHTML = '';
|
|
92
|
+
wrapper.appendChild(track);
|
|
93
|
+
|
|
94
|
+
// Force a browser reflow to ensure the -100% takes effect immediately
|
|
95
|
+
track.offsetHeight;
|
|
96
|
+
let currentIndex = 1;
|
|
97
|
+
track.style.transform = `translateX(-100%)`;
|
|
98
|
+
|
|
99
|
+
const state = {
|
|
100
|
+
wrapper, track, originalCount, currentIndex, interval,
|
|
101
|
+
timer: null, started: false, isTransitioning: false, dots: []
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
if (showDots) this._createDots(wrapper, state);
|
|
105
|
+
if (pauseOnHover) {
|
|
106
|
+
wrapper.addEventListener('mouseenter', () => this._pause(state));
|
|
107
|
+
wrapper.addEventListener('mouseleave', () => this._start(state));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this._addTouchListeners(wrapper, state);
|
|
111
|
+
track.addEventListener('transitionend', () => this._handleTransitionEnd(state));
|
|
112
|
+
|
|
113
|
+
this._sliders.push(state);
|
|
114
|
+
this._observe(wrapper, state);
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
_createDots: function(wrapper, state) {
|
|
118
|
+
const dotsContainer = document.createElement('div');
|
|
119
|
+
dotsContainer.style.cssText = 'position:absolute;bottom:20px;left:50%;transform:translateX(-50%);display:flex;gap:10px;z-index:20;';
|
|
120
|
+
|
|
121
|
+
for (let i = 0; i < state.originalCount; i++) {
|
|
122
|
+
const dot = document.createElement('div');
|
|
123
|
+
dot.style.cssText = 'width:10px;height:10px;border-radius:50%;background:rgba(255,255,255,0.5);cursor:pointer;transition:0.3s;';
|
|
124
|
+
if (i === 0) dot.style.background = '#fff';
|
|
125
|
+
dot.onclick = (e) => { e.stopPropagation(); this._goTo(state, i + 1); };
|
|
126
|
+
dotsContainer.appendChild(dot);
|
|
127
|
+
state.dots.push(dot);
|
|
128
|
+
}
|
|
129
|
+
wrapper.appendChild(dotsContainer);
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
_updateDots: function(state) {
|
|
133
|
+
if (state.dots.length === 0) return;
|
|
134
|
+
let activeDot = state.currentIndex - 1;
|
|
135
|
+
if (activeDot >= state.originalCount) activeDot = 0;
|
|
136
|
+
if (activeDot < 0) activeDot = state.originalCount - 1;
|
|
137
|
+
|
|
138
|
+
state.dots.forEach((dot, i) => {
|
|
139
|
+
dot.style.background = i === activeDot ? '#fff' : 'rgba(255,255,255,0.5)';
|
|
140
|
+
dot.style.transform = i === activeDot ? 'scale(1.2)' : 'scale(1)';
|
|
141
|
+
});
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
_addTouchListeners: function(wrapper, state) {
|
|
145
|
+
let startX = 0;
|
|
146
|
+
wrapper.addEventListener('touchstart', (e) => {
|
|
147
|
+
startX = e.touches[0].clientX;
|
|
148
|
+
this._pause(state);
|
|
149
|
+
}, {passive: true});
|
|
150
|
+
|
|
151
|
+
wrapper.addEventListener('touchend', (e) => {
|
|
152
|
+
let endX = e.changedTouches[0].clientX;
|
|
153
|
+
let diff = startX - endX;
|
|
154
|
+
if (Math.abs(diff) > 50) {
|
|
155
|
+
diff > 0 ? this._next(state) : this._prev(state);
|
|
156
|
+
}
|
|
157
|
+
this._start(state);
|
|
158
|
+
}, {passive: true});
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
_handleTransitionEnd: function(state) {
|
|
162
|
+
state.isTransitioning = false;
|
|
163
|
+
if (state.currentIndex === 0) {
|
|
164
|
+
state.track.style.transition = 'none';
|
|
165
|
+
state.currentIndex = state.originalCount;
|
|
166
|
+
state.track.style.transform = `translateX(-${state.currentIndex * 100}%)`;
|
|
167
|
+
} else if (state.currentIndex === state.originalCount + 1) {
|
|
168
|
+
state.track.style.transition = 'none';
|
|
169
|
+
state.currentIndex = 1;
|
|
170
|
+
state.track.style.transform = `translateX(-100%)`;
|
|
171
|
+
}
|
|
172
|
+
this._updateDots(state);
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
_goTo: function(state, index) {
|
|
176
|
+
if (state.isTransitioning) return;
|
|
177
|
+
state.isTransitioning = true;
|
|
178
|
+
state.currentIndex = index;
|
|
179
|
+
state.track.style.transition = 'transform 0.8s cubic-bezier(0.645, 0.045, 0.355, 1)';
|
|
180
|
+
state.track.style.transform = `translateX(-${index * 100}%)`;
|
|
181
|
+
this._updateDots(state);
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
_next: function(state) { this._goTo(state, state.currentIndex + 1); },
|
|
185
|
+
_prev: function(state) { this._goTo(state, state.currentIndex - 1); },
|
|
186
|
+
|
|
187
|
+
_observe: function(target, state) {
|
|
188
|
+
const observer = new IntersectionObserver((entries) => {
|
|
189
|
+
entries.forEach(entry => {
|
|
190
|
+
if (entry.isIntersecting && !state.started) {
|
|
191
|
+
this._start(state);
|
|
192
|
+
state.started = true;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}, { threshold: 0.1 });
|
|
196
|
+
observer.observe(target);
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
_start: function(state) {
|
|
200
|
+
if (state.timer) clearInterval(state.timer);
|
|
201
|
+
state.timer = setInterval(() => this._next(state), state.interval);
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
_pause: function(state) { clearInterval(state.timer); }
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
/**************************
|
|
209
|
+
How to use this library
|
|
210
|
+
**************************
|
|
211
|
+
*/
|
|
212
|
+
|
|
213
|
+
/* orbit.slides({
|
|
214
|
+
IDs: ['ic','ic','ic'],
|
|
215
|
+
interval: 5000,
|
|
216
|
+
dots: true
|
|
217
|
+
})*/
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Racket.js - Generic HTML Element Infinite Slider Engine
|
|
3
|
+
* Supports standard arrays, handles auto-hiding of source nodes,
|
|
4
|
+
* clones entire rich DOM structures, and adapts dynamically
|
|
5
|
+
* to mobile viewports (1 slide) and desktop viewports (3 slides).
|
|
6
|
+
* Use for image slider or carousel
|
|
7
|
+
*/
|
|
8
|
+
class Racket {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.rawInputList = []; // Holds raw ID/selector inputs
|
|
11
|
+
this.slides = []; // Stores resolved, hidden source nodes
|
|
12
|
+
this.intervalDuration = 2000; // Carousel speed
|
|
13
|
+
this.currentPage = 0; // Slide page index
|
|
14
|
+
this.container = null; // Slider mounting viewport
|
|
15
|
+
this.timer = null; // Animation rotation timer
|
|
16
|
+
this.isPlaying = false; // Playback state
|
|
17
|
+
this.track = null; // Inner moving rail
|
|
18
|
+
this.changeCallback = null; // Realtime callback updates
|
|
19
|
+
this.isTransitioning = false; // Transition guard locks
|
|
20
|
+
this.isHovered = false; // Cursor tracking
|
|
21
|
+
this.isHoverPaused = false; // Hover suspension state
|
|
22
|
+
this.lastItemsPerPage = this.getItemsPerPage(); // Cache to check breakpoint changes
|
|
23
|
+
this.resizeBound = false; // Guard duplicate resize listeners
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Helper to detect active items per page based on viewport size.
|
|
28
|
+
* Mobile (< 768px) shows 1 slide at a time. Desktop (>= 768px) shows 3.
|
|
29
|
+
* @returns {number}
|
|
30
|
+
*/
|
|
31
|
+
getItemsPerPage() {
|
|
32
|
+
if (typeof window !== 'undefined' && window.innerWidth < 768) {
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
return 3;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Mounts the Racket slider onto a targeted DOM element wrapper.
|
|
40
|
+
* @param {string|HTMLElement} selector
|
|
41
|
+
*/
|
|
42
|
+
mount(selector) {
|
|
43
|
+
this.container = typeof selector === 'string'
|
|
44
|
+
? document.querySelector(selector)
|
|
45
|
+
: selector;
|
|
46
|
+
|
|
47
|
+
if (!this.container) {
|
|
48
|
+
console.warn(`Racket.js: Mounting container "${selector}" not found yet. Retrying on render.`);
|
|
49
|
+
} else {
|
|
50
|
+
this.setupHoverEvents();
|
|
51
|
+
this.setupResizeEvent();
|
|
52
|
+
}
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Listens for mouse hover patterns to suspend playback.
|
|
58
|
+
*/
|
|
59
|
+
setupHoverEvents() {
|
|
60
|
+
if (!this.container) return;
|
|
61
|
+
|
|
62
|
+
// Use a flag to avoid adding duplicate event listeners
|
|
63
|
+
if (this.container.dataset.racketHoverBound) return;
|
|
64
|
+
this.container.dataset.racketHoverBound = "true";
|
|
65
|
+
|
|
66
|
+
this.container.addEventListener('mouseenter', () => {
|
|
67
|
+
this.isHovered = true;
|
|
68
|
+
if (this.isPlaying && !this.isHoverPaused) {
|
|
69
|
+
this.isHoverPaused = true;
|
|
70
|
+
if (this.timer) {
|
|
71
|
+
clearInterval(this.timer);
|
|
72
|
+
this.timer = null;
|
|
73
|
+
}
|
|
74
|
+
this.updatePosition();
|
|
75
|
+
if (window.logToSandboxConsole) {
|
|
76
|
+
window.logToSandboxConsole('Carousel auto-rotation paused on hover 🧊', 'info');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
this.container.addEventListener('mouseleave', () => {
|
|
82
|
+
this.isHovered = false;
|
|
83
|
+
if (this.isPlaying && this.isHoverPaused) {
|
|
84
|
+
this.isHoverPaused = false;
|
|
85
|
+
this.startTimer();
|
|
86
|
+
this.updatePosition();
|
|
87
|
+
if (window.logToSandboxConsole) {
|
|
88
|
+
window.logToSandboxConsole('Carousel auto-rotation resumed 🚀', 'info');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Attaches window resize listeners to trigger smooth layout adaptions.
|
|
96
|
+
*/
|
|
97
|
+
setupResizeEvent() {
|
|
98
|
+
if (this.resizeBound || typeof window === 'undefined') return;
|
|
99
|
+
this.resizeBound = true;
|
|
100
|
+
|
|
101
|
+
let resizeTimeout;
|
|
102
|
+
window.addEventListener('resize', () => {
|
|
103
|
+
clearTimeout(resizeTimeout);
|
|
104
|
+
resizeTimeout = setTimeout(() => {
|
|
105
|
+
const currentBreakpointVal = this.getItemsPerPage();
|
|
106
|
+
if (currentBreakpointVal !== this.lastItemsPerPage) {
|
|
107
|
+
this.lastItemsPerPage = currentBreakpointVal;
|
|
108
|
+
this.currentPage = 0; // Reset to start index to avoid layout miscalculation
|
|
109
|
+
this.render();
|
|
110
|
+
if (window.logToSandboxConsole) {
|
|
111
|
+
window.logToSandboxConsole(`Responsive Layout update: Displaying ${currentBreakpointVal} item(s) per frame 📱💻`, 'info');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}, 150);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Accepts array list representing raw image IDs or direct URLs.
|
|
120
|
+
* @param {Array} imgArray
|
|
121
|
+
*/
|
|
122
|
+
images(imgArray) {
|
|
123
|
+
this.rawInputList = Array.isArray(imgArray) ? imgArray : [imgArray];
|
|
124
|
+
this.render();
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Adjusts the transition frequency duration limit.
|
|
130
|
+
* @param {number} ms
|
|
131
|
+
*/
|
|
132
|
+
duration(ms) {
|
|
133
|
+
this.intervalDuration = Number(ms) || 2000;
|
|
134
|
+
if (this.isPlaying && !this.isHoverPaused) {
|
|
135
|
+
this.play();
|
|
136
|
+
} else {
|
|
137
|
+
this.updatePosition();
|
|
138
|
+
}
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Resolves elements from IDs, clones them, hides the static versions,
|
|
144
|
+
* and compiles the infinite scrolling layout.
|
|
145
|
+
*/
|
|
146
|
+
render() {
|
|
147
|
+
this.slides = [];
|
|
148
|
+
this.rawInputList.forEach(item => {
|
|
149
|
+
if (typeof item === 'string') {
|
|
150
|
+
const trimmed = item.trim();
|
|
151
|
+
const element = document.getElementById(trimmed) || document.querySelector(trimmed);
|
|
152
|
+
if (element) {
|
|
153
|
+
// Hide the static element from the page layout so they don't appear stacked!
|
|
154
|
+
element.style.display = 'none';
|
|
155
|
+
this.slides.push({ type: 'dom', node: element });
|
|
156
|
+
} else {
|
|
157
|
+
// Fallback to simple URL string
|
|
158
|
+
this.slides.push({ type: 'url', url: item });
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
this.slides.push({ type: 'url', url: String(item) });
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (this.slides.length === 0) return;
|
|
166
|
+
|
|
167
|
+
if (!this.container) {
|
|
168
|
+
const firstDom = this.slides.find(s => s.type === 'dom');
|
|
169
|
+
if (firstDom && firstDom.node.parentElement) {
|
|
170
|
+
this.container = firstDom.node.parentElement;
|
|
171
|
+
this.setupHoverEvents();
|
|
172
|
+
this.setupResizeEvent();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!this.container) return;
|
|
177
|
+
|
|
178
|
+
// Setup container dimensions & structural viewport wrapper
|
|
179
|
+
this.container.style.position = 'relative';
|
|
180
|
+
this.container.style.overflow = 'hidden';
|
|
181
|
+
|
|
182
|
+
let viewport = this.container.querySelector('.racket-viewport-wrapper');
|
|
183
|
+
if (!viewport) {
|
|
184
|
+
viewport = document.createElement('div');
|
|
185
|
+
viewport.className = 'racket-viewport-wrapper';
|
|
186
|
+
viewport.style.overflow = 'hidden';
|
|
187
|
+
viewport.style.width = '100%';
|
|
188
|
+
this.container.appendChild(viewport);
|
|
189
|
+
} else {
|
|
190
|
+
viewport.innerHTML = ''; // Clean old slider rails
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const track = document.createElement('div');
|
|
194
|
+
track.className = 'racket-track';
|
|
195
|
+
track.style.display = 'flex';
|
|
196
|
+
track.style.transition = 'transform 0.6s cubic-bezier(0.16, 1, 0.3, 1)';
|
|
197
|
+
track.style.width = '100%';
|
|
198
|
+
viewport.appendChild(track);
|
|
199
|
+
|
|
200
|
+
// Dynamic items per view page depending on responsiveness parameters
|
|
201
|
+
const itemsPerPage = this.getItemsPerPage();
|
|
202
|
+
const originalCount = this.slides.length;
|
|
203
|
+
const paddedSlides = [...this.slides];
|
|
204
|
+
|
|
205
|
+
// Align padding sizes dynamically to avoid division remainders
|
|
206
|
+
while (paddedSlides.length % itemsPerPage !== 0) {
|
|
207
|
+
paddedSlides.push(this.slides[paddedSlides.length % originalCount]);
|
|
208
|
+
}
|
|
209
|
+
const totalPages = paddedSlides.length / itemsPerPage;
|
|
210
|
+
|
|
211
|
+
// Build responsive dynamic circular buffers (cloned items for infinite scrolling loop)
|
|
212
|
+
const lastPageItems = paddedSlides.slice((totalPages - 1) * itemsPerPage);
|
|
213
|
+
const firstPageItems = paddedSlides.slice(0, itemsPerPage);
|
|
214
|
+
const trackItems = [...lastPageItems, ...paddedSlides, ...firstPageItems];
|
|
215
|
+
|
|
216
|
+
trackItems.forEach((slide) => {
|
|
217
|
+
const itemWrapper = document.createElement('div');
|
|
218
|
+
itemWrapper.style.flex = `0 0 calc(100% / ${itemsPerPage})`;
|
|
219
|
+
itemWrapper.style.maxWidth = `calc(100% / ${itemsPerPage})`;
|
|
220
|
+
itemWrapper.style.boxSizing = 'border-box';
|
|
221
|
+
itemWrapper.style.padding = '8px'; // Uniform gap spacing
|
|
222
|
+
|
|
223
|
+
if (slide.type === 'dom') {
|
|
224
|
+
// Clone the entire rich card element (maintaining styles, names, text, and images)
|
|
225
|
+
const clone = slide.node.cloneNode(true);
|
|
226
|
+
clone.style.display = ''; // Clear display: none
|
|
227
|
+
clone.style.width = '100%';
|
|
228
|
+
clone.removeAttribute('id'); // Avoid element ID duplication
|
|
229
|
+
itemWrapper.appendChild(clone);
|
|
230
|
+
} else {
|
|
231
|
+
// Render simple fallback image block
|
|
232
|
+
const imgWrapper = document.createElement('div');
|
|
233
|
+
imgWrapper.style.position = 'relative';
|
|
234
|
+
imgWrapper.style.width = '100%';
|
|
235
|
+
imgWrapper.style.overflow = 'hidden';
|
|
236
|
+
imgWrapper.style.borderRadius = '12px';
|
|
237
|
+
imgWrapper.style.aspectRatio = '4/3';
|
|
238
|
+
imgWrapper.style.backgroundColor = '#111827';
|
|
239
|
+
|
|
240
|
+
const img = document.createElement('img');
|
|
241
|
+
img.src = slide.url;
|
|
242
|
+
img.style.width = '100%';
|
|
243
|
+
img.style.height = '100%';
|
|
244
|
+
img.style.objectFit = 'cover';
|
|
245
|
+
imgWrapper.appendChild(img);
|
|
246
|
+
itemWrapper.appendChild(imgWrapper);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
track.appendChild(itemWrapper);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
this.track = track;
|
|
253
|
+
|
|
254
|
+
this.track.addEventListener('transitionend', () => {
|
|
255
|
+
this.isTransitioning = false;
|
|
256
|
+
if (this.currentPage >= totalPages) {
|
|
257
|
+
this.track.style.transition = 'none';
|
|
258
|
+
this.currentPage = 0;
|
|
259
|
+
this.updatePosition();
|
|
260
|
+
this.track.offsetHeight; // Force layout flow reflow
|
|
261
|
+
this.track.style.transition = 'transform 0.6s cubic-bezier(0.16, 1, 0.3, 1)';
|
|
262
|
+
}
|
|
263
|
+
if (this.currentPage <= -1) {
|
|
264
|
+
this.track.style.transition = 'none';
|
|
265
|
+
this.currentPage = totalPages - 1;
|
|
266
|
+
this.updatePosition();
|
|
267
|
+
this.track.offsetHeight; // Force layout flow reflow
|
|
268
|
+
this.track.style.transition = 'transform 0.6s cubic-bezier(0.16, 1, 0.3, 1)';
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
this.updatePosition();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Transforms slider tracks smoothly relative to target index.
|
|
277
|
+
*/
|
|
278
|
+
updatePosition() {
|
|
279
|
+
if (!this.track || this.slides.length === 0) return;
|
|
280
|
+
const itemsPerPage = this.getItemsPerPage();
|
|
281
|
+
const originalCount = this.slides.length;
|
|
282
|
+
const totalPages = Math.ceil(originalCount / itemsPerPage);
|
|
283
|
+
|
|
284
|
+
const visualPageIndex = this.currentPage + 1;
|
|
285
|
+
this.track.style.transform = `translateX(-${visualPageIndex * 100}%)`;
|
|
286
|
+
|
|
287
|
+
if (this.changeCallback) {
|
|
288
|
+
let normalizedPage = this.currentPage;
|
|
289
|
+
if (normalizedPage >= totalPages) normalizedPage = 0;
|
|
290
|
+
if (normalizedPage < 0) normalizedPage = totalPages - 1;
|
|
291
|
+
|
|
292
|
+
this.changeCallback({
|
|
293
|
+
currentIndex: normalizedPage * itemsPerPage,
|
|
294
|
+
pageIndex: normalizedPage,
|
|
295
|
+
totalPages: totalPages,
|
|
296
|
+
interval: this.intervalDuration,
|
|
297
|
+
isPlaying: this.isPlaying,
|
|
298
|
+
isHoverPaused: this.isHoverPaused,
|
|
299
|
+
itemsPerPage: itemsPerPage
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
next() {
|
|
305
|
+
if (this.isTransitioning) return this;
|
|
306
|
+
this.isTransitioning = true;
|
|
307
|
+
this.currentPage++;
|
|
308
|
+
this.updatePosition();
|
|
309
|
+
return this;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
prev() {
|
|
313
|
+
if (this.isTransitioning) return this;
|
|
314
|
+
this.isTransitioning = true;
|
|
315
|
+
this.currentPage--;
|
|
316
|
+
this.updatePosition();
|
|
317
|
+
return this;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
startTimer() {
|
|
321
|
+
if (this.timer) clearInterval(this.timer);
|
|
322
|
+
this.timer = setInterval(() => {
|
|
323
|
+
this.next();
|
|
324
|
+
}, this.intervalDuration);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
play() {
|
|
328
|
+
this.isPlaying = true;
|
|
329
|
+
if (this.isHovered) {
|
|
330
|
+
this.isHoverPaused = true;
|
|
331
|
+
if (this.timer) {
|
|
332
|
+
clearInterval(this.timer);
|
|
333
|
+
this.timer = null;
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
this.isHoverPaused = false;
|
|
337
|
+
this.startTimer();
|
|
338
|
+
}
|
|
339
|
+
this.updatePosition();
|
|
340
|
+
return this;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
stop() {
|
|
344
|
+
this.isPlaying = false;
|
|
345
|
+
this.isHoverPaused = false;
|
|
346
|
+
if (this.timer) {
|
|
347
|
+
clearInterval(this.timer);
|
|
348
|
+
this.timer = null;
|
|
349
|
+
}
|
|
350
|
+
this.updatePosition();
|
|
351
|
+
return this;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
onUpdate(fn) {
|
|
355
|
+
this.changeCallback = fn;
|
|
356
|
+
return this;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (typeof window !== 'undefined') {
|
|
361
|
+
window.Racket = Racket;
|
|
362
|
+
window.racket = new Racket();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
/**************************
|
|
367
|
+
How to use this library
|
|
368
|
+
**************************
|
|
369
|
+
*/
|
|
370
|
+
/*
|
|
371
|
+
racket.images(['img1', 'img2', 'img3'])//list of image IDs
|
|
372
|
+
racket.duration([2000])//Duration
|
|
373
|
+
racket.play()//Start carousel
|
|
374
|
+
*/
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rinx Text Slider V1.0
|
|
3
|
+
* Specifically designed for sliding text blocks inside overlays
|
|
4
|
+
* without breaking parent container layouts.
|
|
5
|
+
* This library allows us to make a container holding some texts to slide one by one
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const rinx = {
|
|
9
|
+
_instances: [],
|
|
10
|
+
|
|
11
|
+
slides: function(config) {
|
|
12
|
+
const ids = config.IDs || [];
|
|
13
|
+
const interval = config.interval || 3000;
|
|
14
|
+
const effect = config.effect || 'fade'; // 'fade' or 'slide'
|
|
15
|
+
|
|
16
|
+
const elements = ids.map(id => document.getElementById(id)).filter(el => el !== null);
|
|
17
|
+
if (elements.length === 0) return;
|
|
18
|
+
|
|
19
|
+
// Setup Initial State
|
|
20
|
+
elements.forEach((el, index) => {
|
|
21
|
+
el.style.transition = 'all 0.8s ease-in-out';
|
|
22
|
+
if (index === 0) {
|
|
23
|
+
el.style.opacity = '1';
|
|
24
|
+
el.style.display = 'block';
|
|
25
|
+
el.style.transform = 'translateX(0)';
|
|
26
|
+
} else {
|
|
27
|
+
el.style.opacity = '0';
|
|
28
|
+
el.style.display = 'none';
|
|
29
|
+
el.style.transform = 'translateX(20px)';
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
let currentIndex = 0;
|
|
34
|
+
|
|
35
|
+
const state = {
|
|
36
|
+
elements: elements,
|
|
37
|
+
interval: interval,
|
|
38
|
+
timer: null,
|
|
39
|
+
next: () => {
|
|
40
|
+
const current = elements[currentIndex];
|
|
41
|
+
currentIndex = (currentIndex + 1) % elements.length;
|
|
42
|
+
const nextEl = elements[currentIndex];
|
|
43
|
+
|
|
44
|
+
// Hide Current
|
|
45
|
+
current.style.opacity = '0';
|
|
46
|
+
current.style.transform = 'translateX(-20px)';
|
|
47
|
+
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
current.style.display = 'none';
|
|
50
|
+
|
|
51
|
+
// Show Next
|
|
52
|
+
nextEl.style.display = 'block';
|
|
53
|
+
// Small timeout to trigger CSS transition after display:block
|
|
54
|
+
setTimeout(() => {
|
|
55
|
+
nextEl.style.opacity = '1';
|
|
56
|
+
nextEl.style.transform = 'translateX(0)';
|
|
57
|
+
}, 50);
|
|
58
|
+
}, 400); // Wait for fade out
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
state.timer = setInterval(state.next, interval);
|
|
63
|
+
this._instances.push(state);
|
|
64
|
+
return state;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
destroyAll: function() {
|
|
68
|
+
if (!this._instances || this._instances.length === 0) return;
|
|
69
|
+
this._instances.forEach((instance) => {
|
|
70
|
+
if (instance && instance.timer) clearInterval(instance.timer);
|
|
71
|
+
if (instance) instance.timer = null;
|
|
72
|
+
});
|
|
73
|
+
this._instances = [];
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
/**************************
|
|
79
|
+
How to use this library
|
|
80
|
+
***************************
|
|
81
|
+
*/
|
|
82
|
+
/*
|
|
83
|
+
rinx.slides({
|
|
84
|
+
IDs: ['scroll1', 'scroll2', 'scroll3'],
|
|
85
|
+
interval: 5000,
|
|
86
|
+
effect:'slide'
|
|
87
|
+
});
|
|
88
|
+
*/
|