Merge branch 'Raven24-show-more'
This commit is contained in:
commit
3c83f671ac
11 changed files with 122 additions and 439 deletions
|
|
@ -21,7 +21,6 @@ javascripts:
|
||||||
- public/javascripts/vendor/jquery.autoresize.js
|
- public/javascripts/vendor/jquery.autoresize.js
|
||||||
- public/javascripts/vendor/jquery-ui-1.8.9.custom.min.js
|
- public/javascripts/vendor/jquery-ui-1.8.9.custom.min.js
|
||||||
- public/javascripts/vendor/jquery.charcount.js
|
- public/javascripts/vendor/jquery.charcount.js
|
||||||
- public/javascripts/vendor/jquery.expander.js
|
|
||||||
- public/javascripts/vendor/jquery.placeholder.js
|
- public/javascripts/vendor/jquery.placeholder.js
|
||||||
- public/javascripts/vendor/timeago.js
|
- public/javascripts/vendor/timeago.js
|
||||||
- public/javascripts/vendor/facebox.js
|
- public/javascripts/vendor/facebox.js
|
||||||
|
|
|
||||||
24
features/show_more.feature
Normal file
24
features/show_more.feature
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
@javascript
|
||||||
|
Feature: collapsing and expanding long posts
|
||||||
|
In order to tame the lengths of posts in my stream
|
||||||
|
As a rocket scientist
|
||||||
|
I want long posts to be collapsed and expand on click
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given a user with username "bob"
|
||||||
|
And I sign in as "bob@bob.bob"
|
||||||
|
And I am on the home page
|
||||||
|
|
||||||
|
Scenario: post a very long message
|
||||||
|
Given I post an extremely long status message
|
||||||
|
And I go to the home page
|
||||||
|
|
||||||
|
Then the post should be collapsed
|
||||||
|
|
||||||
|
Scenario: expand a very long message
|
||||||
|
Given I post an extremely long status message
|
||||||
|
And I go to the home page
|
||||||
|
And I expand the post
|
||||||
|
|
||||||
|
Then the post should be expanded
|
||||||
|
|
||||||
|
|
@ -2,6 +2,14 @@ Then /^the post "([^"]*)" should be marked nsfw$/ do |text|
|
||||||
assert_nsfw(text)
|
assert_nsfw(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Then /^the post should be collapsed$/ do
|
||||||
|
first_post_collapsed?
|
||||||
|
end
|
||||||
|
|
||||||
|
Then /^the post should be expanded$/ do
|
||||||
|
first_post_expanded?
|
||||||
|
end
|
||||||
|
|
||||||
Then /^I should see an uploaded image within the photo drop zone$/ do
|
Then /^I should see an uploaded image within the photo drop zone$/ do
|
||||||
find("#photodropzone img")["src"].should include("uploads/images")
|
find("#photodropzone img")["src"].should include("uploads/images")
|
||||||
end
|
end
|
||||||
|
|
@ -32,6 +40,9 @@ When /^I click on the first block button/ do
|
||||||
find(".block_user").click
|
find(".block_user").click
|
||||||
end
|
end
|
||||||
|
|
||||||
|
When /^I expand the post$/ do
|
||||||
|
expand_first_post
|
||||||
|
end
|
||||||
|
|
||||||
Then /^I should see "([^"]*)" as the first post in my stream$/ do |text|
|
Then /^I should see "([^"]*)" as the first post in my stream$/ do |text|
|
||||||
first_post_text.should include(text)
|
first_post_text.should include(text)
|
||||||
|
|
@ -44,3 +55,7 @@ end
|
||||||
When /^I click the publisher and post "([^"]*)"$/ do |text|
|
When /^I click the publisher and post "([^"]*)"$/ do |text|
|
||||||
click_and_post(text)
|
click_and_post(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
When /^I post an extremely long status message$/ do
|
||||||
|
click_and_post("I am a very interesting message " * 64)
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,22 @@ module PublishingCukeHelpers
|
||||||
')
|
')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def expand_first_post
|
||||||
|
find(".stream_element:first .expander").click
|
||||||
|
wait_until{ !find(".expander").visible? }
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_post_collapsed?
|
||||||
|
find(".stream_element:first .collapsible").should have_css(".expander")
|
||||||
|
find(".stream_element:first .collapsible").has_selector?(".collapsed")
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_post_expanded?
|
||||||
|
find(".stream_element:first .expander").should_not be_visible
|
||||||
|
find(".stream_element:first .collapsible").has_no_selector?(".collapsed")
|
||||||
|
find(".stream_element:first .collapsible").has_selector?(".opened")
|
||||||
|
end
|
||||||
|
|
||||||
def first_post_text
|
def first_post_text
|
||||||
find('.stream_element:first .post-content').text()
|
find('.stream_element:first .post-content').text()
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@ app.views.Comment = app.views.Content.extend({
|
||||||
|
|
||||||
className : "comment media",
|
className : "comment media",
|
||||||
|
|
||||||
events : {
|
events : function() {
|
||||||
"click .comment_delete": "destroyModel"
|
return _.extend(app.views.Content.prototype.events, {
|
||||||
|
"click .comment_delete": "destroyModel"
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
presenter : function() {
|
presenter : function() {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
app.views.Content = app.views.StreamObject.extend({
|
app.views.Content = app.views.StreamObject.extend({
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
"click .oembed .thumb": "showOembedContent"
|
"click .oembed .thumb": "showOembedContent",
|
||||||
|
"click .expander": "expandPost"
|
||||||
},
|
},
|
||||||
|
|
||||||
presenter : function(){
|
presenter : function(){
|
||||||
|
|
@ -41,7 +42,17 @@ app.views.Content = app.views.StreamObject.extend({
|
||||||
var paramSeparator = ( /\?/.test(insertHTML.attr("src")) ) ? "&" : "?";
|
var paramSeparator = ( /\?/.test(insertHTML.attr("src")) ) ? "&" : "?";
|
||||||
insertHTML.attr("src", insertHTML.attr("src") + paramSeparator + "autoplay=1");
|
insertHTML.attr("src", insertHTML.attr("src") + paramSeparator + "autoplay=1");
|
||||||
oembed.html( insertHTML );
|
oembed.html( insertHTML );
|
||||||
|
},
|
||||||
|
|
||||||
|
expandPost: function(evt) {
|
||||||
|
var el = $(this.el).find('.collapsible');
|
||||||
|
el.removeClass('collapsed').addClass('opened');
|
||||||
|
el.animate({'height':el.data('orig-height')}, 550, function() {
|
||||||
|
el.css('height','auto');
|
||||||
|
});
|
||||||
|
$(evt.currentTarget).hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.views.StatusMessage = app.views.Content.extend({
|
app.views.StatusMessage = app.views.Content.extend({
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,5 @@
|
||||||
app.views.StreamObject = app.views.Base.extend({
|
app.views.StreamObject = app.views.Base.extend({
|
||||||
|
|
||||||
postRenderTemplate : function() {
|
|
||||||
// collapse long posts
|
|
||||||
this.$(".collapsible").expander({
|
|
||||||
slicePoint: 400,
|
|
||||||
widow: 12,
|
|
||||||
expandPrefix: "",
|
|
||||||
expandText: Diaspora.I18n.t("show_more"),
|
|
||||||
userCollapse: false,
|
|
||||||
beforeExpand: function() {
|
|
||||||
if ($(this).find('.summary').length == 0) { // Sigh. See comments in the spec.
|
|
||||||
var readMoreDiv = $(this).find('.read-more');
|
|
||||||
var lastElementBeforeReadMore = readMoreDiv.prev();
|
|
||||||
var firstElementAfterReadMore = readMoreDiv.next().children().first();
|
|
||||||
|
|
||||||
if (lastElementBeforeReadMore.is('p')) {
|
|
||||||
lastElementBeforeReadMore.append(firstElementAfterReadMore.html());
|
|
||||||
firstElementAfterReadMore.remove();
|
|
||||||
|
|
||||||
} else if (lastElementBeforeReadMore.is('ul') && firstElementAfterReadMore.is('ul')) {
|
|
||||||
var firstBullet = firstElementAfterReadMore.children().first();
|
|
||||||
lastElementBeforeReadMore.find('li').last().append(firstBullet.html());
|
|
||||||
firstBullet.remove();
|
|
||||||
}
|
|
||||||
readMoreDiv.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
destroyModel: function(evt) {
|
destroyModel: function(evt) {
|
||||||
if (evt) {
|
if (evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ app.views.Stream = Backbone.View.extend({
|
||||||
|
|
||||||
setupEvents : function(){
|
setupEvents : function(){
|
||||||
this.stream.bind("fetched", this.removeLoader, this)
|
this.stream.bind("fetched", this.removeLoader, this)
|
||||||
|
this.stream.bind("fetched", this.postRender, this)
|
||||||
this.stream.bind("allPostsLoaded", this.unbindInfScroll, this)
|
this.stream.bind("allPostsLoaded", this.unbindInfScroll, this)
|
||||||
this.collection.bind("add", this.addPost, this);
|
this.collection.bind("add", this.addPost, this);
|
||||||
if(window.app.user()) {
|
if(window.app.user()) {
|
||||||
|
|
@ -53,6 +54,35 @@ app.views.Stream = Backbone.View.extend({
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
postRender : function() {
|
||||||
|
// collapse long posts
|
||||||
|
var collHeight = 190,
|
||||||
|
collElem = $(this.el).find(".collapsible");
|
||||||
|
|
||||||
|
_.each(collElem, function(elem) {
|
||||||
|
var elem = $(elem),
|
||||||
|
oembed = elem.find(".oembed"),
|
||||||
|
addHeight = 0;
|
||||||
|
|
||||||
|
if( $.trim(oembed.html()) != "" ) {
|
||||||
|
addHeight = oembed.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
// only collapse if height exceeds collHeight+20%
|
||||||
|
if( elem.height() > ((collHeight*1.2)+addHeight) && !elem.is(".opened") ) {
|
||||||
|
elem.data("orig-height", elem.height() );
|
||||||
|
elem
|
||||||
|
.height( Math.max(collHeight, addHeight) )
|
||||||
|
.addClass("collapsed")
|
||||||
|
.append(
|
||||||
|
$('<div />')
|
||||||
|
.addClass('expander')
|
||||||
|
.text( Diaspora.I18n.t("show_more") )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
appendLoader: function(){
|
appendLoader: function(){
|
||||||
$("#paginate").html($("<img>", {
|
$("#paginate").html($("<img>", {
|
||||||
src : "/images/static-loader.png",
|
src : "/images/static-loader.png",
|
||||||
|
|
|
||||||
338
public/javascripts/vendor/jquery.expander.js
vendored
338
public/javascripts/vendor/jquery.expander.js
vendored
|
|
@ -1,338 +0,0 @@
|
||||||
/*!
|
|
||||||
* jQuery Expander Plugin v1.3
|
|
||||||
*
|
|
||||||
* Date: Sat Sep 17 00:37:34 2011 EDT
|
|
||||||
* Requires: jQuery v1.3+
|
|
||||||
*
|
|
||||||
* Copyright 2011, Karl Swedberg
|
|
||||||
* Dual licensed under the MIT and GPL licenses (just like jQuery):
|
|
||||||
* http://www.opensource.org/licenses/mit-license.php
|
|
||||||
* http://www.gnu.org/licenses/gpl.html
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
(function($) {
|
|
||||||
$.expander = {
|
|
||||||
version: '1.3',
|
|
||||||
defaults: {
|
|
||||||
// the number of characters at which the contents will be sliced into two parts.
|
|
||||||
slicePoint: 100,
|
|
||||||
|
|
||||||
// whether to keep the last word of the summary whole (true) or let it slice in the middle of a word (false)
|
|
||||||
preserveWords: true,
|
|
||||||
|
|
||||||
// widow: a threshold of sorts for whether to initially hide/collapse part of the element's contents.
|
|
||||||
// If after slicing the contents in two there are fewer words in the second part than
|
|
||||||
// the value set by widow, we won't bother hiding/collapsing anything.
|
|
||||||
widow: 4,
|
|
||||||
|
|
||||||
// text displayed in a link instead of the hidden part of the element.
|
|
||||||
// clicking this will expand/show the hidden/collapsed text
|
|
||||||
expandText: 'read more',
|
|
||||||
expandPrefix: '… ',
|
|
||||||
|
|
||||||
// class names for summary element and detail element
|
|
||||||
summaryClass: 'summary',
|
|
||||||
detailClass: 'details',
|
|
||||||
|
|
||||||
// class names for <span> around "read-more" link and "read-less" link
|
|
||||||
moreClass: 'read-more',
|
|
||||||
lessClass: 'read-less',
|
|
||||||
|
|
||||||
// number of milliseconds after text has been expanded at which to collapse the text again.
|
|
||||||
// when 0, no auto-collapsing
|
|
||||||
collapseTimer: 0,
|
|
||||||
|
|
||||||
// effects for expanding and collapsing
|
|
||||||
expandEffect: 'fadeIn',
|
|
||||||
expandSpeed: 250,
|
|
||||||
collapseEffect: 'fadeOut',
|
|
||||||
collapseSpeed: 200,
|
|
||||||
|
|
||||||
// allow the user to re-collapse the expanded text.
|
|
||||||
userCollapse: true,
|
|
||||||
|
|
||||||
// text to use for the link to re-collapse the text
|
|
||||||
userCollapseText: 'read less',
|
|
||||||
userCollapsePrefix: ' ',
|
|
||||||
|
|
||||||
|
|
||||||
// all callback functions have the this keyword mapped to the element in the jQuery set when .expander() is called
|
|
||||||
|
|
||||||
onSlice: null, // function() {}
|
|
||||||
beforeExpand: null, // function() {},
|
|
||||||
afterExpand: null, // function() {},
|
|
||||||
onCollapse: null // function(byUser) {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$.fn.expander = function(options) {
|
|
||||||
|
|
||||||
var opts = $.extend({}, $.expander.defaults, options),
|
|
||||||
rSelfClose = /^<(?:area|br|col|embed|hr|img|input|link|meta|param).*>$/i,
|
|
||||||
rAmpWordEnd = /(&(?:[^;]+;)?|\w+)$/,
|
|
||||||
rOpenCloseTag = /<\/?(\w+)[^>]*>/g,
|
|
||||||
rOpenTag = /<(\w+)[^>]*>/g,
|
|
||||||
rCloseTag = /<\/(\w+)>/g,
|
|
||||||
rTagPlus = /^<[^>]+>.?/,
|
|
||||||
delayedCollapse;
|
|
||||||
|
|
||||||
this.each(function() {
|
|
||||||
var i, l, tmp, summTagLess, summOpens, summCloses, lastCloseTag, detailText,
|
|
||||||
$thisDetails, $readMore,
|
|
||||||
openTagsForDetails = [],
|
|
||||||
closeTagsForsummaryText = [],
|
|
||||||
defined = {},
|
|
||||||
thisEl = this,
|
|
||||||
$this = $(this),
|
|
||||||
$summEl = $([]),
|
|
||||||
o = $.meta ? $.extend({}, opts, $this.data()) : opts,
|
|
||||||
hasDetails = !!$this.find('.' + o.detailClass).length,
|
|
||||||
hasBlocks = !!$this.find('*').filter(function() {
|
|
||||||
var display = $(this).css('display');
|
|
||||||
return (/^block|table|list/).test(display);
|
|
||||||
}).length,
|
|
||||||
el = hasBlocks ? 'div' : 'span',
|
|
||||||
detailSelector = el + '.' + o.detailClass,
|
|
||||||
moreSelector = 'span.' + o.moreClass,
|
|
||||||
expandSpeed = o.expandSpeed || 0,
|
|
||||||
allHtml = $.trim( $this.html() ),
|
|
||||||
allText = $.trim( $this.text() ),
|
|
||||||
summaryText = allHtml.slice(0, o.slicePoint);
|
|
||||||
|
|
||||||
// bail out if we've already set up the expander on this element
|
|
||||||
if ( $.data(this, 'expander') ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$.data(this, 'expander', true);
|
|
||||||
|
|
||||||
// determine which callback functions are defined
|
|
||||||
$.each(['onSlice','beforeExpand', 'afterExpand', 'onCollapse'], function(index, val) {
|
|
||||||
defined[val] = $.isFunction(o[val]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// back up if we're in the middle of a tag or word
|
|
||||||
summaryText = backup(summaryText);
|
|
||||||
|
|
||||||
// summary text sans tags length
|
|
||||||
summTagless = summaryText.replace(rOpenCloseTag,'').length;
|
|
||||||
|
|
||||||
// add more characters to the summary, one for each character in the tags
|
|
||||||
while (summTagless < o.slicePoint) {
|
|
||||||
newChar = allHtml.charAt(summaryText.length);
|
|
||||||
if (newChar == '<') {
|
|
||||||
newChar = allHtml.slice(summaryText.length).match(rTagPlus)[0];
|
|
||||||
}
|
|
||||||
summaryText += newChar;
|
|
||||||
summTagless++;
|
|
||||||
}
|
|
||||||
|
|
||||||
summaryText = backup(summaryText, o.preserveWords);
|
|
||||||
|
|
||||||
// separate open tags from close tags and clean up the lists
|
|
||||||
summOpens = summaryText.match(rOpenTag) || [];
|
|
||||||
summCloses = summaryText.match(rCloseTag) || [];
|
|
||||||
|
|
||||||
// filter out self-closing tags
|
|
||||||
tmp = [];
|
|
||||||
$.each(summOpens, function(index, val) {
|
|
||||||
if ( !rSelfClose.test(val) ) {
|
|
||||||
tmp.push(val);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
summOpens = tmp;
|
|
||||||
|
|
||||||
// strip close tags to just the tag name
|
|
||||||
l = summCloses.length;
|
|
||||||
for (i = 0; i < l; i++) {
|
|
||||||
summCloses[i] = summCloses[i].replace(rCloseTag, '$1');
|
|
||||||
}
|
|
||||||
|
|
||||||
// tags that start in summary and end in detail need:
|
|
||||||
// a). close tag at end of summary
|
|
||||||
// b). open tag at beginning of detail
|
|
||||||
$.each(summOpens, function(index, val) {
|
|
||||||
var thisTagName = val.replace(rOpenTag, '$1');
|
|
||||||
var closePosition = $.inArray(thisTagName, summCloses);
|
|
||||||
if (closePosition === -1) {
|
|
||||||
openTagsForDetails.push(val);
|
|
||||||
closeTagsForsummaryText.push('</' + thisTagName + '>');
|
|
||||||
|
|
||||||
} else {
|
|
||||||
summCloses.splice(closePosition, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// reverse the order of the close tags for the summary so they line up right
|
|
||||||
closeTagsForsummaryText.reverse();
|
|
||||||
|
|
||||||
// create necessary summary and detail elements if they don't already exist
|
|
||||||
if ( !hasDetails ) {
|
|
||||||
|
|
||||||
// end script if detail has fewer words than widow option
|
|
||||||
detailText = allHtml.slice(summaryText.length);
|
|
||||||
if ( detailText.split(/\s+/).length < o.widow && !hasDetails ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise, continue...
|
|
||||||
lastCloseTag = closeTagsForsummaryText.pop() || '';
|
|
||||||
summaryText += closeTagsForsummaryText.join('');
|
|
||||||
detailText = openTagsForDetails.join('') + detailText;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// assume that even if there are details, we still need readMore/readLess/summary elements
|
|
||||||
// (we already bailed out earlier when readMore el was found)
|
|
||||||
// but we need to create els differently
|
|
||||||
|
|
||||||
// remove the detail from the rest of the content
|
|
||||||
detailText = $this.find(detailSelector).remove().html();
|
|
||||||
|
|
||||||
// The summary is what's left
|
|
||||||
summaryText = $this.html();
|
|
||||||
|
|
||||||
// allHtml is the summary and detail combined (this is needed when content has block-level elements)
|
|
||||||
allHtml = summaryText + detailText;
|
|
||||||
|
|
||||||
lastCloseTag = '';
|
|
||||||
}
|
|
||||||
o.moreLabel = $this.find(moreSelector).length ? '' : buildMoreLabel(o);
|
|
||||||
|
|
||||||
if (hasBlocks) {
|
|
||||||
detailText = allHtml;
|
|
||||||
}
|
|
||||||
summaryText += lastCloseTag;
|
|
||||||
|
|
||||||
// onSlice callback
|
|
||||||
o.summary = summaryText;
|
|
||||||
o.details = detailText;
|
|
||||||
o.lastCloseTag = lastCloseTag;
|
|
||||||
|
|
||||||
if (defined.onSlice) {
|
|
||||||
// user can choose to return a modified options object
|
|
||||||
// one last chance for user to change the options. sneaky, huh?
|
|
||||||
// but could be tricky so use at your own risk.
|
|
||||||
tmp = o.onSlice.call(thisEl, o);
|
|
||||||
|
|
||||||
// so, if the returned value from the onSlice function is an object with a details property, we'll use that!
|
|
||||||
o = tmp && tmp.details ? tmp : o;
|
|
||||||
}
|
|
||||||
|
|
||||||
// build the html with summary and detail and use it to replace old contents
|
|
||||||
var html = buildHTML(o, hasBlocks);
|
|
||||||
$this.html( html );
|
|
||||||
|
|
||||||
// set up details and summary for expanding/collapsing
|
|
||||||
$thisDetails = $this.find(detailSelector);
|
|
||||||
$readMore = $this.find(moreSelector);
|
|
||||||
$thisDetails.hide();
|
|
||||||
$readMore.find('a').unbind('click.expander').bind('click.expander', expand);
|
|
||||||
|
|
||||||
$summEl = $this.find('div.' + o.summaryClass);
|
|
||||||
|
|
||||||
if ( o.userCollapse && !$this.find('span.' + o.lessClass).length ) {
|
|
||||||
$this
|
|
||||||
.find(detailSelector)
|
|
||||||
.append('<span class="' + o.lessClass + '">' + o.userCollapsePrefix + '<a href="#">' + o.userCollapseText + '</a></span>');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this
|
|
||||||
.find('span.' + o.lessClass + ' a')
|
|
||||||
.unbind('click.expander')
|
|
||||||
.bind('click.expander', function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
clearTimeout(delayedCollapse);
|
|
||||||
var $detailsCollapsed = $(this).closest(detailSelector);
|
|
||||||
reCollapse(o, $detailsCollapsed);
|
|
||||||
if (defined.onCollapse) {
|
|
||||||
o.onCollapse.call(thisEl, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function expand(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
$readMore.hide();
|
|
||||||
$summEl.hide();
|
|
||||||
if (defined.beforeExpand) {
|
|
||||||
o.beforeExpand.call(thisEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
$thisDetails.stop(false, true)[o.expandEffect](expandSpeed, function() {
|
|
||||||
$thisDetails.css({zoom: ''});
|
|
||||||
if (defined.afterExpand) {o.afterExpand.call(thisEl);}
|
|
||||||
delayCollapse(o, $thisDetails, thisEl);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}); // this.each
|
|
||||||
|
|
||||||
function buildHTML(o, blocks) {
|
|
||||||
var el = 'span',
|
|
||||||
summary = o.summary;
|
|
||||||
if ( blocks ) {
|
|
||||||
el = 'div';
|
|
||||||
// tuck the moreLabel inside the last close tag
|
|
||||||
summary = summary.replace(/(<\/[^>]+>)\s*$/, o.moreLabel + '$1');
|
|
||||||
|
|
||||||
// and wrap it in a div
|
|
||||||
summary = '<div class="' + o.summaryClass + '">' + summary + '</div>';
|
|
||||||
} else {
|
|
||||||
summary += o.moreLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
summary,
|
|
||||||
'<',
|
|
||||||
el + ' class="' + o.detailClass + '"',
|
|
||||||
'>',
|
|
||||||
o.details,
|
|
||||||
'</' + el + '>'
|
|
||||||
].join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMoreLabel(o) {
|
|
||||||
var ret = '<span class="' + o.moreClass + '">' + o.expandPrefix;
|
|
||||||
ret += '<a href="#">' + o.expandText + '</a></span>';
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function backup(txt, preserveWords) {
|
|
||||||
if ( txt.lastIndexOf('<') > txt.lastIndexOf('>') ) {
|
|
||||||
txt = txt.slice( 0, txt.lastIndexOf('<') );
|
|
||||||
}
|
|
||||||
if (preserveWords) {
|
|
||||||
txt = txt.replace(rAmpWordEnd,'');
|
|
||||||
}
|
|
||||||
return txt;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reCollapse(o, el) {
|
|
||||||
el.stop(true, true)[o.collapseEffect](o.collapseSpeed, function() {
|
|
||||||
var prevMore = el.prev('span.' + o.moreClass).show();
|
|
||||||
if (!prevMore.length) {
|
|
||||||
el.parent().children('div.' + o.summaryClass).show()
|
|
||||||
.find('span.' + o.moreClass).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function delayCollapse(option, $collapseEl, thisEl) {
|
|
||||||
if (option.collapseTimer) {
|
|
||||||
delayedCollapse = setTimeout(function() {
|
|
||||||
reCollapse(option, $collapseEl);
|
|
||||||
if ( $.isFunction(option.onCollapse) ) {
|
|
||||||
option.onCollapse.call(thisEl, false);
|
|
||||||
}
|
|
||||||
}, option.collapseTimer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
// plugin defaults
|
|
||||||
$.fn.expander.defaults = $.expander.defaults;
|
|
||||||
})(jQuery);
|
|
||||||
|
|
@ -1828,6 +1828,26 @@ ul#press_logos
|
||||||
:color #777
|
:color #777
|
||||||
|
|
||||||
.collapsible
|
.collapsible
|
||||||
|
:overflow hidden
|
||||||
|
:position relative
|
||||||
|
|
||||||
|
.expander
|
||||||
|
:position absolute
|
||||||
|
:bottom 0
|
||||||
|
:left 0
|
||||||
|
:right 0
|
||||||
|
:height 30px
|
||||||
|
:text-align center
|
||||||
|
:line-height 48px
|
||||||
|
:font-size .8em
|
||||||
|
:color $grey
|
||||||
|
:text-shadow 0 0 7px #FFF
|
||||||
|
:cursor pointer
|
||||||
|
:border-bottom 2px solid #DDD
|
||||||
|
@include border-radius(0, 0, 3px, 3px)
|
||||||
|
@include linear-gradient(rgba(255,255,255,0) , #EEE, 0%, 95%)
|
||||||
|
:background-color transparent
|
||||||
|
|
||||||
.oembed
|
.oembed
|
||||||
:background url('/images/ajax-loader2.gif') no-repeat center center
|
:background url('/images/ajax-loader2.gif') no-repeat center center
|
||||||
:display inline-block
|
:display inline-block
|
||||||
|
|
|
||||||
|
|
@ -39,73 +39,6 @@ describe("app.views.Stream", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('clicking read more', function() {
|
|
||||||
var readMoreLink;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
this.statusMessage = this.stream.posts.models[0];
|
|
||||||
this.statusElement = $(this.view.$(".stream_element")[0]);
|
|
||||||
readMoreLink = this.statusElement.find('.read-more a');
|
|
||||||
readMoreLink.text("read more");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('expands the post', function() {
|
|
||||||
expect(this.statusElement.find('.collapsible .details').attr('style')).toContain('display: none;');
|
|
||||||
readMoreLink.click();
|
|
||||||
expect(this.statusElement.find('.collapsible .details').attr('style')).not.toContain('display: none;');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('differences between firefox and webkit/IE', function() {
|
|
||||||
// Firefox creates 2 divs - one with the summary and one with the whole post.
|
|
||||||
// It hides the summary and shows the whole post when you click show more. Works great!
|
|
||||||
// Webkit and IE also create 2 divs, but they split the post - the 1st has the summary and the 2nd has the rest
|
|
||||||
// of the post. When you click read more, it just shows the 2nd div. This leaves whitespace in odd places.
|
|
||||||
// So there's a callback that this is testing, that fixes the whitespace on webkit & IE.
|
|
||||||
var weAreOnFirefox;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
weAreOnFirefox = this.statusElement.find('.collapsible .summary').length > 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('removes the read-more div on webkit/IE but leaves it on firefox', function() {
|
|
||||||
expect(this.statusElement.find('.read-more').length).toEqual(1);
|
|
||||||
readMoreLink.click();
|
|
||||||
if (weAreOnFirefox === true) {
|
|
||||||
expect(this.statusElement.find('.read-more').length).toEqual(1);
|
|
||||||
} else {
|
|
||||||
expect(this.statusElement.find('.read-more').length).toEqual(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('collapses p elements on webkit/IE but leaves them alone on firefox', function() {
|
|
||||||
expect(this.statusElement.find('.collapsible p').length).toEqual(2);
|
|
||||||
readMoreLink.click();
|
|
||||||
if (weAreOnFirefox === true) {
|
|
||||||
expect(this.statusElement.find('.collapsible p').length).toEqual(2);
|
|
||||||
} else {
|
|
||||||
expect(this.statusElement.find('.collapsible p').length).toEqual(1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('collapses li elements on webkit/IE but leaves them alone on firefox', function() {
|
|
||||||
this.statusMessage = this.stream.posts.models[3];
|
|
||||||
this.statusElement = $(this.view.$(".stream_element")[3]);
|
|
||||||
readMoreLink = this.statusElement.find('.read-more a');
|
|
||||||
readMoreLink.text("read more");
|
|
||||||
|
|
||||||
if (weAreOnFirefox === true) {
|
|
||||||
expect(this.statusElement.find('.collapsible li').length).toEqual(12);
|
|
||||||
readMoreLink.click();
|
|
||||||
expect(this.statusElement.find('.collapsible li').length).toEqual(12);
|
|
||||||
} else {
|
|
||||||
expect(this.statusElement.find('.collapsible li').length).toEqual(9);
|
|
||||||
readMoreLink.click();
|
|
||||||
expect(this.statusElement.find('.collapsible li').length).toEqual(8);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("infScroll", function() {
|
describe("infScroll", function() {
|
||||||
// NOTE: inf scroll happens at 500px
|
// NOTE: inf scroll happens at 500px
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue