Forked from unarist/mastodon-open-link-in-webui.user.js
Last active
January 7, 2018 21:30
-
-
Save karubabu/0e05ff92ec1c6fca1736894245270e52 to your computer and use it in GitHub Desktop.
Mastodon - Open post link in WebUI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Mastodon - Open post link in WebUI | |
// @description Click fa-comment just before URLs in posts, then it shows linked page in WebUI | |
// @namespace https://github.com/unarist/ | |
// @downloadURL https://gist.github.com/unarist/9bed2c719f42853b9588104e6fdb0a20/raw/mastodon-open-link-in-webui.user.js | |
// @version 0.2 | |
// @author unarist | |
// @match https://*/web/* | |
// @grant none | |
// ==/UserScript== | |
/* | |
* Twitter status: embed using widget.js (in sandbox iframe) | |
* Pixiv: thumbnail | |
* OS/AP compatible account/post: resolve via Mastodon and open in WebUI | |
*/ | |
(function() { | |
'use strict'; | |
const app_root = document.querySelector('#mastodon'); | |
if (!app_root) return; | |
const cache = {}; | |
const keys = { | |
state: 'webuiLinkState', | |
icon: 'webuiLinkIcon', | |
}; | |
const tag = (name, props = {}, children = []) => { | |
const e = Object.assign(document.createElement(name), props); | |
//if (typeof e.style === "object") Object.assign(e.style, props.style); | |
(children.forEach ? children : [children]).forEach(c => e.appendChild(c)); | |
return e; | |
}; | |
const defaultOptions = { | |
headers: { | |
'Authorization': 'Bearer ' + JSON.parse(document.getElementById('initial-state').textContent).meta.access_token | |
} | |
}; | |
const api = (path, options) => | |
fetch(location.origin + path, Object.assign({}, defaultOptions, options)) // should be deepMerge | |
.then(resp => { if (!resp.ok) throw new Error(new URL(resp.url).pathname + ' ' + resp.status); return resp.json(); }); | |
const getHistory = () => { | |
try { | |
if (app_root._reactRootContainer) { | |
// >= v16: to descendant | |
let current_node = app_root._reactRootContainer.current.child; | |
while ('function' === typeof current_node.type) { | |
const history = current_node.memoizedProps.history; | |
if (history) return history; | |
current_node = current_node.child; | |
} | |
} else { | |
// < v16: to ancestor | |
const root_instance = Object.entries(app_root.firstElementChild).find(prop=>prop[0].startsWith('__reactInternalInstance'))[1]; | |
let current_node = root_instance._currentElement._owner; | |
while (current_node) { | |
const history = current_node._instance.props.history; | |
if (history) return history; | |
current_node = current_node._currentElement._owner; | |
} | |
} | |
} catch (e) { | |
console.log('mastodon-open-link-in-webui: Failed to get History instance: ', e); | |
return null; | |
} | |
}; | |
const modal = { | |
open(elem) { | |
this.container.appendChild(elem); | |
Object.assign(this.container.style, { display: 'block', opacity: 1 }); | |
}, | |
close() { | |
this.container.addEventListener('transitionend', () => { | |
this.container.style.display = 'none'; | |
while (this.container.firstChild) this.container.removeChild(this.container.firstChild); | |
}, { once: 1 }); | |
this.container.style.opacity = 0; | |
}, | |
container: tag('div', { | |
style: "position:fixed; top:0; width:100%; height:100%; background: rgba(0,0,0,0.8); display:none; z-index: 999; transition: 100ms", | |
onclick: e => modal.close(), | |
onkeydown: e => e.keyCode === 27 && modal.close() | |
}) | |
}; | |
document.body.appendChild(modal.container); | |
const processors = []; | |
processors.push(function() { | |
window.addEventListener('message', e => { | |
const iframe = document.querySelector('.webui-link-iframe'); | |
if (iframe && iframe.contentWindow === e.source && e.data.type === 'webuiLink_setEmbedHeight') { | |
iframe.style.height = e.data.height + 'px'; | |
} | |
}); | |
return url => { | |
const match = url.match(/\/twitter\.com\/.*\/status\/(\d+)/); | |
if (match) | |
return Promise.resolve(() => modal.open(tag('iframe', { | |
className: 'webui-link-iframe', | |
sandbox: 'allow-scripts allow-popups', | |
style: 'width: 480px; max-width: 90vw; max-height: 80vh; position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto; border: none;', | |
srcdoc: ` | |
<style>html,body,div,.twitter-tweet{margin:0 !important;padding:0 !important;}</style> | |
<div id="tweet"></div> | |
<script src="https://platform.twitter.com/widgets.js"></${""}script> | |
<script> | |
twttr.widgets.createTweet('${match[1]}',document.querySelector('#tweet')) | |
.then(window.onresize = () => window.parent.postMessage({type: 'webuiLink_setEmbedHeight', height: document.documentElement.scrollHeight}, '*')); | |
</${""}script>` | |
}))); | |
else | |
return Promise.resolve(); | |
}; | |
}()); | |
processors.push(url => { | |
const match = url.match(/\/www\.pixiv\.net\/.*illust_id=(\d+)/); | |
if (match) | |
return Promise.resolve(() => modal.open(tag('img', { | |
src: 'https://embed.pixiv.net/decorate.php?illust_id=' + match[1], | |
style: 'max-width: 80vw; max-height: 80vh; position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto;' | |
}))); | |
else | |
return Promise.resolve(); | |
}); | |
processors.push(function () { | |
const history = getHistory(); | |
if (!history) return () => Promise.reject(); | |
const openStatus = id => history.push('/statuses/' + id); | |
const openAccount = id => history.push('/accounts/' + id); | |
return url => api('/api/v1/search?resolve=true&q=' + url) | |
.then(json => { | |
if (json.statuses.length) { | |
return openStatus.bind(null, json.statuses[0].id); | |
} else if (json.accounts.length) { | |
return openAccount.bind(null, json.accounts[0].id); | |
} | |
}); | |
}()); | |
const actions = { | |
_history: undefined, | |
attach(elem) { | |
elem.insertBefore(tag('i', { className: 'fa fa-comment '+ keys.icon, style: 'margin-right: .1em' }), elem.firstChild); | |
elem.addEventListener('click', handleClick); | |
}, | |
detatch(elem) { | |
elem.removeChild(elem.querySelector('.' + keys.icon)); | |
elem.removeEventListener('click', handleClick); | |
}, | |
open(elem) { | |
cache[elem.href](); | |
}, | |
refresh(elem) { | |
const icon_elem = elem.querySelector('.' + keys.icon); | |
elem.dataset[keys.state] = 'refreshing'; | |
icon_elem.classList.replace('fa-comment', 'fa-spinner'); | |
const tryNext = (url, rest) => { | |
rest[0](url).then(result => { | |
if (result) { | |
elem.dataset[keys.state] = 'found'; | |
cache[elem.href] = result; | |
icon_elem.classList.replace('fa-spinner', 'fa-comment'); | |
result(); | |
} else { | |
if (rest.length > 1) { | |
tryNext(url, rest.slice(1)); | |
} else { | |
elem.dataset[keys.state] = 'notfound'; | |
actions.detatch(elem); | |
} | |
} | |
}).catch(e => (console.log(e), elem.dataset[keys.state] = 'error', actions.detatch(elem))); | |
}; | |
tryNext(elem.href, processors); | |
} | |
}; | |
const handleClick = e => { | |
if (!e.target.classList.contains(keys.icon)) return; | |
const link = e.currentTarget; | |
switch (link.dataset[keys.state]) { | |
case 'candidate': | |
actions.refresh(link); | |
break; | |
case 'found': | |
actions.open(link); | |
e.preventDefault(); | |
break; | |
} | |
e.preventDefault(); | |
}; | |
new MutationObserver(mutations => { | |
for (const elem of document.querySelectorAll('.status__content a:not([data-webui-link-state])')) { | |
if (!elem.textContent.startsWith('https') || cache[elem.href] === null) { | |
elem.dataset[keys.state] = 'none'; | |
} else { | |
elem.dataset[keys.state] = cache[elem.href] ? 'found' : 'candidate'; | |
actions.attach(elem); | |
} | |
} | |
}).observe(app_root, {childList: true, subtree: true}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
tampermonkey firefox 57.0.4(64bit)archlinux 4.14.12-1-ARCH
この状態だとついったーの機能以外は使用できる
ついったーはiframe内のsrcdocがロードされず使用出来無い
デバッガにあるabout:srcdoc内の記述:
Could not load the source for about:srcdoc.
コンソールにあるエラーと警告はスクリプトをOFFにしても出るものだけ:
わからん!