X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=js%2Futil.js;h=966df4d93fbe085efcd47323e585b12dfacb269f;hb=bb087a965009fd93a5c02a9e10ab90adcc6b7963;hp=47b8bbe6caaef32bbdc2ab4dc297224a05defcd8;hpb=cd20190ba5568c58be8effb0d6431650df69d686;p=quix0rs-gnu-social.git diff --git a/js/util.js b/js/util.js index 47b8bbe6ca..966df4d93f 100644 --- a/js/util.js +++ b/js/util.js @@ -31,7 +31,8 @@ var SN = { // StatusNet CounterBlackout: false, MaxLength: 140, PatternUsername: /^[0-9a-zA-Z\-_.]*$/, - HTTP20x30x: [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307] + HTTP20x30x: [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307], + NoticeFormMaster: null // to be cloned from the one at top }, /** @@ -50,12 +51,6 @@ var SN = { // StatusNet Processing: 'processing', CommandResult: 'command_result', FormNotice: 'form_notice', - NoticeInReplyTo: 'notice_in-reply-to', - NoticeLat: 'notice_data-lat', - NoticeLon: 'notice_data-lon', - NoticeLocationId: 'notice_data-location_id', - NoticeLocationNs: 'notice_data-location_ns', - NoticeGeoName: 'notice_data-geo_name', NoticeDataGeo: 'notice_data-geo', NoticeDataGeoCookie: 'NoticeDataGeo', NoticeDataGeoSelected: 'notice_data-geo_selected', @@ -109,7 +104,7 @@ var SN = { // StatusNet SN.U.Counter(form); - NDT = form.find('[name=status_textarea]'); + NDT = form.find('.notice_data-text:first'); NDT.bind('keyup', function(e) { SN.U.Counter(form); @@ -188,7 +183,7 @@ var SN = { // StatusNet * @return number of chars */ CharacterCount: function(form) { - return form.find('[name=status_textarea]').val().length; + return form.find('.notice_data-text:first').val().length; }, /** @@ -233,6 +228,9 @@ var SN = { // StatusNet * will be extracted and copied in, replacing the original form. * If there's no form, the first paragraph will be used. * + * This will automatically be applied on the 'submit' event for + * any form with the 'ajax' class. + * * @fixme can sometimes explode confusingly if returnd data is bogus * @fixme error handling is pretty vague * @fixme can't submit file uploads @@ -329,7 +327,7 @@ var SN = { // StatusNet dataType: 'xml', timeout: '60000', beforeSend: function(formData) { - if (form.find('[name=status_textarea]').val() == '') { + if (form.find('.notice_data-text:first').val() == '') { form.addClass(SN.C.S.Warning); return false; } @@ -393,9 +391,24 @@ var SN = { // StatusNet // New notice post was successful. If on our timeline, show it! var notice = document._importNode($('li', data)[0], true); var notices = $('#notices_primary .notices:first'); - if (notices.length > 0 && SN.U.belongsOnTimeline(notice)) { + var replyItem = form.closest('li.notice-reply'); + + if (replyItem.length > 0) { + // If this is an inline reply, insert it in place. + var id = $(notice).attr('id'); + if ($("#"+id).length == 0) { + var parentNotice = replyItem.closest('li.notice'); + replyItem.replaceWith(notice); + SN.U.NoticeInlineReplyPlaceholder(parentNotice); + } else { + // Realtime came through before us... + replyItem.remove(); + } + } else if (notices.length > 0 && SN.U.belongsOnTimeline(notice)) { + // Not a reply. If on our timeline, show it at the top! + if ($('#'+notice.id).length === 0) { - var notice_irt_value = $('#'+SN.C.S.NoticeInReplyTo).val(); + var notice_irt_value = form.find('[name=inreplyto]').val(); var notice_irt = '#notices_primary #notice-'+notice_irt_value; if($('body')[0].id == 'conversation') { if(notice_irt_value.length > 0 && $(notice_irt+' .notices').length < 1) { @@ -410,12 +423,12 @@ var SN = { // StatusNet .css({display:'none'}) .fadeIn(2500); SN.U.NoticeWithAttachment($('#'+notice.id)); - SN.U.NoticeReplyTo($('#'+notice.id)); + SN.U.switchInputFormTab("placeholder"); } - } - else { + } else { // Not on a timeline that this belongs on? // Just show a success message. + // @fixme inline showFeedback('success', $('title', data).text()); } } @@ -436,7 +449,7 @@ var SN = { // StatusNet form.find('[name=lon]').val(SN.C.I.NoticeDataGeo.NLon); form.find('[name=location_ns]').val(SN.C.I.NoticeDataGeo.NLNS); form.find('[name=location_id]').val(SN.C.I.NoticeDataGeo.NLID); - $('#'+SN.C.S.NoticeDataGeo).attr('checked', SN.C.I.NoticeDataGeo.NDG); + form.find('[name=notice_data-geo]').attr('checked', SN.C.I.NoticeDataGeo.NDG); } }); }, @@ -446,7 +459,7 @@ var SN = { // StatusNet SN.C.I.NoticeDataGeo.NLon = form.find('[name=lon]').val(); SN.C.I.NoticeDataGeo.NLNS = form.find('[name=location_ns]').val(); SN.C.I.NoticeDataGeo.NLID = form.find('[name=location_id]').val(); - SN.C.I.NoticeDataGeo.NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked'); // @fixme + SN.C.I.NoticeDataGeo.NDG = form.find('[name=notice_data-geo]').attr('checked'); // @fixme var cookieValue = $.cookie(SN.C.S.NoticeDataGeoCookie); @@ -463,10 +476,10 @@ var SN = { // StatusNet } } if (cookieValue == 'disabled') { - SN.C.I.NoticeDataGeo.NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', false).attr('checked'); + SN.C.I.NoticeDataGeo.NDG = form.find('[name=notice_data-geo]').attr('checked', false).attr('checked'); } else { - SN.C.I.NoticeDataGeo.NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', true).attr('checked'); + SN.C.I.NoticeDataGeo.NDG = form.find('[name=notice_data-geo]').attr('checked', true).attr('checked'); } }, @@ -502,32 +515,20 @@ var SN = { // StatusNet * @access private */ NoticeReply: function() { - if ($('#content .notice_reply').length > 0) { - $('#content .notice').each(function() { SN.U.NoticeReplyTo($(this)); }); - } + $('#content .notice_reply').live('click', function(e) { + e.preventDefault(); + var notice = $(this).closest('li.notice'); + var nickname = ($('.author .nickname', notice).length > 0) ? $($('.author .nickname', notice)[0]) : $('.author .nickname.uid'); + SN.U.NoticeInlineReplyTrigger(notice, '@' + nickname.text()); + return false; + }); }, /** - * Setup function -- DOES NOT trigger actions immediately. - * - * Sets up event handlers on the given notice's reply button to - * tweak the new-notice form with needed variables and focus it - * when pushed. - * - * (This replaces the default reply button behavior to submit - * directly to a form which comes back with a specialized page - * with the form data prefilled.) - * - * @param {jQuery} notice: jQuery object containing one or more notices + * Stub -- kept for compat with plugins for now. * @access private */ NoticeReplyTo: function(notice) { - notice.find('.notice_reply').live('click', function(e) { - e.preventDefault(); - var nickname = ($('.author .nickname', notice).length > 0) ? $($('.author .nickname', notice)[0]) : $('.author .nickname.uid'); - SN.U.NoticeInlineReplyTrigger(notice, '@' + nickname.text()); - return false; - }); }, /** @@ -559,116 +560,65 @@ var SN = { // StatusNet // See if the form's already open... var replyForm = $('.notice-reply-form', list); - if (replyForm.length == 0) { + + var nextStep = function() { + // Override...? + replyForm.find('input[name=inreplyto]').val(id); + + // Set focus... + var text = replyForm.find('textarea'); + if (text.length == 0) { + throw "No textarea"; + } + var replyto = ''; + if (initialText) { + replyto = initialText + ' '; + } + text.val(replyto + text.val().replace(RegExp(replyto, 'i'), '')); + text.data('initialText', $.trim(initialText + '')); + text.focus(); + if (text[0].setSelectionRange) { + var len = text.val().length; + text[0].setSelectionRange(len,len); + } + }; + if (replyForm.length > 0) { + // Update the existing form... + nextStep(); + } else { // Remove placeholder if any - $('li.notice-reply-placeholder').remove(); + list.find('li.notice-reply-placeholder').remove(); // Create the reply form entry at the end var replyItem = $('li.notice-reply', list); if (replyItem.length == 0) { - replyItem = $('
  • ' + - '
    ' + - '' + - '
    ' + - '' + - '' + - '' + - '
    ' + - '
    ' + - '
  • '); - - var baseForm = $('#form_notice'); - replyForm = replyItem.find('form'); - replyForm.attr('action', baseForm.attr('action')); - replyForm.find('input[name="token"]').val(baseForm.find('input[name=token]').val()); - replyForm.find('input[type="submit"]').val(SN.msg('reply_submit')); - list.append(replyItem); - - replyForm.find('textarea').blur(function() { - var textarea = $(this); - var txt = $.trim(textarea.val()); - if (txt == '' || txt == textarea.data('initialText')) { - // Nothing to say? Begone! - replyItem.remove(); - if (list.find('li').length > 0) { - SN.U.NoticeInlineReplyPlaceholder(parentNotice); - } else { - list.remove(); - } - } - }); - replyForm.submit(function(event) { - var form = replyForm; - $.ajax({ - type: 'POST', - dataType: 'xml', - url: SN.U.RewriteAjaxAction(form.attr('action')), - data: form.serialize() + '&ajax=1', - beforeSend: function(xhr) { - form - .addClass(SN.C.S.Processing) - .find('.submit') - .addClass(SN.C.S.Disabled) - .attr(SN.C.S.Disabled, SN.C.S.Disabled) - .end() - .find('textarea') - .addClass(SN.C.S.Disabled) - .attr(SN.C.S.Disabled, SN.C.S.Disabled); - }, - error: function (xhr, textStatus, errorThrown) { - alert(errorThrown || textStatus); - }, - success: function(data, textStatus) { - var orig_li = $('li', data)[0]; - if (orig_li) { - var li = document._importNode(orig_li, true); - var id = $(li).attr('id'); - if ($("#"+id).length == 0) { - replyItem.replaceWith(li); - SN.U.NoticeInlineReplyPlaceholder(parentNotice); - } else { - // Realtime came through before us... - replyItem.remove(); - } - } - } - }); - event.preventDefault(); - return false; - }); - } - } + replyItem = $('
  • '); - // Override...? - replyForm.find('input[name=inreplyto]').val(id); + var intermediateStep = function(formMaster) { + var formEl = document._importNode(formMaster, true); + replyItem.append(formEl); + list.append(replyItem); - // Set focus... - var text = replyForm.find('textarea'); - if (text.length == 0) { - throw "No textarea"; - } - var replyto = ''; - if (initialText) { - replyto = initialText + ' '; - } - text.val(replyto + text.val().replace(RegExp(replyto, 'i'), '')); - text.data('initialText', $.trim(initialText + '')); - text.focus(); - if (text[0].setSelectionRange) { - var len = text.val().length; - text[0].setSelectionRange(len,len); - } - }, + var form = replyForm = $(formEl); + SN.Init.NoticeFormSetup(form); - /** - * Setup function -- DOES NOT apply immediately. - * - * Sets up event handlers for favor/disfavor forms to submit via XHR. - * Uses 'live' rather than 'bind', so applies to future as well as present items. - */ - NoticeFavor: function() { - $('.form_favor').live('click', function() { SN.U.FormXHR($(this)); return false; }); - $('.form_disfavor').live('click', function() { SN.U.FormXHR($(this)); return false; }); + nextStep(); + }; + if (SN.C.I.NoticeFormMaster) { + // We've already saved a master copy of the form. + // Clone it in! + intermediateStep(SN.C.I.NoticeFormMaster); + } else { + // Fetch a fresh copy of the notice form over AJAX. + // Warning: this can have a delay, which looks bad. + // @fixme this fallback may or may not work + var url = $('#form_notice').attr('action'); + $.get(url, {ajax: 1}, function(data, textStatus, xhr) { + intermediateStep($('form', data)[0]); + }); + } + } + } }, NoticeInlineReplyPlaceholder: function(notice) { @@ -676,25 +626,24 @@ var SN = { // StatusNet var placeholder = $('
  • ' + '' + '
  • '); - placeholder.click(function() { - SN.U.NoticeInlineReplyTrigger(notice); - }); - placeholder.find('input').val(SN.msg('reply_placeholder')); + placeholder.find('input') + .val(SN.msg('reply_placeholder')); list.append(placeholder); }, /** * Setup function -- DOES NOT apply immediately. * - * Sets up event handlers for favor/disfavor forms to submit via XHR. + * Sets up event handlers for inline reply mini-form placeholders. * Uses 'live' rather than 'bind', so applies to future as well as present items. */ NoticeInlineReplySetup: function() { - $('.threaded-replies').each(function() { - var list = $(this); - var notice = list.closest('.notice'); - SN.U.NoticeInlineReplyPlaceholder(notice); - }); + $('li.notice-reply-placeholder input') + .live('focus', function() { + var notice = $(this).closest('li.notice'); + SN.U.NoticeInlineReplyTrigger(notice); + return false; + }); }, /** @@ -959,53 +908,57 @@ var SN = { // StatusNet * new-notice form. Seems to set up some event handlers for * triggering lookups and using the new values. * + * @param {jQuery} form + * * @fixme tl;dr * @fixme there's not good visual state update here, so users have a * hard time figuring out if it's working or fixing if it's wrong. * */ - NoticeLocationAttach: function() { + NoticeLocationAttach: function(form) { // @fixme this should not be tied to the main notice form, as there may be multiple notice forms... - var NLat = $('#'+SN.C.S.NoticeLat).val(); - var NLon = $('#'+SN.C.S.NoticeLon).val(); - var NLNS = $('#'+SN.C.S.NoticeLocationNs).val(); - var NLID = $('#'+SN.C.S.NoticeLocationId).val(); - var NLN = $('#'+SN.C.S.NoticeGeoName).text(); - var NDGe = $('#'+SN.C.S.NoticeDataGeo); + var NLat = form.find('[name=lat]') + var NLon = form.find('[name=lon]') + var NLNS = form.find('[name=location_ns]').val(); + var NLID = form.find('[name=location_id]').val(); + var NLN = ''; // @fixme + var NDGe = form.find('[name=notice_data-geo]'); + var check = form.find('[name=notice_data-geo]'); + var label = form.find('label.notice_data-geo'); function removeNoticeDataGeo(error) { - $('label[for='+SN.C.S.NoticeDataGeo+']') - .attr('title', jQuery.trim($('label[for='+SN.C.S.NoticeDataGeo+']').text())) + label + .attr('title', jQuery.trim(label.text())) .removeClass('checked'); - $('.form_notice [name=lat]').val(''); - $('.form_notice [name=lon]').val(''); - $('.form_notice [name=location_ns]').val(''); - $('.form_notice [name=location_id]').val(''); - $('#'+SN.C.S.NoticeDataGeo).attr('checked', false); + form.find('[name=lat]').val(''); + form.find('[name=lon]').val(''); + form.find('[name=location_ns]').val(''); + form.find('[name=location_id]').val(''); + form.find('[name=notice_data-geo]').attr('checked', false); $.cookie(SN.C.S.NoticeDataGeoCookie, 'disabled', { path: '/' }); if (error) { - $('.geo_status_wrapper').removeClass('success').addClass('error'); - $('.geo_status_wrapper .geo_status').text(error); + form.find('.geo_status_wrapper').removeClass('success').addClass('error'); + form.find('.geo_status_wrapper .geo_status').text(error); } else { - $('.geo_status_wrapper').remove(); + form.find('.geo_status_wrapper').remove(); } } function getJSONgeocodeURL(geocodeURL, data) { - SN.U.NoticeGeoStatus('Looking up place name...'); + SN.U.NoticeGeoStatus(form, 'Looking up place name...'); $.getJSON(geocodeURL, data, function(location) { var lns, lid; if (typeof(location.location_ns) != 'undefined') { - $('#'+SN.C.S.NoticeLocationNs).val(location.location_ns); + form.find('[name=location_ns]').val(location.location_ns); lns = location.location_ns; } if (typeof(location.location_id) != 'undefined') { - $('#'+SN.C.S.NoticeLocationId).val(location.location_id); + form.find('[name=location_id]').val(location.location_id); lid = location.location_id; } @@ -1016,15 +969,15 @@ var SN = { // StatusNet NLN_text = location.name; } - SN.U.NoticeGeoStatus(NLN_text, data.lat, data.lon, location.url); - $('label[for='+SN.C.S.NoticeDataGeo+']') + SN.U.NoticeGeoStatus(form, NLN_text, data.lat, data.lon, location.url); + label .attr('title', NoticeDataGeo_text.ShareDisable + ' (' + NLN_text + ')'); - $('.form_notice [name=lat]').val(data.lat); - $('.form_notice [name=lon]').val(data.lon); - $('.form_notice [name=location_ns]').val(lns); - $('.form_notice [name=location_id]').val(lid); - $('#'+SN.C.S.NoticeDataGeo).attr('checked', true); + form.find('[name=lat]').val(data.lat); + form.find('[name=lon]').val(data.lon); + form.find('[name=location_ns]').val(lns); + form.find('[name=location_id]').val(lid); + form.find('[name=notice_data-geo]').attr('checked', true); var cookieValue = { NLat: data.lat, @@ -1040,34 +993,33 @@ var SN = { // StatusNet }); } - if (NDGe.length > 0) { + if (check.length > 0) { if ($.cookie(SN.C.S.NoticeDataGeoCookie) == 'disabled') { - NDGe.attr('checked', false); + check.attr('checked', false); } else { - NDGe.attr('checked', true); + check.attr('checked', true); } - var NGW = $('#notice_data-geo_wrap'); - var geocodeURL = NGW.attr('title'); - NGW.removeAttr('title'); + var NGW = form.find('.notice_data-geo_wrap'); + var geocodeURL = NGW.attr('data-api'); - $('label[for='+SN.C.S.NoticeDataGeo+']') - .attr('title', jQuery.trim($('label[for='+SN.C.S.NoticeDataGeo+']').text())); + label + .attr('title', label.text()); - NDGe.change(function() { - if ($('#'+SN.C.S.NoticeDataGeo).attr('checked') === true || $.cookie(SN.C.S.NoticeDataGeoCookie) === null) { - $('label[for='+SN.C.S.NoticeDataGeo+']') + check.change(function() { + if (check.attr('checked') === true || $.cookie(SN.C.S.NoticeDataGeoCookie) === null) { + label .attr('title', NoticeDataGeo_text.ShareDisable) .addClass('checked'); if ($.cookie(SN.C.S.NoticeDataGeoCookie) === null || $.cookie(SN.C.S.NoticeDataGeoCookie) == 'disabled') { if (navigator.geolocation) { - SN.U.NoticeGeoStatus('Requesting location from browser...'); + SN.U.NoticeGeoStatus(form, 'Requesting location from browser...'); navigator.geolocation.getCurrentPosition( function(position) { - $('.form_notice [name=lat]').val(position.coords.latitude); - $('.form_notice [name=lon]').val(position.coords.longitude); + form.find('[name=lat]').val(position.coords.latitude); + form.find('[name=lon]').val(position.coords.longitude); var data = { lat: position.coords.latitude, @@ -1107,22 +1059,22 @@ var SN = { // StatusNet } else { removeNoticeDataGeo(); - $('#'+SN.C.S.NoticeDataGeo).remove(); - $('label[for='+SN.C.S.NoticeDataGeo+']').remove(); + check.remove(); + label.remove(); } } } else { var cookieValue = JSON.parse($.cookie(SN.C.S.NoticeDataGeoCookie)); - $('.form_notice [name=lat]').val(cookieValue.NLat); - $('.form_notice [name=lon]').val(cookieValue.NLon); - $('.form_notice [name=location_ns]').val(cookieValue.NLNS); - $('.form_notice [name=location_id]').val(cookieValue.NLID); - $('#'+SN.C.S.NoticeDataGeo).attr('checked', cookieValue.NDG); + form.find('[name=lat]').val(cookieValue.NLat); + form.find('[name=lon]').val(cookieValue.NLon); + form.find('[name=location_ns]').val(cookieValue.NLNS); + form.find('[name=location_id]').val(cookieValue.NLID); + form.find('[name=notice_data-geo]').attr('checked', cookieValue.NDG); - SN.U.NoticeGeoStatus(cookieValue.NLN, cookieValue.NLat, cookieValue.NLon, cookieValue.NLNU); - $('label[for='+SN.C.S.NoticeDataGeo+']') + SN.U.NoticeGeoStatus(form, cookieValue.NLN, cookieValue.NLat, cookieValue.NLon, cookieValue.NLNU); + label .attr('title', NoticeDataGeo_text.ShareDisable + ' (' + cookieValue.NLN + ')') .addClass('checked'); } @@ -1137,19 +1089,20 @@ var SN = { // StatusNet /** * Create or update a geolocation status widget in this notice posting form. * + * @param {jQuery} form * @param {String} status * @param {String} lat (optional) * @param {String} lon (optional) * @param {String} url (optional) */ - NoticeGeoStatus: function(status, lat, lon, url) + NoticeGeoStatus: function(form, status, lat, lon, url) { - var form = $('#form_notice'); var wrapper = form.find('.geo_status_wrapper'); if (wrapper.length == 0) { wrapper = $('
    '); wrapper.find('button.close').click(function() { - $('#'+SN.C.S.NoticeDataGeo).removeAttr('checked').change(); + form.find('[name=notice_data-geo]').removeAttr('checked').change(); + return false; }); form.append(wrapper); } @@ -1294,7 +1247,7 @@ var SN = { // StatusNet var profileLink = $('#nav_profile a').attr('href'); if (profileLink) { - var authorUrl = $(notice).find('.entry-title .author a.url').attr('href'); + var authorUrl = $(notice).find('.vcard.author a.url').attr('href'); if (authorUrl == profileLink) { if (action == 'all' || action == 'showstream') { // Posts always show on your own friends and profile streams. @@ -1309,7 +1262,35 @@ var SN = { // StatusNet // UI links currently on the page use malleable names. return false; - } + }, + + /** + * Switch to another active input sub-form. + * This will hide the current form (if any), show the new one, and + * update the input type tab selection state. + * + * @param {String} tag + */ + switchInputFormTab: function(tag) { + // The one that's current isn't current anymore + $('.input_form_nav_tab.current').removeClass('current'); + if (tag == 'placeholder') { + // Hack: when showing the placeholder, mark the tab + // as current for 'Status'. + $('#input_form_nav_status').addClass('current'); + } else { + $('#input_form_nav_'+tag).addClass('current'); + } + + $('.input_form.current').removeClass('current'); + $('#input_form_'+tag) + .addClass('current') + .find('.ajax-notice').each(function() { + var form = $(this); + SN.Init.NoticeFormSetup(form); + }) + .find('textarea:first').focus(); + } }, Init: { @@ -1323,13 +1304,67 @@ var SN = { // StatusNet */ NoticeForm: function() { if ($('body.user_in').length > 0) { - SN.U.NoticeLocationAttach(); + // SN.Init.NoticeFormSetup() will get run + // when forms get displayed for the first time... - $('.'+SN.C.S.FormNotice).each(function() { - SN.U.FormNoticeXHR($(this)); - SN.U.FormNoticeEnhancements($(this)); - SN.U.NoticeDataAttach($(this)); + // Hack to initialize the placeholder at top + $('#input_form_placeholder input.placeholder').focus(function() { + SN.U.switchInputFormTab("status"); }); + + // Make inline reply forms self-close when clicking out. + $('body').bind('click', function(e) { + var currentForm = $('#content .input_forms div.current'); + if (currentForm.length > 0) { + if ($('#content .input_forms').has(e.target).length == 0) { + // If all fields are empty, switch back to the placeholder. + var fields = currentForm.find('textarea, input[type=text], input[type=""]'); + var anything = false; + fields.each(function() { + anything = anything || $(this).val(); + }); + if (!anything) { + SN.U.switchInputFormTab("placeholder"); + } + } + } + + var openReplies = $('li.notice-reply'); + if (openReplies.length > 0) { + var target = $(e.target); + openReplies.each(function() { + // Did we click outside this one? + var replyItem = $(this); + if (replyItem.has(e.target).length == 0) { + var textarea = replyItem.find('.notice_data-text:first'); + var cur = $.trim(textarea.val()); + // Only close if there's been no edit. + if (cur == '' || cur == textarea.data('initialText')) { + var parentNotice = replyItem.closest('li.notice'); + replyItem.remove(); + SN.U.NoticeInlineReplyPlaceholder(parentNotice); + } + } + }); + } + }); + } + }, + + /** + * Encapsulate notice form setup for a single form. + * Plugins can add extra setup by monkeypatching this + * function. + * + * @param {jQuery} form + */ + NoticeFormSetup: function(form) { + if (!form.data('NoticeFormSetup')) { + SN.U.NoticeLocationAttach(form); + SN.U.FormNoticeXHR(form); + SN.U.FormNoticeEnhancements(form); + SN.U.NoticeDataAttach(form); + form.data('NoticeFormSetup', true); } }, @@ -1341,7 +1376,10 @@ var SN = { // StatusNet */ Notices: function() { if ($('body.user_in').length > 0) { - SN.U.NoticeFavor(); + var masterForm = $('.form_notice:first'); + if (masterForm.length > 0) { + SN.C.I.NoticeFormMaster = document._importNode(masterForm[0], true); + } SN.U.NoticeRepeat(); SN.U.NoticeReply(); SN.U.NoticeInlineReplySetup(); @@ -1358,12 +1396,6 @@ var SN = { // StatusNet */ EntityActions: function() { if ($('body.user_in').length > 0) { - $('.form_user_subscribe').live('click', function() { SN.U.FormXHR($(this)); return false; }); - $('.form_user_unsubscribe').live('click', function() { SN.U.FormXHR($(this)); return false; }); - $('.form_group_join').live('click', function() { SN.U.FormXHR($(this)); return false; }); - $('.form_group_leave').live('click', function() { SN.U.FormXHR($(this)); return false; }); - $('.form_user_nudge').live('click', function() { SN.U.FormXHR($(this)); return false; }); - SN.U.NewDirectMessage(); } }, @@ -1390,6 +1422,16 @@ var SN = { // StatusNet }); }, + /** + * Set up any generic 'ajax' form so it submits via AJAX with auto-replacement. + */ + AjaxForms: function() { + $('form.ajax').live('submit', function() { + SN.U.FormXHR($(this)); + return false; + }); + }, + /** * Add logic to any file upload forms to handle file size limits, * on browsers that support basic FileAPI. @@ -1426,6 +1468,7 @@ var SN = { // StatusNet * don't start them loading until after DOM-ready time! */ $(document).ready(function(){ + SN.Init.AjaxForms(); SN.Init.UploadForms(); if ($('.'+SN.C.S.FormNotice).length > 0) { SN.Init.NoticeForm();