[s4s] interface

Lets you view the greenposts.

当前为 2018-05-16 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        [s4s] interface
// @namespace   s4s4s4s4s4s4s4s4s4s
// @version     3.2
// @author      le fun css man AKA Doctor Worse Than Hitler, kekero
// @email       [email protected]
// @description Lets you view the greenposts.
// @match       https://boards.4chan.org/s4s/*
// @match       http://boards.4chan.org/s4s/*
// @connect     funposting.online
// @run-at      document-start
// @grant       GM_xmlhttpRequest
// @grant       GM.xmlHttpRequest
// @grant       unsafeWindow
// @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZD0iTTAgMEgxNlYxNkgwIiBmaWxsPSIjZGZkIi8+PHBhdGggZD0iTTMgNCA2IDFoNGwzIDN2OGwtMyAzSDZMMyAxMiIgZmlsbD0iZ3JlZW4iLz48cGF0aCBkPSJtNS41IDExLjVoLTJ2LTdoMnYtM2MtMyAwLTUgMi41LTUgNi41IDAgNCAyIDYuNSA1IDYuNXptNSAzYzMgMCA1LTIuNSA1LTYuNSAwLTQtMi02LjUtNS02LjV2M2gydjdoLTJ6bS00LTRoM0wxMCAyLjVINlptMCAzaDN2LTNoLTN6IiBmaWxsPSIjZmZmIiBzdHJva2U9ImdyZWVuIi8+PC9zdmc+
// ==/UserScript==
"use strict";

if(query("#s4sinterface-css")){
	throw "Multiple instances of [s4s] interface detected"
}

var interfaceLinkRegex = new RegExp('<a[^>]*>&gt;&gt;\\d*( \\(.*\\))?<\\/a>(<span>)?-\\d*');
var weekdays=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
var postForm={}
var lastCommentForm
var updateLinks=new Set()
var cacheCatalogPosts={}
var mode=""
var threadId
var pathName=location.pathname
var threadMatch=pathName.match(/\/thread\/(\d+)/)
if(threadMatch){
	// /board/thread/1
	mode="thread"
	threadId=threadMatch[1]
}else if(/\/catalog$/.test(pathName)){
	// /board/catalog
	mode="catalog"
}else if(/^\/[^\/]+\/\d*$/.test(pathName)){
	// /board/
	mode="index"
}

if(typeof GM=="undefined"){
	window.GM={
		xmlHttpRequest:window.GM_xmlhttpRequest
	}
}

// Request green posts
var serverurl="https://funposting.online/interface/"

if(mode=="thread"){
	getGreenPosts(threadId)
}else if(mode=="catalog"){
	onPageLoad(_=>{
		getGreenPostsCatalog()
	})
}else if(mode=="index") {
  onPageLoad(_=>{
		addGreenPostsToIndex()
	})
  
}

function addGreenPostsToIndex() {
  var threads = document.getElementsByClassName("thread")
  for (var i = 0; i < threads.length; i++) {
    var responses = threads[i].getElementsByClassName("replyContainer")
    var since = 0
    if(responses != null && responses.length > 0) {
    	var since = threads[i].getElementsByClassName("replyContainer")[0].id.substr(2)
    }
    getGreenPosts(threads[i].id.substr(1), since)
  }
}

onPageLoad(_=>{
	// Classic post form
	if(mode=="thread"){
		var nameField=query("#postForm input[name=name]")
		if(nameField){
			var commentField=query("#postForm textarea")
			addCommentForm(commentField,1)
			var greenToggle=element(
				["button#toggle",{
					class:"greenToggle",
					title:"[s4s] Interface",
					onclick:event=>{
						event.preventDefault()
						event.stopPropagation()
						showPostFormClassic()
					}
				},"!"]
			).toggle
			var nameParent=nameField.parentNode
			nameParent.classList.add("nameFieldParent")
			insertBefore(greenToggle,nameField)
		}else{
			// Thread is archived
			showPostFormClassic()
		}
		getUpdateLinks()
	}
})

// Native extension QR
document.addEventListener("QRNativeDialogCreation",onQRCreated)
if(unsafeWindow.Main){
	onNativeextInit()
}else{
	document.addEventListener("4chanMainInit",onNativeextInit)
}

