diff --git a/app/assets/javascripts/discourse/controllers/quote_button_controller.js b/app/assets/javascripts/discourse/controllers/quote_button_controller.js index bceadff0d57..ff617202c99 100644 --- a/app/assets/javascripts/discourse/controllers/quote_button_controller.js +++ b/app/assets/javascripts/discourse/controllers/quote_button_controller.js @@ -10,7 +10,6 @@ **/ Discourse.QuoteButtonController = Discourse.Controller.extend({ needs: ['topic', 'composer'], - started: null, init: function() { this._super(); @@ -19,42 +18,56 @@ Discourse.QuoteButtonController = Discourse.Controller.extend({ // If the buffer is cleared, clear out other state (post) bufferChanged: (function() { - if (this.blank('buffer')) { - return this.set('post', null); - } + if (this.blank('buffer')) this.set('post', null); }).observes('buffer'), - mouseDown: function(e) { - this.started = [e.pageX, e.pageY]; - }, - - mouseUp: function(e) { - if (this.started[1] > e.pageY) { - this.started = [e.pageX, e.pageY]; - } - }, - selectText: function(e) { - var $quoteButton, left, selectedText, top; + // anonymous users cannot "quote-reply" if (!Discourse.get('currentUser')) return; + // there is no need to display the "quote-reply" button if we can't create a post if (!this.get('controllers.topic.content.can_create_post')) return; - selectedText = Discourse.Utilities.selectedText(); + // retrieve the selected range + var range = window.getSelection().getRangeAt(0).cloneRange(); + + // do not be present the "quote reply" button if you select text spanning two posts + // this basically look for the first "DIV" container... + var commonDivAncestorContainer = range.commonAncestorContainer; + while (commonDivAncestorContainer.nodeName !== 'DIV') commonDivAncestorContainer = commonDivAncestorContainer.parentNode; + // ... and check it has the 'cooked' class (which indicates we're in a post) + if (commonDivAncestorContainer.className.indexOf('cooked') === -1) return; + + var selectedText = Discourse.Utilities.selectedText(); if (this.get('buffer') === selectedText) return; if (this.get('lastSelected') === selectedText) return; this.set('post', e.context); this.set('buffer', selectedText); - top = e.pageY + 5; - left = e.pageX + 5; - $quoteButton = $('.quote-button'); - if (this.started) { - top = this.started[1] - 50; - left = ((left - this.started[0]) / 2) + this.started[0] - ($quoteButton.width() / 2); - } - $quoteButton.css({ top: top, left: left }); - this.started = null; - return false; + + // collapse the range at the beginning of the selection + // (ie. moves the end point to the start point) + range.collapse(true); + + // create a marker element containing a single invisible character + var markerElement = document.createElement("span"); + markerElement.appendChild(document.createTextNode("\ufeff")); + // insert it at the beginning of our range + range.insertNode(markerElement); + + // find marker position (cf. http://www.quirksmode.org/js/findpos.html) + var obj = markerElement, left = 0, top = 0; + do { + left += obj.offsetLeft; + top += obj.offsetTop; + obj = obj.offsetParent; + } while (obj.offsetParent); + + // move the quote button at the beginning of the selection + var $quoteButton = $('.quote-button'); + $quoteButton.css({ top: top - $quoteButton.outerHeight(), left: left }); + + // remove the marker + markerElement.parentNode.removeChild(markerElement); }, /** diff --git a/app/assets/javascripts/discourse/views/post_view.js b/app/assets/javascripts/discourse/views/post_view.js index dd798255844..fff8a826c92 100644 --- a/app/assets/javascripts/discourse/views/post_view.js +++ b/app/assets/javascripts/discourse/views/post_view.js @@ -29,8 +29,7 @@ Discourse.PostView = Discourse.View.extend({ }).property('parentView'), postTypeClass: (function() { - if (this.get('post.post_type') === Discourse.get('site.post_types.moderator_action')) return 'moderator'; - return 'regular'; + return this.get('post.post_type') === Discourse.get('site.post_types.moderator_action') ? 'moderator' : 'regular'; }).property('post.post_type'), // If the cooked content changed, add the quote controls @@ -59,12 +58,7 @@ Discourse.PostView = Discourse.View.extend({ }, selectText: (function() { - if (this.get('post.selected')) { - return Em.String.i18n('topic.multi_select.selected', { - count: this.get('controller.selectedCount') - }); - } - return Em.String.i18n('topic.multi_select.select'); + 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'), repliesHidden: (function() { @@ -198,10 +192,9 @@ Discourse.PostView = Discourse.View.extend({ var postView = this; return this.$('aside.quote').each(function(i, e) { - var $aside, $title; - $aside = $(e); + var $aside = $(e); postView.updateQuoteElements($aside, 'chevron-down'); - $title = $('.title', $aside); + var $title = $('.title', $aside); // Unless it's a full quote, allow click to expand if (!($aside.data('full') || $title.data('has-quote-controls'))) { @@ -261,5 +254,3 @@ Discourse.PostView = Discourse.View.extend({ } } }); - - diff --git a/app/assets/javascripts/discourse/views/quote_buton_view.js b/app/assets/javascripts/discourse/views/quote_button_view.js similarity index 76% rename from app/assets/javascripts/discourse/views/quote_buton_view.js rename to app/assets/javascripts/discourse/views/quote_button_view.js index ecb7b3d1595..02288943ea5 100644 --- a/app/assets/javascripts/discourse/views/quote_buton_view.js +++ b/app/assets/javascripts/discourse/views/quote_button_view.js @@ -16,23 +16,21 @@ Discourse.QuoteButtonView = Discourse.View.extend({ }, hasBuffer: (function() { - if (this.present('controller.buffer')) return 'visible'; - return null; + return this.present('controller.buffer') ? 'visible' : null; }).property('controller.buffer'), willDestroyElement: function() { - $(document).unbind("mousedown.quote-button"); + $(document).off("mousedown.quote-button"); }, didInsertElement: function() { // Clear quote button if they click elsewhere var quoteButtonView = this; - return $(document).bind("mousedown.quote-button", function(e) { + $(document).on("mousedown.quote-button", function(e) { if ($(e.target).hasClass('quote-button')) return; if ($(e.target).hasClass('create')) return; - quoteButtonView.controller.mouseDown(e); quoteButtonView.set('controller.lastSelected', quoteButtonView.get('controller.buffer')); - return quoteButtonView.set('controller.buffer', ''); + quoteButtonView.set('controller.buffer', ''); }); }, @@ -42,5 +40,3 @@ Discourse.QuoteButtonView = Discourse.View.extend({ } }); - - diff --git a/app/assets/stylesheets/application/topic.css.scss b/app/assets/stylesheets/application/topic.css.scss index f9693e6163e..9bac97b78cf 100644 --- a/app/assets/stylesheets/application/topic.css.scss +++ b/app/assets/stylesheets/application/topic.css.scss @@ -240,7 +240,6 @@ border: 5px solid rgba(0, 0, 0, 0.75); top: 10px; left: 10px; - z-index: 99999; position: absolute; display: none; @include border-radius-all(4px);