PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.8.8
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.8.8
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
desktop-mode / assets / js / gutenberg-drop-receiver.js

gutenberg-drop-receiver.js in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 0.8.8, at assets/js/gutenberg-drop-receiver.js

272 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 var desktopModeGutenbergDropReceiver = function(exports) {
2 "use strict";
3 function escapeHtml(s) {
4 return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
5 }
6 function isSafeUrl(raw) {
7 const lower = raw.trimStart().toLowerCase();
8 if (lower.startsWith("javascript:")) {
9 return false;
10 }
11 if (lower.startsWith("data:")) {
12 return false;
13 }
14 if (lower.startsWith("vbscript:")) {
15 return false;
16 }
17 return true;
18 }
19 function buildBlockSpec(payload) {
20 if (payload.kind === "attachment") {
21 if (!payload.url || !isSafeUrl(payload.url)) {
22 return null;
23 }
24 const mime = payload.mime || "";
25 if (mime.startsWith("image/")) {
26 return {
27 name: "core/image",
28 attributes: {
29 id: payload.id,
30 url: payload.url,
31 alt: payload.alt || "",
32 caption: ""
33 }
34 };
35 }
36 if (mime.startsWith("video/")) {
37 return {
38 name: "core/video",
39 attributes: {
40 id: payload.id,
41 src: payload.url
42 }
43 };
44 }
45 if (mime.startsWith("audio/")) {
46 return {
47 name: "core/audio",
48 attributes: {
49 id: payload.id,
50 src: payload.url
51 }
52 };
53 }
54 return {
55 name: "core/file",
56 attributes: {
57 id: payload.id,
58 href: payload.url,
59 fileName: payload.title
60 }
61 };
62 }
63 if (!payload.url || !isSafeUrl(payload.url)) {
64 return null;
65 }
66 const safeTitle = escapeHtml(payload.title || payload.url);
67 const safeHref = escapeHtml(payload.url);
68 return {
69 name: "core/paragraph",
70 attributes: {
71 content: `<a href="${safeHref}">${safeTitle}</a>`
72 }
73 };
74 }
75 async function waitForEditor() {
76 return new Promise((resolve, reject) => {
77 let ticks = 0;
78 const MAX_TICKS = 300;
79 const tick = () => {
80 const wp = window.wp;
81 if (wp?.blocks && wp.data) {
82 resolve({ blocks: wp.blocks, data: wp.data });
83 return;
84 }
85 ticks++;
86 if (ticks > MAX_TICKS) {
87 reject(
88 new Error(
89 "desktop-mode/gutenberg-drop-receiver: timed out waiting for wp.blocks + wp.data"
90 )
91 );
92 return;
93 }
94 requestAnimationFrame(tick);
95 };
96 tick();
97 });
98 }
99 async function performInsert(payload) {
100 const spec = buildBlockSpec(payload);
101 if (!spec) {
102 return;
103 }
104 const { blocks, data } = await waitForEditor();
105 const block = blocks.createBlock(spec.name, spec.attributes);
106 data.dispatch("core/block-editor").insertBlocks([block]);
107 }
108 function notifyParentOfFailure(reason) {
109 if (window.parent === window) {
110 return;
111 }
112 try {
113 window.parent.postMessage(
114 { type: "desktop-mode-drop-failed", reason },
115 window.location.origin
116 );
117 } catch {
118 }
119 }
120 function isDropMsg(m) {
121 if (!m || typeof m !== "object") {
122 return false;
123 }
124 const obj = m;
125 if (obj.type !== "desktop-mode-drop") {
126 return false;
127 }
128 const p = obj.payload;
129 if (!p || typeof p !== "object") {
130 return false;
131 }
132 return p.kind === "attachment" || p.kind === "post" || p.kind === "user";
133 }
134 function isDragOverMsg(m) {
135 if (!m || typeof m !== "object") {
136 return false;
137 }
138 const obj = m;
139 if (obj.type !== "desktop-mode-drag-over") {
140 return false;
141 }
142 const p = obj.payload;
143 if (!p || typeof p !== "object") {
144 return false;
145 }
146 return p.kind === "attachment" || p.kind === "post" || p.kind === "user";
147 }
148 function isDragLeaveMsg(m) {
149 if (!m || typeof m !== "object") {
150 return false;
151 }
152 return m.type === "desktop-mode-drag-leave";
153 }
154 let stashedBridgePayload = null;
155 const _debugCounters = {
156 dragOverMsgs: 0,
157 dragLeaveMsgs: 0,
158 nativeDrops: 0,
159 nativeDropsWithStash: 0,
160 docsAttached: 0
161 };
162 function onNativeDrop(e) {
163 _debugCounters.nativeDrops++;
164 if (!stashedBridgePayload) {
165 return;
166 }
167 _debugCounters.nativeDropsWithStash++;
168 const payload = stashedBridgePayload;
169 stashedBridgePayload = null;
170 e.preventDefault();
171 e.stopPropagation();
172 if (typeof e.stopImmediatePropagation === "function") {
173 e.stopImmediatePropagation();
174 }
175 void performInsert(payload).catch((err) => {
176 const reason = err instanceof Error ? err.message : String(err);
177 console.error(
178 "[desktop-mode] Gutenberg drop receiver native-drop insert failed:",
179 err
180 );
181 notifyParentOfFailure(reason);
182 });
183 }
184 function onNativeDragOver(e) {
185 if (!stashedBridgePayload) {
186 return;
187 }
188 e.preventDefault();
189 if (e.dataTransfer) {
190 e.dataTransfer.dropEffect = "copy";
191 }
192 }
193 function attachToDocument(doc) {
194 const sentinel = doc;
195 if (sentinel.__desktopModeDropReceiverAttached) {
196 return;
197 }
198 sentinel.__desktopModeDropReceiverAttached = true;
199 doc.addEventListener("drop", onNativeDrop, true);
200 doc.addEventListener("dragover", onNativeDragOver, true);
201 _debugCounters.docsAttached++;
202 }
203 function attachToAllFrames() {
204 attachToDocument(document);
205 document.querySelectorAll("iframe").forEach((iframe) => {
206 try {
207 const innerDoc = iframe.contentDocument;
208 if (innerDoc) {
209 attachToDocument(innerDoc);
210 }
211 } catch {
212 }
213 });
214 }
215 function install() {
216 const expectedOrigin = window.location.origin;
217 window.addEventListener("message", (e) => {
218 if (e.origin !== expectedOrigin) {
219 return;
220 }
221 if (isDragOverMsg(e.data)) {
222 stashedBridgePayload = e.data.payload;
223 _debugCounters.dragOverMsgs++;
224 return;
225 }
226 if (isDragLeaveMsg(e.data)) {
227 stashedBridgePayload = null;
228 _debugCounters.dragLeaveMsgs++;
229 return;
230 }
231 if (!isDropMsg(e.data)) {
232 return;
233 }
234 stashedBridgePayload = null;
235 void performInsert(e.data.payload).catch((err) => {
236 const reason = err instanceof Error ? err.message : String(err);
237 console.error(
238 "[desktop-mode] Gutenberg drop receiver insert failed:",
239 err
240 );
241 notifyParentOfFailure(reason);
242 });
243 });
244 attachToAllFrames();
245 if (typeof MutationObserver !== "undefined" && document.documentElement) {
246 new MutationObserver(() => attachToAllFrames()).observe(
247 document.documentElement,
248 { childList: true, subtree: true }
249 );
250 }
251 document.addEventListener(
252 "load",
253 (e) => {
254 if (e.target instanceof HTMLIFrameElement) {
255 attachToAllFrames();
256 }
257 },
258 true
259 );
260 window.__desktopModeDropReceiverDebug = () => ({
261 ..._debugCounters,
262 hasStash: stashedBridgePayload !== null,
263 stashKind: stashedBridgePayload?.kind ?? null,
264 iframesNow: document.querySelectorAll("iframe").length
265 });
266 }
267 install();
268 exports.buildBlockSpec = buildBlockSpec;
269 Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
270 return exports;
271 }({});
272