Skip to content
This repository was archived by the owner on Mar 26, 2024. It is now read-only.

Commit 4629c07

Browse files
committed
Unify MarkdownPanelManager and EmbeddedBrowserManager
1 parent a9ebd37 commit 4629c07

File tree

4 files changed

+233
-286
lines changed

4 files changed

+233
-286
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2019-2022 JetBrains s.r.o.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package org.jetbrains.projector.client.web.component
25+
26+
import kotlinx.browser.document
27+
import org.w3c.dom.HTMLIFrameElement
28+
29+
abstract class ClientComponent(
30+
protected val id: Int,
31+
) {
32+
33+
val iFrame: HTMLIFrameElement = createIFrame(id)
34+
35+
var windowId: Int? = null
36+
37+
fun dispose() {
38+
iFrame.remove()
39+
}
40+
41+
private fun createIFrame(browserId: Int) = (document.createElement("iframe") as HTMLIFrameElement).apply {
42+
id = getIFrameId(browserId)
43+
style.apply {
44+
position = "fixed"
45+
backgroundColor = "#FFF"
46+
overflowX = "scroll"
47+
overflowY = "scroll"
48+
display = "none"
49+
}
50+
51+
frameBorder = "0"
52+
53+
document.body!!.appendChild(this)
54+
55+
// cancel auto-started load of about:blank in Firefox
56+
// https://stackoverflow.com/questions/7828502/cannot-set-document-body-innerhtml-of-iframe-in-firefox
57+
contentDocument!!.apply {
58+
open()
59+
close()
60+
}
61+
62+
contentDocument!!.oncontextmenu = { false }
63+
}
64+
65+
protected fun setLinkProcessor(linkProcessor: (String) -> Unit) {
66+
iFrame.contentDocument?.onclick = { e ->
67+
var target = e.target.asDynamic()
68+
while (target != null && target.tagName != "A") {
69+
target = target.parentNode
70+
}
71+
72+
if (target == null) {
73+
true
74+
}
75+
else if (target.tagName == "A" && target.hasAttribute("href").unsafeCast<Boolean>()) {
76+
e.stopPropagation()
77+
78+
val href = target.getAttribute("href").unsafeCast<String>()
79+
if (href[0] == '#') {
80+
val elementId = href.substring(1)
81+
iFrame.contentDocument?.getElementById(elementId)?.scrollIntoView()
82+
}
83+
else {
84+
linkProcessor(href)
85+
}
86+
87+
false
88+
}
89+
else {
90+
null
91+
}
92+
}
93+
}
94+
95+
private fun getIFrameId(browserId: Int) = "${this::class.simpleName}$browserId"
96+
97+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2019-2022 JetBrains s.r.o.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package org.jetbrains.projector.client.web.component
25+
26+
import org.jetbrains.projector.common.misc.Do
27+
import org.jetbrains.projector.common.protocol.data.CommonIntSize
28+
import org.jetbrains.projector.common.protocol.data.Point
29+
30+
abstract class ClientComponentManager<Component : ClientComponent>(
31+
protected val zIndexByWindowIdGetter: (Int) -> Int?,
32+
) {
33+
34+
private val idToComponent = mutableMapOf<Int, Component>()
35+
36+
abstract fun createComponent(componentId: Int): Component
37+
38+
protected fun getOrCreate(componentId: Int): Component {
39+
return idToComponent.getOrPut(componentId) { createComponent(componentId) }
40+
}
41+
42+
fun placeToWindow(componentId: Int, windowId: Int) {
43+
val panel = getOrCreate(componentId)
44+
45+
panel.windowId = windowId
46+
47+
val zIndex = zIndexByWindowIdGetter(windowId) ?: return
48+
49+
panel.iFrame.style.zIndex = (zIndex + 1).toString()
50+
}
51+
52+
fun updatePlacements() {
53+
idToComponent.forEach { (componentId, panel) ->
54+
panel.windowId?.let { placeToWindow(componentId, it) }
55+
}
56+
}
57+
58+
fun show(componentId: Int, show: Boolean, windowId: Int? = null) {
59+
val component = getOrCreate(componentId)
60+
61+
Do exhaustive when (show) {
62+
true -> {
63+
windowId?.also { placeToWindow(componentId, it) }
64+
component.iFrame.style.display = "block"
65+
}
66+
67+
false -> component.dispose()
68+
}
69+
}
70+
71+
fun move(componentId: Int, point: Point) {
72+
val component = getOrCreate(componentId)
73+
74+
component.iFrame.style.apply {
75+
left = "${point.x}px"
76+
top = "${point.y}px"
77+
}
78+
}
79+
80+
fun resize(componentId: Int, size: CommonIntSize) {
81+
val component = getOrCreate(componentId)
82+
83+
component.iFrame.style.apply {
84+
width = "${size.width}px"
85+
height = "${size.height}px"
86+
}
87+
}
88+
89+
fun dispose(componentId: Int) {
90+
val component = getOrCreate(componentId)
91+
92+
component.dispose()
93+
94+
idToComponent.remove(componentId)
95+
}
96+
97+
fun disposeAll() {
98+
idToComponent.values.forEach { it.dispose() }
99+
100+
idToComponent.clear()
101+
}
102+
103+
}

0 commit comments

Comments
 (0)