File size: 9,063 Bytes
b5ba7a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
// Txt2Img Send to Resource
const openoutpaint = {
	frame: null,
	key: null,
};

/**
 * Converts a Data URL string to a file object
 *
 * Based on https://stackoverflow.com/questions/28041840/convert-dataurl-to-file-using-javascript
 *
 * @param {string} dataurl Data URL to load into a file
 * @returns
 */
function openoutpaint_dataURLtoFile(dataurl) {
	var arr = dataurl.split(","),
		mime = arr[0].match(/:(.*?);/)[1],
		bstr = atob(arr[1]),
		n = bstr.length,
		u8arr = new Uint8Array(n);
	while (n--) {
		u8arr[n] = bstr.charCodeAt(n);
	}
	return new File([u8arr], "openOutpaint-file", {type: mime});
}

async function openoutpaint_get_image_from_gallery() {
	var buttons = gradioApp().querySelectorAll(
		'[style="display: block;"].tabitem div[id$=_gallery] .thumbnail-item.thumbnail-small'
	);
	var button = gradioApp().querySelector(
		'[style="display: block;"].tabitem div[id$=_gallery] .thumbnail-item.thumbnail-small.selected'
	);

	if (!button) button = buttons[0];

	if (!button)
		throw new Error("[openoutpaint] No image available in the gallery");

	const canvas = document.createElement("canvas");
	const image = document.createElement("img");
	image.src = button.querySelector("img").src;

	await image.decode();

	canvas.width = image.width;
	canvas.height = image.height;

	canvas.getContext("2d").drawImage(image, 0, 0);

	return canvas.toDataURL();
}

function openoutpaint_send_image(dataURL, name = "Embed Resource") {
	openoutpaint.frame.contentWindow.postMessage({
		key: openoutpaint.key,
		type: "openoutpaint/add-resource",
		image: {
			dataURL,
			resourceName: name,
		},
	});
}

function openoutpaint_gototab(tabname = "openOutpaint", tabsId = "tabs") {
	Array.from(
		gradioApp().querySelectorAll(`#${tabsId} > div:first-child button`)
	).forEach((button) => {
		if (button.textContent.trim() === tabname) {
			button.click();
		}
	});
}

function openoutpaint_send_gallery(name = "Embed Resource") {
	openoutpaint_get_image_from_gallery()
		.then((dataURL) => {
			// Send to openOutpaint
			openoutpaint_send_image(dataURL, name);

			// Send prompt to openOutpaint
			const tab = get_uiCurrentTabContent().id;

			if (["tab_txt2img", "tab_img2img"].includes(tab)) {
				const prompt =
					tab === "tab_txt2img"
						? gradioApp().querySelector("#txt2img_prompt textarea").value
						: gradioApp().querySelector("#img2img_prompt textarea").value;
				const negPrompt =
					tab === "tab_txt2img"
						? gradioApp().querySelector("#txt2img_neg_prompt textarea").value
						: gradioApp().querySelector("#img2img_neg_prompt textarea").value;
				openoutpaint.frame.contentWindow.postMessage({
					key: openoutpaint.key,
					type: "openoutpaint/set-prompt",
					prompt,
					negPrompt,
				});
			}

			// Change Tab
			openoutpaint_gototab();
		})
		.catch((error) => {
			console.warn("[openoutpaint] No image selected to send to openOutpaint");
		});
}

