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!

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

libeco’s picture

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;
}
soxofaan’s picture

Version: 6.x-1.2 » 6.x-1.x-dev

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?

libeco’s picture

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. :-)

libeco’s picture

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;
  });

});
libeco’s picture

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;
  });

});
soxofaan’s picture

Status: Active » Needs work

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

libeco’s picture

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 drupal_add_js(drupal_get_path('module', 'geshifilter') .'/geshifilter.js'); to function geshifilter_init() in geshifilter.module.

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! :-)

soxofaan’s picture

I don't think depending on jQuery Update is a good idea.
Isn't it possible to implement it with with jQuery 1.2?

libeco’s picture

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!

soxofaan’s picture

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.

libeco’s picture

Here is my version without the .live function:

Step 1
Add drupal_add_js(drupal_get_path('module', 'geshifilter') .'/geshifilter.js'); to function geshifilter_init() in geshifilter.module.

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! :-)

soxofaan’s picture

#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.

soxofaan’s picture

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

libeco’s picture

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.

soxofaan’s picture

Status: Needs work » Needs review
FileSize
94.22 KB
3.41 KB

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

libeco’s picture

Does this make any sense for the admin setting? Ofcourse a checkbox should be added to the admin settings for this to work.

if (variable_get('adminsettingname', '') == true) {
  drupal_add_js(drupal_get_path('module', 'geshifilter') .'/geshifilter.js');
}
libeco’s picture

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.

soxofaan’s picture

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

libeco’s picture

Wouldn't it be better to change 'Show more code' to 'Show all code'?

voipfc’s picture

subscribing

soxofaan’s picture

Worked a bit more on the patch

now collapsible code blocks are optional (off by default) and can be triggered with something like

[code language="java" collapsible="true"]
bla bla
[/code]
voipfc’s picture

I am just wondering if this patch can make it into the mainline version.
I'd love not to login via SSH to patch it, on my few sites that use it.

I like it so much that I blogged about it http://devblog.brahmancreations.com/content/geshi-show-more-code.

I wonder if only the 3 of us in this thread that know about it.

Thanks

Frank

soxofaan’s picture

If there is more user feedback on this patch, it's certainly supposed to go in the main line.
(Some simpletest coverage of the feature would also be nice.)
Have you already tried it on a development/localhost site or something?

sanpi’s picture

Great feature! I use successfully the second patch to minimize changes.

voipfc’s picture

#21: 516356_codecollapsing_03.patch queued for re-testing.

voipfc’s picture

Title: Collapse long code block » Collapse long code block - errors applying patch.

Does the patch depend on which version of Geshi is used?

Does it change the Geshi libraries own files, or only those of the Drupal module?

What directory should the patch be applied from?

soxofaan’s picture

Title: Collapse long code block - errors applying patch. » Collapse long code block

at voipfc:

It should be applied against the CVS HEAD version (or the latest 6.x-1.x-dev nighly build tar ball) of the geshifilter module (applying it from the geshifilter directory should work).

It only changes the geshifilter drupal module files, it does not touch the geshi library.

voipfc’s picture

#18: 516356_codecollapsing_02.patch queued for re-testing.

benlotter’s picture

I've just recently started using this module and I think it great. Unfortunately the version I downloaded doesn't seem to have this feature yet. I'm not a developer and I don't know how to apply the patch. Can someone attach a .zip that I can download or make a new build so I can use this?

voipfc’s picture

Does this feature still work for both D6 and D7?

I find myself having to revert my Geshi filter to older versions in order to keep it functioning.

Was it implemented for Drupal 7?

soxofaan’s picture

Here a reroll against 6.x-1.x

voipfc’s picture

I have applied patch #31 to 6.14 and it is working.
A search and replace is required to change existing code blocks to the <langxxx collapsible="true"> syntax to work, ie add the collapsible="true" tag

voipfc’s picture

yukare’s picture

Version: 6.x-1.x-dev » 8.x-1.x-dev
Issue summary: View changes
Status: Needs review » Needs work

Drupal 6 is not supported anymore.
I really think that this is a good thing to have, so i will change the version to 8. I can not work on it now, but will accept a patch if someone else wants to work on it.