// 4chan-X QR integration
if(document.documentElement.classList.contains("fourchan-x")){
	on4chanXInit()
}else{
	document.addEventListener("4chanXInitFinished",on4chanXInit)
}
document.addEventListener("QRDialogCreation",onQRXCreated)

function onPageLoad(func){
	if(document.readyState=="loading"){
		addEventListener("DOMContentLoaded",func)
	}else{
		func()
	}
}
// replaces links like >>1234567-123 in native 4chan posts with an appropriate link back to the interface post.
function replaceInterfaceLinks(post) {
    while(interfaceLinkRegex.test(post.innerHTML)) {
    var link = interfaceLinkRegex.exec(post.innerHTML)[0] //something like <a href="#p6696342" class="quotelink ql-tracked">&gt;&gt;6696342 (You)</a>-6754<br>test pls ignorlol
    var link_afterno = /&gt;\d+/.exec(link)[0].substr(4)
    var link_interfaceno = /-\d+/.exec(link)[0].substr(1)
    var has_span = /<span>/.test(link) // sometimes the end of the link starts with a <span> so lets not forget it later
    var replace = '<a class="quotelink" href="#p'+link_afterno+'-'+link_interfaceno+'">&gt;&gt;'+link_afterno+'-'+link_interfaceno+'</a>'
    if(has_span) replace += '<span>'
  	post.innerHTML = post.innerHTML.replace(interfaceLinkRegex, replace)
  }
}

// Request green posts & add them
function getGreenPosts(thread, since = 0){
	GM.xmlHttpRequest({
		method:"get",
		url:serverurl+"get.php?thread="+thread+((since != 0) ? "since="+since : ""),
		onload:response=>{
			if(response.status==200){
				onPageLoad(_=>{
					var postsObj=JSON.parse(response.responseText)
					var postsCount=Object.keys(postsObj).length
					if(postsCount){
            if(mode == "thread") {
              var oldPosts=queryAll(".greenPostContainer")
              for(var i=0;i<oldPosts.length;i++){
                removeChild(oldPosts[i])
              }
              var currentPost
              for(var i=postsCount;i--;){
                currentPost=addPost(postsObj[i],currentPost)
              }
            }
            else if(mode == "index") {
              for(var i=0; i< postsCount; i++){
                addPost(postsObj[i],document.getElementById(postsObj[i].after_no))
              }
            }
					}
				})
			}
		},
		onerror:response=>{
		}
	})
}

// takes the JSON from the server and converts to an HTML element
function postJsonToElement(aPost){
  var numberless=aPost.options=="numberless"
	var afterNo=numberless?"XXXXXX":aPost.after_no
	var postId=afterNo+"-"+aPost.id
	var date=new Date(aPost.timestamp*1000)
	var dateString=
		padding(date.getMonth()+1,2)+"/"+
		padding(date.getDate(),2)+"/"+
		(""+date.getFullYear()).slice(-2)+
		"("+weekdays[date.getDay()]+")"+
		padding(date.getHours(),2)+":"+
		padding(date.getMinutes(),2)+":"+
		padding(date.getSeconds(),2)
	var linkReply
	if(!numberless){
		linkReply=[0,
			" ",
			["a",{
				href:"#p"+postId,
				title:"Link to this post"
			},"No."],
			["a",{
				href:"javascript:quote('"+postId+"');",
				onclick:insertQuote,
				title:"Reply to this post"
			},postId]
		]
	}
	var replyHideX=document.documentElement.classList.contains("reply-hide")
	var post=element(
		["div#post",{
			class:"postContainer replyContainer greenPostContainer",
			id:"pc"+aPost.after_no
		},
			(replyHideX?
				["div",{
					id:"sa"+postId
				},
					["a",{
						class:"hide-reply-button"
					},
						["span",{
							class:"fa fa-minus-square-o"
						}]
					]
				]
			:
				["div",{
					class:"sideArrows",
					id:"sa"+postId
				},">>"]
			),
			["div",{
				class:"post reply",
				id:"p"+postId
			},
				["div",{
					class:"postInfoM mobile",
					id:"pim"+postId
				},
					["span",{
						class:"nameBlock"
					},
						["span",{
							class:"name"
						},aPost.username],
						["br"]
					],
					["span",{
						class:"dateTime postNum",
						"data-utc":aPost.timestamp
					},
						dateString,
						linkReply
					]
				],
				["div",{
					class:"postInfo desktop",
					id:"pi"+postId
				},
					["input",{
						type:"checkbox",
						name:"ignore",
						value:"delete"
					}],
					["span",{
						class:"nameBlock"
					},
						["span",{
							class:"name"
						},aPost.username]
					],
					" ",
					["span",{
						class:"dateTime",
						"data-utc":aPost.timestamp
					},dateString],
					(!numberless&&
						["span",{
							class:"postNum desktop",
							onclick:insertQuote,
							title:"Reply to this post"
						},linkReply]
					)
				],
				["blockquote",{
					class:"postMessage",
					id:"m"+postId,
					innerHTML:aPost.text.replace(/\r/g,"")
				}]
			]
		]
	).post
  return post
}

