From 01aa38fb82ef12e869a8f6105a47f185b620cee7 Mon Sep 17 00:00:00 2001
From: Waqas Khalid '
+ threadData.body = ' '
+ testText = ''
+ expectedText = ''
+
+ if truncatedText
+ testText = new Array(100).join('test ')
+ expectedText = testText.substring(0, 139)+ '…'
+ else
+ testText = 'Test body'
+ expectedText = 'Test body'
+
+ for i in [0..numberOfImages-1]
+ threadData.body = threadData.body + imageTag
+ if i == 0
+ expectedHtml = expectedHtml + imageTag
+ else
+ expectedHtml = expectedHtml + 'image omitted'
+
+ threadData.body = threadData.body + '' + testText + '
'
+ window.MathJax = { Hub: { Queue: -> } }
+
+ makeView = (thread) ->
+ view = new DiscussionThreadProfileView(el: $("article#thread_#{thread.id}"), model: thread)
+ spyConvertMath(view)
+ return view
+
+ makeThread = (threadData) ->
+ thread = new Thread(threadData)
+ thread.discussion = new Discussion()
+ return thread
+
+ spyConvertMath = (view) ->
+ spyOn(view, "convertMath").andCallFake( ->
+ @model.set('markdownBody', @model.get('body'))
+ )
+
+ checkPostWithImages = (numberOfImages, truncatedText, threadData, imageTag) ->
+ expectedHtml = '
Some images in this post have been omitted
' + else + expectedHtml = expectedHtml + '' + expectedText + '' + + view = makeView(makeThread(threadData)) + view.render() + expect(view.$el.find(".post-body").html()).toEqual(expectedHtml) + + checkBody = (truncated, view, threadData) -> + view.render() + if not truncated + expect(view.model.get("body")).toEqual(view.model.get("abbreviatedBody")) + expect(view.$el.find(".post-body").html()).toEqual(threadData.body) + else + expect(view.model.get("body")).not.toEqual(view.model.get("abbreviatedBody")) + expect(view.$el.find(".post-body").html()).not.toEqual(threadData.body) + outputHtmlStripped = view.$el.find(".post-body").html().replace(/(<([^>]+)>)/ig,""); + outputHtmlStripped = outputHtmlStripped.replace("Some images in this post have been omitted","") + outputHtmlStripped = outputHtmlStripped.replace("image omitted","") + inputHtmlStripped = threadData.body.replace(/(<([^>]+)>)/ig,""); + expectedOutput = inputHtmlStripped.substring(0, 139)+ '…' + expect(outputHtmlStripped).toEqual(expectedOutput) + expect(view.$el.find(".post-body").html().indexOf("…")).toBeGreaterThan(0) + + describe "Body markdown should be correct", -> + + it "untruncated text without markdown body", -> + @threadData.body = "Test body" + view = makeView(makeThread(@threadData)) + checkBody(false, view, @threadData) + + it "truncated text without markdown body", -> + @threadData.body = new Array(100).join("test ") + view = makeView(makeThread(@threadData)) + checkBody(true, view, @threadData) + + it "untruncated text with markdown body", -> + @threadData.body = '' + @imageTag + 'Google top search engine
' + view = makeView(makeThread(@threadData)) + checkBody(false, view, @threadData) + + it "truncated text with markdown body", -> + testText = new Array(100).join("test ") + @threadData.body = '' + @imageTag + @imageTag + '' + testText + '
' + view = makeView(makeThread(@threadData)) + checkBody(true, view, @threadData) + + for numImages in [1, 2, 10] + for truncatedText in [true, false] + it "body with #{numImages} images and #{if truncatedText then "truncated" else "untruncated"} text", -> + checkPostWithImages(numImages, truncatedText, @threadData, @imageTag) diff --git a/common/static/coffee/src/discussion/utils.coffee b/common/static/coffee/src/discussion/utils.coffee index 0835534d0b..50530622b2 100644 --- a/common/static/coffee/src/discussion/utils.coffee +++ b/common/static/coffee/src/discussion/utils.coffee @@ -309,6 +309,16 @@ class @DiscussionUtil minLength++ return text.substr(0, minLength) + gettext('…') + @abbreviateHTML: (html, minLength) -> + # Abbreviates the html to at least minLength characters, stopping at word boundaries + truncated_text = jQuery.truncate(html, {length: minLength, noBreaks: true, ellipsis: gettext('…')}) + $result = $("Some images in this post have been omitted
") + imagesToReplace.replaceWith("image omitted") + $result.html() + @getPaginationParams: (curPage, numPages, pageUrlFunc) => delta = 2 minPage = Math.max(curPage - delta, 1) diff --git a/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee b/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee index 8fa3330076..51afd0bd24 100644 --- a/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee +++ b/common/static/coffee/src/discussion/views/discussion_thread_profile_view.coffee @@ -2,21 +2,20 @@ if Backbone? class @DiscussionThreadProfileView extends Backbone.View render: -> @template = DiscussionUtil.getTemplate("_profile_thread") - if not @model.has('abbreviatedBody') - @abbreviateBody() + @convertMath() + @abbreviateBody() params = $.extend(@model.toJSON(),{permalink: @model.urlFor('retrieve')}) if not @model.get('anonymous') params = $.extend(params, user:{username: @model.username, user_url: @model.user_url}) @$el.html(Mustache.render(@template, params)) @$("span.timeago").timeago() - @convertMath() + element = @$(".post-body") + MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]] @ convertMath: -> - element = @$(".post-body") - element.html DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight element.text() - MathJax.Hub.Queue ["Typeset", MathJax.Hub, element[0]] + @model.set('markdownBody', DiscussionUtil.postMathJaxProcessor DiscussionUtil.markdownWithHighlight @model.get('body')) abbreviateBody: -> - abbreviated = DiscussionUtil.abbreviateString @model.get('body'), 140 + abbreviated = DiscussionUtil.abbreviateHTML @model.get('markdownBody'), 140 @model.set('abbreviatedBody', abbreviated) diff --git a/common/static/js/vendor/jquery.truncate.js b/common/static/js/vendor/jquery.truncate.js new file mode 100755 index 0000000000..22d33e0f06 --- /dev/null +++ b/common/static/js/vendor/jquery.truncate.js @@ -0,0 +1,107 @@ +/** +Copyright (c) 2010-2012 Pathable + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +https://github.com/pathable/truncate +**/ +(function($) { + + // Matches trailing non-space characters. + var chop = /(\s*\S+|\s)$/; + + // Return a truncated html string. Delegates to $.fn.truncate. + $.truncate = function(html, options) { + return $('').append(html).truncate(options).html(); + }; + + // Truncate the contents of an element in place. + $.fn.truncate = function(options) { + if ($.isNumeric(options)) options = {length: options}; + var o = $.extend({}, $.truncate.defaults, options); + + return this.each(function() { + var self = $(this); + + if (o.noBreaks) self.find('br').replaceWith(' '); + + var text = self.text(); + var excess = text.length - o.length; + + if (o.stripTags) self.text(text); + + // Chop off any partial words if appropriate. + if (o.words && excess > 0) { + excess = text.length - text.slice(0, o.length).replace(chop, '').length - 1; + } + + if (excess < 0 || !excess && !o.truncated) return; + + // Iterate over each child node in reverse, removing excess text. + $.each(self.contents().get().reverse(), function(i, el) { + var $el = $(el); + var text = $el.text(); + var length = text.length; + + // If the text is longer than the excess, remove the node and continue. + if (length <= excess) { + o.truncated = true; + excess -= length; + $el.remove(); + return; + } + + // Remove the excess text and append the ellipsis. + if (el.nodeType === 3) { + $(el.splitText(length - excess - 1)).replaceWith(o.ellipsis); + return false; + } + + // Recursively truncate child nodes. + $el.truncate($.extend(o, {length: length - excess})); + return false; + }); + }); + }; + + $.truncate.defaults = { + + // Strip all html elements, leaving only plain text. + stripTags: false, + + // Only truncate at word boundaries. + words: false, + + // Replace instances of