1(function (window, document, $) { 2 'use strict'; 3 4 const state = { 5 loader: { 6 currentButton: null, 7 originalContent: null 8 } 9 }; 10 11 const Loader = { 12 initStyles: function () { 13 if (!document.getElementById('dashicon-rotate-style')) { 14 const style = document.createElement('style'); 15 style.id = 'dashicon-rotate-style'; 16 style.textContent = ` 17 @keyframes rotate { 18 0% { transform: rotate(0deg); } 19 100% { transform: rotate(360deg); } 20 } 21 .button-spinner.dashicons { 22 display: inline-block; 23 animation: rotate 1s linear infinite; 24 text-align: center; 25 flex-shrink: 0; 26 } 27 `; 28 document.head.appendChild(style); 29 } 30 }, 31 32 show: function (button) { 33 if (state.loader.currentButton === button) return; 34 35 state.loader.currentButton = button; 36 state.loader.originalContent = button.innerHTML; 37 38 const rect = button.getBoundingClientRect(); 39 button.style.height = rect.height + 'px'; 40 button.style.minWidth = rect.width + 'px'; 41 button.style.width = 'auto'; 42 button.style.whiteSpace = 'nowrap'; 43 44 const computedStyle = window.getComputedStyle(button); 45 if (computedStyle.display !== 'flex') { 46 button.style.display = 'inline-flex'; 47 button.style.justifyContent = 'center'; 48 button.style.alignItems = 'center'; 49 button.style.gap = '8px'; 50 } 51 52 const spinner = document.createElement('span'); 53 spinner.className = 'button-spinner dashicons dashicons-update'; 54 55 const existingIcon = button.querySelector('.dashicons'); 56 if (existingIcon) { 57 existingIcon.replaceWith(spinner); 58 } else { 59 button.prepend(spinner); 60 } 61 }, 62 63 restore: function () { 64 const btn = state.loader.currentButton; 65 if (btn && state.loader.originalContent !== null) { 66 btn.innerHTML = state.loader.originalContent; 67 68 btn.style.width = ''; 69 btn.style.minWidth = ''; 70 btn.style.height = ''; 71 btn.style.display = ''; 72 btn.style.justifyContent = ''; 73 btn.style.alignItems = ''; 74 btn.style.gap = ''; 75 btn.style.whiteSpace = ''; 76 } 77 78 state.loader.currentButton = null; 79 state.loader.originalContent = null; 80 } 81 }; 82 83 const Popup = { 84 open: function (id) { 85 const overlay = document.getElementById(id + '-overlay'); 86 if (overlay) overlay.style.display = 'flex'; 87 if (typeof Loader !== 'undefined') Loader.restore(); 88 }, 89 90 close: function (overlayElement) { 91 if (overlayElement) overlayElement.style.display = 'none'; 92 if (typeof Loader !== 'undefined') Loader.restore(); 93 }, 94 95 handleGlobalClick: function (e) { 96 const target = e.target; 97 98 if (target.closest('.torlib-popup-close') || target.classList.contains('torlib-popup-overlay') || target.classList.contains('torlib-popup-close-footer')) { 99 e.preventDefault(); 100 e.stopPropagation(); 101 const overlay = target.closest('.torlib-popup-overlay'); 102 this.close(overlay); 103 return; 104 } 105 106 const button = target.closest('[data-popup-target], .torlib-action-loader'); 107 108 if (button) { 109 e.stopPropagation(); 110 111 if (button.querySelector('.button-spinner')) { 112 e.preventDefault(); 113 return; 114 } 115 116 const isPopupTrigger = button.hasAttribute('data-popup-target'); 117 118 if (button.classList.contains('tf-no-loader')) { 119 e.preventDefault(); 120 if (isPopupTrigger) { 121 const targetId = button.getAttribute('data-popup-target'); 122 this.open(targetId); 123 } 124 e.stopImmediatePropagation(); 125 return; 126 } 127 128 const isLink = button.tagName === 'A'; 129 const href = button.getAttribute('href'); 130 const hasRealHref = isLink && href && href !== '#' && !href.startsWith('javascript'); 131 132 if (isPopupTrigger || !hasRealHref) { 133 e.preventDefault(); 134 } 135 136 if (hasRealHref && !isPopupTrigger) { 137 Loader.show(button); 138 setTimeout(() =>
138 Loader.restore(), 3000); 139 return; 140 } 141 142 requestAnimationFrame(() => Loader.show(button)); 143 144 if (isPopupTrigger) { 145 const targetId = button.getAttribute('data-popup-target'); 146 if (targetId) { 147 setTimeout(() => this.open(targetId), 150); 148 } 149 } 150 } 151 } 152 }; 153 154 const Media = { 155 init: function () { 156 if (typeof wp === 'undefined' || !wp.media) { 157 return; 158 } 159 document.addEventListener('click', this.handleClick.bind(this)); 160 }, 161 162 handleClick: function (e) { 163 const uploadBtn = e.target.closest('.toret-media-upload-btn'); 164 if (uploadBtn) { 165 e.preventDefault(); 166 this.openUploader(uploadBtn); 167 return; 168 } 169 170 const removeBtn = e.target.closest('.toret-media-remove-btn'); 171 if (removeBtn) { 172 e.preventDefault(); 173 this.removeImage(removeBtn); 174 return; 175 } 176 }, 177 178 openUploader: function (button) { 179 const container = button.closest('.toret-media-uploader'); 180 181 const inputPath = container.querySelector('input.toret-media-id'); 182 const previewImg = container.querySelector('.toret-media-preview'); 183 const placeholderIcon = container.querySelector('.dashicons'); 184 const removeBtn = container.querySelector('.toret-media-remove-btn'); 185 const previewWrapper = container.querySelector('.toret-media-preview-wrapper'); 186 187 const repeaterRow = button.closest('.repeater-row') || button.closest('tr') || button.closest('.field-wrap'); 188 189 let frame = button.frame; 190 191 if (!frame) { 192 frame = wp.media({ 193 title: 'Select Image', 194 button: { text: 'Use this image' }, 195 multiple: false 196 }); 197 198 button.frame = frame; 199 200 frame.on('select', function () { 201 const attachment = frame.state().get('selection').first().toJSON(); 202 203 if (inputPath) { 204 inputPath.value = attachment.id; 205 } 206 207 if (repeaterRow) { 208 const hiddenImgId = repeaterRow.querySelector('input[name*="[imgid]"]'); 209 if (hiddenImgId) { 210 hiddenImgId.value = attachment.id; 211 } 212 } 213 214 if (previewImg) { 215 previewImg.src = attachment.sizes && attachment.sizes.thumbnail ? attachment.sizes.thumbnail.url : attachment.url; 216 previewImg.classList.remove('hidden'); 217 } 218 219 if (placeholderIcon) placeholderIcon.classList.add('hidden'); 220 if (removeBtn) removeBtn.classList.remove('hidden'); 221 if (previewWrapper) previewWrapper.classList.remove('no-image'); 222 }); 223 } 224 225 frame.open(); 226 }, 227 228 removeImage: function (button) { 229 const container = button.closest('.toret-media-uploader'); 230 const inputPath = container.querySelector('input.toret-media-id'); 231 const previewImg = container.querySelector('.toret-media-preview'); 232 const placeholderIcon = container.querySelector('.dashicons'); 233 const previewWrapper = container.querySelector('.toret-media-preview-wrapper'); 234 const repeaterRow = button.closest('.repeater-row') || button.closest('tr') || button.closest('.field-wrap'); 235 236 if (inputPath) inputPath.value = ''; 237 238 if (repeaterRow) { 239 const hiddenImgId = repeaterRow.querySelector('input[name*="[imgid]"]'); 240 if (hiddenImgId) hiddenImgId.value = ''; 241 } 242 243 if (previewImg) { 244 previewImg.src = ''; 245 previewImg.classList.add('hidden'); 246 } 247 248 if (placeholderIcon) placeholderIcon.classList.remove('hidden'); 249 button.classList.add('hidden'); 250 if (previewWrapper) previewWrapper.classList.add('no-image'); 251 } 252 }; 253 254 const UI = { 255 initCopyLink: function () { 256 $(document).on("click", '.toret-copy-link', function (e) { 257 const link = this.getAttribute('data-link'); 258 if (link && navigator.clipboard) { 259 navigator.clipboard.writeText(link); 260 } 261 }); 262 } 263 }; 264 265 window.ToretLib = { 266 showButtonLoader: Loader.show, 267 restoreButton: Loader.restore, 268 openPopup: Popup.open 269 }; 270 271 $(function () { 272 Loader.initStyles(); 273 UI.initCopyLink(); 274 Media.init(); 275 276 document.addEventListener('click', Popup.handleGlobalClick.bind(Popup), true); 277 }); 278 279})(window, document, jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.