// Add a post to the proper position in the thread
function addPost(aPost,currentPost){
	if(!currentPost){
		currentPost=query(".thread>.postContainer")
	}
  
	var post=postJsonToElement(aPost)
  
  if(mode == "thread") {
    // Add the post
    while(currentPost){
      var lastPost=currentPost
      if(!/^pc\d+$/.test(currentPost.id)||currentPost.id.slice(2)<=aPost.after_no){
        currentPost=currentPost.nextSibling
      }else{
        return insertBefore(post,currentPost)
      }
    }
    return insertAfter(post,lastPost)
  }
  else if(mode == "index") {
  	return insertAfter(post,document.getElementById("pc"+aPost.after_no))
  }
}


// Get green post count on catalog
function getGreenPostsCatalog(){
	var threadContainer=query(".is_catalog #threads,.catalog-mode .board")
	if(!threadContainer||!threadContainer.children.length){
		if(mode=="catalog"){
			return setTimeout(getGreenPostsCatalog,500)
		}else{
			var insertListener=event=>{
				document.removeEventListener("PostsInserted",insertListener)
				getGreenPostsCatalog()
			}
			return document.addEventListener("PostsInserted",insertListener)
		}
	}
	var threads=[]
	var catalogThreads=threadContainer.children
	for(var i=0;i<catalogThreads.length;i++){
		var idMatch=catalogThreads[i].id.match(/\d+/)
		if(idMatch){
			threads.push(idMatch[0])
		}
	}
	GM.xmlHttpRequest({
		method:"post",
		headers:{
			"Content-type":"application/x-www-form-urlencoded"
		},
		url:serverurl+"get.php?mode=catalog",
		data:"thread="+threads.join(","),
		onload:response=>{
			if(response.status==200){
				cacheCatalogPosts=JSON.parse(response.responseText)
				showGreenPostsCatalog()
				if(mode=="catalog"){
					new MutationObserver(mutations=>{
						showGreenPostsCatalog()
					}).observe(threadContainer,{childList:1})
				}else{
					document.addEventListener("PostsInserted",showGreenPostsCatalog)
				}
			}
		},
		onerror:response=>{
		}
	})
}

function showGreenPostsCatalog(){
	var countObj=cacheCatalogPosts
	var oldPosts=queryAll(".greenPostCount")
	for(var i=0;i<oldPosts.length;i++){
		removeChild(oldPosts[i].previousSibling)
		removeChild(oldPosts[i])
	}
	var threadMeta
	for(var thread in countObj){
		if(mode=="catalog"){
			threadMeta=document.getElementById("meta-"+thread)
		}else{
			threadMeta=query("#p"+thread+">.catalog-stats>span")
		}
		if(threadMeta){
			addCatalogPosts(countObj[thread],threadMeta)
		}
	}
}

function addCatalogPosts(count,threadMeta){
	if(count){
		var nativeCatalog=0
		if(mode=="catalog"){
			nativeCatalog=1
		}
		var text=document.createTextNode(" / ")
		var postCount=element(
			["span#span",{
				class:"greenPostCount"
			},
				(nativeCatalog&&
					"G: "
				),
				["b",count]
			]
		).span
		var afterNode=threadMeta.childNodes[nativeCatalog]
		insertAfter(text,afterNode)
		insertAfter(postCount,text)
	}
}

