diff --git a/app/assets/javascripts/discourse/controllers/quote_button_controller.js b/app/assets/javascripts/discourse/controllers/quote_button_controller.js index ae7d478d6c1..8723171c222 100644 --- a/app/assets/javascripts/discourse/controllers/quote_button_controller.js +++ b/app/assets/javascripts/discourse/controllers/quote_button_controller.js @@ -29,17 +29,15 @@ Discourse.QuoteButtonController = Discourse.Controller.extend({ @method selectText **/ - selectText: function(e) { + selectText: function(postId) { // anonymous users cannot "quote-reply" if (!Discourse.get('currentUser')) return; // don't display the "quote-reply" button if we can't create a post if (!this.get('controllers.topic.content.can_create_post')) return; var selection = window.getSelection(); - // no selections if (selection.rangeCount === 0) return; - // retrieve the selected range var range = selection.getRangeAt(0), cloned = range.cloneRange(), @@ -51,9 +49,17 @@ Discourse.QuoteButtonController = Discourse.Controller.extend({ var selectedText = Discourse.Utilities.selectedText(); if (this.get('buffer') === selectedText) return; - if (this.get('lastSelected') === selectedText) return; - this.set('post', e.context); + // we need to retrieve the post data from the posts collection in the topic controller + var posts = this.get('controllers.topic.posts'), + length = posts.length, + post; + + for (var p = 0; p < length; p++) { + if (posts[p].id === postId) { post = posts[p]; break; } + } + + this.set('post', post); this.set('buffer', selectedText); // collapse the range at the beginning of the selection @@ -117,6 +123,18 @@ Discourse.QuoteButtonController = Discourse.Controller.extend({ } this.set('buffer', ''); return false; + }, + + /** + Deselect the currently selected text + + @method deselectText + **/ + deselectText: function() { + // clear selected text + window.getSelection().removeAllRanges(); + // clean up the buffer + this.set('buffer', ''); } }); diff --git a/app/assets/javascripts/discourse/views/post_view.js b/app/assets/javascripts/discourse/views/post_view.js index ab298a45e56..739ffbaf44b 100644 --- a/app/assets/javascripts/discourse/views/post_view.js +++ b/app/assets/javascripts/discourse/views/post_view.js @@ -43,34 +43,19 @@ Discourse.PostView = Discourse.View.extend({ this.set('context', this.get('content')); }, - mouseDown: function(e) { - this.set('isMouseDown', true); - }, - mouseUp: function(e) { if (this.get('controller.multiSelect') && (e.metaKey || e.ctrlKey)) { this.toggleProperty('post.selected'); } - - if (!Discourse.get('currentUser.enable_quoting')) return; - if ($(e.target).closest('.topic-body').length === 0) return; - - var qbc = this.get('controller.controllers.quoteButton'); - if (qbc) { - e.context = this.get('post'); - qbc.selectText(e); - } - - this.set('isMouseDown', false); }, - selectText: (function() { + selectText: function() { return this.get('post.selected') ? Em.String.i18n('topic.multi_select.selected', { count: this.get('controller.selectedCount') }) : Em.String.i18n('topic.multi_select.select'); - }).property('post.selected', 'controller.selectedCount'), + }.property('post.selected', 'controller.selectedCount'), - repliesHidden: (function() { + repliesHidden: function() { return !this.get('repliesShown'); - }).property('repliesShown'), + }.property('repliesShown'), // Click on the replies button showReplies: function() { @@ -262,30 +247,5 @@ Discourse.PostView = Discourse.View.extend({ if (controller && controller.postRendered) { controller.postRendered(post); } - - // make the selection work under iOS - // "selectionchange" event is only supported in IE, Safari & Chrome - var postView = this; - $(document).on('selectionchange', function(e) { - // quoting as been disabled by the user - if (!Discourse.get('currentUser.enable_quoting')) return; - // there is no need to handle this event when the mouse is down - if (postView.get('isMouseDown')) return; - // find out whether we currently are selecting inside a post - var closestPosts = $(window.getSelection().anchorNode).closest('.topic-post'); - if (closestPosts.length === 0) return; - // this event is bound for every posts in the topic, but is triggered on "document" - // we should therefore filter the event to only the right post - if (closestPosts[0].id !== postView.elementId) return; - var qbc = postView.get('controller.controllers.quoteButton'); - if (qbc) { - e.context = postView.get('post'); - qbc.selectText(e); - } - }); - }, - - willDestroyElement: function() { - $(document).off('selectionchange'); } }); diff --git a/app/assets/javascripts/discourse/views/quote_button_view.js b/app/assets/javascripts/discourse/views/quote_button_view.js index 29d3eb19958..a718d2bb0af 100644 --- a/app/assets/javascripts/discourse/views/quote_button_view.js +++ b/app/assets/javascripts/discourse/views/quote_button_view.js @@ -8,32 +8,92 @@ **/ Discourse.QuoteButtonView = Discourse.View.extend({ classNames: ['quote-button'], - classNameBindings: ['hasBuffer'], + classNameBindings: ['visible'], + isMouseDown: false, + /** + Determines whether the pop-up quote button should be visible. + The button is visible whenever there is something in the buffer + (ie. something has been selected) + + @property visible + **/ + visible: function() { + return this.present('controller.buffer'); + }.property('controller.buffer'), + + /** + Renders the pop-up quote button. + + @method render + **/ render: function(buffer) { buffer.push('  '); buffer.push(Em.String.i18n("post.quote_reply")); }, - hasBuffer: function() { - return this.present('controller.buffer') ? 'visible' : null; - }.property('controller.buffer'), - - willDestroyElement: function() { - $(document).off("mousedown.quote-button"); - }, + /** + Binds to the following global events: + - `mousedown` to clear the quote button if they click elsewhere. + - `mouseup` to trigger the display of the quote button. + - `selectionchange` to make the selection work under iOS + @method didInsertElement + **/ didInsertElement: function() { - // Clear quote button if they click elsewhere - var quoteButtonView = this; - $(document).on("mousedown.quote-button", function(e) { - if ($(e.target).hasClass('quote-button')) return; - if ($(e.target).hasClass('create')) return; - quoteButtonView.set('controller.lastSelected', quoteButtonView.get('controller.buffer')); - quoteButtonView.set('controller.buffer', ''); + var controller = this.get('controller'), + view = this; + + $(document) + .on("mousedown.quote-button", function(e) { + view.set('isMouseDown', true); + if ($(e.target).hasClass('quote-button') || $(e.target).hasClass('create')) return; + controller.deselectText(); + }) + .on('mouseup.quote-button', function(e) { + view.selectText(e.target, controller); + view.set('isMouseDown', false); + }) + .on('selectionchange', function() { + // there is no need to handle this event when the mouse is down + if (view.get('isMouseDown')) return; + // `selection.anchorNode` is used as a target + view.selectText(window.getSelection().anchorNode, controller); }); }, + /** + Selects the text + + @method selectText + **/ + selectText: function(target, controller) { + var $target = $(target); + // quoting as been disabled by the user + if (!Discourse.get('currentUser.enable_quoting')) return; + // retrieve the post id from the DOM + var postId = $target.closest('.boxed').data('post-id'); + // select the text + if (postId) controller.selectText(postId); + }, + + /** + Unbinds from global `mouseup, mousedown, selectionchange` events + + @method willDestroyElement + **/ + willDestroyElement: function() { + $(document) + .off("mousedown.quote-button") + .off("mouseup.quote-button") + .off("selectionchange"); + }, + + /** + Quote the selected text when clicking on the quote button. + + @method click + **/ click: function(e) { e.stopPropagation(); return this.get('controller').quoteText(e);