PluginProbe
Copy Anything to Clipboard for WordPress – Copy Button, Copy Text & Copy Code / 5.5.4
Copy Anything to Clipboard for WordPress – Copy Button, Copy Text & Copy Code v5.5.4
5.5.4 5.5.3 3.1.0 3.2.0 3.2.1 3.3.0 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.6.0 3.7.0 3.8.0 3.8.1 3.8.2 3.8.3 4.0.0 4.0.2 4.0.3 4.0.4 4.0.5 4.1.0 All 79 releases
copy-the-code / includes / assets / js / core.js

core.js in Copy Anything to Clipboard for WordPress – Copy Button, Copy Text & Copy Code 5.5.4, at includes/assets/js/core.js

263 lines 6.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function ($) {
2 const CTCCore = {
3
4 /**
5 * Init
6 */
7 init: function () {
8 this._bind();
9 },
10
11 /**
12 * Binds events
13 */
14 _bind: function () {
15 $( document ).on( 'click', 'body:not(.block-editor-page) .ctc-block-copy', this.doCopy );
16 },
17
18 /**
19 * Copy text to clipboard using CTC CopyEngine.
20 *
21 * @param {string} text Text to copy.
22 * @return {Promise<boolean>} Success status.
23 */
24 copyToClipboard: async function ( text ) {
25 // Method 1: Use CTC CopyEngine.
26 if ( window.CTC && window.CTC.CopyEngine ) {
27 try {
28 const copyEngine = new window.CTC.CopyEngine();
29 const result = await copyEngine.execute( { value: text } );
30 if ( result.success ) {
31 return true;
32 }
33 } catch ( err ) {
34 // Fall through to fallbacks.
35 }
36 }
37
38 // Method 2: Clipboard API.
39 if ( navigator.clipboard && navigator.clipboard.writeText ) {
40 try {
41 await navigator.clipboard.writeText( text );
42 return true;
43 } catch ( err ) {
44 // Fall through.
45 }
46 }
47
48 // Method 3: execCommand fallback.
49 try {
50 const textArea = document.createElement( 'textarea' );
51 textArea.value = text;
52 textArea.style.cssText = 'position:fixed;opacity:0;pointer-events:none;';
53 document.body.appendChild( textArea );
54 textArea.select();
55 const success = document.execCommand( 'copy' );
56 document.body.removeChild( textArea );
57 return success;
58 } catch ( err ) {
59 return false;
60 }
61 },
62
63 /**
64 * Copy selection from element.
65 *
66 * @param {jQuery} $source jQuery element to copy selection from.
67 * @return {Promise<boolean>} Success status.
68 */
69 copySelection: async function ( $source ) {
70 if ( ! $source || ! $source.length ) {
71 return false;
72 }
73
74 const element = $source.get( 0 );
75
76 // Get text content, preserving line breaks.
77 const clone = element.cloneNode( true );
78 clone.querySelectorAll( 'br' ).forEach( ( br ) => br.replaceWith( '\n' ) );
79 const text = clone.textContent || '';
80
81 return this.copyToClipboard( text.trim() );
82 },
83
84 /**
85 * Detect coarse device type from user agent (for block analytics).
86 *
87 * @return {string} desktop|mobile|tablet
88 */
89 _detectDevice: function () {
90 const ua = navigator.userAgent;
91 if ( /tablet|ipad|playbook|silk/i.test( ua ) ) {
92 return 'tablet';
93 }
94 if ( /mobile|iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test( ua ) ) {
95 return 'mobile';
96 }
97 return 'desktop';
98 },
99
100 /**
101 * Detect browser family (for block analytics).
102 *
103 * @return {string} Browser slug.
104 */
105 _detectBrowser: function () {
106 const ua = navigator.userAgent;
107 if ( ua.indexOf( 'Chrome' ) > -1 ) {
108 return 'chrome';
109 }
110 if ( ua.indexOf( 'Firefox' ) > -1 ) {
111 return 'firefox';
112 }
113 if ( ua.indexOf( 'Safari' ) > -1 ) {
114 return 'safari';
115 }
116 if ( ua.indexOf( 'Edge' ) > -1 ) {
117 return 'edge';
118 }
119 return 'unknown';
120 },
121
122 /**
123 * Send analytics event for Gutenberg block copy (non-blocking).
124 *
125 * @param {jQuery} block Block wrapper (.ctc-block).
126 * @param {boolean} success Whether copy succeeded.
127 * @param {string|null} errorReason Optional failure reason.
128 */
129 _sendBlockAnalytics: function ( block, success, errorReason ) {
130 if ( ! block || ! block.length ) {
131 return;
132 }
133 const analytics = block.attr( 'data-ctc-analytics' );
134 const source = block.attr( 'data-ctc-source' );
135 if ( ! analytics || source !== 'gutenberg-block' ) {
136 return;
137 }
138 if ( typeof window.ctcBlockAnalytics === 'undefined' || ! window.ctcBlockAnalytics.eventsUrl ) {
139 return;
140 }
141 const event = {
142 success: !! success,
143 error_reason: errorReason || null,
144 source: 'gutenberg-block',
145 device: this._detectDevice(),
146 browser: this._detectBrowser(),
147 metadata: {
148 source: 'gutenberg-block',
149 block_type: block.attr( 'data-ctc-block-type' ) || null,
150 post_id: window.ctcBlockAnalytics.postId || null,
151 post_type: window.ctcBlockAnalytics.postType || null,
152 page_url: window.location.href,
153 },
154 };
155 const send = () => {
156 fetch( window.ctcBlockAnalytics.eventsUrl, {
157 method: 'POST',
158 headers: { 'Content-Type': 'application/json' },
159 body: JSON.stringify( event ),
160 } ).catch( function ( err ) {
161 if ( typeof console !== 'undefined' && console.debug ) {
162 console.debug( 'CTC: Block analytics event failed:', err );
163 }
164 } );
165 };
166 if ( window.requestIdleCallback ) {
167 window.requestIdleCallback( send, { timeout: 500 } );
168 } else {
169 setTimeout( send, 0 );
170 }
171 },
172
173 /**
174 * Do Copy to Clipboard
175 */
176 doCopy: async function (event) {
177 event.preventDefault();
178
179 let btn = $( this ),
180 btnText = btn.find( '.ctc-button-text' ),
181 oldText = btnText.text(),
182 copiedText = btn.attr( 'data-copied' ) || 'Copied',
183 copyAsRaw = btn.attr( 'copy-as-raw' ) || '',
184 block = btn.parents( '.ctc-block' ),
185 textarea = block.find( '.ctc-copy-content' ),
186 content = textarea.val(),
187 selectionTarget = textarea.attr( 'selection-target' ) || '';
188
189 let copySuccess = false;
190
191 // Copy as selection.
192 if ( selectionTarget ) {
193 const source = $( selectionTarget );
194 if ( ! source.length ) {
195 return;
196 }
197 copySuccess = await CTCCore.copySelection( source );
198 } else {
199 if ( ! copyAsRaw ) {
200 // Convert the <br/> tags into new line.
201 content = content.replace( /<br\s*[\/]?>/gi, "\n" );
202
203 // Convert the <div> tags into new line.
204 content = content.replace( /<div\s*[\/]?>/gi, "\n" );
205
206 // Convert the <p> tags into new line.
207 content = content.replace( /<p\s*[\/]?>/gi, "\n\n" );
208
209 // Convert the <li> tags into new line.
210 content = content.replace( /<li\s*[\/]?>/gi, "\n" );
211
212 // Remove all tags.
213 content = content.replace( /(<([^>]+)>)/ig, '' );
214
215 // Remove white spaces.
216 content = content.replace( new RegExp( "/^\s+$/" ), "" );
217 }
218
219 // Remove first and last new line.
220 content = content.trim();
221
222 // Copy using CTC CopyEngine.
223 copySuccess = await CTCCore.copyToClipboard( content );
224 }
225
226 if ( btn.hasClass( 'ctc-block-copy-icon' ) ) {
227 // Copied!
228 btn.addClass( 'copied' );
229 setTimeout(
230 function () {
231 btn.removeClass( 'copied' );
232 },
233 1000
234 );
235 } else {
236 // Copied!
237 btnText.text( copiedText );
238 block.addClass( 'copied' );
239 setTimeout(
240 function () {
241 btnText.text( oldText );
242 block.removeClass( 'copied' );
243 },
244 1000
245 );
246 }
247
248 // Block analytics (non-blocking).
249 CTCCore._sendBlockAnalytics( block, copySuccess, copySuccess ? null : 'copy_failed' );
250 }
251 };
252
253 /**
254 * Initialization
255 */
256 $(
257 function () {
258 CTCCore.init();
259 }
260 );
261
262 })( jQuery );
263