// Classic post form
function showPostFormClassic(hide){
	var formSelector="body>form:not(.greenPostForm)"
	var nameField=query(formSelector+" input[name=name]")
	var optionsField=query(formSelector+" input[name=email]")
	var commentField=query(formSelector+" textarea")
	if(hide){
		if(postForm.classic){
			if(nameField){
				nameField.value=postForm.classic.name.value
				optionsField.value=postForm.classic.options.value
				commentField.value=postForm.classic.comment.value
				lastCommentForm=commentField
			}
			removeChild(postForm.classic.form)
			postForm.classic=0
		}
		return
	}
	if(postForm.classic){
		return
	}
	var username=""
	if(nameField){
		username=nameField.value
	}else{
		var nameMatch=document.cookie.match(/4chan_name=(.*?)(?:;|$)/)
		if(nameMatch){
			username=nameMatch[1]
		}
	}
	postForm.classic=element(
		["form#form",{
			name:"post",
			action:serverurl+"post.php",
			method:"post",
			enctype:"multipart/form-data",
			class:"greenPostForm",
			onsubmit:submitGreenPost
		},
			["input",{
				name:"thread",
				value:threadId,
				type:"hidden"
			}],
			["table",{
				class:"postForm"
			},
				["tbody",
				["tr",
					["td","Name"],
					["td",{
						class:"nameFieldParent"
					},
						(nameField&&
							["button#toggle",{
								class:"greenToggle pressed",
								title:"[s4s] Interface",
								onclick:event=>{
									event.preventDefault()
									event.stopPropagation()
									showPostFormClassic(1)
								}
							},"!"]
						),
						["input#name",{
							type:"text",
							name:"username",
							tabIndex:1,
							placeholder:"Anonymous",
							value:username
						}]
					]
				],
				["tr",
					["td","Options"],
					["td",
						["input#options",{
							type:"text",
							name:"options",
							tabIndex:2,
							value:optionsField?optionsField.value:""
						}],
						["input",{
							type:"submit",
							tabIndex:6,
							value:"Post"
						}]
					]
				],
				["tr",
					["td","Comment"],
					["td",
						["textarea#comment",{
							name:"text",
							tabindex:4,
							cols:48,
							rows:4,
							wrap:"soft",
							value:commentField?commentField.value:""
						}]
					]
				]
				]
			]
		]
	)
	addCommentForm(postForm.classic.comment)
	var originalForm=query("#postForm")
	if(originalForm){
		originalForm=originalForm.parentNode
	}else{
		originalForm=query("body>.closed+*")
		if(!originalForm){
			originalForm=query("#op")
		}
	}
	insertBefore(postForm.classic.form,originalForm)
}

// Native extension initialised
function onNativeextInit(){
	if(mode=="thread"||mode=="index"){
		getUpdateLinks()
		// Native extension quick reply
		unsafeWindow.QR.showInterface=unsafeWindow.QR.show
		var newQRshow=thread=>{
			var event=new CustomEvent("QRNativeDialogCreation",{
				bubbles:true,
				detail:{thread:thread}
			})
			document.dispatchEvent(event)
		}
		if(typeof exportFunction=="function"){
			newQRshow=exportFunction(newQRshow,document.defaultView)
		}
		unsafeWindow.QR.show=newQRshow
	}
}

function onQRCreated(event){
	threadId=event.detail.thread
	try{
		unsafeWindow.QR.showInterface(threadId)
	}catch(e){}
	// Clean up post form if it was initialised before
	var oldToggle=query("#quickReply form:not(.greenPostForm) .greenToggle")
	if(oldToggle){
		removeChild(oldToggle)
	}
	showPostFormQR(1)
	var formSelector="#qrForm"
	var nameField=query(formSelector+" input[name=name]")
	nameField.value=query("#postForm input[name=name]").value
	nameField.tabIndex=0
	var commentField=query(formSelector+" textarea")
	addCommentForm(commentField)
	var toggle=element(
		["button#toggle",{
			type:"button",
			class:"greenToggle",
			title:"[s4s] Interface",
			onclick:event=>{
				event.preventDefault()
				event.stopPropagation()
				showPostFormQR()
			}
		},"!"]
	).toggle
	var nameParent=nameField.parentNode
	nameParent.classList.add("nameFieldParent")
	insertBefore(toggle,nameField)
}

