// Helpers specific to quickreply aka quicker-reply and lessquick-reply aka The Critters // See also jquery.talkform.js, jquery.replyforms.js (function($) { var _ok; var customsubject; var previous; var firstCommentWidgetParent; function can_continue() { if ( _ok == undefined ) _ok = ($("#parenttalkid,#replyto,#dtid,#qrdiv,#qrformdiv,#qrform,#subject").length == 7); return _ok; } function update(data,widget) { // There's three target patterns: // - "entry-dw_dev-215060-reply" (lastn page, reply to entry) // - "topcomment", "bottomcomment" (entry page, reply to entry) // - "1236487" (entry page, reply to comment) var targetParts = data.target.split("-"); if ( targetParts.length === 1 ) { data.dtid = data.target; } else { data.dtid = 0; $("#journal").val(targetParts[1]); $("#itemid").val(targetParts[2]); $("#basepath").val(document.location.protocol + "//" + targetParts[1].replace("_", "-") + "." + Site.user_domain + "/" + targetParts[2] + ".html?"); data.stayOnPage = true; } $("#qrform").data("stayOnPage", data.stayOnPage); $("#parenttalkid, #replyto").val(data.pid); $("#dtid").val(data.dtid); var old_subject; if ( previous ) { old_subject = previous.subject; previous.widget.hide(); } var subject = $("#subject"); var cur_subject = subject.val(); if ( old_subject != undefined && cur_subject != old_subject ) customsubject = true; if ( ! customsubject || cur_subject == "" ) subject.val(data.subject); $("#prop_picture_keyword").change(); // If we previously munged the layout of #qrformdiv, reset it. $('#qrformdiv').removeAttr('style').removeClass('width-adjusted'); // If replying to a comment, sort out the width. If the reply form would be // super-small but there's plenty of whitespace to the left due to comment // indentation, extend the form out to the left. if ( data.target.match(/^\d+$/) ) { if ( ! firstCommentWidgetParent ) { // Parent element of the ljqrtNNNNN divs is different from style to // style, so we can't hardcode it. firstCommentWidgetParent = $('.comment').first().find('[data-quickreply-container]').parent(); } // .width() always gives content width, which is what we want here. var maxAvailableCommentWidth = firstCommentWidgetParent.width(); var plannedWidth = widget.parent().width(); // 750px seems a reasonable size on desktop. If we're mobile or // otherwise too small for that, just max out what we've got. var minWidth = Math.min( 750, maxAvailableCommentWidth ); if ( plannedWidth < minWidth ) { // Ascend and grab the first non-transparent background color we // see, so the form fields aren't just dangling out in space var backgroundColor; // not guessing every browser's exact stringification of computed transparent var rootBackgroundColor = $(':root').css('background-color'); widget.parentsUntil('.comment-thread').each(function(i, element) { var bg = $(element).css('background-color'); if ( bg !== rootBackgroundColor ) { backgroundColor = bg; return false; // exit .each() early } }); // #qrdiv is the sacrificial inline-display wrapper. #qrformdiv is // the block-display workhorse behind it. $('#qrformdiv').css({ 'min-width': minWidth, 'position': 'relative', 'right': minWidth - plannedWidth + 'px', 'background-color': backgroundColor }).addClass('width-adjusted'); } } // display: inline is to keep the whole container from getting kicked sideways by a float. $("#qrdiv").show().css("display", "inline").appendTo(widget); widget.show(); $("#body").focus(); previous = { subject: data.subject, widget: widget }; }; $.widget("dw.quickreply", { options: { target: undefined, stayOnPage: false, dtid: undefined, pid: undefined, subject: undefined }, _create: function() { var self = this; self.element.click(function(e){ if ( ! can_continue() ) return; e.stopPropagation(); e.preventDefault(); update(self.options, self.widget()) }).click(); $(".qr-icon").find("img") .attr("src", $(this).find("option:selected").data("url")) .removeAttr("width").removeAttr("height"); }, widget: function() { return this.options.target ? $("#ljqrt"+this.options.target) : []; } }); $.extend( $.dw.quickreply, { can_continue: function() { return _ok; } } ); })(jQuery); jQuery(function($) { $("#qrform").submit(function(e) { var $form = $(this); if ($form.data("stayOnPage")) { e.preventDefault(); e.stopPropagation(); $("#submitpost").ajaxtip() // init .ajaxtip( "load", { endpoint: "addcomment", ajax: { type: "POST", data: $form.serialize(), success: function( data, status, jqxhr ) { if ( data.error ) { if ( data.error === "Client error: Message looks like spam" ) { // It just wants a captcha; disable ajax and take the long way around. $form.data("stayOnPage", false).submit(); } else { // Go bother grandma cuz mom don't care. $("#submitpost").ajaxtip( "error", data.error ); } } else { var $container = $("#qrdiv").parent(); var $readLink = $("[data-quickreply-target='" + $container.data("quickreply-container") + "'] .entry-readlink a"); $container .slideUp(function() { // reset form $("#subject").val(""); $("#body").val(""); var $iconSelect = $("#prop_picture_keyword"); if ( $iconSelect.length > 0 ) { $iconSelect.get(0).selectedIndex = 0; $iconSelect.trigger("change"); } // for the 0 -> 1 case, when the link starts out hidden $readLink.parent().show(); $readLink.ajaxtip(); // init if (data.extra) { // success message plus extra actions $readLink.ajaxtip("sticky", data.message + ' ' + data.extra); } else { // plain success message $readLink.ajaxtip("success", data.message); } var commentText = ''; if ( data.count == 1 ) { commentText = $readLink.data('sing'); } else if ( data.count == 2 ) { commentText = $readLink.data('dual'); } else { commentText = $readLink.data('plur').replace(/\d+/, data.count); } $readLink.text(commentText); // replace count }); } } } }); } else { // prevent double-submits $form.find('input[type="submit"]').prop("disabled", true); var dtid = $("#dtid"); if ( ! Number(dtid.val()) ) dtid.val("0"); // ...and then carry on. } }); $("#submitpview").on("click", function(e){ $("#qrform").data("stayOnPage", false); // Why use a hidden input for the real "submitpreview" instead of just // relying on the button? Because we disable the submit buttons to guard // against double-submits, and that causes the preview button's value to // appear as falsy when the form data arrives back in perl-land, which // in turn causes surprise posts. $("#qrform input[name='submitpreview']").val(1); $("#qrform").attr("action", Site.siteroot + "/talkpost_do" ); }); $("#submitpost").on("click", function(e){ $("#qrform").attr("action", Site.siteroot + "/talkpost_do" ); }); $("#submitmoreopts").on("click", function(e) { e.preventDefault(); e.stopPropagation(); var qrform = $("#qrform"); var replyto = Number($("#dtid").val()); var pid = Number($("#parenttalkid").val()); var basepath = $("#basepath").val(); var isSameDomain = (new URL(basepath)).hostname === document.location.hostname; if(replyto > 0 && pid > 0) { qrform.attr("action", basepath + "replyto=" + replyto ); } else { qrform.attr("action", basepath + "mode=reply" ); } qrform.data("stayOnPage", false); if ( fetch && !isSameDomain ) { // Do a preflight request to ensure we have a domain session cookie // on the other journal, so we don't lose comment text for replies // from the reading page. // get_domain_session wants a "return" URL. Doesn't matter what it // is, as long as it's 1. on the target journal domain and 2. fast. var returnTo = new URL(basepath); returnTo.pathname = '/robots.txt'; returnTo.search = ''; fetch(Site.siteroot + '/misc/get_domain_session?return=' + encodeURIComponent(returnTo.href), {mode: 'no-cors', credentials: 'include'} ).then(function() { qrform.submit(); }); } else { qrform.submit(); } }); }); function quickreply(target, pid, newsubject, trigger) { trigger = trigger || document; $(trigger).quickreply({ target: target, pid: pid, subject: newsubject }) .attr("onclick", null); return ! $.dw.quickreply.can_continue(); }