X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=js%2Futil.js;h=55f2920cd5f4a9e25a13bb9c92ce2304b3e25266;hb=c30dcbebbcb383fbd2d4ba92ef5294fbb3e44244;hp=00c97165576f042f1eefd03b83363c5cb01a3b7e;hpb=822f08e3ed71bee1f223e1551bcf60f0ba10d5ad;p=quix0rs-gnu-social.git
diff --git a/js/util.js b/js/util.js
index 00c9716557..55f2920cd5 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,17 +51,6 @@ var SN = { // StatusNet
Processing: 'processing',
CommandResult: 'command_result',
FormNotice: 'form_notice',
- NoticeDataText: 'notice_data-text',
- NoticeTextCount: 'notice_text-count',
- NoticeInReplyTo: 'notice_in-reply-to',
- NoticeDataAttach: 'notice_data-attach',
- NoticeDataAttachSelected: 'notice_data-attach_selected',
- NoticeActionSubmit: 'notice_action-submit',
- 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',
@@ -106,7 +96,7 @@ var SN = { // StatusNet
*/
FormNoticeEnhancements: function(form) {
if (jQuery.data(form[0], 'ElementData') === undefined) {
- MaxLength = form.find('#'+SN.C.S.NoticeTextCount).text();
+ MaxLength = form.find('.count').text();
if (typeof(MaxLength) == 'undefined') {
MaxLength = SN.C.I.MaxLength;
}
@@ -114,7 +104,7 @@ var SN = { // StatusNet
SN.U.Counter(form);
- NDT = form.find('#'+SN.C.S.NoticeDataText);
+ NDT = form.find('.notice_data-text:first');
NDT.bind('keyup', function(e) {
SN.U.Counter(form);
@@ -134,11 +124,7 @@ var SN = { // StatusNet
.bind('paste', delayedUpdate);
}
else {
- form.find('#'+SN.C.S.NoticeTextCount).text(jQuery.data(form[0], 'ElementData').MaxLength);
- }
-
- if ($('body')[0].id != 'conversation' && window.location.hash.length === 0 && $(window).scrollTop() == 0) {
- form.find('textarea').focus();
+ form.find('.count').text(jQuery.data(form[0], 'ElementData').MaxLength);
}
},
@@ -166,7 +152,7 @@ var SN = { // StatusNet
}
var remaining = MaxLength - SN.U.CharacterCount(form);
- var counter = form.find('#'+SN.C.S.NoticeTextCount);
+ var counter = form.find('.count');
if (remaining.toString() != counter.text()) {
if (!SN.C.I.CounterBlackout || remaining === 0) {
@@ -197,7 +183,7 @@ var SN = { // StatusNet
* @return number of chars
*/
CharacterCount: function(form) {
- return form.find('#'+SN.C.S.NoticeDataText).val().length;
+ return form.find('.notice_data-text:first').val().length;
},
/**
@@ -242,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
@@ -264,16 +253,34 @@ var SN = { // StatusNet
.attr(SN.C.S.Disabled, SN.C.S.Disabled);
},
error: function (xhr, textStatus, errorThrown) {
- alert(errorThrown || textStatus);
+ // If the server end reported an error from StatusNet,
+ // find it -- otherwise we'll see what was reported
+ // from the browser.
+ var errorReported = null;
+ if (xhr.responseXML) {
+ errorReported = $('#error', xhr.responseXML).text();
+ }
+ alert(errorReported || errorThrown || textStatus);
+
+ // Restore the form to original state.
+ // Hopefully. :D
+ form
+ .removeClass(SN.C.S.Processing)
+ .find('.submit')
+ .removeClass(SN.C.S.Disabled)
+ .removeAttr(SN.C.S.Disabled);
},
success: function(data, textStatus) {
if (typeof($('form', data)[0]) != 'undefined') {
form_new = document._importNode($('form', data)[0], true);
form.replaceWith(form_new);
}
- else {
+ else if (typeof($('p', data)[0]) != 'undefined') {
form.replaceWith(document._importNode($('p', data)[0], true));
}
+ else {
+ alert('Unknown error.');
+ }
}
});
},
@@ -338,13 +345,13 @@ var SN = { // StatusNet
dataType: 'xml',
timeout: '60000',
beforeSend: function(formData) {
- if (form.find('#'+SN.C.S.NoticeDataText)[0].value.length === 0) {
+ if (form.find('.notice_data-text:first').val() == '') {
form.addClass(SN.C.S.Warning);
return false;
}
form
.addClass(SN.C.S.Processing)
- .find('#'+SN.C.S.NoticeActionSubmit)
+ .find('.submit')
.addClass(SN.C.S.Disabled)
.attr(SN.C.S.Disabled, SN.C.S.Disabled);
@@ -355,7 +362,7 @@ var SN = { // StatusNet
error: function (xhr, textStatus, errorThrown) {
form
.removeClass(SN.C.S.Processing)
- .find('#'+SN.C.S.NoticeActionSubmit)
+ .find('.submit')
.removeClass(SN.C.S.Disabled)
.removeAttr(SN.C.S.Disabled, SN.C.S.Disabled);
removeFeedback();
@@ -372,7 +379,7 @@ var SN = { // StatusNet
if (parseInt(xhr.status) === 0 || jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) {
form
.resetForm()
- .find('#'+SN.C.S.NoticeDataAttachSelected).remove();
+ .find('.attach-status').remove();
SN.U.FormNoticeEnhancements(form);
}
else {
@@ -402,9 +409,28 @@ 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, remove the form...
+ var list = form.closest('.threaded-replies');
+ var placeholder = list.find('.notice-reply-placeholder');
+ replyItem.remove();
+
+ var id = $(notice).attr('id');
+ if ($("#"+id).length == 0) {
+ $(notice).insertBefore(placeholder);
+ } else {
+ // Realtime came through before us...
+ }
+
+ // ...and show the placeholder form.
+ placeholder.show();
+ } 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) {
@@ -419,35 +445,101 @@ 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());
}
}
form.resetForm();
- form.find('#'+SN.C.S.NoticeInReplyTo).val('');
- form.find('#'+SN.C.S.NoticeDataAttachSelected).remove();
+ form.find('[name=inreplyto]').val('');
+ form.find('.attach-status').remove();
SN.U.FormNoticeEnhancements(form);
}
},
complete: function(xhr, textStatus) {
form
.removeClass(SN.C.S.Processing)
- .find('#'+SN.C.S.NoticeActionSubmit)
+ .find('.submit')
.removeAttr(SN.C.S.Disabled)
.removeClass(SN.C.S.Disabled);
- $('#'+SN.C.S.NoticeLat).val(SN.C.I.NoticeDataGeo.NLat);
- $('#'+SN.C.S.NoticeLon).val(SN.C.I.NoticeDataGeo.NLon);
- if ($('#'+SN.C.S.NoticeLocationNs)) {
- $('#'+SN.C.S.NoticeLocationNs).val(SN.C.I.NoticeDataGeo.NLNS);
- $('#'+SN.C.S.NoticeLocationId).val(SN.C.I.NoticeDataGeo.NLID);
+ form.find('[name=lat]').val(SN.C.I.NoticeDataGeo.NLat);
+ 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);
+ form.find('[name=notice_data-geo]').attr('checked', SN.C.I.NoticeDataGeo.NDG);
+ }
+ });
+ },
+
+ FormProfileSearchXHR: function(form) {
+ $.ajax({
+ type: 'POST',
+ dataType: 'xml',
+ url: 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);
+ },
+ error: function (xhr, textStatus, errorThrown) {
+ alert(errorThrown || textStatus);
+ },
+ success: function(data, textStatus) {
+ var results_placeholder = $('#profile_search_results');
+ if (typeof($('ul', data)[0]) != 'undefined') {
+ var list = document._importNode($('ul', data)[0], true);
+ results_placeholder.replaceWith(list);
+ }
+ else {
+ var _error = $('
').append(document._importNode($('p', data)[0], true));
+ results_placeholder.html(_error);
+ }
+ form
+ .removeClass(SN.C.S.Processing)
+ .find('.submit')
+ .removeClass(SN.C.S.Disabled)
+ .attr(SN.C.S.Disabled, false);
+ }
+ });
+ },
+
+ FormPeopletagsXHR: function(form) {
+ $.ajax({
+ type: 'POST',
+ dataType: 'xml',
+ url: form.attr('action'),
+ data: form.serialize() + '&ajax=1',
+ beforeSend: function(xhr) {
+ form.find('.submit')
+ .addClass(SN.C.S.Processing)
+ .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 results_placeholder = form.parents('.entity_tags');
+ if (typeof($('.entity_tags', data)[0]) != 'undefined') {
+ var tags = document._importNode($('.entity_tags', data)[0], true);
+ $(tags).find('.editable').append($(''));
+ results_placeholder.replaceWith(tags);
+ } else {
+ results_placeholder.find('p').remove();
+ results_placeholder.append(document._importNode($('p', data)[0], true));
+ form.removeClass(SN.C.S.Processing)
+ .find('.submit')
+ .removeClass(SN.C.S.Disabled)
+ .attr(SN.C.S.Disabled, false);
}
- $('#'+SN.C.S.NoticeDataGeo).attr('checked', SN.C.I.NoticeDataGeo.NDG);
}
});
},
@@ -457,7 +549,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);
@@ -474,13 +566,14 @@ 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');
}
},
+
/**
* Fetch an XML DOM from an XHR's response data.
*
@@ -513,33 +606,19 @@ var SN = { // StatusNet
* @access private
*/
NoticeReply: function() {
- if ($('#'+SN.C.S.NoticeDataText).length > 0 && $('#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');
+ SN.U.NoticeInlineReplyTrigger(notice);
+ 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.NoticeReplySet(nickname.text(), $($('.notice_id', notice)[0]).text()); */
- SN.U.NoticeInlineReplyTrigger(notice, '@' + nickname.text());
- return false;
- });
},
/**
@@ -564,177 +643,118 @@ var SN = { // StatusNet
// and we'll add on the end of it. Will add if needed.
list = $('ul.threaded-replies', notice);
if (list.length == 0) {
- list = $('');
- notice.append(list);
+ SN.U.NoticeInlineReplyPlaceholder(notice);
+ list = $('ul.threaded-replies', notice);
}
}
// See if the form's already open...
var replyForm = $('.notice-reply-form', list);
- if (replyForm.length == 0) {
- // Remove placeholder if any
- $('li.notice-reply-placeholder').remove();
+
+ var nextStep = function() {
+ // Override...?
+ replyForm.find('input[name=inreplyto]').val(id);
+ replyForm.find('#notice_to').attr('disabled', 'disabled').hide();
+ replyForm.find('#notice_private').attr('disabled', 'disabled').hide();
+ replyForm.find('label[for=notice_to]').hide();
+ replyForm.find('label[for=notice_private]').hide();
+
+ // 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 {
+ // Hide the placeholder...
+ var placeholder = list.find('li.notice-reply-placeholder').hide();
// 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); // *after* the placeholder
- // 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);
- /**
- * FIXME OBSOLETE?
- * Updates the new notice posting form with bits for replying to the
- * given user. Adds replyto parameter to the form, and a "@foo" to the
- * text area.
- *
- * @fixme replyto is a global variable, but probably shouldn't be
- *
- * @param {String} nick
- * @param {String} id
- */
- NoticeReplySet: function(nick,id) {
- if (nick.match(SN.C.I.PatternUsername)) {
- var text = $('#'+SN.C.S.NoticeDataText);
- if (text.length > 0) {
- replyto = '@' + nick + ' ';
- text.val(replyto + text.val().replace(RegExp(replyto, 'i'), ''));
- $('#'+SN.C.S.FormNotice+' #'+SN.C.S.NoticeInReplyTo).val(id);
-
- text[0].focus();
- if (text[0].setSelectionRange) {
- var len = text.val().length;
- text[0].setSelectionRange(len,len);
+ 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]);
+ });
}
}
}
},
- /**
- * 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; });
- },
-
NoticeInlineReplyPlaceholder: function(notice) {
var list = notice.find('ul.threaded-replies');
+ if (list.length == 0) {
+ list = $('');
+ notice.append(list);
+ list = notice.find('ul.threaded-replies');
+ }
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;
+ });
+ $('li.notice-reply-comments a')
+ .live('click', function() {
+ var url = $(this).attr('href');
+ var area = $(this).closest('.threaded-replies');
+ $.get(url, {ajax: 1}, function(data, textStatus, xhr) {
+ var replies = $('.threaded-replies', data);
+ if (replies.length) {
+ area.replaceWith(document._importNode(replies[0], true));
+ }
+ });
+ return false;
+ });
},
/**
@@ -854,36 +874,34 @@ var SN = { // StatusNet
*
* This preview box will also allow removing the attachment
* prior to posting.
+ *
+ * @param {jQuery} form
*/
- NoticeDataAttach: function() {
- NDA = $('#'+SN.C.S.NoticeDataAttach);
+ NoticeDataAttach: function(form) {
+ var NDA = form.find('input[type=file]');
NDA.change(function(event) {
+ form.find('.attach-status').remove();
+
var filename = $(this).val();
if (!filename) {
// No file -- we've been tricked!
- $('#'+SN.C.S.NoticeDataAttachSelected).remove();
return false;
}
- // @fixme appending filename straight in is potentially unsafe
- S = ''+filename+'
';
- NDAS = $('#'+SN.C.S.NoticeDataAttachSelected);
- if (NDAS.length > 0) {
- NDAS.replaceWith(S);
- }
- else {
- $('#'+SN.C.S.FormNotice).append(S);
- }
- $('#'+SN.C.S.NoticeDataAttachSelected+' button').click(function(){
- $('#'+SN.C.S.NoticeDataAttachSelected).remove();
+ var attachStatus = $('
');
+ attachStatus.find('code').text(filename);
+ attachStatus.find('button').click(function(){
+ attachStatus.remove();
NDA.val('');
return false;
});
+ form.append(attachStatus);
+
if (typeof this.files == "object") {
// Some newer browsers will let us fetch the files for preview.
for (var i = 0; i < this.files.length; i++) {
- SN.U.PreviewAttach(this.files[i]);
+ SN.U.PreviewAttach(form, this.files[i]);
}
}
});
@@ -919,13 +937,14 @@ var SN = { // StatusNet
* Known fail:
* - Opera 10.63, 11 beta (no input.files interface)
*
+ * @param {jQuery} form
* @param {File} file
*
* @todo use configured thumbnail size
* @todo detect pixel size?
* @todo should we render a thumbnail to a canvas and then use the smaller image?
*/
- PreviewAttach: function(file) {
+ PreviewAttach: function(form, file) {
var tooltip = file.type + ' ' + Math.round(file.size / 1024) + 'KB';
var preview = true;
@@ -985,11 +1004,11 @@ var SN = { // StatusNet
.attr('alt', tooltip)
.attr('src', url)
.attr('style', 'height: 120px');
- $('#'+SN.C.S.NoticeDataAttachSelected).append(img);
+ form.find('.attach-status').append(img);
});
} else {
var img = $('').text(tooltip);
- $('#'+SN.C.S.NoticeDataAttachSelected).append(img);
+ form.find('.attach-status').append(img);
}
},
@@ -1000,53 +1019,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');
- $('#'+SN.C.S.NoticeLat).val('');
- $('#'+SN.C.S.NoticeLon).val('');
- $('#'+SN.C.S.NoticeLocationNs).val('');
- $('#'+SN.C.S.NoticeLocationId).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;
}
@@ -1057,15 +1080,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 + ')');
- $('#'+SN.C.S.NoticeLat).val(data.lat);
- $('#'+SN.C.S.NoticeLon).val(data.lon);
- $('#'+SN.C.S.NoticeLocationNs).val(lns);
- $('#'+SN.C.S.NoticeLocationId).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,
@@ -1081,34 +1104,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) {
- $('#'+SN.C.S.NoticeLat).val(position.coords.latitude);
- $('#'+SN.C.S.NoticeLon).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,
@@ -1148,22 +1170,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));
- $('#'+SN.C.S.NoticeLat).val(cookieValue.NLat);
- $('#'+SN.C.S.NoticeLon).val(cookieValue.NLon);
- $('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS);
- $('#'+SN.C.S.NoticeLocationId).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');
}
@@ -1178,19 +1200,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);
}
@@ -1335,7 +1358,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.
@@ -1350,7 +1373,43 @@ 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');
+ }
+
+ // Don't remove 'current' if we also have the "nonav" class.
+ // An example would be the message input form. removing
+ // 'current' will cause the form to vanish from the page.
+ var nonav = $('.input_form.current.nonav');
+ if (nonav.length > 0) {
+ return;
+ }
+
+ $('.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: {
@@ -1364,14 +1423,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));
+ // Hack to initialize the placeholder at top
+ $('#input_form_placeholder input.placeholder').focus(function() {
+ SN.U.switchInputFormTab("status");
});
- SN.U.NoticeDataAttach();
+ // 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();
+ parentNotice.find('li.notice-reply-placeholder').show();
+ }
+ }
+ });
+ }
+ });
+ }
+ },
+
+ /**
+ * 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);
}
},
@@ -1383,7 +1495,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();
@@ -1405,11 +1520,23 @@ var SN = { // StatusNet
$('.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; });
+ $('.form_peopletag_subscribe').live('click', function() { SN.U.FormXHR($(this)); return false; });
+ $('.form_peopletag_unsubscribe').live('click', function() { SN.U.FormXHR($(this)); return false; });
+ $('.form_user_add_peopletag').live('click', function() { SN.U.FormXHR($(this)); return false; });
+ $('.form_user_remove_peopletag').live('click', function() { SN.U.FormXHR($(this)); return false; });
SN.U.NewDirectMessage();
}
},
+ ProfileSearch: function() {
+ if ($('body.user_in').length > 0) {
+ $('.form_peopletag_edit_user_search input.submit').live('click', function() {
+ SN.U.FormProfileSearchXHR($(this).parents('form')); return false;
+ });
+ }
+ },
+
/**
* Run setup code for login form:
*
@@ -1432,6 +1559,122 @@ var SN = { // StatusNet
});
},
+ /**
+ * Called when a people tag edit box is shown in the interface
+ *
+ * - loads the jQuery UI autocomplete plugin
+ * - sets event handlers for tag completion
+ *
+ */
+ PeopletagAutocomplete: function(txtBox) {
+ var split = function(val) {
+ return val.split( /\s+/ );
+ }
+ var extractLast = function(term) {
+ return split(term).pop();
+ }
+
+ // don't navigate away from the field on tab when selecting an item
+ txtBox.live( "keydown", function( event ) {
+ if ( event.keyCode === $.ui.keyCode.TAB &&
+ $(this).data( "autocomplete" ).menu.active ) {
+ event.preventDefault();
+ }
+ }).autocomplete({
+ minLength: 0,
+ source: function(request, response) {
+ // delegate back to autocomplete, but extract the last term
+ response($.ui.autocomplete.filter(
+ SN.C.PtagACData, extractLast(request.term)));
+ },
+ focus: function() {
+ return false;
+ },
+ select: function(event, ui) {
+ var terms = split(this.value);
+ terms.pop();
+ terms.push(ui.item.value);
+ terms.push("");
+ this.value = terms.join(" ");
+ return false;
+ }
+ }).data('autocomplete')._renderItem = function(ul, item) {
+ // FIXME: with jQuery UI you cannot have it highlight the match
+ var _l = '' + item.tag
+ + ' ' + item.mode + ''
+ + '' + item.freq + ''
+
+ return $("")
+ .addClass('mode-' + item.mode)
+ .addClass('ptag-ac-line')
+ .data("item.autocomplete", item)
+ .append(_l)
+ .appendTo(ul);
+ }
+ },
+
+ /**
+ * Run setup for the ajax people tags editor
+ *
+ * - show edit button
+ * - set event handle for click on edit button
+ * - loads people tag autocompletion data if not already present
+ * or if it is stale.
+ *
+ */
+ PeopleTags: function() {
+ $('.user_profile_tags .editable').append($(''));
+
+ $('.peopletags_edit_button').live('click', function() {
+ var form = $(this).parents('dd').eq(0).find('form');
+ // We can buy time from the above animation
+
+ $.ajax({
+ url: _peopletagAC,
+ dataType: 'json',
+ data: {token: $('#token').val()},
+ ifModified: true,
+ success: function(data) {
+ // item.label is used to match
+ for (i=0; i < data.length; i++) {
+ data[i].label = data[i].tag;
+ }
+
+ SN.C.PtagACData = data;
+ SN.Init.PeopletagAutocomplete(form.find('#tags'));
+ }
+ });
+
+ $(this).parents('ul').eq(0).fadeOut(200, function() {form.fadeIn(200).find('input#tags')});
+ });
+
+ $('.user_profile_tags form .submit').live('click', function() {
+ SN.U.FormPeopletagsXHR($(this).parents('form')); return false;
+ });
+ },
+
+ /**
+ * 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;
+ });
+ $('form.ajax input[type=submit]').live('click', function() {
+ // Some forms rely on knowing which submit button was clicked.
+ // Save a hidden input field which'll be picked up during AJAX
+ // submit...
+ var button = $(this);
+ var form = button.closest('form');
+ form.find('.hidden-submit-button').remove();
+ $('')
+ .attr('name', button.attr('name'))
+ .val(button.val())
+ .appendTo(form);
+ });
+ },
+
/**
* Add logic to any file upload forms to handle file size limits,
* on browsers that support basic FileAPI.
@@ -1456,7 +1699,26 @@ var SN = { // StatusNet
}
}
});
- }
+ },
+
+ CheckBoxes: function() {
+ $("span[class='checkbox-wrapper']").addClass("unchecked");
+ $(".checkbox-wrapper").click(function(){
+ if($(this).children("input").attr("checked")){
+ // uncheck
+ $(this).children("input").attr({checked: ""});
+ $(this).removeClass("checked");
+ $(this).addClass("unchecked");
+ $(this).children("label").text("Private?");
+ }else{
+ // check
+ $(this).children("input").attr({checked: "checked"});
+ $(this).removeClass("unchecked");
+ $(this).addClass("checked");
+ $(this).children("label").text("Private");
+ }
+ });
+ }
}
};
@@ -1468,7 +1730,9 @@ var SN = { // StatusNet
* don't start them loading until after DOM-ready time!
*/
$(document).ready(function(){
+ SN.Init.AjaxForms();
SN.Init.UploadForms();
+ SN.Init.CheckBoxes();
if ($('.'+SN.C.S.FormNotice).length > 0) {
SN.Init.NoticeForm();
}
@@ -1481,4 +1745,11 @@ $(document).ready(function(){
if ($('#form_login').length > 0) {
SN.Init.Login();
}
+ if ($('#profile_search_results').length > 0) {
+ SN.Init.ProfileSearch();
+ }
+ if ($('.user_profile_tags .editable').length > 0) {
+ SN.Init.PeopleTags();
+ }
});
+