The version of vichan running on lainchan.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

450 lines
13KB

  1. /*
  2. * quick-reply.js
  3. * https://github.com/savetheinternet/Tinyboard/blob/master/js/quick-reply.js
  4. *
  5. * Released under the MIT license
  6. * Copyright (c) 2013 Michael Save <savetheinternet@tinyboard.org>
  7. * Copyright (c) 2013-2014 Marcin Łabanowski <marcin@6irc.net>
  8. *
  9. * Usage:
  10. * $config['additional_javascript'][] = 'js/jquery.min.js';
  11. * $config['additional_javascript'][] = 'js/jquery-ui.custom.min.js'; // Optional; if you want the form to be draggable.
  12. * $config['additional_javascript'][] = 'js/quick-reply.js';
  13. *
  14. */
  15. (function() {
  16. var settings = new script_settings('quick-reply');
  17. var do_css = function() {
  18. $('#quick-reply-css').remove();
  19. // Find background of reply posts
  20. var dummy_reply = $('<div class="post reply"></div>').appendTo($('body'));
  21. var reply_background = dummy_reply.css('backgroundColor');
  22. var reply_border_style = dummy_reply.css('borderStyle');
  23. var reply_border_color = dummy_reply.css('borderColor');
  24. var reply_border_width = dummy_reply.css('borderWidth');
  25. dummy_reply.remove();
  26. $('<style type="text/css" id="quick-reply-css">\
  27. #quick-reply {\
  28. position: fixed;\
  29. right: 5%;\
  30. top: 5%;\
  31. float: right;\
  32. display: block;\
  33. padding: 0 0 0 0;\
  34. width: 300px;\
  35. }\
  36. #quick-reply table {\
  37. border-collapse: collapse;\
  38. background: ' + reply_background + ';\
  39. border-style: ' + reply_border_style + ';\
  40. border-width: ' + reply_border_width + ';\
  41. border-color: ' + reply_border_color + ';\
  42. margin: 0;\
  43. width: 100%;\
  44. }\
  45. #quick-reply tr td:nth-child(2) {\
  46. white-space: nowrap;\
  47. text-align: right;\
  48. padding-right: 4px;\
  49. }\
  50. #quick-reply tr td:nth-child(2) input[type="submit"] {\
  51. width: 100%;\
  52. }\
  53. #quick-reply th, #quick-reply td {\
  54. margin: 0;\
  55. padding: 0;\
  56. }\
  57. #quick-reply th {\
  58. text-align: center;\
  59. padding: 2px 0;\
  60. border: 1px solid #222;\
  61. }\
  62. #quick-reply th .handle {\
  63. float: left;\
  64. width: 100%;\
  65. display: inline-block;\
  66. }\
  67. #quick-reply th .close-btn {\
  68. float: right;\
  69. padding: 0 5px;\
  70. }\
  71. #quick-reply input[type="text"], #quick-reply select {\
  72. width: 100%;\
  73. padding: 2px;\
  74. font-size: 10pt;\
  75. box-sizing: border-box;\
  76. -webkit-box-sizing:border-box;\
  77. -moz-box-sizing: border-box;\
  78. }\
  79. #quick-reply textarea {\
  80. width: 100%;\
  81. box-sizing: border-box;\
  82. -webkit-box-sizing:border-box;\
  83. -moz-box-sizing: border-box;\
  84. font-size: 10pt;\
  85. resize: vertical;\
  86. }\
  87. #quick-reply input, #quick-reply select, #quick-reply textarea {\
  88. margin: 0 0 1px 0;\
  89. }\
  90. #quick-reply input[type="file"] {\
  91. padding: 5px 2px;\
  92. }\
  93. #quick-reply .nonsense {\
  94. display: none;\
  95. }\
  96. #quick-reply td.submit {\
  97. width: 1%;\
  98. }\
  99. #quick-reply td.recaptcha {\
  100. text-align: center;\
  101. padding: 0 0 1px 0;\
  102. }\
  103. #quick-reply td.recaptcha span {\
  104. display: inline-block;\
  105. width: 100%;\
  106. background: white;\
  107. border: 1px solid #ccc;\
  108. cursor: pointer;\
  109. }\
  110. #quick-reply td.recaptcha-response {\
  111. padding: 0 0 1px 0;\
  112. }\
  113. @media screen and (max-width: 800px) {\
  114. #quick-reply {\
  115. display: none !important;\
  116. }\
  117. }\
  118. </style>').appendTo($('head'));
  119. };
  120. var show_quick_reply = function(){
  121. if($('div.banner').length == 0)
  122. return;
  123. if($('#quick-reply').length != 0)
  124. return;
  125. do_css();
  126. var $postForm = $('form[name="post"]').clone();
  127. $postForm.clone();
  128. $dummyStuff = $('<div class="nonsense"></div>').appendTo($postForm);
  129. $postForm.find('table tr').each(function() {
  130. var $th = $(this).children('th:first');
  131. var $td = $(this).children('td:first');
  132. if ($th.length && $td.length) {
  133. $td.attr('colspan', 2);
  134. if ($td.find('input[type="text"]').length) {
  135. // Replace <th> with input placeholders
  136. $td.find('input[type="text"]')
  137. .removeAttr('size')
  138. .attr('placeholder', $th.clone().children().remove().end().text());
  139. }
  140. // Move anti-spam nonsense and remove <th>
  141. $th.contents().filter(function() {
  142. return this.nodeType == 3; // Node.TEXT_NODE
  143. }).remove();
  144. $th.contents().appendTo($dummyStuff);
  145. $th.remove();
  146. if ($td.find('input[name="password"]').length) {
  147. // Hide password field
  148. $(this).hide();
  149. }
  150. // Fix submit button
  151. if ($td.find('input[type="submit"]').length) {
  152. $td.removeAttr('colspan');
  153. $('<td class="submit"></td>').append($td.find('input[type="submit"]')).insertAfter($td);
  154. }
  155. // reCAPTCHA
  156. if ($td.find('#recaptcha_widget_div').length) {
  157. // Just show the image, and have it interact with the real form.
  158. var $captchaimg = $td.find('#recaptcha_image img');
  159. $captchaimg
  160. .removeAttr('id')
  161. .removeAttr('style')
  162. .addClass('recaptcha_image')
  163. .click(function() {
  164. $('#recaptcha_reload').click();
  165. });
  166. // When we get a new captcha...
  167. $('#recaptcha_response_field').focus(function() {
  168. if ($captchaimg.attr('src') != $('#recaptcha_image img').attr('src')) {
  169. $captchaimg.attr('src', $('#recaptcha_image img').attr('src'));
  170. $postForm.find('input[name="recaptcha_challenge_field"]').val($('#recaptcha_challenge_field').val());
  171. $postForm.find('input[name="recaptcha_response_field"]').val('').focus();
  172. }
  173. });
  174. $postForm.submit(function() {
  175. setTimeout(function() {
  176. $('#recaptcha_reload').click();
  177. }, 200);
  178. });
  179. // Make a new row for the response text
  180. var $newRow = $('<tr><td class="recaptcha-response" colspan="2"></td></tr>');
  181. $newRow.children().first().append(
  182. $td.find('input').removeAttr('style')
  183. );
  184. $newRow.find('#recaptcha_response_field')
  185. .removeAttr('id')
  186. .addClass('recaptcha_response_field')
  187. .attr('placeholder', $('#recaptcha_response_field').attr('placeholder'));
  188. $('#recaptcha_response_field').addClass('recaptcha_response_field')
  189. $td.replaceWith($('<td class="recaptcha" colspan="2"></td>').append($('<span></span>').append($captchaimg)));
  190. $newRow.insertAfter(this);
  191. }
  192. // Upload section
  193. if ($td.find('input[type="file"]').length) {
  194. if ($td.find('input[name="file_url"]').length) {
  195. $file_url = $td.find('input[name="file_url"]');
  196. if (settings.get('show_remote', false)) {
  197. // Make a new row for it
  198. var $newRow = $('<tr><td colspan="2"></td></tr>');
  199. $file_url.clone().attr('placeholder', _('Upload URL')).appendTo($newRow.find('td'));
  200. $newRow.insertBefore(this);
  201. }
  202. $file_url.parent().remove();
  203. $td.find('label').remove();
  204. $td.contents().filter(function() {
  205. return this.nodeType == 3; // Node.TEXT_NODE
  206. }).remove();
  207. $td.find('input[name="file_url"]').removeAttr('id');
  208. }
  209. if ($(this).find('input[name="spoiler"]').length) {
  210. $td.removeAttr('colspan');
  211. }
  212. }
  213. // Disable embedding if configured so
  214. if (!settings.get('show_embed', false) && $td.find('input[name="embed"]').length) {
  215. $(this).remove();
  216. }
  217. // Remove oekaki if existent
  218. if ($(this).is('#oekaki')) {
  219. $(this).remove();
  220. }
  221. // Remove upload selection
  222. if ($td.is('#upload_selection')) {
  223. $(this).remove();
  224. }
  225. // Remove mod controls, because it looks shit.
  226. if ($td.find('input[type="checkbox"]').length) {
  227. var tr = this;
  228. $td.find('input[type="checkbox"]').each(function() {
  229. if ($(this).attr('name') == 'spoiler') {
  230. $td.find('label').remove();
  231. $(this).attr('id', 'q-spoiler-image');
  232. $postForm.find('input[type="file"]').parent()
  233. .removeAttr('colspan')
  234. .after($('<td class="spoiler"></td>').append(this, ' ', $('<label for="q-spoiler-image">').text(_('Spoiler Image'))));
  235. } else {
  236. $(tr).remove();
  237. }
  238. });
  239. }
  240. $td.find('small').hide();
  241. }
  242. });
  243. $postForm.find('textarea[name="body"]').removeAttr('id').removeAttr('cols').attr('placeholder', _('Comment'));
  244. $postForm.find('textarea:not([name="body"]),input[type="hidden"]').removeAttr('id').appendTo($dummyStuff);
  245. $postForm.find('br').remove();
  246. $postForm.find('table').prepend('<tr><th colspan="2">\
  247. <span class="handle">\
  248. <a class="close-btn" href="javascript:void(0)">X</a>\
  249. ' + _('Quick Reply') + '\
  250. </span>\
  251. </th></tr>');
  252. $postForm.attr('id', 'quick-reply');
  253. $postForm.appendTo($('body')).hide();
  254. $origPostForm = $('form[name="post"]:first');
  255. // Synchronise body text with original post form
  256. $origPostForm.find('textarea[name="body"]').on('change input propertychange', function() {
  257. $postForm.find('textarea[name="body"]').val($(this).val());
  258. });
  259. $postForm.find('textarea[name="body"]').on('change input propertychange', function() {
  260. $origPostForm.find('textarea[name="body"]').val($(this).val());
  261. });
  262. $postForm.find('textarea[name="body"]').focus(function() {
  263. $origPostForm.find('textarea[name="body"]').removeAttr('id');
  264. $(this).attr('id', 'body');
  265. });
  266. $origPostForm.find('textarea[name="body"]').focus(function() {
  267. $postForm.find('textarea[name="body"]').removeAttr('id');
  268. $(this).attr('id', 'body');
  269. });
  270. // Synchronise other inputs
  271. $origPostForm.find('input[type="text"],select').on('change input propertychange', function() {
  272. $postForm.find('[name="' + $(this).attr('name') + '"]').val($(this).val());
  273. });
  274. $postForm.find('input[type="text"],select').on('change input propertychange', function() {
  275. $origPostForm.find('[name="' + $(this).attr('name') + '"]').val($(this).val());
  276. });
  277. if (typeof $postForm.draggable != 'undefined') {
  278. if (localStorage.quickReplyPosition) {
  279. var offset = JSON.parse(localStorage.quickReplyPosition);
  280. if (offset.top < 0)
  281. offset.top = 0;
  282. if (offset.right > $(window).width() - $postForm.width())
  283. offset.right = $(window).width() - $postForm.width();
  284. if (offset.top > $(window).height() - $postForm.height())
  285. offset.top = $(window).height() - $postForm.height();
  286. $postForm.css('right', offset.right).css('top', offset.top);
  287. }
  288. $postForm.draggable({
  289. handle: 'th .handle',
  290. containment: 'window',
  291. distance: 10,
  292. scroll: false,
  293. stop: function() {
  294. var offset = {
  295. top: $(this).offset().top - $(window).scrollTop(),
  296. right: $(window).width() - $(this).offset().left - $(this).width(),
  297. };
  298. localStorage.quickReplyPosition = JSON.stringify(offset);
  299. $postForm.css('right', offset.right).css('top', offset.top).css('left', 'auto');
  300. }
  301. });
  302. $postForm.find('th .handle').css('cursor', 'move');
  303. }
  304. $postForm.find('th .close-btn').click(function() {
  305. $origPostForm.find('textarea[name="body"]').attr('id', 'body');
  306. $postForm.remove();
  307. floating_link();
  308. });
  309. // Fix bug when table gets too big for form. Shouldn't exist, but crappy CSS etc.
  310. $postForm.show();
  311. $postForm.width($postForm.find('table').width());
  312. $postForm.hide();
  313. $(window).trigger('quick-reply');
  314. $(window).ready(function() {
  315. if (settings.get('hide_at_top', true)) {
  316. $(window).scroll(function() {
  317. if ($(this).width() <= 800)
  318. return;
  319. if ($(this).scrollTop() < $origPostForm.offset().top + $origPostForm.height() - 100)
  320. $postForm.fadeOut(100);
  321. else
  322. $postForm.fadeIn(100);
  323. }).scroll();
  324. } else {
  325. $postForm.show();
  326. }
  327. $(window).on('stylesheet', function() {
  328. do_css();
  329. if ($('link#stylesheet').attr('href')) {
  330. $('link#stylesheet')[0].onload = do_css;
  331. }
  332. });
  333. });
  334. };
  335. $(window).on('cite', function(e, id, with_link) {
  336. if ($(this).width() <= 800)
  337. return;
  338. show_quick_reply();
  339. if (with_link) {
  340. $(document).ready(function() {
  341. if ($('#' + id).length) {
  342. highlightReply(id);
  343. $(document).scrollTop($('#' + id).offset().top);
  344. }
  345. // Honestly, I'm not sure why we need setTimeout() here, but it seems to work.
  346. // Same for the "tmp" variable stuff you see inside here:
  347. setTimeout(function() {
  348. var tmp = $('#quick-reply textarea[name="body"]').val();
  349. $('#quick-reply textarea[name="body"]').val('').focus().val(tmp);
  350. }, 1);
  351. });
  352. }
  353. });
  354. var floating_link = function() {
  355. if (!settings.get('floating_link', false))
  356. return;
  357. $('<a href="javascript:void(0)" class="quick-reply-btn">'+_('Quick Reply')+'</a>')
  358. .click(function() {
  359. show_quick_reply();
  360. $(this).remove();
  361. }).appendTo($('body'));
  362. $(window).on('quick-reply', function() {
  363. $('.quick-reply-btn').remove();
  364. });
  365. };
  366. if (settings.get('floating_link', false)) {
  367. $(window).ready(function() {
  368. if($('div.banner').length == 0)
  369. return;
  370. $('<style type="text/css">\
  371. a.quick-reply-btn {\
  372. position: fixed;\
  373. right: 0;\
  374. bottom: 0;\
  375. display: block;\
  376. padding: 5px 13px;\
  377. text-decoration: none;\
  378. }\
  379. </style>').appendTo($('head'));
  380. floating_link();
  381. if (settings.get('hide_at_top', true)) {
  382. $('.quick-reply-btn').hide();
  383. $(window).scroll(function() {
  384. if ($(this).width() <= 800)
  385. return;
  386. if ($(this).scrollTop() < $('form[name="post"]:first').offset().top + $('form[name="post"]:first').height() - 100)
  387. $('.quick-reply-btn').fadeOut(100);
  388. else
  389. $('.quick-reply-btn').fadeIn(100);
  390. }).scroll();
  391. }
  392. });
  393. }
  394. })();