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.

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