function showPostFormQR(hide){
	var formSelector="#qrForm"
	var nameField=query(formSelector+" input[name=name]")
	var optionsField=query(formSelector+" input[name=email]")
	var commentField=query(formSelector+" textarea")
	if(hide){
		if(postForm.QR){
			nameField.value=postForm.QR.name.value
			optionsField.value=postForm.QR.options.value
			commentField.value=postForm.QR.comment.value
			lastCommentForm=commentField
			removeChild(postForm.QR.form)
			postForm.QR=0
		}
		return
	}
	var qr=query("#quickReply form:not(.greenPostForm)")
	if(postForm.QR||!qr){
		return
	}
	postForm.QR=element(
		["form#form",{
			name:"post",
			action:serverurl+"post.php",
			method:"post",
			enctype:"multipart/form-data",
			class:"greenPostForm",
			onsubmit:submitGreenPost
		},
			["input",{
				name:"thread",
				value:threadId,
				type:"hidden"
			}],
			["div",{
				class:"nameFieldParent"
			},
				["button",{
					type:"button",
					class:"greenToggle pressed",
					title:"[s4s] Interface",
					onclick:event=>{
						showPostFormQR(1)
					}
				},"!"],
				["input#name",{
					type:"text",
					name:"username",
					class:"field",
					placeholder:"Anonymous",
					value:nameField.value
				}]
			],
			["div",
				["input#options",{
					type:"text",
					name:"options",
					class:"field",
					placeholder:"Options",
					value:optionsField.value
				}]
			],
			["div",
				["textarea#comment",{
					name:"text",
					class:"field",
					cols:48,
					rows:4,
					wrap:"soft",
					placeholder:"Comment",
					value:commentField.value
				}],
			],
			["div",
				["span",{
					class:"greenSubmit",
					onclick:event=>{
						submitGreenPost(event,postForm.QR.form)
					}
				},"Post"]
			]
		]
	)
	addCommentForm(postForm.QR.comment)
	insertBefore(postForm.QR.form,qr)
}

// 4chan-X initialised
function on4chanXInit(){
	if(mode=="index"&&document.documentElement.classList.contains("catalog-mode")){
		getGreenPostsCatalog()
	}
}

// 4chan-X QR
function onQRXCreated(){
	getUpdateLinks()
	var formSelector="#qr form:not(.greenPostForm)"
	var commentField=query(formSelector+" textarea")
	addCommentForm(commentField)
	var toggle=element(
		["button#toggle",{
			type:"button",
			class:"greenToggle",
			title:"[s4s] Interface",
			onclick:event=>{
				event.preventDefault()
				event.stopPropagation()
				showPostFormQRX()
			}
		},"!"]
	).toggle
	var qrPersona=query("#qr .persona")
	insertBefore(toggle,qrPersona.firstChild)
}

function showPostFormQRX(hide){
	var formSelector="#qr form:not(.greenPostForm)"
	var nameField=query(formSelector+" input[name=name]")
	var optionsField=query(formSelector+" input[name=email]")
	var commentField=query(formSelector+" textarea")
	if(hide){
		if(postForm.QRX){
			nameField.value=postForm.QRX.name.value
			optionsField.value=postForm.QRX.options.value
			commentField.value=postForm.QRX.comment.value
			lastCommentForm=commentField
			removeChild(postForm.QRX.form)
			postForm.QRX=0
		}
		return
	}
	var qrx=query(formSelector)
	if(postForm.QRX||!qrx){
		return
	}
	threadId=query("#qr select[data-name=thread]").value
	postForm.QRX=element(
		["form#form",{
			name:"post",
			action:serverurl+"post.php",
			method:"post",
			enctype:"multipart/form-data",
			class:"greenPostForm",
			onsubmit:submitGreenPost
		},
			["input",{
				name:"thread",
				value:threadId,
				type:"hidden"
			}],
			["div",{
				class:"persona"
			},
				["button",{
					type:"button",
					class:"greenToggle pressed",
					title:"[s4s] Interface",
					onclick:event=>{
						showPostFormQRX(1)
					}
				},"!"],
				["input#name",{
					name:"username",
					class:"field",
					placeholder:"Name",
					size:1,
					value:nameField.value
				}],
				["input#options",{
					name:"options",
					class:"field",
					placeholder:"Options",
					size:1,
					value:optionsField.value
				}]
			],
			["textarea#comment",{
				name:"text",
				class:"field",
				placeholder:"Comment",
				value:commentField.value
			}],
			["div",{
				class:"file-n-submit"
			},
				["input",{
					type:"submit",
					value:"Submit"
				}]
			]
		]
	)
	addCommentForm(postForm.QRX.comment)
	insertBefore(postForm.QRX.form,qrx)
}

