// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later /* Copyright (c) 2010-2012, Diaspora Inc. This file is * licensed under the Affero General Public License version 3 or later. See * the COPYRIGHT file. */ //= require ./publisher/aspect_selector_view //= require ./publisher/getting_started_view //= require ./publisher/mention_view //= require ./publisher/poll_creator_view //= require ./publisher/services_view //= require ./publisher/uploader_view //= require jquery-textchange app.views.Publisher = Backbone.View.extend({ el : "#publisher", events : { "keydown #status_message_text": "keyDown", "focus textarea" : "open", "submit form" : "createStatusMessage", "click #submit" : "createStatusMessage", "textchange #status_message_text": "checkSubmitAvailability", "click #locator" : "showLocation", "click #poll_creator" : "togglePollCreator", "click #hide_location" : "destroyLocation", "keypress #location_address" : "avoidEnter" }, initialize : function(opts){ this.standalone = opts ? opts.standalone : false; this.prefillMention = opts && opts.prefillMention ? opts.prefillMention : undefined; this.disabled = false; // init shortcut references to the various elements this.inputEl = this.$("#status_message_text"); this.wrapperEl = this.$("#publisher_textarea_wrapper"); this.submitEl = this.$("input[type=submit], button#submit"); this.photozoneEl = this.$("#photodropzone"); // if there is data in the publisher we ask for a confirmation // before the user is able to leave the page $(window).on("beforeunload", _.bind(this._beforeUnload, this)); $(window).on("unload", this.clear.bind(this)); // hide close and preview buttons and manage services link // in case publisher is standalone // (e.g. bookmarklet, mentions popup) if( this.standalone ) { this.$(".question_mark").hide(); } // this has to be here, otherwise for some reason the callback for the // textchange event won't be called in Backbone... this.inputEl.bind("textchange", $.noop); $("body").click(function(event) { var $target = $(event.target); if ($target.closest("#publisher").length === 0 && !$target.hasClass("dropdown-backdrop")) { this.tryClose(); } }.bind(this)); // close publisher on post this.on("publisher:add", function() { this.close(); this.showSpinner(true); }); // open publisher on post error this.on("publisher:error", function() { this.open(); this.showSpinner(false); }); // resetting the poll view this.on("publisher:sync", function() { this.viewPollCreator.render(); }); this.initSubviews(); this.checkSubmitAvailability(); this.triggerGettingStarted(); return this; }, initSubviews: function() { this.mention = new app.views.PublisherMention({ el: this.$("#publisher_textarea_wrapper") }); if(this.prefillMention) { this.mention.prefillMention([this.prefillMention]); } var form = this.$(".content_creation form"); this.view_services = new app.views.PublisherServices({ el: this.$("#publisher-service-icons"), input: this.inputEl, form: form }); this.viewAspectSelector = new app.views.PublisherAspectSelector({ el: this.$(".public_toggle .aspect_dropdown"), form: form }); this.viewGettingStarted = new app.views.PublisherGettingStarted({ firstMessageEl: this.inputEl, visibilityEl: this.$(".public_toggle .aspect_dropdown > .dropdown-toggle"), streamEl: $("#main_stream") }); this.viewUploader = new app.views.PublisherUploader({ el: this.$("#file-upload"), publisher: this }); this.viewUploader.on("change", this.checkSubmitAvailability, this); var self = this; var mdEditorOptions = { onPreview: function() { self.wrapperEl.addClass("markdown-preview"); return self.createPostPreview(); }, onHidePreview: function() { self.wrapperEl.removeClass("markdown-preview"); }, onPostPreview: function() { var photoAttachments = self.wrapperEl.find(".photo_attachments"); if (photoAttachments.length > 0) { new app.views.Gallery({el: photoAttachments}); } }, onChange: function() { self.inputEl.trigger("textchange"); } }; if (!this.standalone) { mdEditorOptions.onClose = function() { self.clear(); }; } this.markdownEditor = new Diaspora.MarkdownEditor(this.inputEl, mdEditorOptions); this.viewPollCreator = new app.views.PublisherPollCreator({ el: this.$("#poll_creator_container") }); this.viewPollCreator.on("change", this.checkSubmitAvailability, this); this.viewPollCreator.render(); if (this.prefillMention) { this.checkSubmitAvailability(); } }, // set the selected aspects in the dropdown by their ids setSelectedAspects: function(ids) { this.viewAspectSelector.updateAspectsSelector(ids); }, // inject content into the publisher textarea setText: function(txt) { this.inputEl.val(txt); this.prefillText = txt; this.inputEl.trigger("input"); autosize.update(this.inputEl); this.checkSubmitAvailability(); }, // show the "getting started" popups around the publisher triggerGettingStarted: function() { if (gon.preloads.getting_started) { this.open(); this.viewGettingStarted.show(); } }, createStatusMessage : function(evt) { this.setButtonsEnabled(false); var self = this; if(evt){ evt.preventDefault(); } // Auto-adding a poll answer always leaves an empty box when the user starts // typing in the last box. We'll delete the last one to avoid submitting an // empty poll answer and failing validation. this.viewPollCreator.removeLastAnswer(); var serializedForm = $(evt.target).closest("form").serializeObject(); // disable input while posting, must be after the form is serialized this.setInputEnabled(false); this.wrapperEl.addClass("submitting"); // lulz this code should be killed. var statusMessage = new app.models.Post(); if( app.publisher ) { app.publisher.trigger("publisher:add"); } statusMessage.save({ "status_message" : { "text" : serializedForm["status_message[text]"] }, "aspect_ids" : serializedForm["aspect_ids[]"], "photos" : serializedForm["photos[]"], "services" : serializedForm["services[]"], "location_address" : $("#location_address").val(), "location_coords" : serializedForm["location[coords]"], "poll_question" : serializedForm["poll_question"], "poll_answers" : serializedForm["poll_answers[]"] }, { url : "/status_messages", success : function() { if( app.publisher ) { app.publisher.$el.trigger("ajax:success"); app.publisher.trigger("publisher:sync"); self.viewPollCreator.trigger("publisher:sync"); } if(app.stream && !self.standalone){ app.stream.addNow(statusMessage.toJSON()); } // clear state self.clear(); // standalone means single-shot posting (until further notice) if( self.standalone ) self.setEnabled(false); }, error: function(model, resp) { if( app.publisher ) { app.publisher.trigger("publisher:error"); } self.setInputEnabled(true); app.flashMessages.error(resp.responseText); self.setButtonsEnabled(true); self.setInputEnabled(true); self.wrapperEl.removeClass("submitting"); self.checkSubmitAvailability(); autosize.update(self.inputEl); } }); }, // creates the location showLocation: function(){ if($("#location").length === 0){ this.$(".location-container").append("
"); this.wrapperEl.addClass("with-location"); this.view_locator = new app.views.Location(); } }, // destroys the location destroyLocation: function(){ if(this.view_locator){ this.view_locator.remove(); this.wrapperEl.removeClass("with-location"); delete this.view_locator; } }, togglePollCreator: function(){ this.wrapperEl.toggleClass("with-poll"); this.inputEl.focus(); }, // avoid submitting form when pressing Enter key avoidEnter: function(evt){ if(evt.which === Keycodes.ENTER) { return false; } }, getUploadedPhotos: function() { var photos = []; $("li.publisher_photo img").each(function() { var file = $(this).attr("src").substring("/uploads/images/".length); photos.push( { "sizes": { "small" : "/uploads/images/thumb_small_" + file, "medium" : "/uploads/images/thumb_medium_" + file, "large" : "/uploads/images/scaled_full_" + file } } ); }); return photos; }, getPollData: function(serializedForm) { var poll; var pollQuestion = serializedForm.poll_question; var pollAnswersArray = _.flatten([serializedForm["poll_answers[]"]]); var pollAnswers = _.map(pollAnswersArray, function(answer){ if (answer) { return {"answer": answer, "vote_count": 0}; } }); pollAnswers = _.without(pollAnswers, undefined); if(pollQuestion && pollAnswers.length) { poll = { "question": pollQuestion, "poll_answers": pollAnswers, "participation_count": "0" }; } return poll; }, createPostPreview: function() { var serializedForm = $("#new_status_message").serializeObject(); var photos = this.getUploadedPhotos(); var poll = this.getPollData(serializedForm); var locationCoords = serializedForm["location[coords]"]; if(!locationCoords || locationCoords === "") { locationCoords = ["", ""]; } else { locationCoords = locationCoords.split(","); } var location = { "address": $("#location_address").val(), "lat": locationCoords[0], "lng": locationCoords[1] }; var previewMessage = { "id": 0, "text": serializedForm["status_message[text]"], "public": serializedForm["aspect_ids[]"] === "public", "created_at": new Date().toISOString(), "interacted_at": new Date().toISOString(), "author": app.currentUser ? app.currentUser.attributes : {}, "mentioned_people": this.mention.getMentionedPeople(), "photos": photos, "title": serializedForm["status_message[text]"], "location": location, "interactions": {"likes": [], "reshares": [], "comments_count": 0, "likes_count": 0, "reshares_count": 0}, "poll": poll }; var previewPost = new app.views.PreviewPost({model: new app.models.Post(previewMessage)}).render().el; return $("").append(previewPost).html(); }, keyDown : function(evt) { if (evt.which === Keycodes.ENTER && (evt.metaKey || evt.ctrlKey)) { this.$("form").submit(); this.open(); return false; } }, clear : function() { // remove mentions this.mention.reset(); // clear text this.inputEl.val(""); this.inputEl.trigger("keyup") .trigger("keydown"); autosize.update(this.inputEl); // remove photos this.photozoneEl.find("li").remove(); this.$("input[name='photos[]']").remove(); this.wrapperEl.removeClass("with_attachments"); // empty upload-photo this.$("#fileInfo").empty(); // remove preview and close publishing area (CSS) this.markdownEditor.hidePreview(); this.close(); // disable submitting this.checkSubmitAvailability(); // hide spinner this.showSpinner(false); // enable input this.setInputEnabled(true); this.wrapperEl.removeClass("submitting"); // enable buttons this.setButtonsEnabled(true); // clear location this.destroyLocation(); // clear poll form this.viewPollCreator.clearInputs(); // force textchange plugin to update lastValue this.inputEl.data("lastValue", ""); return this; }, tryClose : function(){ // if it is not submittable and not in preview mode, close it. if (!this._submittable() && !this.markdownEditor.isPreviewMode()) { this.close(); } }, open : function() { if( this.disabled ) return; // visually 'open' the publisher this.$el.removeClass("closed"); this.wrapperEl.addClass("active"); autosize.update(this.inputEl); return this; }, close : function() { $(this.el).addClass("closed"); this.wrapperEl.removeClass("active"); this.inputEl.css("height", ""); autosize.update(this.inputEl); this.wrapperEl.removeClass("with-poll"); return this; }, showSpinner: function(bool) { if (bool) { this.$("#publisher_spinner").removeClass("hidden"); } else { this.$("#publisher_spinner").addClass("hidden"); } }, checkSubmitAvailability: function() { if( this._submittable() ) { this.setButtonsEnabled(true); } else { this.setButtonsEnabled(false); } }, setEnabled: function(bool) { this.setInputEnabled(bool); this.disabled = !bool; this.checkSubmitAvailability(); }, setButtonsEnabled: function(bool) { if (bool) { this.submitEl.removeAttr("disabled"); } else { this.submitEl.prop("disabled", true); } }, setInputEnabled: function(bool) { if (bool) { this.inputEl.removeAttr("disabled"); } else { this.inputEl.prop("disabled", true); } }, // determine submit availability _submittable: function() { var onlyWhitespaces = ($.trim(this.inputEl.val()) === ""), isPhotoAttached = (this.photozoneEl.children().length > 0), isValidPoll = this.viewPollCreator.validatePoll(); return (!onlyWhitespaces || isPhotoAttached) && isValidPoll && !this.disabled; }, _beforeUnload: function(e) { if(this._submittable() && this.inputEl.val() !== this.prefillText){ var confirmationMessage = Diaspora.I18n.t("confirm_unload"); (e || window.event).returnValue = confirmationMessage; //Gecko + IE return confirmationMessage; //Webkit, Safari, Chrome, etc. } } }); // jQuery helper for serializing a