Collapse long code block
libeco - July 11, 2009 - 09:41
| Project: | GeSHi Filter for syntax highlighting |
| Version: | 6.x-1.x-dev |
| Component: | User interface |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | needs review |
Description
Hi there,
Thanks for this module. I have a request though. Would it be possible to make long code blocks automatically collapse to a smaller area? I think this would be possible with jQuery through a theme, but wouldn't it be nice to have as a feature?
I would ask to look at the PHPBB-module which has this functionality, but it seems like the demo site is not working at this moment.
Thanks in advance!

#1
I have started something, I'm not very experienced with jQuery, so I don't know if there's a better way. This is still pure jQuery, not implemented into Drupal.
The jQuery:
$(document).ready(function() {
var $container = $('div.geshifilter');
var $containerHeight = $container.outerHeight();
var $expandContainer = '<div class="collapse-link"><a class="expand" href="#">Expand</a></div>';
var $collapseContainer = '<div class="collapse-link"><a class="collapse" href="#">Collapse</a></div>';
if ($container.outerHeight() > 180) {
$container.addClass('collapsed');
var $containerHeightNew = $container.outerHeight();
$container.after($expandContainer);
}
var $expandLink = $('a.expand');
$expandLink.live('click', function() {
$container.removeClass('collapsed').height($containerHeight);
$(this).parent().remove();
$container.after($collapseContainer);
return false;
});
var $collapseLink = $('a.collapse');
$collapseLink.live('click', function() {
$container.addClass('collapsed').height($containerHeightNew);
$(this).parent().remove();
$container.after($expandContainer);
return false;
});
});
And some css:
.collapsed {height: 200px;
overflow: auto;
}
.collapse-link {
text-align: right;
}
#2
interesting,
I'll try it out when I have more time.
looking at the code: does it also work when there are several geshifilter snippets on the same page?
maybe you should also check out the height(val) function (http://docs.jquery.com/CSS/height),
and maybe we can avoid the CSS part and do everything in jquery?
#3
I haven't tried it on the real page yet. I'll see if I can implement it into Drupal, I haven't really thought about the behaviour of multiple GeSHi snippets. The reason I used CSS is the ability for the user to easily change the height of the box, but perhaps this could also be an admin setting?
The height function is already in the code. As I said I'm not an experienced jQuery developer, don't know if I'm using the most efficient code, but it's a start and I'm willing to learn from corrections. :-)
#4
Here's a rewrite of the previous code which will work for different GeSHi snippets, however, it still needs some work for snippets with different heights.
$(document).ready(function() {
var $container = $('div.geshifilter');
var $containerHeight = $container.outerHeight();
var $expandContainer = '<div class="collapse-link"><a class="expand" href="#">Expand</a></div>';
var $collapseContainer = '<div class="collapse-link"><a class="collapse" href="#">Collapse</a></div>';
if ($container.outerHeight() > 180) {
$container.addClass('collapsed');
var $containerHeightNew = $container.outerHeight();
$container.after($expandContainer);
}
$('a.expand').live('click', function() {
var $linkDiv = $(this).parent();
$linkDiv.prev().removeClass('collapsed').height($containerHeight);
$linkDiv.prev().after($collapseContainer);
$linkDiv.remove();
return false;
});
$('a.collapse').live('click', function() {
var $linkDiv = $(this).parent();
$linkDiv.prev().addClass('collapsed').height($containerHeightNew);
$linkDiv.prev().after($expandContainer);
$linkDiv.remove();
return false;
});
});
#5
And I think this is the finished code which works well with multiple GeSHi snippets:
$(document).ready(function() {
var $container = $('div.geshifilter');
var $containerHeight = new Array();
$container.each(function() {
$containerHeight.push($(this).outerHeight());
});
var $expandContainer = '<div class="collapse-link"><a class="expand" href="#">Expand</a></div>';
var $collapseContainer = '<div class="collapse-link"><a class="collapse" href="#">Collapse</a></div>';
if ($container.outerHeight() > 180) {
$container.addClass('collapsed');
var $containerHeightNew = $container.outerHeight();
$container.after($expandContainer);
}
$('a.expand').live('click', function() {
var $linkDiv = $(this).parent();
var $currentItem = $container.index($linkDiv.prev());
$linkDiv.prev().removeClass('collapsed').height($containerHeight[$currentItem]);
$linkDiv.prev().after($collapseContainer);
$linkDiv.remove();
return false;
});
$('a.collapse').live('click', function() {
var $linkDiv = $(this).parent();
var $currentItem = $container.index($linkDiv.prev());
$linkDiv.prev().addClass('collapsed').height($containerHeightNew);
$linkDiv.prev().after($expandContainer);
$linkDiv.remove();
return false;
});
});
#6
I tried your code, but it doesn't work because of the 'live' function,
apparently that's a jQuery 1.3 (http://docs.jquery.com/Events/live)
I think Drupal 6 ships only a 1.2.something version by default
#7
You're right, Ok I've just tried this and it seems to work:
Step 1
Download jQuery Update module and enable it. Make sure you use the 6.x.2.x (currently still dev).
Step 2
Add
<?phpdrupal_add_js(drupal_get_path('module', 'geshifilter') .'/geshifilter.js');
?>
Step 3
Create a new geshifilter.js file and add this code:
Drupal.behaviors.collapseGeSHi = function() {
var $container = $('div.geshifilter');
var $containerHeight = new Array();
var $containerHeightNew = new Array();
var $expandContainer = '<div class="collapse-link"><a class="expandGeSHi" href="#">Expand</a></div>';
var $collapseContainer = '<div class="collapse-link"><a class="collapseGeSHi" href="#">Collapse</a></div>';
$container.each(function() {
$containerHeight.push($(this).outerHeight());
if ($(this).outerHeight() > 180) {
$(this).addClass('collapsedGeSHi');
$containerHeightNew.push($(this).outerHeight());
$(this).after($expandContainer);
} else {
$containerHeightNew.push($(this).outerHeight());
}
});
$('a.expandGeSHi').live('click', function() {
var $linkDiv = $(this).parent();
var $currentItem = $container.index($linkDiv.prev());
$linkDiv.prev().removeClass('collapsedGeSHi').height($containerHeight[$currentItem]);
$linkDiv.prev().after($collapseContainer);
$linkDiv.remove();
return false;
});
$('a.collapseGeSHi').live('click', function() {
var $linkDiv = $(this).parent();
var $currentItem = $container.index($linkDiv.prev());
$linkDiv.prev().addClass('collapsedGeSHi').height($containerHeightNew[$currentItem]);
$linkDiv.prev().after($expandContainer);
$linkDiv.remove();
return false;
});
};
Step 4
Add these lines to geshifilter.css:
div.collapsedGeSHi {height: 200px;
overflow: auto;
}
.collapse-link {
text-align: right;
}
Maybe you need to clear your cache, but now it should work! :-)
#8
I don't think depending on jQuery Update is a good idea.
Isn't it possible to implement it with with jQuery 1.2?
#9
The live function is available only since 1.3. This function makes it possible to create an event listener form an element before the element exists. I have no idea how to do this without this function.
I found an example of doing it the 1.2 way, but I will have to look into it to see if I can convert the code to work that way.
Why would depending on a newer, better version of jQuery not be a good idea? jQuery 1.3 is said to be much faster and it's functions like .live that make it very easy to use. Or is it just the idea of having to install a module for such little added functionality?
Thanks!
#10
I have nothing against 1.3 :)
it is indeed about having to install an extra module for a rather small feature. GeSHi filter has no dependencies outside of Drupal core and I want to keep it that way.
I've been experimenting myself a bit and came up with the following (based on http://andylangton.co.uk/articles/javascript/jquery-show-hide-multiple-e...):
$(document).ready(function() {$('div.geshifilter').append('<div class="geshifiltertools"><a href="#" class="geshifilter-tools-toggle">show more/less</a></div>');
$('div.geshifiltertools').prev().hide();
$('a.geshifilter-tools-toggle').click(function(){
$(this).parent().prev().toggle('slow');
return false;
});
});
it's still very rough, (shows all or nothing of a snippet, instead of partial collapsing), but I don't need the live function something alike.
#11
Here is my version without the .live function:
Step 1
Add
<?phpdrupal_add_js(drupal_get_path('module', 'geshifilter') .'/geshifilter.js');
?>
Step 2
Create a new geshifilter.js file and add this code:
Drupal.behaviors.collapseGeSHi = function() {
var $container = $('div.geshifilter');
var $containerHeight = new Array();
var $containerHeightNew = new Array();
var $expandContainer = '<div class="collapse-link"><a class="expandGeSHi" href="#">Expand</a></div>';
var $collapseContainer = '<div class="collapse-link"><a class="collapseGeSHi" href="#">Collapse</a></div>';
var $expandLink = '<a class="expandGeSHi" href="#">Expand</a>';
var $collapseLink = '<a class="collapseGeSHi" href="#">Collapse</a>';
$container.each(function() {
$containerHeight.push($(this).outerHeight());
if ($(this).outerHeight() > 180) {
$(this).addClass('collapsedGeSHi');
$containerHeightNew.push($(this).outerHeight());
$(this).after($expandContainer);
} else {
$containerHeightNew.push($(this).outerHeight());
}
});
// Expanding the box
$('div.collapse-link').bind('click', function(e) {
var $linkDiv = $(this);
var $currentItem = $container.index($linkDiv.prev());
var $clicked = $(e.target);
if($clicked.is('a.expandGeSHi')) {
$linkDiv.prev().removeClass('collapsedGeSHi').height($containerHeight[$currentItem]);
$linkDiv.append($collapseLink);
$clicked.remove();
return false;
}
});
// Collapsing the box
$('div.collapse-link').bind('click', function(e) {
var $linkDiv = $(this);
var $currentItem = $container.index($linkDiv.prev());
var $clicked = $(e.target);
if($clicked.is('a.collapseGeSHi')) {
$linkDiv.prev().addClass('collapsedGeSHi').height($containerHeightNew[$currentItem]);
$linkDiv.append($expandLink);
$clicked.remove();
return false;
}
});
};
Step 3
Add these lines to geshifilter.css:
div.collapsedGeSHi {height: 200px;
overflow: auto;
}
.collapse-link {
text-align: right;
}
Maybe you need to clear your cache, but now it should work! :-)
#12
#11 doesn't seem to work for me: code is not collapsed and collapse link does not change much
I'm also trying some stuff out.
I found this interesting inspiration: http://sim.plified.com/2008/09/15/sliding-content-from-a-partial-height-... (also try out the demo).
A big advantage of this approach is that the collapsing/expanding is animated, which is much more user friendly than instantaneous hiding/showing.
#13
this seems to work well for me:
var geshifilter_collapse_height = 100;
$(document).ready(function() {
$('div.geshifilter').each(function() {
// Get code container and its height.
var code_container = $(this).children(':first');
var code_container_original_height = code_container.height();
// Only do the collapsing where it makes sense.
if (code_container_original_height > geshifilter_collapse_height) {
// Add more/less link.
$(this).append('<div class="geshifilter-tools"><a href="#" class="geshifilter-tools-toggle">Read more</a></div>');
// Store the original heights of the geshifilter divs.
code_container.attr('original_height', code_container_original_height);
// Collapse by default.
code_container.addClass('geshifilter-collapsed').css("height", geshifilter_collapse_height);
}
});
// Add handler to read more/collapse link.
$("a.geshifilter-tools-toggle").click(function() {
// Get code container.
var code_container = $(this).parent().prev();
if (code_container.hasClass('geshifilter-collapsed')) {
// Show more (use original height).
var original_height = code_container.attr('original_height') + 'px';
code_container.removeClass('geshifilter-collapsed').animate({height: original_height}, 'slow');
$(this).text('Show less');
}
else {
// Collapse.
code_container.addClass('geshifilter-collapsed').animate({height: geshifilter_collapse_height}, 'slow');
$(this).text('Read more');
}
// Return false so that the link is no followed.
return false;
});
});
still some (styling) issues to do
#14
I wonder why #11 is not working for you, here it works perfectly.
#13 works well for me, but what I read from the book Drupal 6 JavaScript and jQuery it is better to let Drupal handle the jQuery with
Drupal.behaviors.collapseGeSHi = function() {Or ofcourse some other name you choose.
#15
ok, here is a real patch (and a screenshot).
About the behaviors: from what I understand of it, behaviors is only about attaching event handlers to objects. Here we are also adding objects on their own (the toggle link inside a div), so I think Drupal.behaviors is not needed here. (The click event handler added to the toggle link could be done with Drupal.behaviors, but it's not necessary as far as I know, and it would make the code more difficult to understand.)
still to do/figure out:
* make it an opt-in feature through the admin interface?
* make configurable inline like
[code collapsible=true]foo bar code [/code]?* collapsing should also be optional for geshinodes and geshifields
#16
Does this make any sense for the admin setting? Ofcourse a checkbox should be added to the admin settings for this to work.
<?phpif (variable_get('adminsettingname', '') == true) {
drupal_add_js(drupal_get_path('module', 'geshifilter') .'/geshifilter.js');
}
?>
#17
I've applied the patch with TortoiseMerge and it did not create the .js file, don't know if it's a limitation of TortoiseMerge, patch-files or this one in particular.
#18
adding new files should be no problem with patch files, so I'm afraid it's a limitation of ToroiseMerge
anyway here is an updated patch and individual js file
#19
Wouldn't it be better to change 'Show more code' to 'Show all code'?
#20
subscribing