Picture libraries: take advantage of Web friendly formats

When loading Web pages, pictures are most often a performance laggard. SharePoint picture libraries have a nice feature that can help you here: every time you upload a picture, SharePoint actually stores 3 files: the original picture, a version optimized for the Web, and a thumbnail.

A classic trap

You’ll often see that in order to create a thumbnail, people just take the original picture and force its dimensions. For example:

<img src=”MyPicture.jpg” width=”60px” height=”40px” />

Sure, the result looks like a thumbnail. But still, the browser has actually downloaded the full size picture.

Where SharePoint helps

Let’s take an example. Here is the URL of a picture that has a full size of 405 kb:
http://www.pathtosharepoint.com/PhotoVideo/LiJiang/IMG_0496.JPG

I have uploaded it to a picture library called PhotoVideo. Automatically SharePoint has generated two more pictures:
– a Web friendly picture of 52 kb, 8 times smaller than the original picture:
http://www.pathtosharepoint.com/PhotoVideo/LiJiang/_w/IMG_0496_JPG.jpg
– a thumbnail: 5 kb, 80 times smaller than the original picture:
http://www.pathtosharepoint.com/PhotoVideo/LiJiang/_t/IMG_0496_JPG.jpg

So how does it work? Well, if you upload a picture called CutePic.jpg to a picture library called PictureLibrary, then:
– you can find the thumbnail ïn the “_t” folder: PictureLibrary/_t/CutePic_jpg.jpg
– the Web copy will be in the “_w” folder: PictureLibrary/_w/CutePic_jpg.jpg

As for OtherCutePic.png:
– thumbnail: PictureLibrary/_t/OtherCutePic_png.jpg
– Web copy: PictureLibrary/_w/OtherCutePic_png.jpg

How to automate the retrieval of Web formats and thumbnails?

Let’s assume you have a picture library, and for any given picture you want a script to generate the URL of the derived image – Web optimized or thumbnail. It turns out that SharePoint already has a function for this:

function CreateDerivedImageUrl(originalImageUrl, subdirStr)
{
	var url=originalImageUrl.replace(/\.([^\.]+)$/, "_$1");
	url=url+".jpg";
	url=url.replace(/\/([^\/]+)$/, subdirStr+"$1");
	return url;
}

originalImageUrl is the URL of the full size picture; subdirStr will take the values “/_t/” for thumbnail or “/_w/” for Web preview.

I am using a similar script for my Image Rotator.

How about XSLT, for example if you want to include thumbnails in a Data View Web Part? The expression below is taken from a comment by Drasko (in this post, on novoculus.com) :
{@FileDirRef}/_t/{concat(substring-before(@FileLeafRef,concat(’.’,@FileType)),’_’,@FileType,’.’,@FileType)}
 For Web previews, simply replace _t with _w.

So, do your site visitors a favor: save them time by taking advantage of SharePoint’s Web friendly formats!

Advertisements

Image Rotator: now with hyperlinks

I have added a new option to the Image Rotator: you now have the possibility to make the pictures clickable. Clicking on a picture will open a new window, to display either the full size picture or a link that you have referenced in a text column.
By default the text column is called [Link]. You can of course choose your own column name, but do not use spaces or special characters in the name. Remember to include that column in the default view of your picture library.

Splitting the top navigation in two

 topnav3

This is a follow-up on a post by Jan Tielens, so I recommend that you read his post first for more details (the screenshot is taken from his post btw). The idea is to break the top navigation bar in two when you have too many menu items.

My personal comment was that in this precise case jQuery didn’t bring much value. To make my point, here is an example of plain JavaScript that gives the same result:

<script type="text/javascript">

//Contact: Christophe@PathToSharePoint.com

// Grab the top navigation menu
var Menu = document.getElementById("zz1_TopNavigationMenu");

// Count half of the tabs
var counter = Math.round(Menu.getElementsByTagName("table").length/2);

// Add a row above the existing one
Menu.insertRow(0);

// Move half of the tabs to the first row (each tab is made of 3 cells)
for (i=0;i<counter*3;i++) {
Menu.rows&#91;0&#93;.appendChild(Menu.rows&#91;1&#93;.cells&#91;0&#93;);
}

</script>

As usual, you can use a Content Editor Web Part to add the script to your page.

Note that I am not trying to argue about Jan’s approach. His post clearly states the specific context of his demo, where he was responding to a jQuery question (if you’re interested in learning jQuery, note his use of the gt() and lt() selectors). I am just taking this opportunity to remind you that jQuery is not necessarily the miracle answer [Note to self: write a post about what jQuery is NOT good at].
If you already use jQuery in your page, then either solution is fine. If not, then just use my script and don’t bother about loading the jQuery library.

The Jan Tielens challenge

I have already talked about Jan in a previous post. End users who visit his blog can certainly feel like a kid in front of a bakery display: so many goodies that are out of reach!
So here is my proposal: if you find on Jan’s site a tool you’d like to have as an end user, send me the challenge! If a topic gets enough votes, I’ll work on a solution that can be implemented on the client side.

Oh, and don’t ask about highlighting rows in a list, it’s already done!

Display a list in another site (Cont’d)

Two months ago I published a simple method to display a SharePoint list in another site.

A few days ago, Nathan posted the following comment:

The one (minor) inconvenience I have found is that when you click the ID or Title field to navigate to the full record (dispform.aspx), the Close button will redirect you to the default view of the source list […] Would it be possible to modify the script in order to append something like Source=location.href to the ID/Title hyperlink? That way, the Close button on dispform.aspx would redirect you back to the starting page.

So here we go. As Nathan points out, SharePoint offers a very convenient “Source” querystring that tells where the user should be redirected after an action is completed.

This is the modified JavaScript version:

<!-- Load and display list - iframe version -->
<!-- Questions and comments: Christophe@PathToSharePoint.com -->

<DIV id="ListPlaceholder"><IMG src="/_layouts/images/GEARS_AN.GIF"></DIV>

<!-- Paste the URL of the source list below: -->


<script type="text/javascript">
function DisplayThisList()
{
var placeholder = document.getElementById("ListPlaceholder");

var displaylist = null;
var sourcelist = document.getElementById("SourceList");

try {
   if(sourcelist.contentDocument)
      // Firefox, Opera

      {displaylist = sourcelist.contentDocument.getElementById("WebPartWPQ1") ;}
   else if(sourcelist.contentWindow)
      // Internet Explorer

      {displaylist = sourcelist.contentWindow.document.getElementById("WebPartWPQ1") ;}
   else if(sourcelist.document)
      // Others?

      {displaylist = sourcelist.document.getElementById("WebPartWPQ1") ;}
}
catch(err) { alert ("Loading failed");}

displaylist.removeChild(displaylist.getElementsByTagName("table")[0]);
var allDescendants = displaylist.getElementsByTagName("*");
for (i=0;i<allDescendants.length;i++) {
allDescendants&#91;i&#93;.removeAttribute("id");
allDescendants&#91;i&#93;.removeAttribute("onclick");
allDescendants&#91;i&#93;.removeAttribute("onfocus");
allDescendants&#91;i&#93;.removeAttribute("onmouseover");
try {
if (allDescendants&#91;i&#93;.href.indexOf("?") > 0) {allDescendants[i].href = allDescendants[i].href + "\&Source=" + location.href;}
else {allDescendants[i].href = allDescendants[i].href + "?Source=" + location.href;}
}
catch(err) {}
}
placeholder.innerHTML = displaylist.innerHTML;
}
</script>

And the modified jQuery version:

<!-- Load and display list - jQuery version -->
<!-- Questions and comments: Christophe@PathToSharePoint.com -->
<DIV id="ListPlaceholder"><IMG src="/_layouts/images/GEARS_AN.GIF"></DIV>

<script type="text/javascript">
// Paste the URL of the source list below:
var SelectedView = "http://domain.com/SiteCollection1/SourceSite/SourceList/MyView.aspx";
$("#ListPlaceholder").load(SelectedView+" #WebPartWPQ1 .ms-listviewtable",function() {
$("#ListPlaceholder *").removeAttr("id").removeAttr("onclick").removeAttr("onfocus").removeAttr("onmouseover");
$("#ListPlaceholder a").each(function() { 
if ($(this).attr("href").indexOf("?") > 0) {$(this).attr("href", $(this).attr("href")+"\&Source="+location.href);}
else {$(this).attr("href", $(this).attr("href")+"?Source="+location.href);}
});
});
</script>

See the original post for more details. In particular, if you are using MOSS remember to replace WebPartWPQ1 with WebPartWPQ2 in the above scripts.

Update (March 24th): I have modified yesterday’s post to make the scripts work for document libraries as well. I have also corrected the jQuery version (thanks to Peter Allen for alerting me).

Update [6/16/2009]: I have modified the jQuery code for better performance. Thanks Danny van Loon for the advice!

Using calculated columns to write scripts

In the past 6 months, I have proposed many examples using calculated columns to write HTML. If you are not familiar with this method, I strongly recommend that you first check out the post that started it all before reading the rest of this article.

Warning! This article doesn’t offer any plug and play example. Wait for my next article to see the method at work.

The HTML calculated column method has allowed us to include “div”, “a”, “img”, “iframe”,”table” and other tags in our formulas. Now, have you tried to use “script” tags? If it worked, this would open up a whole new world of opportunities! Continue reading

Boxed views: one column only

[March 17, 2010] An updated script is now available here.

Yesterday I shared a live demo that shows how the usability of boxed views can be improved. Really cool effects, thanks again jQuery!

But, as Charlie Epes commented, a script that changes the boxed view from two columns to one would already be really useful.

So here is a simple one – 4 lines!

<script type="text/javascript">
var boxedview = document.getElementById("WebPartWPQ1").innerHTML;
boxedview = boxedview.replace(/<td width="1.5%">&nbsp;<\/td>/gi,"<\/tr><tr style='font-size:6px\;'><td>&nbsp;<\/td><\/tr><tr>");
boxedview = boxedview.replace(/<td width="1%">&nbsp;<\/td>/gi,"<\/tr><tr style='font-size:6px\;'><td>&nbsp;<\/td><\/tr><tr>");
document.getElementById("WebPartWPQ1").innerHTML = boxedview;
</script>

Add the script to a CEWP, under the boxed view. You can see it at work on this page.

The above script is written for wss. It identifies the Web Part by its id “WebPartWPQ1”. If you use MOSS, or if the boxed view is on a page along with other Web Parts (typically on the site home page), you’ll need to change the id to “WebPartWPQ2” or “WebPartWPQn“. Or you can modify the code to scroll through all the Web Parts on the page and grab the boxed views.