From 892cb44d8439368fbad07e6863c157f87f6cef95 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 10 Feb 2020 02:59:00 +0900
Subject: [PATCH] Resolve #3644

---
 src/client/components/url-preview-popup.vue | 55 +++++++++++++++++++++
 src/client/components/url.vue               | 50 ++++++++++++++++++-
 2 files changed, 104 insertions(+), 1 deletion(-)
 create mode 100644 src/client/components/url-preview-popup.vue

diff --git a/src/client/components/url-preview-popup.vue b/src/client/components/url-preview-popup.vue
new file mode 100644
index 0000000000..fd127c873c
--- /dev/null
+++ b/src/client/components/url-preview-popup.vue
@@ -0,0 +1,55 @@
+<template>
+<div class="fgmtyycl _panel" :style="{ top: top + 'px', left: left + 'px' }" @mouseover="() => { $emit('mouseover'); }" @mouseleave="() => { $emit('mouseleave'); }">
+	<x-url-preview :url="url" style="width: 600px;"/>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import i18n from '../i18n';
+import XUrlPreview from './url-preview.vue';
+
+export default Vue.extend({
+	i18n,
+
+	components: {
+		XUrlPreview
+	},
+
+	props: {
+		url: {
+			type: String,
+			required: true
+		},
+		source: {
+			required: true
+		}
+	},
+
+	data() {
+		return {
+			u: null,
+			top: 0,
+			left: 0,
+		};
+	},
+
+	mounted() {
+		const rect = this.source.getBoundingClientRect();
+		const x = ((rect.left + (this.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset;
+		const y = rect.top + this.source.offsetHeight + window.pageYOffset;
+
+		this.top = y;
+		this.left = x;
+	},
+});
+</script>
+
+<style lang="scss" scoped>
+.fgmtyycl {
+	position: absolute;
+	z-index: 11000;
+	//width: 300px;
+	overflow: hidden;
+}
+</style>
diff --git a/src/client/components/url.vue b/src/client/components/url.vue
index 219921558c..b8dbacc6de 100644
--- a/src/client/components/url.vue
+++ b/src/client/components/url.vue
@@ -1,5 +1,8 @@
 <template>
-<component :is="hasRoute ? 'router-link' : 'a'" class="ieqqeuvs _link" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target">
+<component :is="hasRoute ? 'router-link' : 'a'" class="ieqqeuvs _link" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target"
+	@mouseover="onMouseover"
+	@mouseleave="onMouseleave"
+>
 	<template v-if="!self">
 		<span class="schema">{{ schema }}//</span>
 		<span class="hostname">{{ hostname }}</span>
@@ -20,6 +23,7 @@ import Vue from 'vue';
 import { faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
 import { toUnicode as decodePunycode } from 'punycode';
 import { url as local } from '../config';
+import XUrlPreview from './url-preview-popup.vue';
 
 export default Vue.extend({
 	props: {
@@ -51,6 +55,9 @@ export default Vue.extend({
 			hasRoute: hasRoute,
 			attr: hasRoute ? 'to' : 'href',
 			target: hasRoute ? null : '_blank',
+			showTimer: null,
+			hideTimer: null,
+			preview: null,
 			faExternalLinkSquareAlt
 		};
 	},
@@ -62,6 +69,47 @@ export default Vue.extend({
 		this.pathname = decodeURIComponent(url.pathname);
 		this.query = decodeURIComponent(url.search);
 		this.hash = decodeURIComponent(url.hash);
+	},
+	methods: {
+		showPreview() {
+			if (!document.body.contains(this.$el)) return;
+			if (this.preview) return;
+
+			this.preview = new XUrlPreview({
+				parent: this,
+				propsData: {
+					url: this.url,
+					source: this.$el
+				}
+			}).$mount();
+
+			this.preview.$on('mouseover', () => {
+				clearTimeout(this.hideTimer);
+			});
+
+			this.preview.$on('mouseleave', () => {
+				clearTimeout(this.showTimer);
+				this.hideTimer = setTimeout(this.closePreview, 500);
+			});
+
+			document.body.appendChild(this.preview.$el);
+		},
+		closePreview() {
+			if (this.preview) {
+				this.preview.destroyDom();
+				this.preview = null;
+			}
+		},
+		onMouseover() {
+			clearTimeout(this.showTimer);
+			clearTimeout(this.hideTimer);
+			this.showTimer = setTimeout(this.showPreview, 500);
+		},
+		onMouseleave() {
+			clearTimeout(this.showTimer);
+			clearTimeout(this.hideTimer);
+			this.hideTimer = setTimeout(this.closePreview, 500);
+		}
 	}
 });
 </script>