// Track last used comment field for inserting quotes
function addCommentForm(commentField,notLast){
	if(!notLast){
		lastCommentForm=commentField
	}
	commentField.addEventListener("focus",event=>{
		lastCommentForm=event.currentTarget
	})
}

function insertQuote(event){
	var commentField=lastCommentForm
	if(commentField&&document.contains(commentField)){
		event.preventDefault()
		event.stopPropagation()
		var isQRX=commentField.closest("#qr")
		if(isQRX){
			isQRX.hidden=0
		}
		var text=">>"+event.currentTarget.firstChild.data+"\n"
		var caretPos=commentField.selectionStart
		commentField.value=
			commentField.value.slice(0,caretPos)
			+text
			+commentField.value.slice(commentField.selectionEnd)
		var range=caretPos+text.length
		commentField.setSelectionRange(range,range)
		commentField.focus()
	}
}

// Manually update thread with green posts
function getUpdateLinks(){
	var update=queryAll("[data-cmd=update],.updatelink>a")
	for(var i=0;i<update.length;i++){
		if(!updateLinks.has(update[i])){
			update[i].addEventListener("click",event=>{
				getGreenPosts(threadId)
			})
			updateLinks.add(update[i])
		}
	}
}

// Submit a green post
function submitGreenPost(event,form){
	event.preventDefault()
	event.stopPropagation()
	if(!form){
		form=event.currentTarget
	}
	var submit={}
	submit.button=form.querySelector(":scope input[type=submit],:scope .greenSubmit")
	submit.fakeButton=submit.button.classList.contains("greenSubmit")
	if(submit.fakeButton){
		submit.text=submit.button.firstChild.data
		submit.button.firstChild.data="..."
		submit.button.classList.add("greenSubmitDisabled")
	}else{
		submit.text=submit.button.value
		submit.button.value="..."
		submit.button.disabled=1
	}
	var data=[]
	var formData=new FormData(form)
	for(var nameValue of formData){
		data.push(
			nameValue[0]+"="
			+encodeURIComponent(nameValue[1].replace(/\r?\n/g,"\r"))
		)
	}
	data=data.join("&")
	GM.xmlHttpRequest({
		method:"post",
		headers:{
			"Content-type":"application/x-www-form-urlencoded"
		},
		url:serverurl+"post.php",
		data:data,
		onload:response=>{
			if(response.status==200){
				if(/Post Successful/.test(response.responseText)){
					form.getElementsByTagName("textarea")[0].value=""
					if(mode=="thread"){
						getGreenPosts(threadId)
					}else{
						alert("Post successful")
					}
				}else{
					return postSubmitted(submit,response.status,response.responseText)
				}
			}
			postSubmitted(submit,response.status)
		},
		onerror:response=>{
			postSubmitted(submit)
		}
	})
}

function postSubmitted(submit,errorCode,responseText){
	if(submit.fakeButton){
		submit.button.firstChild.data=submit.text
		submit.button.classList.remove("greenSubmitDisabled")
	}else{
		submit.button.value=submit.text
		submit.button.disabled=0
	}
	if(errorCode==200){
		if(responseText){
			alert("Could not submit post ("+responseText+")")
		}
	}else{
		var alertText="Could not connect to the [s4s] interface"
		if(errorCode){
			alertText+=" ("+errorCode+")"
		}
		alert(alertText)
	}
}

//updates native 4chan posts with whatever, atm it's only fixing links
function updatePosts() {
  var posts=document.querySelectorAll('.postMessage:not(.interfaced)');
    for(var i=0;i<posts.length;i++){
    var post = posts[i];
    post.classList.add('interfaced');
    replaceInterfaceLinks(post);
   }
}

// add listenering for when posts are inserted.
document.addEventListener('4chanParsingDone',updatePosts)
document.addEventListener('PostsInserted',updatePosts)