const openoutpaintjs = async () => {
	const frame = gradioApp().getElementById("openoutpaint-iframe");
	const key = gradioApp().getElementById("openoutpaint-key").value;

	openoutpaint.frame = frame;
	openoutpaint.key = key;

	// Listens for messages from the frame
	console.info("[openoutpaint] Add message listener");
	window.addEventListener("message", ({data, origin, source}) => {
		if (source === frame.contentWindow) {
			switch (data.type) {
				case "openoutpaint/ack":
					if (data.message.type === "openoutpaint/init") {
						console.info("[openoutpaint] Received Init Ack");
						clearTimeout(initLoop);
						initLoop = null;
					}
					break;
				case "openoutpaint/sendto":
					console.info(
						`[openoutpaint] Exported image to '${data.message.destination}'`
					);
					const container = new DataTransfer();
					const file = openoutpaint_dataURLtoFile(data.message.image);
					container.items.add(file);

					const setImageInput = (selector) => {
						const inputel = gradioApp().querySelector(selector);
						inputel.files = container.files;
						inputel.dispatchEvent(new Event("change"));
					};

					switch (data.message.destination) {
						case "img2img":
							openoutpaint_gototab("img2img");
							openoutpaint_gototab("img2img", "mode_img2img");
							setImageInput("#img2img_img2img_tab input[type=file]");
							break;
						case "img2img_sketch":
							openoutpaint_gototab("img2img");
							openoutpaint_gototab("Sketch", "mode_img2img");
							setImageInput("#img2img_img2img_sketch_tab input[type=file]");
							break;
						case "img2img_inpaint":
							openoutpaint_gototab("img2img");
							openoutpaint_gototab("Inpaint", "mode_img2img");
							setImageInput("#img2img_inpaint_tab input[type=file]");
							break;
						case "img2img_sketch_inpaint":
							openoutpaint_gototab("img2img");
							openoutpaint_gototab("Inpaint sketch", "mode_img2img");
							setImageInput("#img2img_inpaint_sketch_tab input[type=file]");
							break;
						case "extras":
							openoutpaint_gototab("Extras");
							setImageInput("#extras_single_tab input[type=file]");
							break;
						case "pnginfo":
							openoutpaint_gototab("PNG Info");
							setImageInput("#tab_pnginfo input[type=file]");
							break;
						default:
							console.warn(
								`[openoutpaint] Unknown destination ${data.message.destination}`
							);
					}
					break;
			}
		}
	});

	// Initializes communication channel
	let initLoop = null;
	const sendInit = () => {
		console.info("[openoutpaint] Sending init message");
		const pathname = window.location.pathname;
		const host = `${window.location.origin}${
			pathname.endsWith("/")
				? pathname.substring(0, pathname.length - 1)
				: pathname
		}`;
		frame.contentWindow.postMessage({
			type: "openoutpaint/init",
			key,
			host,
			destinations: [
				{
					name: "Image to Image",
					id: "img2img",
				},
				{
					name: "Sketch",
					id: "img2img_sketch",
				},
				{
					name: "Inpaint",
					id: "img2img_inpaint",
				},
				{
					name: "Sketch & Inpaint",
					id: "img2img_sketch_inpaint",
				},
				{
					name: "Extras",
					id: "extras",
				},
				{
					name: "PNG Info",
					id: "pnginfo",
				},
			],
		});
		initLoop = setTimeout(sendInit, 1000);
	};

	frame.addEventListener("load", () => {
		sendInit();
	});

	// Setup openOutpaint tab scaling
	const tabEl = gradioApp().getElementById("tab_openOutpaint");
	frame.style.left = "0px";

	const refreshBtn = document.createElement("button");
	refreshBtn.id = "openoutpaint-refresh";
	refreshBtn.textContent = "🔄";
	refreshBtn.title = "Refresh openOutpaint";
	refreshBtn.style.width = "fit-content";
	refreshBtn.classList.add("gr-button", "gr-button-lg", "gr-button-secondary");
	refreshBtn.addEventListener("click", async () => {
		if (confirm("Are you sure you want to refresh openOutpaint?")) {
			frame.contentWindow.location.reload();
		}
	});
	tabEl.appendChild(refreshBtn);

	const recalculate = () => {
		// If we are on the openoutpaint tab, recalculate
		if (tabEl.style.display !== "none") {
			frame.style.height = window.innerHeight + "px";
			const current = document.body.scrollHeight;
			const bb = frame.getBoundingClientRect();
			const iframeh = bb.height;
			const innerh = window.innerHeight;
			frame.style.height = `${Math.floor(iframeh + (innerh - current)) - 1}px`;
			frame.style.width = `${Math.floor(window.innerWidth) - 1}px`;
			frame.style.left = `${Math.floor(
				parseInt(frame.style.left, 10) - bb.x
			)}px`;
		}
	};

	window.addEventListener("resize", () => {
		recalculate();
	});

	new MutationObserver((e) => {
		recalculate();
	}).observe(tabEl, {
		attributes: true,
	});

	// Add button to other tabs
	const createButton = () => {
		const button = document.createElement("button");
		button.id = "openOutpaint_tab";
		button.classList.add("lg", "secondary", "gradio-button", "svelte-1ipelgc");
		button.textContent = "Send to openOutpaint";
		return button;
	};

	const extrasBtn = createButton();
	extrasBtn.addEventListener("click", () =>
		openoutpaint_send_gallery("WebUI Extras Resource")
	);
	gradioApp().querySelector("#tab_extras button#extras_tab").after(extrasBtn);

	const pnginfoBtn = createButton();
	pnginfoBtn.addEventListener("click", () => {
		const image = gradioApp().querySelector("#pnginfo_image img");
		if (image && image.src) {
			openoutpaint_send_image(image.src, "WebUI PNGInfo Resource");
			openoutpaint_gototab();
		}
	});
	gradioApp().querySelector("#tab_pnginfo button#extras_tab").after(pnginfoBtn);

	// Initial calculations
	sendInit();
	recalculate();

	new MutationObserver((mutations) => {
		if (
			mutations.some(
				(mutation) =>
					mutation.attributeName === "style" &&
					mutation.target.style.display !== "none"
			)
		)
			frame.contentWindow.focus();
	}).observe(tabEl, {
		attributes: true,
	});

	if (tabEl.style.display !== "none") frame.contentWindow.focus();
};
document.addEventListener("DOMContentLoaded", () => {
	const onload = () => {
		if (gradioApp().getElementById("openoutpaint-iframe")) {
			openoutpaintjs();
		} else {
			setTimeout(onload, 10);
		}
	};
	onload();
});