X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=js%2Futil.js;h=966df4d93fbe085efcd47323e585b12dfacb269f;hb=bb087a965009fd93a5c02a9e10ab90adcc6b7963;hp=3cd4bbbe44714febea274778fc4a1af3ff80332e;hpb=b89f390b3319df0112fc68764f2206bcd83a1029;p=quix0rs-gnu-social.git diff --git a/js/util.js b/js/util.js index 3cd4bbbe44..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()); } } @@ -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; + }); }, /** @@ -972,12 +921,14 @@ var SN = { // StatusNet var NLon = form.find('[name=lon]') var NLNS = form.find('[name=location_ns]').val(); var NLID = form.find('[name=location_id]').val(); - var NLN = $('#'+SN.C.S.NoticeGeoName).text(); // @fixme does this exist? + 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.find('[name=lat]').val(''); @@ -989,15 +940,15 @@ var SN = { // StatusNet $.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; @@ -1018,8 +969,8 @@ 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.find('[name=lat]').val(data.lat); @@ -1042,30 +993,29 @@ 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.find('[name=lat]').val(position.coords.latitude); @@ -1109,8 +1059,8 @@ var SN = { // StatusNet } else { removeNoticeDataGeo(); - $('#'+SN.C.S.NoticeDataGeo).remove(); - $('label[for='+SN.C.S.NoticeDataGeo+']').remove(); + check.remove(); + label.remove(); } } } @@ -1123,8 +1073,8 @@ var SN = { // StatusNet 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'); } @@ -1139,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); } @@ -1296,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. @@ -1311,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: { @@ -1325,13 +1304,67 @@ var SN = { // StatusNet */ NoticeForm: function() { if ($('body.user_in').length > 0) { - $('.'+SN.C.S.FormNotice).each(function() { - var form = $(this); - SN.U.NoticeLocationAttach(form); - SN.U.FormNoticeXHR(form); - SN.U.FormNoticeEnhancements(form); - SN.U.NoticeDataAttach(form); + // SN.Init.NoticeFormSetup() will get run + // when forms get displayed for the first time... + + // 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); } }, @@ -1343,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(); @@ -1360,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(); } }, @@ -1392,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. @@ -1428,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();