diff --git a/app/assets/javascripts/discourse/controllers/quote-button.js.es6 b/app/assets/javascripts/discourse/controllers/quote-button.js.es6 index e348cb05f5c..e2442cc23c2 100644 --- a/app/assets/javascripts/discourse/controllers/quote-button.js.es6 +++ b/app/assets/javascripts/discourse/controllers/quote-button.js.es6 @@ -8,9 +8,7 @@ export default DiscourseController.extend({ loadScript('defer/html-sanitizer-bundle'); }.on('init'), - /** - If the buffer is cleared, clear out other state (post) - **/ + // If the buffer is cleared, clear out other state (post) bufferChanged: function() { if (this.blank('buffer')) this.set('post', null); }.observes('buffer'), @@ -18,40 +16,38 @@ export default DiscourseController.extend({ /** Save the currently selected text and displays the "quote reply" button - - @method selectText **/ - selectText: function(postId) { + selectText(postId) { // anonymous users cannot "quote-reply" if (!Discourse.User.current()) return; // don't display the "quote-reply" button if we can't create a post if (!this.get('controllers.topic.model.details.can_create_post')) return; - var selection = window.getSelection(); + const selection = window.getSelection(); // no selections if (selection.rangeCount === 0) return; // retrieve the selected range - var range = selection.getRangeAt(0), - cloned = range.cloneRange(), - $ancestor = $(range.commonAncestorContainer); + const range = selection.getRangeAt(0), + cloned = range.cloneRange(), + $ancestor = $(range.commonAncestorContainer); if ($ancestor.closest('.cooked').length === 0) { this.set('buffer', ''); return; } - var selectedText = Discourse.Utilities.selectedText(); + const selectedText = Discourse.Utilities.selectedText(); if (this.get('buffer') === selectedText) return; // we need to retrieve the post data from the posts collection in the topic controller - var postStream = this.get('controllers.topic.postStream'); + const postStream = this.get('controllers.topic.postStream'); this.set('post', postStream.findLoadedPost(postId)); this.set('buffer', selectedText); // create a marker element - var markerElement = document.createElement("span"); + const markerElement = document.createElement("span"); // containing a single invisible character markerElement.appendChild(document.createTextNode("\u{feff}")); @@ -61,42 +57,37 @@ export default DiscourseController.extend({ range.insertNode(markerElement); // retrieve the position of the market - var markerOffset = $(markerElement).offset(), - $quoteButton = $('.quote-button'); + const markerOffset = $(markerElement).offset(), + $quoteButton = $('.quote-button'); // remove the marker markerElement.parentNode.removeChild(markerElement); // work around Chrome that would sometimes lose the selection - var sel = window.getSelection(); + const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(cloned); // move the quote button above the marker Em.run.schedule('afterRender', function() { - var top = markerOffset.top; - var left = markerOffset.left; + let topOff = markerOffset.top; + let leftOff = markerOffset.left; if (Discourse.Mobile.isMobileDevice) { - top = top + 20; - left = Math.min(left + 10, $(window).width() - $quoteButton.outerWidth()); + topOff = topOff + 20; + leftOff = Math.min(leftOff + 10, $(window).width() - $quoteButton.outerWidth()); } else { - top = top - $quoteButton.outerHeight() - 5; + topOff = topOff - $quoteButton.outerHeight() - 5; } - $quoteButton.offset({ top: top, left: left }); + $quoteButton.offset({ top: topOff, left: leftOff }); }); }, - /** - Quote the currently selected text - - @method quoteText - **/ - quoteText: function() { - var post = this.get('post'); - var composerController = this.get('controllers.composer'); - var composerOpts = { + quoteText() { + const post = this.get('post'); + const composerController = this.get('controllers.composer'); + const composerOpts = { action: Discourse.Composer.REPLY, draftKey: this.get('post.topic.draft_key') }; @@ -108,13 +99,13 @@ export default DiscourseController.extend({ } // If the composer is associated with a different post, we don't change it. - var composerPost = composerController.get('content.post'); + const composerPost = composerController.get('content.post'); if (composerPost && (composerPost.get('id') !== this.get('post.id'))) { composerOpts.post = composerPost; } - var buffer = this.get('buffer'); - var quotedText = Discourse.Quote.build(post, buffer); + const buffer = this.get('buffer'); + const quotedText = Discourse.Quote.build(post, buffer); composerOpts.quote = quotedText; if (composerController.get('content.viewOpen') || composerController.get('content.viewDraft')) { composerController.appendBlockAtCursor(quotedText.trim()); @@ -125,12 +116,7 @@ export default DiscourseController.extend({ return false; }, - /** - Deselect the currently selected text - - @method deselectText - **/ - deselectText: function() { + deselectText() { // clear selected text window.getSelection().removeAllRanges(); // clean up the buffer diff --git a/app/assets/javascripts/discourse/lib/quote.js b/app/assets/javascripts/discourse/lib/quote.js index 54692464ca5..c93cd5a89b1 100644 --- a/app/assets/javascripts/discourse/lib/quote.js +++ b/app/assets/javascripts/discourse/lib/quote.js @@ -1,21 +1,8 @@ -/** - Build the BBCode for a Quote - - @class BBCode - @namespace Discourse - @module Discourse -**/ Discourse.Quote = { REGEXP: /\[quote=([^\]]*)\]((?:[\s\S](?!\[quote=[^\]]*\]))*?)\[\/quote\]/im, - /** - Build the BBCode quote around the selected text - - @method buildQuote - @param {Discourse.Post} post The post we are quoting - @param {String} contents The text selected - **/ + // Build the BBCode quote around the selected text build: function(post, contents) { var contents_hashed, result, sansQuotes, stripped, stripped_hashed, tmp; if (!contents) contents = ""; diff --git a/app/assets/javascripts/discourse/views/composer.js.es6 b/app/assets/javascripts/discourse/views/composer.js.es6 index 9a9cd0d0778..19f10e9c144 100644 --- a/app/assets/javascripts/discourse/views/composer.js.es6 +++ b/app/assets/javascripts/discourse/views/composer.js.es6 @@ -1,6 +1,7 @@ import userSearch from 'discourse/lib/user-search'; import afterTransition from 'discourse/lib/after-transition'; import loadScript from 'discourse/lib/load-script'; +import avatarTemplate from 'discourse/lib/avatar-template'; const ComposerView = Discourse.View.extend(Ember.Evented, { _lastKeyTimeout: null, @@ -244,7 +245,10 @@ const ComposerView = Discourse.View.extend(Ember.Evented, { if (posts) { const quotedPost = posts.findProperty("post_number", postNumber); if (quotedPost) { - return Discourse.Utilities.tinyAvatar(quotedPost.get("avatar_template")); + const username = quotedPost.get('username'), + uploadId = quotedPost.get('uploaded_avatar_id'); + + return Discourse.Utilities.tinyAvatar(avatarTemplate(username, uploadId)); } } } diff --git a/app/assets/javascripts/discourse/views/quote-button.js.es6 b/app/assets/javascripts/discourse/views/quote-button.js.es6 index fe68d4db560..cb4c3b07034 100644 --- a/app/assets/javascripts/discourse/views/quote-button.js.es6 +++ b/app/assets/javascripts/discourse/views/quote-button.js.es6 @@ -1,11 +1,3 @@ -/** - This view is used for rendering the pop-up quote button - - @class QuoteButtonView - @extends Discourse.View - @namespace Discourse - @module Discourse -**/ export default Discourse.View.extend({ classNames: ['quote-button'], classNameBindings: ['visible'], @@ -13,20 +5,12 @@ export default Discourse.View.extend({ isTouchInProgress: 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: Em.computed.notEmpty('controller.buffer'), - /** - Renders the pop-up quote button. - - @method render - **/ - render: function(buffer) { + render(buffer) { buffer.push(I18n.t("post.quote_reply")); }, @@ -38,15 +22,15 @@ export default Discourse.View.extend({ @method didInsertElement **/ - didInsertElement: function() { - var controller = this.get('controller'), - view = this; + didInsertElement() { + const controller = this.get('controller'), + view = this; $(document) .on("mousedown.quote-button", function(e) { view.set('isMouseDown', true); - var $target = $(e.target); + const $target = $(e.target); // we don't want to deselect when we click on buttons that use it if ($target.hasClass('quote-button') || $target.closest('.create').length || @@ -75,27 +59,17 @@ export default Discourse.View.extend({ }); }, - /** - Selects the text - - @method selectText - **/ - selectText: function(target, controller) { - var $target = $(target); + selectText(target, controller) { + const $target = $(target); // breaks if quoting has been disabled by the user if (!Discourse.User.currentProp('enable_quoting')) return; // retrieve the post id from the DOM - var postId = $target.closest('.boxed').data('post-id'); + const 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() { + willDestroyElement() { $(document) .off("mousedown.quote-button") .off("mouseup.quote-button") @@ -104,12 +78,7 @@ export default Discourse.View.extend({ .off("selectionchange"); }, - /** - Quote the selected text when clicking on the quote button. - - @method click - **/ - click: function(e) { + click(e) { e.stopPropagation(); return this.get('controller').quoteText(e); } diff --git a/lib/pretty_text.rb b/lib/pretty_text.rb index d095bd0c6a3..fadc410ed8f 100644 --- a/lib/pretty_text.rb +++ b/lib/pretty_text.rb @@ -20,12 +20,28 @@ module PrettyText end end - # function here are available to v8 + # functions here are available to v8 def avatar_template(username) return "" unless username user = User.find_by(username_lower: username.downcase) return "" unless user.present? - schemaless absolute user.avatar_template + + + # TODO: Add support for ES6 and call `avatar-template` directly + if !user.uploaded_avatar_id && SiteSetting.default_avatars.present? + split_avatars = SiteSetting.default_avatars.split("\n") + if split_avatars.present? + hash = username.each_char.reduce(0) do |result, char| + [((result << 5) - result) + char.ord].pack('L').unpack('l').first + end + + avatar_template = split_avatars[hash.abs % split_avatars.size] + end + else + avatar_template = user.avatar_template + end + + schemaless absolute avatar_template end def is_username_valid(username) @@ -65,6 +81,8 @@ module PrettyText ctx.eval("var window = {}; window.devicePixelRatio = 2;") # hack to make code think stuff is retina ctx.eval("var I18n = {}; I18n.t = function(a,b){ return helpers.t(a,b); }"); + ctx.eval("var modules = {};") + decorate_context(ctx) ctx_load(ctx, @@ -74,7 +92,7 @@ module PrettyText "app/assets/javascripts/discourse/dialects/dialect.js", "app/assets/javascripts/discourse/lib/utilities.js", "app/assets/javascripts/discourse/lib/html.js", - "app/assets/javascripts/discourse/lib/markdown.js" + "app/assets/javascripts/discourse/lib/markdown.js", ) Dir["#{app_root}/app/assets/javascripts/discourse/dialects/**.js"].sort.each do |dialect|