Skip to content

Instantly share code, notes, and snippets.

@anod700
Created January 7, 2018 14:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anod700/dbfd90a2dff25d3eb6c078fcb8ddbf5d to your computer and use it in GitHub Desktop.
Save anod700/dbfd90a2dff25d3eb6c078fcb8ddbf5d to your computer and use it in GitHub Desktop.
増田ヘルパー
{
"manifest_version": 2,
"name": "masd",
"description": "増田ヘルパー",
"version": "1.0",
"browser_action": {
"default_popup": "popup.html"
},
"permissions": [ "https://anond.hatelabo.jp/" ]
}
* {
box-sizing: border-box;
}
body {
font-size: 12px;
margin: 0;
}
.container {
width: 100vw;
height: 100vh;
min-width: 600px;
min-height: 400px;
display: flex;
flex-flow: row nowrap;
align-items: stretch;
}
aside {
width: 60px;
flex: none;
background: #484848;
color: #f0f0f0;
border-right: 1px solid #333;
padding: 5px;
position: relative;
}
aside div {
margin: 15px 0;
cursor: pointer;
}
aside div.active {
font-weight: bold;
font-size: 1.05em;
color: #fafafa;
text-shadow: 1px 1px 0px rgba(255,255,255,0.4);
}
#opentab {
position: absolute;
bottom: 0;
font-size: 22px;
border: 2px solid #aaa;
border-radius: 4px;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
main {
width: calc(100% - 60px);
flex: none;
padding: 10px;
}
main section {
width: 100%;
height: 100%;
overflow: auto;
}
main section:not(.active) {
display: none!important;
}
main ul {
margin: 0;
padding: 0;
list-style: none;
}
#panel-edit {
display: flex;
flex-flow: column nowrap;
}
#panel-edit div {
padding: 5px;
}
#panel-edit div:nth-child(2) {
flex: 1 0 0;
}
#panel-edit div:nth-child(3) {
text-align: right;
}
#panel-edit #text-title, #panel-edit #text-body {
display: block;
width: 100%;
height: 100%;
border: 1px solid #5279e7;
padding: 5px 10px;
resize: none;
font-size: 14px;
font-family: meiryo;
}
li {
margin: 5px 0;
padding: 5px;
border-bottom: 1px solid #5279e7;
}
li h1 {
font-size: 15px;
margin: 5px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
li p {
margin: 5px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
li a {
color: #5279e7;
}
li div {
text-align: right;
margin-right: 10px;
}
li input {
margin-left: calc(100% - 45px);
}
#panel-draft li {
cursor: pointer;
}
<!doctype html>
<meta charset="utf-8">
<link rel="stylesheet" href="popup.css">
<script src="popup.js" defer></script>
<div class="container">
<aside>
<div id="edit" class="active">書く</div>
<div id="draft">下書</div>
<div id="list">一覧</div>
<div id="opentab" title="タブで開く">➚</div>
</aside>
<main>
<section id="panel-edit" class="active">
<div><input id="text-title"></div>
<div><textarea id="text-body"></textarea></div>
<div><input id="draft-btn" type="button" value="下書き"><input id="submit-btn" type="button" value="投稿"></div>
</section>
<section id="panel-draft">
<ul></ul>
</section>
<section id="panel-list">
<ul></ul>
</section>
</main>
</div>
async function parsePage(url){
const res = await fetch(url, {credentials: "include"})
const html = await res.text()
return new DOMParser().parseFromString(html, "text/html")
}
async function getURLs(top_url){
const doc = await parsePage(top_url)
const menu_links = [...doc.querySelectorAll("#globalheader a")]
// ログアウトがみつからないのはログインしていない状態
if(!menu_links.find(e => e.innerHTML.trim() === "ログアウト")){
return {}
}
const my_page = menu_links.find(e => e.innerHTML.trim().endsWith("の日記")).getAttribute("href")
const edit_page = menu_links.find(e => e.innerHTML.trim() === "日記を書く").getAttribute("href")
return {
my_page: new URL(my_page, urls.top).href,
edit_page: new URL(edit_page, urls.top).href,
}
}
async function getEditForm(edit_url){
const doc = await parsePage(edit_url)
return doc.querySelector("#body form")
}
async function post(title, body){
if(!urls.edit_page){
alert("ログインしていません")
return
}
const form = await getEditForm(urls.edit_page)
form.querySelector("#text-title").value = title
form.querySelector("#text-body").value = body
form.action = urls.edit_page
const frame = document.createElement("iframe")
frame.hidden = true
document.body.append(frame)
return new Promise(s => {
frame.contentDocument.body.append(form)
frame.onload = event => {
frame.remove()
s()
}
frame.contentDocument.querySelector(`input[value="この内容を登録する"]`).click()
})
}
async function getMyEntries(){
if(!urls.my_page){
alert("ログインしていません")
return
}
const doc = await parsePage(urls.my_page)
return Array.from(doc.querySelectorAll(".sanchor"), e => {
const title = e.closest("h3").textContent
const href = new URL(e.closest("a").getAttribute("href"), urls.top).href
const tb = e.closest(".section").querySelector(".sectionfooter").textContent.match(/記事への反応\(([0-9]+)\)/)[1]
return {title, tb, href}
})
}
const urls = {
top: "https://anond.hatelabo.jp/",
}
const text_title = document.querySelector("#text-title")
const text_body = document.querySelector("#text-body")
!async function(){
Object.assign(urls, await getURLs(urls.top))
if(Object.keys(urls).length === 1){
alert("ログインしていません")
}
}()
async function loadList(){
const entries = await getMyEntries()
const ul = document.querySelector("#panel-list ul")
ul.innerHTML = ""
for(const item of entries){
const li = document.createElement("li")
li.innerHTML = `
<h1><a href="${item.href}" target="_blank">${item.title}</a></h1>
<div>Trackback: ${item.tb}</div>
`
ul.append(li)
}
}
function loadDraftList(){
const drafts = JSON.parse(localStorage.drafts || "[]")
const ul = document.querySelector("#panel-draft ul")
ul.innerHTML = ""
// 新しいのを上に表示する
for(const draft of drafts.sort((a, b) => b.lastUpdated - a.lastUpdated)){
const li = document.createElement("li")
li.innerHTML = `
<h1>${draft.title}</h1>
<p>${draft.body}</p>
<input type="button" value="削除">
`
ul.append(li)
}
}
function saveDraft(title, body){
const drafts = JSON.parse(localStorage.drafts || "[]")
drafts.push({
title,
body,
lastUpdated: Date.now(),
})
localStorage.drafts = JSON.stringify(drafts)
}
function deleteDraft(title, body){
const drafts = JSON.parse(localStorage.drafts || "[]")
const idx = drafts.findIndex(e => e.title === title && e.body === body)
drafts.splice(idx, 1)
localStorage.drafts = JSON.stringify(drafts)
}
function switchPanel(name){
for(const e of document.querySelectorAll(".active")){
e.classList.remove("active")
}
for(const e of document.querySelectorAll(`#${name},#panel-${name}`)){
e.classList.add("active")
}
}
function onswitch(name){
if(name === "draft"){
loadDraftList()
}else if(name === "list"){
loadList()
}
}
function escapeHtml(text){
return Object.assign(document.createElement("div"), {
textContent: text,
}).innerHTML
}
document.querySelector("aside").onclick = event => {
const btn = event.target.closest("aside>div")
if (!btn) return
const type = btn.id
switch(type){
case "edit":
case "draft":
case "list":
switchPanel(type)
onswitch(type)
break
case "opentab":
window.open(location.href)
break
}
}
document.querySelector("#draft-btn").onclick = event => {
saveDraft(text_title.value, text_body.value)
}
document.querySelector("#submit-btn").onclick = async event => {
await post(text_title.value, text_body.value)
alert("投稿しました\n一覧 から確認してください")
}
document.querySelector("#panel-draft").onclick = event => {
const draft = event.target.closest("li")
if (!draft) return
const title = draft.querySelector("h1").textContent
const body = draft.querySelector("p").textContent
const del = event.target.closest("input")
if (del) {
deleteDraft(title, body)
loadDraftList()
return
}
text_title.value = title
text_body.value = body
switchPanel("edit")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment