[s4s] interface

Lets you view the greenposts.

目前为 2018-04-08 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name [s4s] interface
  3. // @namespace s4s4s4s4s4s4s4s4s4s
  4. // @version 2.1
  5. // @author le fun css man AKA Doctor Worse Than Hitler, kekero
  6. // @email doctorworsethanhitler@gmail.com
  7. // @description Lets you view the greenposts.
  8. // @match https://boards.4chan.org/s4s/thread/*
  9. // @match http://boards.4chan.org/s4s/thread/*
  10. // @connect funposting.online
  11. // @run-at document-start
  12. // @grant GM_xmlhttpRequest
  13. // @grant GM.xmlHttpRequest
  14. // @grant unsafeWindow
  15. // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHBhdGggZD0iTTAgMEgxNlYxNkgwIiBmaWxsPSIjZGZkIi8+PHBhdGggZD0iTTMgNCA2IDFoNGwzIDN2OGwtMyAzSDZMMyAxMiIgZmlsbD0iZ3JlZW4iLz48cGF0aCBkPSJtNS41IDExLjVoLTJ2LTdoMnYtM2MtMyAwLTUgMi41LTUgNi41IDAgNCAyIDYuNSA1IDYuNXptNSAzYzMgMCA1LTIuNSA1LTYuNSAwLTQtMi02LjUtNS02LjV2M2gydjdoLTJ6bS00LTRoM0wxMCAyLjVINlptMCAzaDN2LTNoLTN6IiBmaWxsPSIjZmZmIiBzdHJva2U9ImdyZWVuIi8+PC9zdmc+
  16. // ==/UserScript==
  17.  
  18. if(query("#s4sinterface-css")){
  19. throw "Multiple instances of [s4s] interface detected"
  20. }
  21.  
  22. var threadId=location.pathname.match(/\/thread\/(\d+)/)[1]
  23. var weekdays=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]
  24. var postForm={}
  25. var lastCommentForm
  26. var updateLinks=new Set()
  27.  
  28. if(typeof GM=="undefined"){
  29. window.GM={
  30. xmlHttpRequest:window.GM_xmlhttpRequest
  31. }
  32. }
  33.  
  34. // Request green posts
  35. var serverurl="https://funposting.online/interface/"
  36. getGreenPosts()
  37.  
  38. onPageLoad(_=>{
  39. // Classic post form
  40. var nameField=query("#postForm input[name=name]")
  41. if(nameField){
  42. var commentField=query("#postForm textarea")
  43. addCommentForm(commentField,1)
  44. var greenToggle=element(
  45. ["button#toggle",{
  46. class:"greenToggle",
  47. title:"[s4s] Interface",
  48. onclick:event=>{
  49. event.preventDefault()
  50. event.stopPropagation()
  51. showPostFormClassic()
  52. }
  53. },"!"]
  54. ).toggle
  55. var nameParent=nameField.parentNode
  56. nameParent.classList.add("nameFieldParent")
  57. insertBefore(greenToggle,nameField)
  58. }else{
  59. // Thread is archived
  60. showPostFormClassic()
  61. }
  62. getUpdateLinks()
  63. })
  64.  
  65. // Native extension QR
  66. document.addEventListener("QRNativeDialogCreation",onQRCreated)
  67. if(unsafeWindow.Main){
  68. onNativeextInit()
  69. }else{
  70. document.addEventListener("4chanMainInit",onNativeextInit)
  71. }
  72.  
  73. // 4chan-X QR integration
  74. document.addEventListener("QRDialogCreation",onQRXCreated)
  75.  
  76. function onPageLoad(func){
  77. if(document.readyState=="loading"){
  78. addEventListener("DOMContentLoaded",func)
  79. }else{
  80. func()
  81. }
  82. }
  83.  
  84. // Request green posts
  85. function getGreenPosts(){
  86. GM.xmlHttpRequest({
  87. method:"get",
  88. url:serverurl+"get.php?thread="+threadId,
  89. onload:response=>{
  90. if(response.status==200){
  91. onPageLoad(_=>{
  92. var postsObj=JSON.parse(response.responseText)
  93. var postsCount=Object.keys(postsObj).length
  94. if(postsCount){
  95. var oldPosts=queryAll(".greenPostContainer")
  96. for(var i=0;i<oldPosts.length;i++){
  97. removeChild(oldPosts[i])
  98. }
  99. var currentPost
  100. for(var i=postsCount;i--;){
  101. currentPost=addPost(postsObj[i],currentPost)
  102. }
  103. }
  104. })
  105. }
  106. },
  107. onerror:response=>{
  108. }
  109. })
  110. }
  111.  
  112. // Add a post to the proper position in the thread
  113. function addPost(aPost,currentPost){
  114. if(!currentPost){
  115. currentPost=query(".thread>.postContainer")
  116. }
  117. var numberless=aPost.options=="numberless"
  118. var afterNo=numberless?"XXXXXX":aPost.after_no
  119. var postId=afterNo+"-"+aPost.id
  120. var date=new Date(aPost.timestamp*1000)
  121. var dateString=
  122. padding(date.getDate(),2)+"/"+
  123. padding(date.getMonth()+1,2)+"/"+
  124. (""+date.getFullYear()).slice(-2)+
  125. "("+weekdays[date.getDay()]+")"+
  126. padding(date.getHours(),2)+":"+
  127. padding(date.getMinutes(),2)+":"+
  128. padding(date.getSeconds(),2)
  129. var linkReply
  130. if(!numberless){
  131. linkReply=[0,
  132. " ",
  133. ["a",{
  134. href:"#p"+postId,
  135. title:"Link to this post"
  136. },"No."],
  137. ["a",{
  138. href:"javascript:quote('"+postId+"');",
  139. onclick:insertQuote,
  140. title:"Reply to this post"
  141. },postId]
  142. ]
  143. }
  144. var post=element(
  145. ["div#post",{
  146. class:"postContainer replyContainer greenPostContainer",
  147. id:"pc"+aPost.after_no
  148. },
  149. ["div",{
  150. class:"sideArrows",
  151. id:"sa"+postId
  152. },">>"],
  153. ["div",{
  154. class:"post reply",
  155. id:"p"+postId
  156. },
  157. ["div",{
  158. class:"postInfoM mobile",
  159. id:"pim"+postId
  160. },
  161. ["span",{
  162. class:"nameBlock"
  163. },
  164. ["span",{
  165. class:"name"
  166. },aPost.username],
  167. ["br"]
  168. ],
  169. ["span",{
  170. class:"dateTime postNum",
  171. "data-utc":aPost.timestamp
  172. },
  173. dateString,
  174. linkReply
  175. ]
  176. ],
  177. ["div",{
  178. class:"postInfo desktop",
  179. id:"pi"+postId
  180. },
  181. ["input",{
  182. type:"checkbox",
  183. name:"ignore",
  184. value:"delete"
  185. }],
  186. ["span",{
  187. class:"nameBlock"
  188. },
  189. ["span",{
  190. class:"name"
  191. },aPost.username]
  192. ],
  193. " ",
  194. ["span",{
  195. class:"dateTime",
  196. "data-utc":aPost.timestamp
  197. },dateString],
  198. (!numberless&&
  199. ["span",{
  200. class:"postNum desktop",
  201. onclick:insertQuote,
  202. title:"Reply to this post"
  203. },linkReply]
  204. )
  205. ],
  206. ["blockquote",{
  207. class:"postMessage",
  208. id:"m"+postId,
  209. innerHTML:aPost.text.replace(/\r/g,'')
  210. }]
  211. ]
  212. ]
  213. ).post
  214. // Add the post
  215. while(currentPost.nextSibling){
  216. if(!/^pc\d+$/.test(currentPost.id)||currentPost.id.slice(2)<=aPost.after_no){
  217. currentPost=currentPost.nextSibling
  218. }else{
  219. return insertBefore(post,currentPost)
  220. }
  221. }
  222. return insertAfter(post,currentPost)
  223. }
  224.  
  225. // Classic post form
  226. function showPostFormClassic(hide){
  227. var formSelector="body>form:not(.greenPostForm)"
  228. var nameField=query(formSelector+" input[name=name]")
  229. var optionsField=query(formSelector+" input[name=email]")
  230. var commentField=query(formSelector+" textarea")
  231. if(hide){
  232. if(postForm.classic){
  233. if(nameField){
  234. nameField.value=postForm.classic.name.value
  235. optionsField.value=postForm.classic.options.value
  236. commentField.value=postForm.classic.comment.value
  237. lastCommentForm=commentField
  238. }
  239. removeChild(postForm.classic.form)
  240. postForm.classic=0
  241. }
  242. return
  243. }
  244. if(postForm.classic){
  245. return
  246. }
  247. var username=""
  248. if(nameField){
  249. username=nameField.value
  250. }else{
  251. var nameMatch=document.cookie.match(/4chan_name=(.*?)(?:;|$)/)
  252. if(nameMatch){
  253. username=nameMatch[1]
  254. }
  255. }
  256. postForm.classic=element(
  257. ["form#form",{
  258. name:"post",
  259. action:serverurl+"post.php",
  260. method:"post",
  261. enctype:"multipart/form-data",
  262. class:"greenPostForm",
  263. onsubmit:submitGreenPost
  264. },
  265. ["input",{
  266. name:"thread",
  267. value:threadId,
  268. type:"hidden"
  269. }],
  270. ["table",{
  271. class:"postForm"
  272. },
  273. ["tbody",
  274. ["tr",
  275. ["td","Name"],
  276. ["td",{
  277. class:"nameFieldParent"
  278. },
  279. (nameField&&
  280. ["button#toggle",{
  281. class:"greenToggle pressed",
  282. title:"[s4s] Interface",
  283. onclick:event=>{
  284. event.preventDefault()
  285. event.stopPropagation()
  286. showPostFormClassic(1)
  287. }
  288. },"!"]
  289. ),
  290. ["input#name",{
  291. type:"text",
  292. name:"username",
  293. tabIndex:1,
  294. placeholder:"Anonymous",
  295. value:username
  296. }]
  297. ]
  298. ],
  299. ["tr",
  300. ["td","Options"],
  301. ["td",
  302. ["input#options",{
  303. type:"text",
  304. name:"options",
  305. tabIndex:2,
  306. value:optionsField?optionsField.value:""
  307. }],
  308. ["input",{
  309. type:"submit",
  310. tabIndex:6,
  311. value:"Post"
  312. }]
  313. ]
  314. ],
  315. ["tr",
  316. ["td","Comment"],
  317. ["td",
  318. ["textarea#comment",{
  319. name:"text",
  320. tabindex:4,
  321. cols:48,
  322. rows:4,
  323. wrap:"soft",
  324. value:commentField?commentField.value:""
  325. }]
  326. ]
  327. ]
  328. ]
  329. ]
  330. ]
  331. )
  332. addCommentForm(postForm.classic.comment)
  333. originalForm=query("#postForm")
  334. if(originalForm){
  335. originalForm=originalForm.parentNode
  336. }else{
  337. originalForm=query("body>.closed+*")
  338. if(!originalForm){
  339. originalForm=query("#op")
  340. }
  341. }
  342. insertBefore(postForm.classic.form,originalForm)
  343. }
  344.  
  345. // Native extension quick reply
  346. function onNativeextInit(){
  347. getUpdateLinks()
  348. unsafeWindow.QR.showInterface=unsafeWindow.QR.show
  349. var newQRshow=function(){
  350. var event=document.createEvent("Event")
  351. event.initEvent("QRNativeDialogCreation",false,false)
  352. document.dispatchEvent(event)
  353. }
  354. if(typeof exportFunction=="function"){
  355. newQRshow=exportFunction(newQRshow,document.defaultView)
  356. }
  357. unsafeWindow.QR.show=newQRshow
  358. }
  359.  
  360. function onQRCreated(){
  361. try{
  362. unsafeWindow.QR.showInterface(threadId)
  363. }catch(e){}
  364. // Clean up post form if it was initialised before
  365. var oldToggle=query("#quickReply form:not(.greenPostForm) .greenToggle")
  366. if(oldToggle){
  367. removeChild(oldToggle)
  368. }
  369. var oldPostForm=query("#quickReply form:not(.greenPostForm)")
  370. if(oldPostForm){
  371. showPostFormQR(1)
  372. }
  373. var formSelector="#qrForm"
  374. var nameField=query(formSelector+" input[name=name]")
  375. nameField.value=query("#postForm input[name=name]").value
  376. nameField.tabIndex=0
  377. var commentField=query(formSelector+" textarea")
  378. addCommentForm(commentField)
  379. var toggle=element(
  380. ["button#toggle",{
  381. type:"button",
  382. class:"greenToggle",
  383. title:"[s4s] Interface",
  384. onclick:event=>{
  385. event.preventDefault()
  386. event.stopPropagation()
  387. showPostFormQR()
  388. }
  389. },"!"]
  390. ).toggle
  391. var nameParent=nameField.parentNode
  392. nameParent.classList.add("nameFieldParent")
  393. insertBefore(toggle,nameField)
  394. }
  395.  
  396. function showPostFormQR(hide){
  397. var formSelector="#qrForm"
  398. var nameField=query(formSelector+" input[name=name]")
  399. var optionsField=query(formSelector+" input[name=email]")
  400. var commentField=query(formSelector+" textarea")
  401. if(hide){
  402. if(postForm.QR){
  403. nameField.value=postForm.QR.name.value
  404. optionsField.value=postForm.QR.options.value
  405. commentField.value=postForm.QR.comment.value
  406. lastCommentForm=commentField
  407. removeChild(postForm.QR.form)
  408. postForm.QR=0
  409. }
  410. return
  411. }
  412. var qr=query("#quickReply form:not(.greenPostForm)")
  413. if(postForm.QR||!qr){
  414. return
  415. }
  416. postForm.QR=element(
  417. ["form#form",{
  418. name:"post",
  419. action:serverurl+"post.php",
  420. method:"post",
  421. enctype:"multipart/form-data",
  422. class:"greenPostForm",
  423. onsubmit:submitGreenPost
  424. },
  425. ["input",{
  426. name:"thread",
  427. value:threadId,
  428. type:"hidden"
  429. }],
  430. ["div",{
  431. class:"nameFieldParent"
  432. },
  433. ["button",{
  434. type:"button",
  435. class:"greenToggle pressed",
  436. title:"[s4s] Interface",
  437. onclick:event=>{
  438. showPostFormQR(1)
  439. }
  440. },"!"],
  441. ["input#name",{
  442. type:"text",
  443. name:"username",
  444. class:"field",
  445. placeholder:"Anonymous",
  446. value:nameField.value
  447. }]
  448. ],
  449. ["div",
  450. ["input#options",{
  451. type:"text",
  452. name:"options",
  453. class:"field",
  454. placeholder:"Options",
  455. value:optionsField.value
  456. }]
  457. ],
  458. ["div",
  459. ["textarea#comment",{
  460. name:"text",
  461. class:"field",
  462. cols:48,
  463. rows:4,
  464. wrap:"soft",
  465. placeholder:"Comment",
  466. value:commentField.value
  467. }],
  468. ],
  469. ["div",
  470. ["span",{
  471. class:"greenSubmit",
  472. onclick:event=>{
  473. submitGreenPost(event,postForm.QR.form)
  474. }
  475. },"Post"]
  476. ]
  477. ]
  478. )
  479. addCommentForm(postForm.QR.comment)
  480. insertBefore(postForm.QR.form,qr)
  481. }
  482.  
  483. // 4chan-X QR
  484. function onQRXCreated(){
  485. getUpdateLinks()
  486. var qrPersona=query("#qr .persona")
  487. if(!qrPersona){
  488. return
  489. }
  490. var formSelector="#qr form:not(.greenPostForm)"
  491. var commentField=query(formSelector+" textarea")
  492. addCommentForm(commentField)
  493. var toggle=element(
  494. ["button#toggle",{
  495. type:"button",
  496. class:"greenToggle",
  497. title:"[s4s] Interface",
  498. onclick:event=>{
  499. event.preventDefault()
  500. event.stopPropagation()
  501. showPostFormQRX()
  502. }
  503. },"!"]
  504. ).toggle
  505. insertBefore(toggle,qrPersona.firstChild)
  506. }
  507.  
  508. function showPostFormQRX(hide){
  509. var formSelector="#qr form:not(.greenPostForm)"
  510. var nameField=query(formSelector+" input[name=name]")
  511. var optionsField=query(formSelector+" input[name=email]")
  512. var commentField=query(formSelector+" textarea")
  513. if(hide){
  514. if(postForm.QRX){
  515. nameField.value=postForm.QRX.name.value
  516. optionsField.value=postForm.QRX.options.value
  517. commentField.value=postForm.QRX.comment.value
  518. lastCommentForm=commentField
  519. removeChild(postForm.QRX.form)
  520. postForm.QRX=0
  521. }
  522. return
  523. }
  524. var qrx=query(formSelector)
  525. if(postForm.QRX||!qrx){
  526. return
  527. }
  528. postForm.QRX=element(
  529. ["form#form",{
  530. name:"post",
  531. action:serverurl+"post.php",
  532. method:"post",
  533. enctype:"multipart/form-data",
  534. class:"greenPostForm",
  535. onsubmit:submitGreenPost
  536. },
  537. ["input",{
  538. name:"thread",
  539. value:threadId,
  540. type:"hidden"
  541. }],
  542. ["div",{
  543. class:"persona"
  544. },
  545. ["button",{
  546. type:"button",
  547. class:"greenToggle pressed",
  548. title:"[s4s] Interface",
  549. onclick:event=>{
  550. showPostFormQRX(1)
  551. }
  552. },"!"],
  553. ["input#name",{
  554. name:"username",
  555. class:"field",
  556. placeholder:"Name",
  557. size:1,
  558. value:nameField.value
  559. }],
  560. ["input#options",{
  561. name:"options",
  562. class:"field",
  563. placeholder:"Options",
  564. size:1,
  565. value:optionsField.value
  566. }]
  567. ],
  568. ["textarea#comment",{
  569. name:"text",
  570. class:"field",
  571. placeholder:"Comment",
  572. value:commentField.value
  573. }],
  574. ["div",{
  575. class:"file-n-submit"
  576. },
  577. ["input",{
  578. type:"submit",
  579. value:"Submit"
  580. }]
  581. ]
  582. ]
  583. )
  584. addCommentForm(postForm.QRX.comment)
  585. insertBefore(postForm.QRX.form,qrx)
  586. }
  587.  
  588. // Track last used comment field for inserting quotes
  589. function addCommentForm(commentField,notLast){
  590. if(!notLast){
  591. lastCommentForm=commentField
  592. }
  593. commentField.addEventListener("focus",event=>{
  594. lastCommentForm=event.currentTarget
  595. })
  596. }
  597.  
  598. function insertQuote(event){
  599. var commentField=lastCommentForm
  600. if(commentField&&document.contains(commentField)){
  601. event.preventDefault()
  602. event.stopPropagation()
  603. var isQRX=commentField.closest("#qr")
  604. if(isQRX){
  605. isQRX.hidden=0
  606. }
  607. var text=">>"+event.currentTarget.firstChild.data+"\n"
  608. var caretPos=commentField.selectionStart
  609. commentField.value=
  610. commentField.value.slice(0,caretPos)
  611. +text
  612. +commentField.value.slice(commentField.selectionEnd)
  613. var range=caretPos+text.length
  614. commentField.setSelectionRange(range,range)
  615. commentField.focus()
  616. }
  617. }
  618.  
  619. // Manually update thread with green posts
  620. function getUpdateLinks(){
  621. var update=queryAll("[data-cmd=update],.updatelink>a")
  622. for(var i=0;i<update.length;i++){
  623. if(!updateLinks.has(update[i])){
  624. update[i].addEventListener("click",getGreenPosts)
  625. updateLinks.add(update[i])
  626. }
  627. }
  628. }
  629.  
  630. // Submit a green post
  631. function submitGreenPost(event,form){
  632. event.preventDefault()
  633. event.stopPropagation()
  634. if(!form){
  635. form=event.currentTarget
  636. }
  637. var submit={}
  638. submit.button=form.querySelector(":scope input[type=submit],:scope .greenSubmit")
  639. submit.fakeButton=submit.button.classList.contains("greenSubmit")
  640. if(submit.fakeButton){
  641. submit.text=submit.button.firstChild.data
  642. submit.button.firstChild.data="..."
  643. submit.button.classList.add("greenSubmitDisabled")
  644. }else{
  645. submit.text=submit.button.value
  646. submit.button.value="..."
  647. submit.button.disabled=1
  648. }
  649. var data=[]
  650. var formData=new FormData(form)
  651. for(var nameValue of formData){
  652. data.push(
  653. nameValue[0]+"="
  654. +encodeURIComponent(nameValue[1].replace(/\r?\n/g,"\r"))
  655. )
  656. }
  657. data=data.join("&")
  658. GM.xmlHttpRequest({
  659. method:"post",
  660. headers:{
  661. "Content-type":"application/x-www-form-urlencoded"
  662. },
  663. url:serverurl+"post.php",
  664. data:data,
  665. onload:response=>{
  666. if(response.status==200){
  667. if(/Post Successful/.test(response.responseText)){
  668. form.getElementsByTagName("textarea")[0].value=""
  669. getGreenPosts()
  670. }else{
  671. return postSubmitted(submit,response.status,response.responseText)
  672. }
  673. }
  674. postSubmitted(submit,response.status)
  675. },
  676. onerror:response=>{
  677. postSubmitted(submit)
  678. }
  679. })
  680. }
  681.  
  682. function postSubmitted(submit,errorCode,responseText){
  683. if(submit.fakeButton){
  684. submit.button.firstChild.data=submit.text
  685. submit.button.classList.remove("greenSubmitDisabled")
  686. }else{
  687. submit.button.value=submit.text
  688. submit.button.disabled=0
  689. }
  690. if(errorCode==200){
  691. if(responseText){
  692. alert("Could not submit post ("+responseText+")")
  693. }
  694. }else{
  695. var alertText="Could not connect to the [s4s] interface"
  696. if(errorCode){
  697. alertText+=" ("+errorCode+")"
  698. }
  699. alert(alertText)
  700. }
  701. }
  702.  
  703. // Stylesheet
  704. var stylesheet=`
  705. .greenPostForm+form .postForm>tbody>tr:not(.rules),
  706. #quickReply .greenPostForm+form,
  707. #qr .greenPostForm+form{
  708. display:none!important;
  709. }
  710. .greenPostForm .file-n-submit{
  711. display:flex;
  712. align-items:stretch;
  713. justify-content:flex-end;
  714. height:25px;
  715. margin-top:1px;
  716. }
  717. .greenPostForm .file-n-submit input{
  718. width:25%;
  719. background:linear-gradient(to bottom,#f8f8f8,#dcdcdc) no-repeat;
  720. border:1px solid #bbb;
  721. border-radius:2px;
  722. height:100%;
  723. }
  724. .greenPostContainer .post.reply{
  725. background-color:#dfd!important;
  726. border:2px solid #008000!important;
  727. }
  728. .greenPostContainer .postMessage{
  729. color:#000!important;
  730. }
  731. .greenToggle{
  732. font-family:monospace;
  733. font-size:16px;
  734. line-height:17px;
  735. background:#ceb!important;
  736. width:24px;
  737. padding:0;
  738. border:1px solid #bbb;
  739. }
  740. .greenPostForm input:not([type=submit]),
  741. .greenPostForm textarea{
  742. background-color:#dfd;
  743. color:#000;
  744. }
  745. .greenToggle.pressed{
  746. background:#6d6!important;
  747. font-weight:bold;
  748. color:#fff;
  749. }
  750. .postForm .greenToggle+input{
  751. width:220px!important;
  752. }
  753. .postForm .nameFieldParent,
  754. #quickReply .nameFieldParent{
  755. display:flex;
  756. flex-direction:row;
  757. }
  758. .postForm textarea{
  759. width:292px;
  760. }
  761. #quickReply .greenToggle{
  762. width:23px;
  763. height:23px;
  764. }
  765. #quickReply .greenToggle+input{
  766. width:273px!important;
  767. }
  768. .greenSubmit{
  769. display:inline-block;
  770. width:75px;
  771. float:right;
  772. padding:1px 6px;
  773. text-align:center;
  774. border:1px solid #adadad;
  775. background-color:#e1e1e1;
  776. box-sizing:border-box;
  777. user-select:none;
  778. font:400 13.3333px Arial,sans-serif;
  779. font:-moz-button;
  780. color:#000;
  781. cursor:default;
  782. }
  783. .greenSubmit:hover{
  784. border-color:#0078d7;
  785. background-color:#e5f1fb;
  786. }
  787. .greenSubmit:active{
  788. border-color:#005499;
  789. background-color:#cce4f7;
  790. }
  791. .greenSubmitDisabled{
  792. color:#808080;
  793. pointer-events:none;
  794. }
  795. @media only screen and (max-width:480px){
  796. .postForm .greenToggle+input{
  797. width:196px!important;
  798. }
  799. .postForm input[type="submit"]{
  800. width:60px;
  801. padding:2px 4px 3px;
  802. margin:0;
  803. }
  804. .postForm:not(.hideMobile){
  805. margin-top:20px;
  806. }
  807. }
  808. `.replace(/\n\s*/g,"")
  809. element(
  810. document.head||document.documentElement,
  811. ["style",{
  812. id:"s4sinterface-css"
  813. },stylesheet]
  814. )
  815.  
  816. function padding(string,num){
  817. return (""+string).padStart(num,0)
  818. }
  819.  
  820. function query(selector){
  821. return document.querySelector(selector)
  822. }
  823.  
  824. function queryAll(selector){
  825. return document.querySelectorAll(selector)
  826. }
  827.  
  828. function insertBefore(newElement,targetElement){
  829. return targetElement.parentNode.insertBefore(newElement,targetElement)
  830. }
  831.  
  832. function insertAfter(newElement,targetElement){
  833. var nextSibling=targetElement.nextSibling
  834. if(nextSibling){
  835. return insertBefore(newElement,nextSibling)
  836. }else{
  837. return targetElement.parentNode.appendChild(newElement)
  838. }
  839. }
  840.  
  841. function removeChild(targetElement){
  842. return targetElement.parentNode.removeChild(targetElement)
  843. }
  844.  
  845. function element(){
  846. var parent
  847. var lasttag
  848. var createdtag
  849. var toreturn={}
  850. for(var i=0;i<arguments.length;i++){
  851. var current=arguments[i]
  852. if(current){
  853. if(current.nodeType){
  854. parent=lasttag=current
  855. }else if(Array.isArray(current)){
  856. for(var j=0;j<current.length;j++){
  857. if(current[j]){
  858. if(!j&&typeof current[j]=="string"){
  859. var tagname=current[0].split("#")
  860. lasttag=createdtag=document.createElement(tagname[0])
  861. if(tagname[1]){
  862. toreturn[tagname[1]]=createdtag
  863. }
  864. }else if(current[j].constructor==Object){
  865. if(lasttag){
  866. for(var value in current[j]){
  867. if(value!="style"&&value in lasttag){
  868. lasttag[value]=current[j][value]
  869. }else{
  870. lasttag.setAttribute(value,current[j][value])
  871. }
  872. }
  873. }
  874. }else{
  875. var returned=element(lasttag,current[j])
  876. for(var k in returned){
  877. toreturn[k]=returned[k]
  878. }
  879. }
  880. }
  881. }
  882. }else if(current){
  883. createdtag=document.createTextNode(current)
  884. }
  885. if(parent&&createdtag){
  886. parent.appendChild(createdtag)
  887. }
  888. createdtag=0
  889. }
  890. }
  891. return toreturn
  892. }