fancoolo-fx 1.6.1 → 1.7.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/README.md +10 -0
- package/package.json +1 -1
- package/readme.txt +6 -0
- package/src/fx.js +80 -9
- package/.distignore +0 -18
- package/inc/Admin.php +0 -48
- package/inc/AdminPage.php +0 -398
- package/inc/Editor.php +0 -31
- package/inc/Frontend.php +0 -50
- package/inc/SaveHandler.php +0 -66
- package/inc/Settings.php +0 -41
- package/inc/update.php +0 -154
package/README.md
CHANGED
|
@@ -159,6 +159,16 @@ All functions accept `(element, options)`:
|
|
|
159
159
|
|
|
160
160
|
Set `trigger: 'scroll'` to enable ScrollTrigger. Pass `scrollTrigger: { trigger: someEl }` to use a different trigger element.
|
|
161
161
|
|
|
162
|
+
### Utility Methods
|
|
163
|
+
|
|
164
|
+
| Method | Description |
|
|
165
|
+
|--------|-------------|
|
|
166
|
+
| `FX.init()` | Re-scan DOM and apply animations (for dynamic content) |
|
|
167
|
+
| `FX.refresh()` | Re-split text after layout change (sidebar toggle, font load) |
|
|
168
|
+
| `FX.config` | Global config object |
|
|
169
|
+
|
|
170
|
+
**Resize handling:** Text-based effects (`textReveal`, `typeWriter`, `splitWords`) automatically re-split when the browser width changes. After one-shot animations complete, the SplitText DOM is reverted so text reflows naturally.
|
|
171
|
+
|
|
162
172
|
## Using in a New Project
|
|
163
173
|
|
|
164
174
|
1. Copy this repo (or `npm install`)
|
package/package.json
CHANGED
package/readme.txt
CHANGED
|
@@ -68,6 +68,12 @@ Yes. Use the `fx-start-[top center]` modifier class, or set `scrollStart` in the
|
|
|
68
68
|
|
|
69
69
|
== Changelog ==
|
|
70
70
|
|
|
71
|
+
= 1.7.0 =
|
|
72
|
+
* Fix: Text-based effects (textReveal, typeWriter, splitWords) now re-split on browser resize — line breaks stay correct at every viewport width
|
|
73
|
+
* New: SplitText is reverted after one-shot animations complete — text reflows naturally without extra DOM wrappers
|
|
74
|
+
* New: FX.refresh() public method — manually re-split text after layout changes (sidebar toggle, font load)
|
|
75
|
+
* New: Automatic debounced resize handler (200ms, width-only) for pending scroll-triggered text animations
|
|
76
|
+
|
|
71
77
|
= 1.0.0 =
|
|
72
78
|
* Initial release
|
|
73
79
|
* 5 animation effects
|
package/src/fx.js
CHANGED
|
@@ -157,6 +157,49 @@
|
|
|
157
157
|
return st;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
// ── SplitText resize handling ───────────────
|
|
161
|
+
|
|
162
|
+
var _splitRegistry = [];
|
|
163
|
+
var _lastWidth = window.innerWidth;
|
|
164
|
+
var _resizeTimer;
|
|
165
|
+
|
|
166
|
+
function registerSplit(entry) {
|
|
167
|
+
_splitRegistry.push(entry);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function unregisterSplit(entry) {
|
|
171
|
+
var idx = _splitRegistry.indexOf(entry);
|
|
172
|
+
if (idx > -1) _splitRegistry.splice(idx, 1);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function refreshSplits() {
|
|
176
|
+
if (_splitRegistry.length === 0) return;
|
|
177
|
+
|
|
178
|
+
var pending = [];
|
|
179
|
+
|
|
180
|
+
for (var i = _splitRegistry.length - 1; i >= 0; i--) {
|
|
181
|
+
var entry = _splitRegistry[i];
|
|
182
|
+
if (entry.tween) entry.tween.kill();
|
|
183
|
+
if (entry.split) entry.split.revert();
|
|
184
|
+
pending.push(entry);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
_splitRegistry.length = 0;
|
|
188
|
+
|
|
189
|
+
pending.forEach(function (entry) {
|
|
190
|
+
entry.effectFn(entry.el, entry.opts);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
ScrollTrigger.refresh();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
window.addEventListener('resize', function () {
|
|
197
|
+
if (window.innerWidth === _lastWidth) return;
|
|
198
|
+
_lastWidth = window.innerWidth;
|
|
199
|
+
clearTimeout(_resizeTimer);
|
|
200
|
+
_resizeTimer = setTimeout(refreshSplits, 200);
|
|
201
|
+
});
|
|
202
|
+
|
|
160
203
|
// ── Effects ──────────────────────────────────
|
|
161
204
|
|
|
162
205
|
function textReveal(el, opts) {
|
|
@@ -172,6 +215,9 @@
|
|
|
172
215
|
wrapper.appendChild(line);
|
|
173
216
|
});
|
|
174
217
|
|
|
218
|
+
var isOneShot = !(opts.trigger === 'scroll' || opts.scrollTrigger) || config.scrollOnce;
|
|
219
|
+
var entry = { el: el, split: split, tween: null, effectFn: textReveal, opts: opts };
|
|
220
|
+
|
|
175
221
|
var tweenVars = {
|
|
176
222
|
y: '100%',
|
|
177
223
|
opacity: 0,
|
|
@@ -179,19 +225,21 @@
|
|
|
179
225
|
ease: o.ease,
|
|
180
226
|
stagger: o.stagger,
|
|
181
227
|
delay: o.delay,
|
|
182
|
-
onComplete: function () {
|
|
183
|
-
split.lines.forEach(function (line) {
|
|
184
|
-
line.style.transform = '';
|
|
185
|
-
line.style.opacity = '';
|
|
186
|
-
});
|
|
187
|
-
},
|
|
188
228
|
};
|
|
189
229
|
|
|
230
|
+
if (isOneShot) {
|
|
231
|
+
tweenVars.onComplete = function () {
|
|
232
|
+
split.revert();
|
|
233
|
+
unregisterSplit(entry);
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
190
237
|
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
191
238
|
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
192
239
|
}
|
|
193
240
|
|
|
194
|
-
gsap.from(split.lines, tweenVars);
|
|
241
|
+
entry.tween = gsap.from(split.lines, tweenVars);
|
|
242
|
+
registerSplit(entry);
|
|
195
243
|
}
|
|
196
244
|
|
|
197
245
|
function reveal(el, opts) {
|
|
@@ -377,6 +425,9 @@
|
|
|
377
425
|
var split = new SplitText(el, { type: 'chars' });
|
|
378
426
|
gsap.set(split.chars, { opacity: 0 });
|
|
379
427
|
|
|
428
|
+
var isOneShot = !(opts.trigger === 'scroll' || opts.scrollTrigger) || config.scrollOnce;
|
|
429
|
+
var entry = { el: el, split: split, tween: null, effectFn: typeWriter, opts: opts };
|
|
430
|
+
|
|
380
431
|
var tweenVars = {
|
|
381
432
|
opacity: 1,
|
|
382
433
|
duration: o.duration,
|
|
@@ -385,11 +436,19 @@
|
|
|
385
436
|
delay: o.delay,
|
|
386
437
|
};
|
|
387
438
|
|
|
439
|
+
if (isOneShot) {
|
|
440
|
+
tweenVars.onComplete = function () {
|
|
441
|
+
split.revert();
|
|
442
|
+
unregisterSplit(entry);
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
388
446
|
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
389
447
|
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
390
448
|
}
|
|
391
449
|
|
|
392
|
-
gsap.to(split.chars, tweenVars);
|
|
450
|
+
entry.tween = gsap.to(split.chars, tweenVars);
|
|
451
|
+
registerSplit(entry);
|
|
393
452
|
}
|
|
394
453
|
|
|
395
454
|
function drawSVG(el, opts) {
|
|
@@ -464,6 +523,9 @@
|
|
|
464
523
|
|
|
465
524
|
var split = new SplitText(el, { type: 'words' });
|
|
466
525
|
|
|
526
|
+
var isOneShot = !(opts.trigger === 'scroll' || opts.scrollTrigger) || config.scrollOnce;
|
|
527
|
+
var entry = { el: el, split: split, tween: null, effectFn: splitWords, opts: opts };
|
|
528
|
+
|
|
467
529
|
var tweenVars = {
|
|
468
530
|
y: opts.y != null ? opts.y : 30,
|
|
469
531
|
opacity: 0,
|
|
@@ -473,11 +535,19 @@
|
|
|
473
535
|
delay: o.delay,
|
|
474
536
|
};
|
|
475
537
|
|
|
538
|
+
if (isOneShot) {
|
|
539
|
+
tweenVars.onComplete = function () {
|
|
540
|
+
split.revert();
|
|
541
|
+
unregisterSplit(entry);
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
476
545
|
if (opts.trigger === 'scroll' || opts.scrollTrigger) {
|
|
477
546
|
tweenVars.scrollTrigger = buildScrollTrigger(el, opts.scrollTrigger || {});
|
|
478
547
|
}
|
|
479
548
|
|
|
480
|
-
gsap.from(split.words, tweenVars);
|
|
549
|
+
entry.tween = gsap.from(split.words, tweenVars);
|
|
550
|
+
registerSplit(entry);
|
|
481
551
|
}
|
|
482
552
|
|
|
483
553
|
function slideIn(el, opts) {
|
|
@@ -774,5 +844,6 @@
|
|
|
774
844
|
splitWords: splitWords,
|
|
775
845
|
slideIn: slideIn,
|
|
776
846
|
init: init,
|
|
847
|
+
refresh: refreshSplits,
|
|
777
848
|
};
|
|
778
849
|
})();
|
package/.distignore
DELETED
package/inc/Admin.php
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace FancooloFX;
|
|
4
|
-
|
|
5
|
-
if ( ! defined( 'ABSPATH' ) ) {
|
|
6
|
-
exit;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
class Admin {
|
|
10
|
-
|
|
11
|
-
public static function register_menu() {
|
|
12
|
-
add_theme_page(
|
|
13
|
-
'Fancoolo FX',
|
|
14
|
-
'Fancoolo FX',
|
|
15
|
-
'edit_theme_options',
|
|
16
|
-
'fancoolo-fx',
|
|
17
|
-
array( AdminPage::class, 'render' )
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
public static function enqueue( $hook ) {
|
|
22
|
-
if ( 'appearance_page_fancoolo-fx' !== $hook ) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
wp_enqueue_style( 'fancoolo-fx-admin', FANCOOLO_FX_URL . 'assets/admin.css', array(), FANCOOLO_FX_VERSION );
|
|
27
|
-
wp_enqueue_script( 'fancoolo-fx-admin', FANCOOLO_FX_URL . 'assets/admin.js', array( 'jquery' ), FANCOOLO_FX_VERSION, true );
|
|
28
|
-
|
|
29
|
-
$settings = wp_enqueue_code_editor( array( 'type' => 'text/javascript' ) );
|
|
30
|
-
|
|
31
|
-
if ( false !== $settings ) {
|
|
32
|
-
wp_add_inline_script(
|
|
33
|
-
'code-editor',
|
|
34
|
-
sprintf(
|
|
35
|
-
'jQuery(function($) {
|
|
36
|
-
if ($("#fancoolo-fx-editor").length) {
|
|
37
|
-
var editor = wp.codeEditor.initialize($("#fancoolo-fx-editor"), %s);
|
|
38
|
-
editor.codemirror.on("change", function(cm) {
|
|
39
|
-
cm.refresh();
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
});',
|
|
43
|
-
wp_json_encode( $settings )
|
|
44
|
-
)
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
package/inc/AdminPage.php
DELETED
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace FancooloFX;
|
|
4
|
-
|
|
5
|
-
if ( ! defined( 'ABSPATH' ) ) {
|
|
6
|
-
exit;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
class AdminPage {
|
|
10
|
-
|
|
11
|
-
public static function render() {
|
|
12
|
-
$custom_file = Settings::get_custom_file_path();
|
|
13
|
-
$content = file_exists( $custom_file ) ? file_get_contents( $custom_file ) : '';
|
|
14
|
-
$s = Settings::get();
|
|
15
|
-
|
|
16
|
-
settings_errors( 'fancoolo_fx' );
|
|
17
|
-
?>
|
|
18
|
-
<!-- Styles and scripts enqueued via Admin::enqueue() -->
|
|
19
|
-
|
|
20
|
-
<div class="wrap">
|
|
21
|
-
<h1>Fancoolo FX</h1>
|
|
22
|
-
<p>
|
|
23
|
-
GSAP animation wrapper is active. Add <code>.fx-*</code> classes to blocks in Gutenberg
|
|
24
|
-
and they will animate automatically.
|
|
25
|
-
</p>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<!-- ── Tabs ── -->
|
|
29
|
-
<div class="ffx-tabs">
|
|
30
|
-
<button class="ffx-tab active" data-tab="editor">Editor</button>
|
|
31
|
-
<button class="ffx-tab" data-tab="config">Config Reference</button>
|
|
32
|
-
<button class="ffx-tab" data-tab="classes">Classes Reference</button>
|
|
33
|
-
</div>
|
|
34
|
-
|
|
35
|
-
<!-- ═══ Editor Tab ═══ -->
|
|
36
|
-
<div class="ffx-panel ffx-panel-editor active" data-panel="editor">
|
|
37
|
-
<form method="post">
|
|
38
|
-
<?php wp_nonce_field( 'fancoolo_fx_save_action', 'fancoolo_fx_nonce' ); ?>
|
|
39
|
-
|
|
40
|
-
<!-- Editor main area -->
|
|
41
|
-
<div class="ffx-editor-main">
|
|
42
|
-
<input type="submit" name="fancoolo_fx_save" class="button button-primary ffx-save-btn" value="Save Changes">
|
|
43
|
-
<textarea
|
|
44
|
-
id="fancoolo-fx-editor"
|
|
45
|
-
name="fancoolo_fx_code"
|
|
46
|
-
rows="20"
|
|
47
|
-
style="width: 100%; font-family: monospace;"
|
|
48
|
-
><?php echo esc_textarea( $content ); ?></textarea>
|
|
49
|
-
</div>
|
|
50
|
-
|
|
51
|
-
<!-- Settings sidebar -->
|
|
52
|
-
<div class="ffx-sidebar">
|
|
53
|
-
<h3>Settings</h3>
|
|
54
|
-
|
|
55
|
-
<label for="ffx-scroll-start">Scroll Start</label>
|
|
56
|
-
<input type="text" id="ffx-scroll-start" name="fancoolo_fx_scroll_start" value="<?php echo esc_attr( $s['scroll_start'] ); ?>" placeholder="top 85%">
|
|
57
|
-
<p class="ffx-hint">When scroll animations trigger (e.g. top 85%, top center)</p>
|
|
58
|
-
|
|
59
|
-
<label for="ffx-section-selector">Section Selector</label>
|
|
60
|
-
<input type="text" id="ffx-section-selector" name="fancoolo_fx_section_selector" value="<?php echo esc_attr( $s['section_selector'] ); ?>" placeholder="section">
|
|
61
|
-
<p class="ffx-hint">Containers for bare-class triggering</p>
|
|
62
|
-
|
|
63
|
-
<label for="ffx-exclude-selectors">Exclude Selectors</label>
|
|
64
|
-
<input type="text" id="ffx-exclude-selectors" name="fancoolo_fx_exclude_selectors" value="<?php echo esc_attr( $s['exclude_selectors'] ); ?>" placeholder=".no-fx, .wp-block-navigation">
|
|
65
|
-
<p class="ffx-hint">Elements matching these selectors are never animated</p>
|
|
66
|
-
|
|
67
|
-
<hr>
|
|
68
|
-
|
|
69
|
-
<div class="ffx-toggle">
|
|
70
|
-
<input type="checkbox" id="ffx-scroll-once" name="fancoolo_fx_scroll_once" value="1" <?php checked( $s['scroll_once'], '1' ); ?>>
|
|
71
|
-
<label for="ffx-scroll-once">Play once</label>
|
|
72
|
-
</div>
|
|
73
|
-
<p class="ffx-hint">Uncheck to replay animations on every scroll</p>
|
|
74
|
-
|
|
75
|
-
<div class="ffx-toggle">
|
|
76
|
-
<input type="checkbox" id="ffx-debug-markers" name="fancoolo_fx_debug_markers" value="1" <?php checked( $s['debug_markers'], '1' ); ?>>
|
|
77
|
-
<label for="ffx-debug-markers">Debug markers</label>
|
|
78
|
-
</div>
|
|
79
|
-
<p class="ffx-hint">Show ScrollTrigger markers (admin only, visitors won't see them)</p>
|
|
80
|
-
|
|
81
|
-
<div class="ffx-toggle">
|
|
82
|
-
<input type="checkbox" id="ffx-disable-mobile" name="fancoolo_fx_disable_mobile" value="1" <?php checked( $s['disable_mobile'], '1' ); ?>>
|
|
83
|
-
<label for="ffx-disable-mobile">Disable on mobile</label>
|
|
84
|
-
</div>
|
|
85
|
-
<div class="ffx-mobile-breakpoint" style="<?php echo $s['disable_mobile'] === '1' ? '' : 'display:none;'; ?>">
|
|
86
|
-
<label for="ffx-mobile-breakpoint">Breakpoint (px)</label>
|
|
87
|
-
<input type="text" id="ffx-mobile-breakpoint" name="fancoolo_fx_mobile_breakpoint" value="<?php echo esc_attr( $s['mobile_breakpoint'] ); ?>" placeholder="768" style="width: 80px; margin-bottom: 12px;">
|
|
88
|
-
</div>
|
|
89
|
-
|
|
90
|
-
<div class="ffx-toggle">
|
|
91
|
-
<input type="checkbox" id="ffx-respect-reduced-motion" name="fancoolo_fx_respect_reduced_motion" value="1" <?php checked( $s['respect_reduced_motion'], '1' ); ?>>
|
|
92
|
-
<label for="ffx-respect-reduced-motion">Respect reduced motion</label>
|
|
93
|
-
</div>
|
|
94
|
-
<p class="ffx-hint">Skip animations when OS has reduced motion enabled</p>
|
|
95
|
-
|
|
96
|
-
<label for="ffx-speed-multiplier">Speed Multiplier</label>
|
|
97
|
-
<select id="ffx-speed-multiplier" name="fancoolo_fx_speed_multiplier" style="width: 100%; margin-bottom: 12px;">
|
|
98
|
-
<?php foreach ( array( '0.5' => '0.5x (faster)', '0.75' => '0.75x', '1' => '1x (default)', '1.25' => '1.25x', '1.5' => '1.5x', '2' => '2x (slower)' ) as $val => $lbl ) : ?>
|
|
99
|
-
<option value="<?php echo esc_attr( $val ); ?>" <?php selected( $s['speed_multiplier'], $val ); ?>><?php echo esc_html( $lbl ); ?></option>
|
|
100
|
-
<?php endforeach; ?>
|
|
101
|
-
</select>
|
|
102
|
-
|
|
103
|
-
<div class="ffx-toggle">
|
|
104
|
-
<input type="checkbox" id="ffx-gutenberg-panel" name="fancoolo_fx_gutenberg_panel" value="1" <?php checked( $s['gutenberg_panel'], '1' ); ?>>
|
|
105
|
-
<label for="ffx-gutenberg-panel">Gutenberg integration</label>
|
|
106
|
-
</div>
|
|
107
|
-
<p class="ffx-hint">Show FX Animation panel in the block editor sidebar</p>
|
|
108
|
-
|
|
109
|
-
<hr>
|
|
110
|
-
|
|
111
|
-
<h3>Global Tag Map</h3>
|
|
112
|
-
<p class="ffx-hint" style="margin-top: -8px;">Auto-animate elements by tag inside sections. No classes needed.</p>
|
|
113
|
-
|
|
114
|
-
<div id="ffx-tagmap-rows">
|
|
115
|
-
<?php foreach ( $s['tag_map'] as $i => $row ) : ?>
|
|
116
|
-
<div class="ffx-tagmap-row">
|
|
117
|
-
<input type="text" name="fancoolo_fx_tag_map[<?php echo $i; ?>][selector]" value="<?php echo esc_attr( $row['selector'] ); ?>" placeholder="h1,h2,h3">
|
|
118
|
-
<select name="fancoolo_fx_tag_map[<?php echo $i; ?>][effect]">
|
|
119
|
-
<?php foreach ( self::get_effects_list() as $value => $label ) : ?>
|
|
120
|
-
<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $row['effect'], $value ); ?>><?php echo esc_html( $label ); ?></option>
|
|
121
|
-
<?php endforeach; ?>
|
|
122
|
-
</select>
|
|
123
|
-
<button type="button" class="ffx-tagmap-remove" title="Remove">×</button>
|
|
124
|
-
</div>
|
|
125
|
-
<?php endforeach; ?>
|
|
126
|
-
</div>
|
|
127
|
-
<button type="button" id="ffx-tagmap-add" class="button button-small">+ Add Rule</button>
|
|
128
|
-
|
|
129
|
-
<hr>
|
|
130
|
-
|
|
131
|
-
<h3>Import / Export</h3>
|
|
132
|
-
<div class="ffx-import-export">
|
|
133
|
-
<button type="button" id="ffx-export" class="button button-small" style="width: 100%; margin-bottom: 6px;">Export Settings</button>
|
|
134
|
-
<button type="button" id="ffx-import-btn" class="button button-small" style="width: 100%; margin-bottom: 6px;">Import Settings</button>
|
|
135
|
-
<input type="file" id="ffx-import-file" accept=".json" style="display: none;">
|
|
136
|
-
<button type="button" id="ffx-reset" class="button button-small" style="width: 100%; color: #a00;">Reset to Defaults</button>
|
|
137
|
-
</div>
|
|
138
|
-
</div>
|
|
139
|
-
</form>
|
|
140
|
-
</div>
|
|
141
|
-
|
|
142
|
-
<!-- ═══ Config Reference Tab ═══ -->
|
|
143
|
-
<div class="ffx-panel" data-panel="config">
|
|
144
|
-
|
|
145
|
-
<h3>Config Options</h3>
|
|
146
|
-
<table class="widefat fixed striped" style="max-width: 700px;">
|
|
147
|
-
<thead>
|
|
148
|
-
<tr><th>Option</th><th>Default</th><th>Description</th></tr>
|
|
149
|
-
</thead>
|
|
150
|
-
<tbody>
|
|
151
|
-
<tr><td><code>FX.config.tagMap</code></td><td><code>null</code></td><td>Auto-animate elements by tag/selector — no classes needed</td></tr>
|
|
152
|
-
<tr><td><code>FX.config.sectionSelector</code></td><td><code>'section'</code></td><td>Containers for bare-class and tagMap triggering</td></tr>
|
|
153
|
-
<tr><td><code>FX.config.scrollStart</code></td><td><code>'top 85%'</code></td><td>When scroll animations trigger (GSAP ScrollTrigger start format)</td></tr>
|
|
154
|
-
<tr><td><code>FX.config.scrollOnce</code></td><td><code>true</code></td><td>Play once or replay on every scroll</td></tr>
|
|
155
|
-
<tr><td><code>FX.config.excludeSelectors</code></td><td><code>''</code></td><td>CSS selectors for elements to never animate</td></tr>
|
|
156
|
-
<tr><td><code>FX.config.disableMobile</code></td><td><code>false</code></td><td>Skip all animations on mobile</td></tr>
|
|
157
|
-
<tr><td><code>FX.config.mobileBreakpoint</code></td><td><code>768</code></td><td>Width threshold for mobile detection (px)</td></tr>
|
|
158
|
-
<tr><td><code>FX.config.speedMultiplier</code></td><td><code>1</code></td><td>Global duration multiplier (0.5 = faster, 2 = slower)</td></tr>
|
|
159
|
-
<tr><td><code>FX.config.respectReducedMotion</code></td><td><code>true</code></td><td>Skip animations when OS prefers reduced motion</td></tr>
|
|
160
|
-
</tbody>
|
|
161
|
-
</table>
|
|
162
|
-
|
|
163
|
-
<h3 style="margin-top: 24px;">JS API Functions</h3>
|
|
164
|
-
<table class="widefat fixed striped" style="max-width: 700px;">
|
|
165
|
-
<thead>
|
|
166
|
-
<tr><th>Function</th><th>Description</th></tr>
|
|
167
|
-
</thead>
|
|
168
|
-
<tbody>
|
|
169
|
-
<tr><td><code>FX.textReveal(el, opts)</code></td><td>Split text lines, masked reveal upward</td></tr>
|
|
170
|
-
<tr><td><code>FX.reveal(el, opts)</code></td><td>Slide up with fade</td></tr>
|
|
171
|
-
<tr><td><code>FX.spinReveal(el, opts)</code></td><td>Rotate and scale in</td></tr>
|
|
172
|
-
<tr><td><code>FX.bgReveal(el, opts)</code></td><td>Background slide up</td></tr>
|
|
173
|
-
<tr><td><code>FX.scaleIn(el, opts)</code></td><td>Scale up with fade</td></tr>
|
|
174
|
-
<tr><td><code>FX.fadeIn(el, opts)</code></td><td>Opacity + subtle scale, no movement</td></tr>
|
|
175
|
-
<tr><td><code>FX.blurIn(el, opts)</code></td><td>Fade in while deblurring</td></tr>
|
|
176
|
-
<tr><td><code>FX.clipUp(el, opts)</code></td><td>Clip-path wipe from bottom</td></tr>
|
|
177
|
-
<tr><td><code>FX.clipDown(el, opts)</code></td><td>Clip-path wipe from top</td></tr>
|
|
178
|
-
<tr><td><code>FX.tiltIn(el, opts)</code></td><td>3D perspective reveal (scrub-based)</td></tr>
|
|
179
|
-
<tr><td><code>FX.typeWriter(el, opts)</code></td><td>Character-by-character typing reveal</td></tr>
|
|
180
|
-
<tr><td><code>FX.drawSVG(el, opts)</code></td><td>SVG stroke drawing animation</td></tr>
|
|
181
|
-
<tr><td><code>FX.parallax(el, opts)</code></td><td>Scroll-linked Y parallax shift</td></tr>
|
|
182
|
-
<tr><td><code>FX.splitWords(el, opts)</code></td><td>Word-by-word fade and slide up</td></tr>
|
|
183
|
-
<tr><td><code>FX.slideIn(el, opts)</code></td><td>Horizontal slide from left or right</td></tr>
|
|
184
|
-
<tr><td><code>FX.init()</code></td><td>Re-scan DOM — call after changing any config</td></tr>
|
|
185
|
-
</tbody>
|
|
186
|
-
</table>
|
|
187
|
-
|
|
188
|
-
<h3 style="margin-top: 24px;">Examples <span style="font-weight:normal;color:#646970;font-size:13px;">(click code to copy)</span></h3>
|
|
189
|
-
|
|
190
|
-
<h4 style="margin-top: 16px;">Auto-animate by tag</h4>
|
|
191
|
-
<div class="ffx-copy-wrap">
|
|
192
|
-
<button class="ffx-copy-btn" data-target="ex1">Copy</button>
|
|
193
|
-
<pre class="ffx-pre" id="ex1">FX.config.tagMap = {
|
|
194
|
-
'h1,h2,h3,h4,h5,h6': 'textReveal',
|
|
195
|
-
'p,blockquote': 'textReveal',
|
|
196
|
-
'img,video': 'reveal',
|
|
197
|
-
};
|
|
198
|
-
FX.config.sectionSelector = 'section, .wp-block-group';
|
|
199
|
-
FX.init();</pre>
|
|
200
|
-
</div>
|
|
201
|
-
|
|
202
|
-
<h4 style="margin-top: 16px;">Change scroll trigger position</h4>
|
|
203
|
-
<div class="ffx-copy-wrap">
|
|
204
|
-
<button class="ffx-copy-btn" data-target="ex2">Copy</button>
|
|
205
|
-
<pre class="ffx-pre" id="ex2">FX.config.scrollStart = 'top center';
|
|
206
|
-
FX.init();</pre>
|
|
207
|
-
</div>
|
|
208
|
-
|
|
209
|
-
<h4 style="margin-top: 16px;">Replay animations on re-scroll</h4>
|
|
210
|
-
<div class="ffx-copy-wrap">
|
|
211
|
-
<button class="ffx-copy-btn" data-target="ex3">Copy</button>
|
|
212
|
-
<pre class="ffx-pre" id="ex3">FX.config.scrollOnce = false;
|
|
213
|
-
FX.init();</pre>
|
|
214
|
-
</div>
|
|
215
|
-
|
|
216
|
-
<h4 style="margin-top: 16px;">Compound sequence</h4>
|
|
217
|
-
<div class="ffx-copy-wrap">
|
|
218
|
-
<button class="ffx-copy-btn" data-target="ex4">Copy</button>
|
|
219
|
-
<pre class="ffx-pre" id="ex4">document.addEventListener('DOMContentLoaded', function () {
|
|
220
|
-
var hero = document.querySelector('.wp-block-cover');
|
|
221
|
-
if (!hero) return;
|
|
222
|
-
|
|
223
|
-
FX.scaleIn(hero, {
|
|
224
|
-
trigger: 'scroll',
|
|
225
|
-
scrollTrigger: { trigger: hero }
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
var heading = hero.querySelector('h2');
|
|
229
|
-
if (heading) {
|
|
230
|
-
FX.textReveal(heading, {
|
|
231
|
-
trigger: 'scroll',
|
|
232
|
-
delay: 0.2,
|
|
233
|
-
scrollTrigger: { trigger: hero }
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
});</pre>
|
|
237
|
-
</div>
|
|
238
|
-
</div>
|
|
239
|
-
|
|
240
|
-
<!-- ═══ Classes Reference Tab ═══ -->
|
|
241
|
-
<div class="ffx-panel" data-panel="classes">
|
|
242
|
-
|
|
243
|
-
<p style="margin-bottom: 16px; color: #646970;">
|
|
244
|
-
Add these in Gutenberg: select a block → Advanced → Additional CSS class(es). Click any class to copy it.
|
|
245
|
-
</p>
|
|
246
|
-
|
|
247
|
-
<?php self::render_classes_reference(); ?>
|
|
248
|
-
</div>
|
|
249
|
-
|
|
250
|
-
<p style="margin-top: 24px; color: #646970;">
|
|
251
|
-
This code loads after fx.js on the frontend. Leave empty to use defaults only.<br>
|
|
252
|
-
<strong style="color:#1d2327;">Important:</strong> Always add <code>FX.init();</code> at the end when changing config — it re-scans the page with your new settings.
|
|
253
|
-
</p>
|
|
254
|
-
<p style="margin-top: 12px;">
|
|
255
|
-
<a href="https://krstivoja.github.io/fancoolo-fx/documentation/" target="_blank">
|
|
256
|
-
Full Documentation →
|
|
257
|
-
</a>
|
|
258
|
-
</p>
|
|
259
|
-
</div>
|
|
260
|
-
<?php
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
public static function get_effects_list() {
|
|
264
|
-
return array(
|
|
265
|
-
'textReveal' => 'Text Reveal',
|
|
266
|
-
'reveal' => 'Reveal',
|
|
267
|
-
'spinReveal' => 'Spin Reveal',
|
|
268
|
-
'bgReveal' => 'BG Reveal',
|
|
269
|
-
'scaleIn' => 'Scale In',
|
|
270
|
-
'fadeIn' => 'Fade In',
|
|
271
|
-
'blurIn' => 'Blur In',
|
|
272
|
-
'clipUp' => 'Clip Up',
|
|
273
|
-
'clipDown' => 'Clip Down',
|
|
274
|
-
'tiltIn' => 'Tilt In',
|
|
275
|
-
'typeWriter' => 'Type Writer',
|
|
276
|
-
'drawSVG' => 'Draw SVG',
|
|
277
|
-
'parallax' => 'Parallax',
|
|
278
|
-
'splitWords' => 'Split Words',
|
|
279
|
-
'slideIn' => 'Slide In',
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
private static function render_classes_reference() {
|
|
284
|
-
$groups = array(
|
|
285
|
-
'Text Reveal' => array(
|
|
286
|
-
'fx-text-reveal-pl' => 'Page load — masked line-by-line reveal',
|
|
287
|
-
'fx-text-reveal-st' => 'Scroll triggered',
|
|
288
|
-
'fx-text-reveal' => 'Auto triggered inside a section',
|
|
289
|
-
),
|
|
290
|
-
'Reveal' => array(
|
|
291
|
-
'fx-reveal-pl' => 'Page load — slide up with fade',
|
|
292
|
-
'fx-reveal-st' => 'Scroll triggered',
|
|
293
|
-
'fx-reveal' => 'Auto triggered inside a section',
|
|
294
|
-
),
|
|
295
|
-
'Spin Reveal' => array(
|
|
296
|
-
'fx-spin-reveal-pl' => 'Page load — rotate and scale in',
|
|
297
|
-
'fx-spin-reveal-st' => 'Scroll triggered',
|
|
298
|
-
'fx-spin-reveal' => 'Auto triggered inside a section',
|
|
299
|
-
),
|
|
300
|
-
'BG Reveal' => array(
|
|
301
|
-
'fx-bg-reveal-pl' => 'Page load — background slide up',
|
|
302
|
-
'fx-bg-reveal-st' => 'Scroll triggered',
|
|
303
|
-
'fx-bg-reveal' => 'Auto triggered inside a section',
|
|
304
|
-
),
|
|
305
|
-
'Scale In' => array(
|
|
306
|
-
'fx-scale-in-pl' => 'Page load — scale up with fade',
|
|
307
|
-
'fx-scale-in-st' => 'Scroll triggered',
|
|
308
|
-
'fx-scale-in' => 'Auto triggered inside a section',
|
|
309
|
-
),
|
|
310
|
-
'Fade In' => array(
|
|
311
|
-
'fx-fade-in-pl' => 'Page load — opacity only, no movement',
|
|
312
|
-
'fx-fade-in-st' => 'Scroll triggered',
|
|
313
|
-
'fx-fade-in' => 'Auto triggered inside a section',
|
|
314
|
-
),
|
|
315
|
-
'Blur In' => array(
|
|
316
|
-
'fx-blur-in-pl' => 'Page load — fade in while deblurring',
|
|
317
|
-
'fx-blur-in-st' => 'Scroll triggered',
|
|
318
|
-
'fx-blur-in' => 'Auto triggered inside a section',
|
|
319
|
-
),
|
|
320
|
-
'Clip Reveal' => array(
|
|
321
|
-
'fx-clip-up-pl' => 'Page load — clip-path wipe from bottom',
|
|
322
|
-
'fx-clip-up-st' => 'Scroll triggered',
|
|
323
|
-
'fx-clip-up' => 'Auto triggered inside a section',
|
|
324
|
-
'fx-clip-down-pl' => 'Page load — clip-path wipe from top',
|
|
325
|
-
'fx-clip-down-st' => 'Scroll triggered',
|
|
326
|
-
'fx-clip-down' => 'Auto triggered inside a section',
|
|
327
|
-
),
|
|
328
|
-
'Type Writer' => array(
|
|
329
|
-
'fx-type-writer-pl' => 'Page load — character-by-character typing',
|
|
330
|
-
'fx-type-writer-st' => 'Scroll triggered',
|
|
331
|
-
'fx-type-writer' => 'Auto triggered inside a section',
|
|
332
|
-
),
|
|
333
|
-
'Draw SVG' => array(
|
|
334
|
-
'fx-draw-svg-pl' => 'Page load — SVG stroke drawing animation',
|
|
335
|
-
'fx-draw-svg-st' => 'Scroll triggered',
|
|
336
|
-
'fx-draw-svg' => 'Auto triggered inside a section',
|
|
337
|
-
'fx-draw-svg-scrub' => 'Draws progressively as you scroll (scrub-based)',
|
|
338
|
-
),
|
|
339
|
-
'Split Words' => array(
|
|
340
|
-
'fx-split-words-pl' => 'Page load — word-by-word fade and slide',
|
|
341
|
-
'fx-split-words-st' => 'Scroll triggered',
|
|
342
|
-
'fx-split-words' => 'Auto triggered inside a section',
|
|
343
|
-
),
|
|
344
|
-
'Slide In' => array(
|
|
345
|
-
'fx-slide-left-pl' => 'Page load — slide in from left',
|
|
346
|
-
'fx-slide-left-st' => 'Scroll triggered — from left',
|
|
347
|
-
'fx-slide-left' => 'Auto triggered inside a section — from left',
|
|
348
|
-
'fx-slide-right-pl' => 'Page load — slide in from right',
|
|
349
|
-
'fx-slide-right-st' => 'Scroll triggered — from right',
|
|
350
|
-
'fx-slide-right' => 'Auto triggered inside a section — from right',
|
|
351
|
-
),
|
|
352
|
-
'Tilt In|scrub — tied to scroll position' => array(
|
|
353
|
-
'fx-tilt-in-st' => '3D perspective reveal linked to scroll',
|
|
354
|
-
'fx-tilt-in' => 'Auto triggered inside a section',
|
|
355
|
-
),
|
|
356
|
-
'Parallax|scrub — tied to scroll position' => array(
|
|
357
|
-
'fx-parallax-st' => 'Parallax Y-shift linked to scroll',
|
|
358
|
-
'fx-parallax' => 'Auto triggered inside a section',
|
|
359
|
-
),
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
foreach ( $groups as $title => $classes ) {
|
|
363
|
-
$parts = explode( '|', $title );
|
|
364
|
-
$label = $parts[0];
|
|
365
|
-
$sub = isset( $parts[1] ) ? ' <span style="font-weight:normal;color:#646970;font-size:12px;">(' . esc_html( $parts[1] ) . ')</span>' : '';
|
|
366
|
-
echo '<div class="ffx-group-title">' . esc_html( $label ) . $sub . '</div>';
|
|
367
|
-
foreach ( $classes as $cls => $desc ) {
|
|
368
|
-
echo '<div class="ffx-class-row"><code data-copy>' . esc_html( $cls ) . '</code><span class="ffx-desc">' . esc_html( $desc ) . '</span></div>';
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Stagger Children
|
|
373
|
-
echo '<div class="ffx-group-title" style="margin-top: 20px;">Stagger Children <span style="font-weight:normal;color:#646970;font-size:12px;">(pair with an effect class)</span></div>';
|
|
374
|
-
$stagger = array(
|
|
375
|
-
'fx-stagger-all-[img]' => 'Target all img children — requires effect class',
|
|
376
|
-
'fx-stagger-all-[img,p]' => 'Target img and p children',
|
|
377
|
-
'fx-stagger-all-[.card]' => 'Target children by CSS class',
|
|
378
|
-
);
|
|
379
|
-
foreach ( $stagger as $cls => $desc ) {
|
|
380
|
-
echo '<div class="ffx-class-row"><code data-copy>' . esc_html( $cls ) . '</code><span class="ffx-desc">' . esc_html( $desc ) . '</span></div>';
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Modifiers
|
|
384
|
-
echo '<div class="ffx-group-title" style="margin-top: 20px;">Modifiers <span style="font-weight:normal;color:#646970;font-size:12px;">(combine with any effect class)</span></div>';
|
|
385
|
-
$modifiers = array(
|
|
386
|
-
'fx-duration-[1.5]' => 'Custom duration (seconds)',
|
|
387
|
-
'fx-delay-[0.3]' => 'Delay before animating (seconds)',
|
|
388
|
-
'fx-stagger-[0.25]' => 'Delay between staggered siblings',
|
|
389
|
-
'fx-ease-[power2.inOut]' => 'GSAP easing function',
|
|
390
|
-
'fx-start-[top center]' => 'Scroll trigger start position',
|
|
391
|
-
'fx-y-[80]' => 'Parallax Y-shift intensity (parallax only)',
|
|
392
|
-
'fx-scrub-[0.6]' => 'Scrub smoothing (drawSVG scrub mode)',
|
|
393
|
-
);
|
|
394
|
-
foreach ( $modifiers as $cls => $desc ) {
|
|
395
|
-
echo '<div class="ffx-class-row"><code data-copy>' . esc_html( $cls ) . '</code><span class="ffx-desc">' . esc_html( $desc ) . '</span></div>';
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
package/inc/Editor.php
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace FancooloFX;
|
|
4
|
-
|
|
5
|
-
if ( ! defined( 'ABSPATH' ) ) {
|
|
6
|
-
exit;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
class Editor {
|
|
10
|
-
|
|
11
|
-
public static function enqueue() {
|
|
12
|
-
$s = Settings::get();
|
|
13
|
-
if ( ! $s['gutenberg_panel'] ) {
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
$asset_file = FANCOOLO_FX_PATH . 'assets/editor/index.asset.php';
|
|
18
|
-
if ( ! file_exists( $asset_file ) ) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
$asset = include $asset_file;
|
|
23
|
-
wp_enqueue_script(
|
|
24
|
-
'fancoolo-fx-editor',
|
|
25
|
-
FANCOOLO_FX_URL . 'assets/editor/index.js',
|
|
26
|
-
$asset['dependencies'],
|
|
27
|
-
$asset['version'],
|
|
28
|
-
true
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
}
|
package/inc/Frontend.php
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace FancooloFX;
|
|
4
|
-
|
|
5
|
-
if ( ! defined( 'ABSPATH' ) ) {
|
|
6
|
-
exit;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
class Frontend {
|
|
10
|
-
|
|
11
|
-
public static function enqueue() {
|
|
12
|
-
wp_enqueue_script( 'gsap', FANCOOLO_FX_URL . 'assets/gsap.min.js', array(), '3.14.2', true );
|
|
13
|
-
wp_enqueue_script( 'gsap-scrolltrigger', FANCOOLO_FX_URL . 'assets/ScrollTrigger.min.js', array( 'gsap' ), '3.14.2', true );
|
|
14
|
-
wp_enqueue_script( 'gsap-splittext', FANCOOLO_FX_URL . 'assets/SplitText.min.js', array( 'gsap' ), '3.14.2', true );
|
|
15
|
-
wp_enqueue_script( 'fancoolo-fx', FANCOOLO_FX_URL . 'assets/fx.js', array( 'gsap', 'gsap-scrolltrigger', 'gsap-splittext' ), FANCOOLO_FX_VERSION, true );
|
|
16
|
-
|
|
17
|
-
$s = Settings::get();
|
|
18
|
-
$config = array(
|
|
19
|
-
'scrollStart' => $s['scroll_start'],
|
|
20
|
-
'scrollOnce' => (bool) $s['scroll_once'],
|
|
21
|
-
'sectionSelector' => $s['section_selector'],
|
|
22
|
-
'disableMobile' => (bool) $s['disable_mobile'],
|
|
23
|
-
'mobileBreakpoint' => (int) $s['mobile_breakpoint'],
|
|
24
|
-
'speedMultiplier' => (float) $s['speed_multiplier'],
|
|
25
|
-
'respectReducedMotion' => (bool) $s['respect_reduced_motion'],
|
|
26
|
-
'excludeSelectors' => $s['exclude_selectors'],
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
if ( ! empty( $s['tag_map'] ) ) {
|
|
30
|
-
$tag_map_obj = array();
|
|
31
|
-
foreach ( $s['tag_map'] as $row ) {
|
|
32
|
-
$tag_map_obj[ $row['selector'] ] = $row['effect'];
|
|
33
|
-
}
|
|
34
|
-
$config['tagMap'] = (object) $tag_map_obj;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
wp_add_inline_script( 'fancoolo-fx', 'window.__FX_CONFIG__ = ' . wp_json_encode( $config ) . ';', 'before' );
|
|
38
|
-
|
|
39
|
-
if ( $s['debug_markers'] && is_user_logged_in() && current_user_can( 'manage_options' ) ) {
|
|
40
|
-
wp_add_inline_script( 'fancoolo-fx', 'window.__FX_DEBUG_MARKERS__ = true;', 'before' );
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
$custom_file = Settings::get_custom_file_path();
|
|
44
|
-
if ( file_exists( $custom_file ) && filesize( $custom_file ) > 0 ) {
|
|
45
|
-
$upload_dir = wp_upload_dir();
|
|
46
|
-
$custom_url = $upload_dir['baseurl'] . '/fancoolo-fx/custom.js';
|
|
47
|
-
wp_enqueue_script( 'fancoolo-fx-custom', $custom_url, array( 'fancoolo-fx' ), filemtime( $custom_file ), true );
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
package/inc/SaveHandler.php
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace FancooloFX;
|
|
4
|
-
|
|
5
|
-
if ( ! defined( 'ABSPATH' ) ) {
|
|
6
|
-
exit;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
class SaveHandler {
|
|
10
|
-
|
|
11
|
-
public static function handle() {
|
|
12
|
-
if ( ! isset( $_POST['fancoolo_fx_save'] ) ) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if ( ! check_admin_referer( 'fancoolo_fx_save_action', 'fancoolo_fx_nonce' ) ) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if ( ! current_user_can( 'edit_theme_options' ) ) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Save custom JS file.
|
|
25
|
-
$content = isset( $_POST['fancoolo_fx_code'] ) ? wp_unslash( $_POST['fancoolo_fx_code'] ) : '';
|
|
26
|
-
$upload_dir = wp_upload_dir();
|
|
27
|
-
$dir = $upload_dir['basedir'] . '/fancoolo-fx';
|
|
28
|
-
|
|
29
|
-
if ( ! file_exists( $dir ) ) {
|
|
30
|
-
wp_mkdir_p( $dir );
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
file_put_contents( $dir . '/custom.js', $content );
|
|
34
|
-
|
|
35
|
-
// Build tag map.
|
|
36
|
-
$tag_map = array();
|
|
37
|
-
if ( ! empty( $_POST['fancoolo_fx_tag_map'] ) && is_array( $_POST['fancoolo_fx_tag_map'] ) ) {
|
|
38
|
-
foreach ( $_POST['fancoolo_fx_tag_map'] as $row ) {
|
|
39
|
-
$selector = sanitize_text_field( $row['selector'] ?? '' );
|
|
40
|
-
$effect = sanitize_text_field( $row['effect'] ?? '' );
|
|
41
|
-
if ( '' !== $selector && '' !== $effect ) {
|
|
42
|
-
$tag_map[] = array( 'selector' => $selector, 'effect' => $effect );
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Save settings.
|
|
48
|
-
$settings = array(
|
|
49
|
-
'scroll_start' => sanitize_text_field( $_POST['fancoolo_fx_scroll_start'] ?? 'top 85%' ),
|
|
50
|
-
'scroll_once' => isset( $_POST['fancoolo_fx_scroll_once'] ) ? '1' : '0',
|
|
51
|
-
'section_selector' => sanitize_text_field( $_POST['fancoolo_fx_section_selector'] ?? 'section' ),
|
|
52
|
-
'debug_markers' => isset( $_POST['fancoolo_fx_debug_markers'] ) ? '1' : '0',
|
|
53
|
-
'disable_mobile' => isset( $_POST['fancoolo_fx_disable_mobile'] ) ? '1' : '0',
|
|
54
|
-
'mobile_breakpoint' => sanitize_text_field( $_POST['fancoolo_fx_mobile_breakpoint'] ?? '768' ),
|
|
55
|
-
'speed_multiplier' => sanitize_text_field( $_POST['fancoolo_fx_speed_multiplier'] ?? '1' ),
|
|
56
|
-
'respect_reduced_motion' => isset( $_POST['fancoolo_fx_respect_reduced_motion'] ) ? '1' : '0',
|
|
57
|
-
'exclude_selectors' => sanitize_text_field( $_POST['fancoolo_fx_exclude_selectors'] ?? '' ),
|
|
58
|
-
'gutenberg_panel' => isset( $_POST['fancoolo_fx_gutenberg_panel'] ) ? '1' : '0',
|
|
59
|
-
'tag_map' => $tag_map,
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
Settings::save( $settings );
|
|
63
|
-
|
|
64
|
-
add_settings_error( 'fancoolo_fx', 'fancoolo_fx_saved', 'Settings saved.', 'success' );
|
|
65
|
-
}
|
|
66
|
-
}
|
package/inc/Settings.php
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace FancooloFX;
|
|
4
|
-
|
|
5
|
-
if ( ! defined( 'ABSPATH' ) ) {
|
|
6
|
-
exit;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
class Settings {
|
|
10
|
-
|
|
11
|
-
private static $defaults = array(
|
|
12
|
-
'scroll_start' => 'top 85%',
|
|
13
|
-
'scroll_once' => '1',
|
|
14
|
-
'section_selector' => 'section',
|
|
15
|
-
'debug_markers' => '0',
|
|
16
|
-
'disable_mobile' => '0',
|
|
17
|
-
'mobile_breakpoint' => '768',
|
|
18
|
-
'speed_multiplier' => '1',
|
|
19
|
-
'respect_reduced_motion' => '1',
|
|
20
|
-
'exclude_selectors' => '',
|
|
21
|
-
'gutenberg_panel' => '1',
|
|
22
|
-
'tag_map' => array(),
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
public static function get_defaults() {
|
|
26
|
-
return self::$defaults;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public static function get() {
|
|
30
|
-
return wp_parse_args( get_option( 'fancoolo_fx_settings', array() ), self::$defaults );
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public static function save( $settings ) {
|
|
34
|
-
update_option( 'fancoolo_fx_settings', $settings );
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public static function get_custom_file_path() {
|
|
38
|
-
$upload_dir = wp_upload_dir();
|
|
39
|
-
return $upload_dir['basedir'] . '/fancoolo-fx/custom.js';
|
|
40
|
-
}
|
|
41
|
-
}
|
package/inc/update.php
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
// ************************************************************************************************
|
|
3
|
-
// Updater Version: 1.0.2
|
|
4
|
-
// ************************************************************************************************
|
|
5
|
-
|
|
6
|
-
if (!defined('ABSPATH')) exit; // Exit if accessed directly
|
|
7
|
-
|
|
8
|
-
if (!class_exists('FANCOOLOFX_DPUpdateChecker')) {
|
|
9
|
-
|
|
10
|
-
class FANCOOLOFX_DPUpdateChecker {
|
|
11
|
-
|
|
12
|
-
public $slug;
|
|
13
|
-
public $version;
|
|
14
|
-
public $cache_key;
|
|
15
|
-
public $remote_url;
|
|
16
|
-
public $type;
|
|
17
|
-
|
|
18
|
-
public function __construct($slug, $version, $cache_key, $remote_url, $type = 'plugin') {
|
|
19
|
-
$this->slug = $slug;
|
|
20
|
-
$this->version = $version;
|
|
21
|
-
$this->cache_key = $cache_key;
|
|
22
|
-
$this->remote_url = $remote_url;
|
|
23
|
-
$this->type = $type;
|
|
24
|
-
add_action('admin_init', array($this, 'check_update_conditions'));
|
|
25
|
-
add_action('plugin_row_meta', array($this, 'add_check_updates_button'), 10, 2);add_action('admin_post_check_for_updates', array($this, 'check_for_updates'));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public function check_update_conditions() {
|
|
29
|
-
add_filter('plugins_api', array($this, 'info'), 20, 3);add_filter('site_transient_update_plugins', array($this, 'update_plugins'));
|
|
30
|
-
add_action('upgrader_process_complete', array($this, 'purge'), 10, 2);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public function request() {
|
|
34
|
-
$remote = get_transient($this->cache_key);
|
|
35
|
-
|
|
36
|
-
if (false === $remote) {
|
|
37
|
-
$response = wp_remote_get(
|
|
38
|
-
$this->remote_url,
|
|
39
|
-
array(
|
|
40
|
-
'timeout' => 10,
|
|
41
|
-
'headers' => array('Accept' => 'application/json')
|
|
42
|
-
)
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
is_wp_error($response)
|
|
47
|
-
|| 200 !== wp_remote_retrieve_response_code($response)
|
|
48
|
-
|| empty(wp_remote_retrieve_body($response))
|
|
49
|
-
) {
|
|
50
|
-
error_log('Hoster update check error: ' . wp_remote_retrieve_body($response) ?: $response->get_error_message());
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
$remote = wp_remote_retrieve_body($response);
|
|
55
|
-
set_transient($this->cache_key, $remote, 6 * HOUR_IN_SECONDS);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if(!is_array($remote)){
|
|
59
|
-
$remote = json_decode($remote);
|
|
60
|
-
}
|
|
61
|
-
return $remote;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
public function info($res, $action, $args) {
|
|
66
|
-
if ('plugin_information' !== $action || $this->slug !== $args->slug) {
|
|
67
|
-
return $res;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
$remote = $this->request();
|
|
71
|
-
if (!$remote) return $res;
|
|
72
|
-
|
|
73
|
-
$res = new stdClass();
|
|
74
|
-
$res->name = $remote->name;
|
|
75
|
-
$res->slug = $remote->slug;
|
|
76
|
-
$res->version = $remote->version;
|
|
77
|
-
$res->tested = $remote->tested;
|
|
78
|
-
$res->requires = $remote->requires;
|
|
79
|
-
$res->author = $remote->author;
|
|
80
|
-
$res->author_profile = $remote->author_profile;
|
|
81
|
-
$res->download_link = $remote->download_url;
|
|
82
|
-
$res->trunk = $remote->download_url;
|
|
83
|
-
$res->requires_php = $remote->requires_php;
|
|
84
|
-
$res->last_updated = $remote->last_updated;
|
|
85
|
-
$res->sections = (array)$remote->sections;
|
|
86
|
-
|
|
87
|
-
if (!empty($remote->banners)) {
|
|
88
|
-
$res->banners = (array)$remote->banners;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (!empty($remote->icons)) {
|
|
92
|
-
$res->icons = (array)$remote->icons;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return $res;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
public function update_plugins($transient) {
|
|
99
|
-
if (empty($transient->checked)) return $transient;
|
|
100
|
-
|
|
101
|
-
$remote = $this->request();
|
|
102
|
-
if (!$remote) return $transient;
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
version_compare($this->version, $remote->version, '<')
|
|
106
|
-
&& version_compare($remote->requires, get_bloginfo('version'), '<=')
|
|
107
|
-
&& version_compare($remote->requires_php, PHP_VERSION, '<=')
|
|
108
|
-
) {
|
|
109
|
-
$res = new stdClass();
|
|
110
|
-
$res->slug = $this->slug;
|
|
111
|
-
$res->new_version = $remote->version;
|
|
112
|
-
$res->tested = $remote->tested;
|
|
113
|
-
$res->package = $remote->download_url;
|
|
114
|
-
|
|
115
|
-
if (!empty($remote->icons)) {
|
|
116
|
-
$res->icons = (array)$remote->icons;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
$res->plugin = $this->slug;
|
|
120
|
-
$transient->response[$res->plugin] = $res;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return $transient;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
public function add_check_updates_button($plugin_meta, $plugin_file) {
|
|
127
|
-
if ($plugin_file === $this->slug) {
|
|
128
|
-
$url = wp_nonce_url(admin_url('admin-post.php?action=check_for_updates&plugin=' . $this->slug), 'check_for_updates');
|
|
129
|
-
$plugin_meta[] = '<a href="' . esc_url($url) . '">Check for updates</a>';
|
|
130
|
-
}
|
|
131
|
-
return $plugin_meta;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public function check_for_updates() {
|
|
135
|
-
if (!current_user_can('update_plugins')) {
|
|
136
|
-
wp_die(__('You do not have sufficient permissions to update plugins.'));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
check_admin_referer('check_for_updates');
|
|
140
|
-
delete_transient($this->cache_key);
|
|
141
|
-
$this->request();
|
|
142
|
-
wp_redirect(admin_url('plugins.php'));
|
|
143
|
-
exit;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
public function purge($upgrader, $options) {
|
|
149
|
-
if ('update' === $options['action'] && $this->type === $options['type']) {
|
|
150
|
-
delete_transient($this->cache_key);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|