PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.9.2
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.9.2
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.9.2, at assets/js/gutenberg-drop-receiver.js

286 lines 7.4 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 let stashedBridgePayload = null;
135 function dragCarriesOsFiles(e) {
136 const types = e.dataTransfer?.types;
137 if (!types) {
138 return false;
139 }
140 const list = types;
141 if (typeof list.includes === "function") {
142 return list.includes("Files");
143 }
144 if (typeof list.contains === "function") {
145 return list.contains("Files");
146 }
147 for (let i = 0; i < list.length; i++) {
148 if (list[i] === "Files") {
149 return true;
150 }
151 }
152 return false;
153 }
154 function isDragOverMsg(m) {
155 if (!m || typeof m !== "object") {
156 return false;
157 }
158 const obj = m;
159 if (obj.type !== "desktop-mode-drag-over") {
160 return false;
161 }
162 const p = obj.payload;
163 if (!p || typeof p !== "object") {
164 return false;
165 }
166 return p.kind === "attachment" || p.kind === "post" || p.kind === "user";
167 }
168 function isDragLeaveMsg(m) {
169 return !!m && typeof m === "object" && m.type === "desktop-mode-drag-leave";
170 }
171 function onNativeDragOver(e) {
172 if (!stashedBridgePayload) {
173 return;
174 }
175 if (dragCarriesOsFiles(e)) {
176 return;
177 }
178 e.preventDefault();
179 if (e.dataTransfer) {
180 e.dataTransfer.dropEffect = "copy";
181 }
182 }
183 function onNativeDrop(e) {
184 if (!stashedBridgePayload) {
185 return;
186 }
187 if (dragCarriesOsFiles(e)) {
188 stashedBridgePayload = null;
189 return;
190 }
191 const payload = stashedBridgePayload;
192 stashedBridgePayload = null;
193 e.preventDefault();
194 e.stopPropagation();
195 if (typeof e.stopImmediatePropagation === "function") {
196 e.stopImmediatePropagation();
197 }
198 void performInsert(payload).catch((err) => {
199 const reason = err instanceof Error ? err.message : String(err);
200 console.error(
201 "[desktop-mode] Gutenberg drop receiver native-drop insert failed:",
202 err
203 );
204 notifyParentOfFailure(reason);
205 });
206 }
207 function attachToDocument(doc) {
208 const sentinel = doc;
209 if (sentinel.__desktopModeDropReceiverAttached) {
210 return;
211 }
212 sentinel.__desktopModeDropReceiverAttached = true;
213 doc.addEventListener("drop", onNativeDrop, true);
214 doc.addEventListener("dragover", onNativeDragOver, true);
215 }
216 function attachToAllFrames() {
217 attachToDocument(document);
218 document.querySelectorAll("iframe").forEach((iframe) => {
219 try {
220 const innerDoc = iframe.contentDocument;
221 if (innerDoc) {
222 attachToDocument(innerDoc);
223 }
224 } catch {
225 }
226 });
227 }
228 function install() {
229 const expectedOrigin = window.location.origin;
230 window.addEventListener("message", (e) => {
231 if (e.origin !== expectedOrigin) {
232 return;
233 }
234 if (isDragOverMsg(e.data)) {
235 stashedBridgePayload = e.data.payload;
236 return;
237 }
238 if (isDragLeaveMsg(e.data)) {
239 stashedBridgePayload = null;
240 return;
241 }
242 if (!isDropMsg(e.data)) {
243 return;
244 }
245 stashedBridgePayload = null;
246 void performInsert(e.data.payload).catch((err) => {
247 const reason = err instanceof Error ? err.message : String(err);
248 console.error(
249 "[desktop-mode] Gutenberg drop receiver insert failed:",
250 err
251 );
252 notifyParentOfFailure(reason);
253 });
254 });
255 attachToAllFrames();
256 if (typeof MutationObserver !== "undefined" && document.documentElement) {
257 new MutationObserver((records) => {
258 for (const r of records) {
259 for (const node of Array.from(r.addedNodes)) {
260 if (node instanceof HTMLIFrameElement || node instanceof Element && node.querySelector?.("iframe")) {
261 attachToAllFrames();
262 return;
263 }
264 }
265 }
266 }).observe(document.documentElement, {
267 childList: true,
268 subtree: true
269 });
270 }
271 document.addEventListener(
272 "load",
273 (e) => {
274 if (e.target instanceof HTMLIFrameElement) {
275 attachToAllFrames();
276 }
277 },
278 true
279 );
280 }
281 install();
282 exports.buildBlockSpec = buildBlockSpec;
283 Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
284 return exports;
285 }({});
286