// Stylesheet
var stylesheet=`
.greenPostForm+form .postForm>tbody>tr:not(.rules),
#quickReply .greenPostForm+form,
#qr .greenPostForm+form,
#qr:not(.reply-to-thread) .greenToggle:not(.pressed){
	display:none!important;
}
.greenPostForm .file-n-submit{
	display:flex;
	align-items:stretch;
	justify-content:flex-end;
	height:25px;
	margin-top:1px;
}
.greenPostForm .file-n-submit input{
	width:25%;
	background:linear-gradient(to bottom,#f8f8f8,#dcdcdc) no-repeat;
	border:1px solid #bbb;
	border-radius:2px;
	height:100%;
}
.greenPostContainer .post.reply{
	background-color:#dfd!important;
	border:2px solid #008000!important;
}
.greenPostContainer .postMessage{
	color:#000!important;
}
.greenToggle{
	font-family:monospace;
	font-size:16px;
	line-height:17px;
	background:#ceb!important;
	width:24px;
	padding:0;
	border:1px solid #bbb;
}
.greenPostForm input:not([type=submit]),
.greenPostForm textarea{
	background-color:#dfd;
	color:#000;
}
.greenToggle.pressed{
	background:#6d6!important;
	font-weight:bold;
	color:#fff;
}
.postForm .greenToggle+input{
	width:220px!important;
}
.postForm .nameFieldParent,
#quickReply .nameFieldParent{
	display:flex;
	flex-direction:row;
}
.postForm textarea{
	width:292px;
}
#quickReply .greenToggle{
	width:23px;
	height:23px;
}
#quickReply .greenToggle+input{
	width:273px!important;
}
.greenSubmit{
	display:inline-block;
	width:75px;
	float:right;
	padding:1px 6px;
	text-align:center;
	border:1px solid #adadad;
	background-color:#e1e1e1;
	box-sizing:border-box;
	user-select:none;
	font:400 13.3333px Arial,sans-serif;
	font:-moz-button;
	color:#000;
	cursor:default;
}
.greenSubmit:hover{
	border-color:#0078d7;
	background-color:#e5f1fb;
}
.greenSubmit:active{
	border-color:#005499;
	background-color:#cce4f7;
}
.greenSubmitDisabled{
	color:#808080;
	pointer-events:none;
}
.greenPostCount{
	color:#060;
}
.greenPostContainer .hide-reply-button{
	opacity:0!important;
	pointer-events:none;
}
@media only screen and (max-width:480px){
	.postForm .greenToggle+input{
		width:196px!important;
	}
	.postForm input[type="submit"]{
		width:60px;
		padding:2px 4px 3px;
		margin:0;
	}
	.postForm:not(.hideMobile){
		margin-top:20px;
	}
}
`.replace(/\n\s*/g,"")
element(
	document.head||document.documentElement,
	["style",{
		id:"s4sinterface-css"
	},stylesheet]
)

function padding(string,num){
	return (""+string).padStart(num,0)
}

function query(selector){
	return document.querySelector(selector)
}

function queryAll(selector){
	return document.querySelectorAll(selector)
}

function insertBefore(newElement,targetElement){
	return targetElement.parentNode.insertBefore(newElement,targetElement)
}

function insertAfter(newElement,targetElement){
	var nextSibling=targetElement.nextSibling
	if(nextSibling){
		return insertBefore(newElement,nextSibling)
	}else{
		return targetElement.parentNode.appendChild(newElement)
	}
}

function removeChild(targetElement){
	return targetElement.parentNode.removeChild(targetElement)
}

function element(){
	var parent
	var lasttag
	var createdtag
	var toreturn={}
	for(var i=0;i<arguments.length;i++){
		var current=arguments[i]
		if(current){
			if(current.nodeType){
				parent=lasttag=current
			}else if(Array.isArray(current)){
				for(var j=0;j<current.length;j++){
					if(current[j]){
						if(!j&&typeof current[j]=="string"){
							var tagname=current[0].split("#")
							lasttag=createdtag=document.createElement(tagname[0])
							if(tagname[1]){
								toreturn[tagname[1]]=createdtag
							}
						}else if(current[j].constructor==Object){
							if(lasttag){
								for(var value in current[j]){
									if(value!="style"&&value in lasttag){
										lasttag[value]=current[j][value]
									}else{
										lasttag.setAttribute(value,current[j][value])
									}
								}
							}
						}else{
							var returned=element(lasttag,current[j])
							for(var k in returned){
								toreturn[k]=returned[k]
							}
						}
					}
				}
			}else if(current){
				createdtag=document.createTextNode(current)
			}
			if(parent&&createdtag){
				parent.appendChild(createdtag)
			}
			createdtag=0
		}
	}
	return toreturn
}