Category Archives: Requests

Poll for SharePoint

March 15, 2015: I have updated this solution. You find v2.0 here.

Please note: The Google Image Charts used in this solution is deprecated and will stop working April 20, 2015.

Change log
January 28. 2014
v1.54 fixes one incompatibility with SP 2013. I have not fully tested this so please let me know if you find any bugs.

July 01. 2013 v1.53: The sort order of answers in chart now reflects the order given in the setup. I have removed some script references so that you now must refer spjs-utility.js – get it here

You must update the CEWP code to include reference to spjs-utility.js

15.12.2011 v1.5.1: Bugfix in the file “PollForSharePoint.js” – line 238. The property “listBaseUrl” were missing – thanks to Jaap Dijkma for finding this bug.


04.12.2011 I have updated the solution to support putting the list in a central location. See “listBaseUrl” below.


22.10.2011 Updated the code to support free input rather than predefined answers – as requested by Claudia:

Hi, thank you for this wonderful piece of code, I think it’s great and we use it on our intranet :).
I wondered if it is possible to use a single line of text for free input instead of radiobuttons with preset answers?
We like to use the poll for ‘brandtagging’ so that people can type keywords.
Just wondering…..
thanks! Claudia

I thought this was a clever idea and have implemented this in v1.4. See updated CEWP code and description below.


17.08.2011 Updated the code with these changes:

  • Lets you view the results and come back to answer.
  • Tidied up the code.
  • The charts now supports https (Thanks to Google).
  • Removed the poll question from the chart title to preserve the formatting.

01.03.2011 Updated the code to fix a bug when “singleResponse” is true. Thanks to “Al”. I have also included two more settings in the “argObj”: qStyle and aStyle. These sets the CSS style of question and answers. See CEWP code below.


28.11.2010 Updated the code to support special characters in the poll question and answers. Thanks to Christophe for the tip.


This code lets you generate polls using JavaScript/jQuery only. No need for server side installed WebParts.

Poll
IMG

Result with column chart
IMG

Result with bar chart
IMG

Result with pie chart
IMG

Result with “table chart”
IMG

Already answered
IMG

Not active
IMG

Finished
IMG

The charts are generated using Google Chart Tools / Image Charts (aka Chart API).


Create a custom list with the following fields

  • Answer: Single line of text
  • Question: Single line of text

Name it anything you like, but keep the display name fairly simple (no special characters) as you will use the display name in the CEWP code.

CEWP code

The CEWP code below refers jQuery from Google. If you have a local copy of jQuery you can change the script src. You find the code for the file “PollForSharePoint.js” at the bottom of the page. In this example the file “PollForSharePoint.js” is placed in a document library named “Javascript”.

NOTE: You must change the script src for the file “PollForSharePoint.js” and “spjs-utility.js” to point your instance of the files – the CEWP code will not work unless you do this.

Place this code where you want the poll to appear:

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/Poll/spjs-utility.js"></script>
<script type="text/javascript" src="/test/English/Javascript/Poll/PollForSharePoint.js"></script>
<script type="text/javascript">

buildQueryWP({pollAnswerListName:'PollA',
				listBaseUrl:L_Menu_BaseUrl,				
				id:'myPoll_01-03-2011', // Allowed characters id a-z, 0-9 - and _
				start:'10/22/2011', // format: mm/dd/yyyy
				end:'11/10/2011', // format: mm/dd/yyyy
				singleResponse:false,
				q:"What is your<br />favorite junk food?",
				qStyle:'font-size:small;font-weight:bold;color:gray',
				aStyle:'font-size:xx-small',
				a:['Pizza','Hot dog','Hamburger','Neither of them'], // Leave empty for free input				
				color:['32CD32','FFCC11','FF3300','C0C0C0'],
				forceLowerCaseAnswer:false, // Group result by lowercase				
				chart:'col', // table, bar, col or pie
				height:100,
				width:450});
</script>

You can control these values in the top of the file “PollForSharePoint.js”:

var submitLinkText = 'Submit';
var backLinkText = 'Back';
var showResultBtnText = 'Show result';
var pollNotActiveText = 'The poll is not active prior to {0}';
var pollEndedText = 'The poll ended on {0}';
var alreadyRespondedText = 'You answered: ';
var totalResponsesLabel = "Total responses: ";

Object attributes explained

  • pollAnswerListName: DisplayName or GUID of the list that stores the answers
  • listBaseUrl: The baseUrl of the site. This is like “/sites/hr” when the list is located in the site “hr” under “/sites”. Use L_Menu_BaseUrl (or omit the property) for current site. New in v1.5
  • id: The unique id of the poll. All poll answers are stored in a list and this id is used to separate each poll
  • start: Start date in the format mm/dd/yyyy
  • end: End date in the format mm/dd/yyyy
  • singleResponse: true for one reply per user, false for unlimited number of replies
  • q: Poll question. To have a linefeed in the question, use <br>
  • qStyle: CSS syntax style
  • aStyle: CSS syntax style
  • a: Answers in an array format. To use free input and not predefined answers, leave the array empty.
  • color: Colors for the chart in an array format. This must have the same length as the previous parameter – one color for each answer
  • forceLowerCaseAnswer: Primarily for use with free input to avoid getting two “series” when the only difference are uppercase characters. New in v1.4
  • chart: “bar” for bar chart, “col” for column chart, “pie” for pie chart or “table” for a plain table.
  • height: Height in pixels
  • width: Width in pixels

Regarding free input
If you leave the attribute “a” as an empty array, the user can supply free text as “answer”. When using free input, the result are automatically presented as a table.

Download code

The code for the file “PollForSharePoint.js”:
Download code from this location

Ask if anything is unclear.

Alexander

Visual status indicator based on 5 date columns in a list view

31.07.2010 Updated the code for the file “FiveDateColumnStatus.js” and the CEWP code for ListView, NewForm, DispForm and EditForm. Added support for column name in the mouse over tooltip and the percentage is now “0 %” if none of the date fields are applicable. The CEWP code has changed as the “arrOfFields” has become an object (changed the parameter name to “arrOfFieldsObj”).

29.07.2010 Fixed bug when using using calculated columns and a date field is empty. Thanks to Charlie Epes for noticing the bug. The code for the file “FiveDateColumnStatus.js” is updated.

16.07.2010 Updated the file “FiveDateColumnStatus.js” to support calculated columns.

03.07.2010 Updated the code to add DispForm integration and made the number of “date fields” dynamic – you can add as many as you like to the array. The code in the file “FiveDateColumnStatus.js” is updated, and there has been added a new section for DispForm.

I got an e-mail from Charlie Epes about a ticket over at STP

Hi Alexander:
You are mentioned in the thick of a request started by me. If you want to chime in here, please do!

http://www.endusersharepoint.com/STP/viewtopic.php?f=10&t=1319

My challenge was:
Here’s an example of what I’m trying to do in my List.

Let’s say my dog’s veterinarian offers 5 shots for my dog. The vet’s record needs to show the expiration of each of the 5 shots. The vet likes to keep a “score” for my dog showing that Rover is 80% up to date on his shots.

Of course there are exceptions. My dog will never need 1 of the shots, so it’s “not applicable” and using it as one of the 5 to calculate my percentage throws off my score. I figure that entering a distant date of 12/31/8900 would indicate that the shot is not applicable.

In my case, I’m using a List to indicate the “Health” of a client’s files by giving each client an overall score based on how up to date “eligible” files are.


I do not mean to hijack this discussion, but this code was to complex to insert in a comment over at STP… Feel free to continue the discussion where it originally started.

I thought of it and found that i could do this in another way than Charlie and Dessie had going in the discussion.

My approach reuses some scripts from this article Vertical Scrolling WebPart to get the items from a list view.

It then parses the dates and present the status by colored “boxes” and a “percent complete” value like this:
IMG

IMG

IMG = a date to come
IMG = a date that has passed
IMG = a date that is applicable, but is empty
IMG = a date that does not apply

It provides a mouse over with the “Overdue by X days” or “Due in X days”

This approach can be used with grouped views.

I have separated the main code in a file named “FiveDateColumnStatus.js”. This file should be saved locally and refered in the CEWP code. The code is found below.

To use this solution you have to:
Create some date columns, corresponding Yes/No type columns and one calculated column with the formula =”*Status*”

The date columns does not have to be in the list view, but the calculated column to hold the status and the “Title (linked to item with edit menu)” must be.

A modification from Charlie’s original idea is the presence of a checkbox for each date field to indicate whether it is applicable and should count in the total percentage – or not.
IMG
If the Checkbox is not checked – the corresponding date column is hidden.

NewForm and EditForm


This code goes in a CEWP below NewForm and/or EditForm and hides the date fields if the corresponding Yes/No column is not checked:
[javascript]
<script type="text/javascript" src="../../Javascript/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
fields = init_fields_v2();
/*
This code goes in a CEWP below NewForm and/or EditForm and hides the date fields if the corresponding Yes/No column is not checked.
arrOfDateFieldsObj:
Array of objects containing FieldInternalName for the date column and FieldInternalName for the bool column to select whether the date column is applicable.
Format is {dateFIN:’FieldInternalNameOfDateColumn’,applicableFIN:’FieldInternalNameOfTriggerYesNoColumn’}.
*/

var arrOfDateFieldsObj = [{dateFIN:’DateField1′,applicableFIN:’Date1Applicable’},
{dateFIN:’DateField2′,applicableFIN:’Date2Applicable’},
{dateFIN:’DateField3′,applicableFIN:’Date3Applicable’},
{dateFIN:’DateField4′,applicableFIN:’Date4Applicable’},
{dateFIN:’DateField5′,applicableFIN:’Date5Applicable’}]

hideDateFieldsNotApplicable(arrOfDateFieldsObj);

function hideDateFieldsNotApplicable(arrOfFieldsObj){
$.each(arrOfFieldsObj,function(i,obj){
var thisChck = $(fields[obj.applicableFIN]).find(‘:checkbox’);
var checked = thisChck.attr(‘checked’);
// Onload
if(!checked){
$(fields[obj.dateFIN]).hide();
}
// Onclick
thisChck.click(function(){
var checked = $(this).attr(‘checked’);
if(checked){
$(fields[obj.dateFIN]).show();
}else if(!checked){
$(fields[obj.dateFIN]).hide();
}
});
});
}

function init_fields_v2(){
var res = {};
$("td.ms-formbody").each(function(){
var myMatch = $(this).html().match(/FieldName="(.+)"s+FieldInternalName="(.+)"s+FieldType="(.+)"s+/);
if(myMatch!=null){
// Display name
var disp = myMatch[1];
// FieldInternalName
var fin = myMatch[2];
// FieldType
var type = myMatch[3];
if(type==’SPFieldNote’){
if($(this).find(‘script’).length>0){
type=type+"_HTML";
}
}
if(type==’SPFieldLookup’){
if($(this).find(‘input’).length>0){
type=type+"_Input";
}
}
// Build object
res[fin] = this.parentNode;
res[fin].FieldDispName = disp;
res[fin].FieldType = type;
}
});
return res;
}
</script>
[/javascript]

DispForm


This code goes in a CEWP below the DispForm:
[javascript]
<script type="text/javascript" src="../../Javascript/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="../../Javascript/FiveDateColumnStatus.js"></script>
<script type="text/javascript">
fields = init_fields_v2();
/*
This code goes in a CEWP below DispForm. It displays the status field in the same way as in the list view.
It also hides the date fields if the corresponding Yes/No column is not checked.

Parameters:
listGuid: The GUID of this list
arrOfDateFieldsObj: Array of objects containing FieldInternalName for the date column, DisplayName for the date column and FieldInternalName for the bool column to select whether the date column is applicable.
Format is {dateFIN:’FieldInternalNameOfDateColumn’,dateDispName:’DisplayNameOfDateColumn’,applicableFIN:’FieldInternalNameOfTriggerYesNoColumn’}.
*/
var listGuid = "31FFC357-74DA-4830-8288-D8214781556D";
var arrOfDateFieldsObj = [{dateFIN:’_Date1′,dateDispName:’Datefield number 1′,applicableFIN:’Date1Applicable’},
{dateFIN:’DateField2′,dateDispName:’Datefield number 2′,applicableFIN:’Date2Applicable’},
{dateFIN:’DateField3′,dateDispName:’Datefield number 3′,applicableFIN:’Date3Applicable’},
{dateFIN:’DateField4′,dateDispName:’Datefield number 4′,applicableFIN:’Date4Applicable’},
{dateFIN:’DateField5′,dateDispName:’Datefield number 5′,applicableFIN:’Date5Applicable’}]

// Get ID from querystring
var thisItemID = getQueryParameters().ID;
// Call function to generate "Status field contents"
var res = getDateFieldStatus(arrOfDateFieldsObj,{‘listName’:listGuid,’ID’:thisItemID});
// Write back the result to the calculated field containig the value "*Status*"
$.each(fields,function(id,obj){
if($(obj).find(‘.ms-formbody’).text().match(/*Status*/)){
$(obj).find(‘.ms-formbody’).html(res[thisItemID].html)
}
});

// Hide fields that does not apply
$.each(arrOfDateFieldsObj,function(i,obj){
var applicable = res[thisItemID][obj.applicableFIN];
if(!applicable){
$(fields[obj.dateFIN]).hide();
$(fields[obj.applicableFIN]).hide();
}
});
</script>
[/javascript]

List view


This code is used in the list view, and must be inserted in a CEWP below the list view webpart:
[javascript]
<script type="text/javascript" src="../../Javascript/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="../../Javascript/FiveDateColumnStatus.js"></script>
<script type="text/javascript">
/*
arrOfDateFieldsObj:
Array of objects containing FieldInternalName for the date column, DisplayName for the date column and FieldInternalName for the bool column to select whether the date column is applicable
Format is {dateFIN:’FieldInternalNameOfDateColumn’,dateDispName:’DisplayNameOfDateColumn’,applicableFIN:’FieldInternalNameOfTriggerYesNoColumn’}
*/

var arrOfDateFieldsObj = [{dateFIN:’_Date1′,dateDispName:’Datefield number 1′,applicableFIN:’Date1Applicable’},
{dateFIN:’DateField2′,dateDispName:’Datefield number 2′,applicableFIN:’Date2Applicable’},
{dateFIN:’DateField3′,dateDispName:’Datefield number 3′,applicableFIN:’Date3Applicable’},
{dateFIN:’DateField4′,dateDispName:’Datefield number 4′,applicableFIN:’Date4Applicable’},
{dateFIN:’DateField5′,dateDispName:’Datefield number 5′,applicableFIN:’Date5Applicable’}]

getDateFieldStatus(arrOfDateFieldsObj);
</script>
[/javascript]

FiveDateColumnStatus.js


This is the code for the file “FiveDateColumnStatus.js”:
[javascript]
/* ———————————————
* Created by Alexander Bautz
* alexander.bautz@gmail.com
* https://spjsblog.com
* Copyright (c) 2010 Alexander Bautz (Licensed under the MIT X11 License)
* v1.1
* LastMod: 31.07.2010
* LastChange:
– Variable number of date columns
– DispForm integration
– Support for calculated columns
– 29.07.2010: Fixed bug when using using calculated columns and a date field is empty.
– 31.07.2010: Added the column name in the mouseover tooltip. If none of the date fields are applicable, the percentage reads "0 %".

* See this blog post for instructions:

Visual status indicator based on 5 date columns in a list view

* ———————————————
*/

function getDateFieldStatus(arrOfFieldsObj,qParam){
if(typeof(arrOfFieldsObj)!=’undefined’){
viewFieldsArr = [];
dateFieldsArr = [];
dateFieldsDispNameArr = [];
dateApplicableArr = [];
$.each(arrOfFieldsObj,function(i,obj){
dateFieldsArr.push(obj.dateFIN);
dateFieldsDispNameArr.push(obj.dateDispName);
dateApplicableArr.push(obj.applicableFIN);
});
viewFields = viewFieldsArr.concat([‘ID’],dateFieldsArr,dateApplicableArr);
}
if(typeof(myCustomQueryRes)==’undefined’){
wsBaseUrl = L_Menu_BaseUrl + ‘/_vti_bin/’;
if(qParam==undefined){
myCustomQueryRes = queryItemsByViewName(ctx.listName,ctx.view,viewFields);
}else{
var query = ‘<Where><Eq><FieldRef Name="ID" /><Value Type="Text">’ + qParam.ID + ‘</Value></Eq></Where>’;
myCustomQueryRes = queryItemsByCAML(qParam.listName,query,viewFields);
}
if(myCustomQueryRes.count==-1){
alert("An error occured in the query.");
}else{
resObj = {};
var now = new Date();
$.each(myCustomQueryRes.items,function(i,item){
var thisID = item.ID;
resObj[thisID] = {};
var dArr = [];
$.each(dateFieldsArr,function(j,fin){
if(item[dateApplicableArr[j]]==1){
dArr.push((item[fin]==null)?”:item[fin].replace(/w+;#/,”).split(‘-‘));
// Add to the object: applicable
resObj[thisID][dateApplicableArr[j]]=true;
}else{
dArr.push(‘NotApplicable’);
// Add to the object: not applicable
resObj[thisID][dateApplicableArr[j]]=false
}
});

var percent = 0;
var increment = ((100/dArr.length)*100)/100;
var htmlBuffer = [];
var notApplicableCount = 0;
$.each(dArr,function(idx,dateArr){
if(typeof(dateArr)==’object’){
if(dateArr.length==1){
date = ”;
}else{
var y = dateArr[0];
var m = (dateArr[1]-1);
var d = dateArr[2].substring(0,dateArr[2].indexOf(‘ ‘));
var date = new Date(y,m,d);
}
}else if(dateArr==’NotApplicable’){
notApplicableCount += 1;
date = ‘NotApplicable’;
}else if(dateArr==undefined){
date = ”;
}
var dummyIMG = "<img src=’"+L_Menu_BaseUrl+"/_layouts/images/blank.gif’ style=’height:7px;width:7px;border:1px silver solid’>";
if(date==”){
htmlBuffer.push("<span title=’"+dateFieldsDispNameArr[idx]+" is empty’ style=’background-color:yellow’>"+dummyIMG+"</span>");
}else if(date==’NotApplicable’){
percent+=increment;
htmlBuffer.push("<span title=’"+dateFieldsDispNameArr[idx]+" is not applicable’ style=’background-color:white’>"+dummyIMG+"</span>");
}else if(date>now){
percent+=increment;
var countDays = Math.floor(((date-now)/86400000)*10)/10; // 86400000 in one day
var postfix = (countDays>1)?"s":”;
htmlBuffer.push("<span title=’"+dateFieldsDispNameArr[idx]+" is due in "+countDays+" day"+postfix+"’ style=’background-color:green’>"+dummyIMG+"</span>");
}else{
var countDays = Math.floor(((now-date)/86400000)*10)/10; // 86400000 in one day
var postfix = (countDays>1)?"s":”;
htmlBuffer.push("<span title=’"+dateFieldsDispNameArr[idx]+" is overdue by "+countDays+" day"+postfix+"’ style=’background-color:red’>"+dummyIMG+"</span>");
}
});
if(notApplicableCount==dArr.length){
var percentagePostfix = "0 %";
}else{
var percentagePostfix = Math.floor(percent)+" %";
}
resObj[thisID].html=htmlBuffer.join(‘&nbsp;’)+" "+percentagePostfix;
});
}
}
if(qParam==undefined){
updateView(resObj);
}else{
return resObj;
}
}

function updateView(obj){
$("table.ms-listviewtable td:contains(‘*Status*’)").each(function(){
var lineID = $(this).parent().find(‘table.ms-unselectedtitle’).attr(‘id’);
$(this).html(obj[lineID].html);
});
}

// Attaches a call to the function to the "expand grouped elements function" for it to function in grouped listview’s
function ExpGroupRenderData(htmlToRender, groupName, isLoaded){
var tbody=document.getElementById("tbod"+groupName+"_");
var wrapDiv=document.createElement("DIV");
wrapDiv.innerHTML="<TABLE><TBODY id="tbod"+groupName+"_" isLoaded=""+isLoaded+"">"+htmlToRender+"</TBODY></TABLE>";
tbody.parentNode.replaceChild(wrapDiv.firstChild.firstChild,tbody);
getDateFieldStatus();
}

/*****************************************************
Web service calls
*****************************************************/
function queryItemsByCAML(listName, query, viewFields, rowLimit, pagingInfo){
var content = wrapQueryContent({‘listName’:listName,’query’:query,’rowLimit’:rowLimit,’viewFields’:viewFields,’pagingInfo’:pagingInfo});
var result = {count:-1, nextPagingInfo:”, items:[]};
wrapSoapRequest(wsBaseUrl + ‘lists.asmx’, ‘http://schemas.microsoft.com/sharepoint/soap/GetListItems’, content, function(data){
result.count = $(data).find("[nodeName=’rs:data’]").attr(‘ItemCount’);
result.nextPagingInfo = $(data).find("[nodeName=’rs:data’]").attr(‘ListItemCollectionPositionNext’);
$(data).find("[nodeName=’z:row’]").each(function(idx, itemData){
var fieldValObj = {}
$.each(viewFields,function(i,field){
var value = $(itemData).attr(‘ows_’ + field);
if(value == undefined) value = null;
fieldValObj[field]=value;
});
result.items.push(fieldValObj);
});
});
return result;
}

// Function to pull items from view
function queryItemsByViewName(listName, viewName, viewFields, pagingInfo){
var content = wrapQueryContent({‘listName’:listName,’viewName’:viewName,’viewFields’:viewFields,’pagingInfo’:pagingInfo});
var result = {count:-1, nextPagingInfo:”, items:[]};
wrapSoapRequest(wsBaseUrl + ‘lists.asmx’, ‘http://schemas.microsoft.com/sharepoint/soap/GetListItems’, content, function(data){
result.count = $(data).find("[nodeName=’rs:data’]").attr(‘ItemCount’);
result.nextPagingInfo = $(data).find("[nodeName=’rs:data’]").attr(‘ListItemCollectionPositionNext’);
$(data).find("[nodeName=’z:row’]").each(function(idx, itemData){
var fieldValObj = {}
$.each(viewFields,function(i,field){
var value = $(itemData).attr(‘ows_’ + field);
if(value == undefined) value = null;
fieldValObj[field]=value;
});
result.items.push(fieldValObj);
});
});
return result;
}

function wrapQueryContent(paramObj){
var result = [];
result.push(‘<GetListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/">’);
result.push(‘<listName>’ + paramObj.listName + ‘</listName>’);
if(paramObj.viewName!=undefined){
result.push(‘<viewName>’ + paramObj.viewName + ‘</viewName>’);
}
if(paramObj.query != null && paramObj.query != ”){
result.push(‘<query><Query xmlns="">’);
result.push(paramObj.query);
result.push(‘</Query></query>’);
}
if(paramObj.viewFields != null && paramObj.viewFields.length > 0){
result.push(‘<viewFields><ViewFields xmlns="">’);
$.each(paramObj.viewFields, function(idx, field){
result.push(‘<FieldRef Name="’ + field + ‘"/>’);
});
result.push(‘</ViewFields></viewFields>’);
}
if(paramObj.rowLimit != undefined && paramObj.rowLimit != null && paramObj.rowLimit > 0){
result.push(‘<rowLimit>’ + paramObj.rowLimit + ‘</rowLimit>’);
}else{
result.push(‘<rowLimit>100000</rowLimit>’);
}
result.push(‘<queryOptions><QueryOptions xmlns=""><IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>’);
if(paramObj.pagingInfo != undefined && paramObj.pagingInfo != null && paramObj.pagingInfo != ”)
result.push(‘<Paging ListItemCollectionPositionNext="’ + paramObj.pagingInfo.replace(/&/g, ‘&amp;’) + ‘" />’);
result.push(‘</QueryOptions></queryOptions>’);
result.push(‘</GetListItems>’);
return result.join(”);
}

function wrapSoapRequest(webserviceUrl,requestHeader,soapBody,successFunc){
var xmlWrap = [];
xmlWrap.push("<?xml version=’1.0′ encoding=’utf-8′?>");
xmlWrap.push("<soap:Envelope xmlns:xsi=’http://www.w3.org/2001/XMLSchema-instance’ xmlns:xsd=’http://www.w3.org/2001/XMLSchema’ xmlns:soap=’http://schemas.xmlsoap.org/soap/envelope/’>");
xmlWrap.push("<soap:Body>");
xmlWrap.push(soapBody);
xmlWrap.push("</soap:Body>");
xmlWrap.push("</soap:Envelope>");
xmlWrap = xmlWrap.join(”);
$.ajax({
async:false,
type:"POST",
url:webserviceUrl,
contentType:"text/xml; charset=utf-8",
processData:false,
data:xmlWrap,
dataType:"xml",
beforeSend:function(xhr){
xhr.setRequestHeader(‘SOAPAction’,requestHeader);
},
success:successFunc
});
}

/*********************************
Helper functions
*********************************/
// Function to separate each url search string parameters
function getQueryParameters(){
qObj = {};
var urlSearch = window.location.search;
if(urlSearch.length>0){
var qpart = urlSearch.substring(1).split(‘&’);
$.each(qpart,function(i,item){
var splitAgain = item.split(‘=’);
qObj[splitAgain[0]] = splitAgain[1];
});
}
return qObj;
}

function init_fields_v2(){
var res = {};
$("td.ms-formbody").each(function(){
var myMatch = $(this).html().match(/FieldName="(.+)"s+FieldInternalName="(.+)"s+FieldType="(.+)"s+/);
if(myMatch!=null){
// Display name
var disp = myMatch[1];
// FieldInternalName
var fin = myMatch[2];
// FieldType
var type = myMatch[3];
if(type==’SPFieldNote’){
if($(this).find(‘script’).length>0){
type=type+"_HTML";
}
}
if(type==’SPFieldLookup’){
if($(this).find(‘input’).length>0){
type=type+"_Input";
}
}
// Build object
res[fin] = this.parentNode;
res[fin].FieldDispName = disp;
res[fin].FieldType = type;
}
});
return res;
}
[/javascript]
Save as “FiveDateColumnStatus.js” – mind the file extension and update the script “src” in the CEWP code to point to where you save this script.

Ask if anything is unclear!
Alexander

Vertical Scrolling WebPart

15.07.2010 Updated the code for the file “VerticalScrollingWebPart.js” to try to fix the overflow issue in IE6.

24.06.2010 The code for the file “VerticalScrollingWebPart.js” is updated. I actually forgot to update it as i made some adjustments in the CEWP code… I hope the overflow issue is fixed with this update.

I got this request from Charlie:

…a solution that I can use as a dashboard web part to vertically scroll the most recent “top 10” items from a 2nd list?

Example: I need to be able to grab the most recently items marked as “Completed” or “Sold” from a list and be able to show 2-3 fields in the scroller.

Click here for a crude example – the scrolling is not so choppy – this is only a animated gif to give you a hint of how the solution works.

The scrolling action pauses when hovered with the mouse.

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
IMG

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.4.2.min. Update the script “src” if you use another version.

The sourcecode for the file “VerticalScrollingWebPart.js” is provided below.

Add this code to a CEWP where you want the scrollable contents to appear:

<script type="text/javascript" src="/test/English/Javascript/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/VerticalScrollingWebPart.js"></script>
<script type="text/javascript">
init_fillScrollableDiv({'listGuid':'A4B4E15A-C5B0-47BC-A08B-739CD48FE58A',
	'listBaseUrl':'/test/English',
	'listViewGuid':'5BD378F4-25D5-4880-9C5B-1667FE43978D',											
	'viewFields':['Title','MultiLine'],
	'viewFieldsStyle':['font-weight:bold','padding:0 0 0 5'],	
	'divID':'myScrollableDiv',
	'divHeight':'250px',
	'divWidth':'500px',	
	'speed':4,
	'linkBack':true});
</script>

Read here how to find the FieldInternalNames for your fields and the “listGuid” or “listViewGuid” for your list.

Parameters explained:

  • listGuid: GUID of the source-list
  • listBaseUrl: Relative URL to site/subsite. If root site use only two quotes representing a blank string – like this: “”.
  • listViewGuid: GUID of the view to use from the source-list – if left empty it uses the default view.
  • viewFields: Array of FieldInternalNames to use in the scrolling webpart.
  • viewFieldsStyle: Array of styles to match the array of FieldInternalNames above.
  • divID: The ID of the scrollable DIV. Can be anything as long as it is unique on the page.
  • divHeight: The hight of the scrollable area.
  • divWidth: The width of the scrollable area.
  • speed: A number representing the scroll speed.
  • linkBack: If set to true it adds a link to open the item clicked on.

The sourcecode for the file “VerticalScrollingWebPart.js”:

/* Pulls items from a list view and presents them as a Vertical Scrolling Web Part
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * Copyright (c) 2009-2010 Alexander Bautz (Licensed under the MIT X11 License)
 * v1.2
 * LastMod: 14.07.2010
 	- LastChange: 23.12.2014 - fixed a jQuery incompatibility issue for newer jQuery versions. Please note that the code is still old and not the best of quality.
 * ---------------------------------------------
 * Include reference to:
 *  jquery - http://jquery.com
 *	VerticalScrollingWebPart.js (this file)
 * ---------------------------------------------
*/

function init_fillScrollableDiv(obj){
	// Build the div
	var myDivBuffer = [];
	myDivBuffer.push("<div style='vertical-align:top;position:relative;overflow:hidden;cursor:default;height:"+obj.divHeight+";width:"+obj.divWidth+"'>");
	myDivBuffer.push("<div id='"+obj.divID+"' style='position:relative'></div>");
	myDivBuffer.push("</div>");
	myDivContainer=myDivBuffer.join('');	
	document.write(myDivContainer);
	$(document).ready(function(){
		fillScrollableDiv(obj)
	});
}

function fillScrollableDiv(info){
	wsBaseUrl = info.listBaseUrl + '/_vti_bin/';
	info.animBegin = 0;
	info.animPart = 0;
	// Query the list for items	
	var res = queryItemsByViewName(info.listGuid,info.listViewGuid,info.viewFields.concat('ID','FileDirRef'));
	if(res.count==-1)alert("An error occured - check the parameters \"listGuid\", \"listBaseUrl\" and \"listViewGuid\".");
	var finalBuffer = [];
	var path = '';
	$.each(res.items,function(i,item){
		var partBuffer = [];
		if(path==''){
			var pathRaw = item['FileDirRef'];
			path = "/"+pathRaw.substring(pathRaw.indexOf(';#')+2);
		}	
		$.each(info.viewFields,function(idx,fin){
			var style = '';
			var thisVal = (item[fin]==null)?'':item[fin];
			if(thisVal.indexOf(';#')>-1){
				thisVal = thisVal.substring(thisVal.indexOf(';#')+2);
			}			
			if(info.viewFieldsStyle[idx]!=undefined){
				style = " style='"+info.viewFieldsStyle[idx]+"'";
			}
			partBuffer.push("<tr><td"+style+">"+thisVal+"</td></tr>");	
		});		
		finalBuffer.push("<hr style='height:1px;color:black' />");
		if(info.linkBack){
			finalBuffer.push("<table title='Go to item' style='cursor:pointer' ");
			finalBuffer.push("onclick='javascript:location.href=\""+path+"/DispForm.aspx?ID="+item['ID']+"&Source="+location.href+"\"' ");
			finalBuffer.push("cellspacing='0' cellpadding='0'>"+partBuffer.join('')+"</table>");
		}else{
			finalBuffer.push("<table cellspacing='0' cellpadding='0'>"+partBuffer.join('')+"</table>");
		}	
	});
	var myContents = finalBuffer.join('');
	// Update the content in the scrollable div
	$("#"+info.divID).html(myContents)
		.mouseenter(function(){			
			var now = new Date();
			info.animPart += (now-info.animBegin);		
			$(this).stop();
		})
		.mouseleave(function(){		
			$(this).stop();
			var partScr = parseInt($(this).css('top'));
			scrollMyDiv(partScr,info);
		});
	// Call scroll function
	scrollMyDiv('',info);
}

function scrollMyDiv(scroll,info){
	info.animBegin = new Date();
	var myDiv = $("#"+info.divID);
	var ch = myDiv.height();
	var chpHeight = myDiv.parent().height();	
	if(scroll==''){
		var scroll=chpHeight;
	}
	var duration = (ch*(info.speed*10))-info.animPart;
	myDiv.css({'top':scroll}).animate({"top":-ch},duration,'linear',function(){
		info.animPart = 0;
		scrollMyDiv('',info);
	});
}

// http://www.steveworkman.com/html5-2/javascript/2011/improving-javascript-xml-node-finding-performance-by-2000/
$.fn.filterNode = function(name) {
	return this.find('*').filter(function() {
		return this.nodeName === name;
	});
};

// Function to pull items from view
function queryItemsByViewName(listName, viewName, viewFields, pagingInfo){
	var content = buildQueryContentByViewName(listName, viewName, viewFields, pagingInfo);
	var result = {count:-1, nextPagingInfo:'', items:[]};
	wrapSoapRequest(wsBaseUrl + 'lists.asmx', 'http://schemas.microsoft.com/sharepoint/soap/GetListItems', content, function(data){
		result.count = parseInt($(data).filterNode("rs:data").attr('ItemCount'),10);
		result.nextPagingInfo = $(data).filterNode("rs:data").attr('ListItemCollectionPositionNext');
		$(data).filterNode('z:row').each(function(idx, itemData){
			var fieldValObj = {}
			$.each(viewFields,function(i,field){
				var value = $(itemData).attr('ows_' + field);
				if(value == undefined) value = null;
				fieldValObj[field]=value;
			});	
			result.items.push(fieldValObj);		
		});
	});
	return result;
}

function buildQueryContentByViewName(listName, viewName, viewFields, pagingInfo){
	var result = [];
	result.push('<GetListItems xmlns="http://schemas.microsoft.com/sharepoint/soap/">');
	result.push('<listName>' + listName + '</listName>');
	result.push('<viewName>' + viewName + '</viewName>');
	if(viewFields != null && viewFields.length > 0){
		result.push('<viewFields><ViewFields xmlns="">');
		$.each(viewFields, function(idx, field){
			result.push('<FieldRef Name="' + field + '"/>');
		});
		result.push('</ViewFields></viewFields>');
	}
	result.push('<queryOptions><QueryOptions xmlns=""><IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>');
	if(pagingInfo != undefined && pagingInfo != null && pagingInfo != '')
		result.push('<Paging ListItemCollectionPositionNext="' + pagingInfo.replace(/&/g, '&') + '" />');
	result.push('</QueryOptions></queryOptions>');
	result.push('</GetListItems>');
	return result.join('');
}

function wrapSoapRequest(webserviceUrl,requestHeader,soapBody,successFunc){
	var xmlWrap = [];
		xmlWrap.push("<?xml version='1.0' encoding='utf-8'?>");
		xmlWrap.push("<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>");
		xmlWrap.push("<soap:Body>");
		xmlWrap.push(soapBody);
		xmlWrap.push("</soap:Body>");
		xmlWrap.push("</soap:Envelope>");
		xmlWrap = xmlWrap.join('');
	$.ajax({
		async:false,
		type:"POST",
		url:webserviceUrl,
		contentType:"text/xml; charset=utf-8",
		processData:false,
		data:xmlWrap,
		dataType:"xml",
		beforeSend:function(xhr){
			xhr.setRequestHeader('SOAPAction',requestHeader);
		},
		success:successFunc
	});
}

Save as “VerticalScrollingWebPart.js” – mind the file extension – and upload to the scriplibrary as shown above.

Ask if anything is unclear!
Alexander

Numbers only in single line text field

18.06.2010 – small update to set “lengthOfInputNumber” relative to the array of FieldInternalnames.

By request from Larry, here is a solution that restricts input in a single line text field to number only.

Note: It is treated as text in SharePoint and you cannot sum or average in a list view.

Add this code to a CEWP below the list form in your NewForm or EditForm:
[javascript]
<script type="text/javascript" src="/test/English/Javascript/jquery-1.4.2.min.js"></script>
<script type="text/javascript">
fields = init_fields_v2();

// Allow numbers only in text fields
// Array of FieldInternalNames to lilmit to numbers only
var arrToCheckForNum = [‘Title’,’Num’];
// Array of length of input string – set to 255 if you do not want to limit input length. Corresponds to the same array index in the "arrToCheckForNum"
var lengthOfInputNumber = [9,2];

$.each(arrToCheckForNum,function(idx,item){
$(fields[item]).find(‘input’).css({‘width’:’75px’});
$(fields[item]).find(‘input’).keyup(function(e){
var thisVal = $(this).val();
thisVal = thisVal.substring(0,lengthOfInputNumber[idx]);
$(this).val(thisVal.replace(/[^0-9]/g,”));
}).blur(function(){
var thisVal = $(this).val();
thisVal = thisVal.substring(0,lengthOfInputNumber[idx]);
$(this).val(thisVal.replace(/[^0-9]/g,”));
});
});

/*
LastMod: 07.05.2010
*/
function init_fields_v2(){
var res = {};
$("td.ms-formbody").each(function(){
var myMatch = $(this).html().match(/FieldName="(.+)"s+FieldInternalName="(.+)"s+FieldType="(.+)"s+/);
if(myMatch!=null){
// Display name
var disp = myMatch[1];
// FieldInternalName
var fin = myMatch[2];
// FieldType
var type = myMatch[3];
if(type==’SPFieldNote’){
if($(this).find(‘script’).length>0){
type=type+"_HTML";
}
}
if(type==’SPFieldLookup’){
if($(this).find(‘input’).length>0){
type=type+"_Input";
}
}
// Build object
res[fin] = this.parentNode;
res[fin].FieldDispName = disp;
res[fin].FieldType = type;
}
});
return res;
}
</script>
[/javascript]

The parameters “arrToCheckForNum” and “lengthOfInputNumber” must be adapted to suite your needs.

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.4.2.min. If you use another version, remember to update the script “src”.

Alexander

Show field description in list view column header

15.05.2012 You find an updated version here.
08.07.2010 Updated the code to make it cross compatible for SP2007 and SP2010.


I got this request from Saran to display the field description when hovering the mouse over the column header in a list view.

IMG

Add this code to a CEWP and place it below the list view:
[javascript]
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript">

if(typeof(_fV4UI)===’undefined’){ // SP2007
var myTooltipObj = customGetList(ctx.listName);
$(".ms-listviewtable th").each(function(){
var fieldDisplayName = $(this).find("table:first").attr(‘DisplayName’);
if(fieldDisplayName==undefined){
fieldDisplayName = $(this).text();
}
if(myTooltipObj[fieldDisplayName]!=undefined){
$(this).attr(‘title’,myTooltipObj[fieldDisplayName]).find(‘a’).attr(‘title’,myTooltipObj[fieldDisplayName]);;
}
});
}else{ // SP2010
var myTooltipObj = customGetList(_spPageContextInfo.pageListId);
$("div.ms-vh-div").each(function(){
var fieldDisplayName = $(this).attr(‘DisplayName’);
if(myTooltipObj[fieldDisplayName]!==undefined){
$(this).attr(‘title’,myTooltipObj[fieldDisplayName]);
}
});
}

function customGetList(listName){
xmlStr = [];
xmlStr.push(‘<GetList xmlns="http://schemas.microsoft.com/sharepoint/soap/">’);
xmlStr.push(‘<listName>’ + listName + ‘</listName>’);
xmlStr.push(‘</GetList>’);
xmlStr = xmlStr.join(”);
var result = {};
wrapSoapRequest(L_Menu_BaseUrl + ‘/_vti_bin/lists.asmx’, ‘http://schemas.microsoft.com/sharepoint/soap/GetList’, xmlStr, function(data){
$(‘Field’, data).each(function(i){
if(result[$(this).attr(‘DisplayName’)]==undefined || result[$(this).attr(‘DisplayName’)]==”){
result[$(this).attr(‘DisplayName’)] = ($(this).attr(‘Description’)==undefined)?”:$(this).attr(‘Description’);
}
});
});
return result;
}

function wrapSoapRequest(webserviceUrl,requestHeader,soapBody,successFunc){
var xmlWrap = [];
xmlWrap.push("<?xml version=’1.0′ encoding=’utf-8′?>");
xmlWrap.push("<soap:Envelope xmlns:xsi=’http://www.w3.org/2001/XMLSchema-instance’ xmlns:xsd=’http://www.w3.org/2001/XMLSchema’ xmlns:soap=’http://schemas.xmlsoap.org/soap/envelope/’>");
xmlWrap.push("<soap:Body>");
xmlWrap.push(soapBody);
xmlWrap.push("</soap:Body>");
xmlWrap.push("</soap:Envelope>");
xmlWrap = xmlWrap.join(”);
$.ajax({
async:false,
type:"POST",
url:webserviceUrl,
contentType:"text/xml; charset=utf-8",
processData:false,
data:xmlWrap,
dataType:"xml",
beforeSend:function(xhr){
xhr.setRequestHeader(‘SOAPAction’,requestHeader);
},
success:successFunc
});
}
</script>
[/javascript]

Note: This will only work when only one list view webpart is in the page.

Ask if anything is unclear.

Alexander

Manipulate upload link in document library

I got this request from JGilmore:

Anyone know how to change the URL of the ‘Upload’ button as well as the links under its dropdown? I would like to have the user directed to a custom upload page with ‘Overwrite existing files’ unchecked by default.

Thanks in advance.


Here is one possible approach

Add this code in a CEWP below the list view to override the default links:
[javascript]
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">

// Manipulate direct click on upload button
$("*[id$=’_UploadMenu’]").parents(‘td:first’).removeAttr(‘onclick’).click(function(){
redirectUpload(false);
});

// Manipulate "Upload Document"
$("*[id$=’_Upload’]").attr(‘onMenuClick’,’redirectUpload(false)’);

// Manipulate "Upload Multiple Documents"
$("*[id$=’_MultipleUpload’]").attr(‘onMenuClick’,’redirectUpload(true)’);

function redirectUpload(multi){
// Set your new upload destination (custom upload page)
// ctx.listName is provided by SharePoint
if(multi){
window.location=’/test/English/MyCustomUploadPage.aspx?List=’+ctx.listName+"&MultipleUpload=1";
}else{
window.location=’/test/English/MyCustomUploadPage.aspx?List=’+ctx.listName;
}
}
</script>

[/javascript]

Change the new upload location in the function “redirectUpload”. Also edit the source of the jQuery script if you prefer to use a local copy.

Ask if anything is unclear.

Alexander

Autocomplete for single line of text

I got a request form Charlie on how to accomplish autocomplete for a single line text-field, reading the values from itself.

This solution resembles my Autocomplete for SharePoint people picker, but it pulls date into a single line text field.

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
IMG

In addition to the above scripts, i have the jQuery UI 1.8 in a separate folder. See the CEWP code and point the links to your jQuery UI location.

The jQuery UI-library is found here. The pictures and the sourcecode refers to jquery-ui-1.8. The autocomplete widget is not found in previous releases.

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.4.2.min. The autocomplete widget is not supported in previous releases.

The scripts “interaction.js” and “stringBuffer.js” is created by Erucy and published on CodePlex.

Read here how to add a CEWP to the NewForm or EditForm, how to find the list Guid of your list, and how to find the FieldInternalName of your columns.

Add a CEWP below the list form in NewForm or EditForm, and insert this code:

[javascript]
<link type="text/css" href="../../jQueryUI18/smoothness/jquery-ui-1.8.custom.css" rel="stylesheet">
<script type="text/javascript" src="../../jQueryUI18/jquery-1.4.2.min.js"></script>
<script type="text/javascript" src="../../jQueryUI18/jquery-ui-1.8.custom.min.js"></script>
<script type="text/javascript" src="../../Javascript/interaction.js"></script>
<script type="text/javascript" src="../../Javascript/StringBuffer.js"></script>
<script type="text/javascript">
fields = init_fields_v2();

/*
Call autocomplete function with these parameters:
sourceFIN: FieldInternalName of the field to query for the value
targetFIN: FieldInternalName of the field to add the autocomplete to
listName: List name or list GUID of the list to query
listBaseUrl: The base url for the list to query. If on a managed path it must reflect this.
*/

setAutocompletefromQuery(‘Title’,’Title’,’0582CD60-C312-4483-996B-A15C27E332AA’,”);

function setAutocompletefromQuery(sourceFIN,targetFIN,listName,listBaseUrl){
var availableTags = [];
wsBaseUrl = listBaseUrl + ‘/_vti_bin/’;
var query = "<Where><IsNotNull><FieldRef Name=’"+sourceFIN+"’ /></IsNotNull></Where>";
var res = queryItems(listName,query,[sourceFIN]);
// Build array of unique values – might be slow if list contains large amounts of data
$.each(res.items,function(i,item){
var currVal = item[sourceFIN];
if($.inArray(currVal,availableTags)==-1){
availableTags.push(currVal);
}
});
// Add autocomplete
$(fields[targetFIN]).find(‘input’).autocomplete({
source: availableTags
});
// Text size in dropdown
$("body").css({‘font-size’:’65%’});
}

/*
LastMod: 07.05.2010
*/
function init_fields_v2(){
var res = {};
$("td.ms-formbody").each(function(){
var myMatch = $(this).html().match(/FieldName="(.+)"s+FieldInternalName="(.+)"s+FieldType="(.+)"s+/);
if(myMatch!=null){
// Display name
var disp = myMatch[1];
// FieldInternalName
var fin = myMatch[2];
// FieldType
var type = myMatch[3];
if(type==’SPFieldNote’){
if($(this).find(‘script’).length>0){
type=type+"_HTML";
}
}
if(type==’SPFieldLookup’){
if($(this).find(‘input’).length>0){
type=type+"_Input";
}
}
// Build object
res[fin] = this.parentNode;
res[fin].FieldDispName = disp;
res[fin].FieldType = type;
}
});
return res;
}
</script>
[/javascript]

Ask if anything is unclear.

Alexander

Image preview on hover over a hyperlink field

05.05.2010 Updated code to adapt the position of the image depending on height and width (and scroll) of the page relative to the image size.


I got this request from Mark:

Hi Alexander,

I did not include the column as a picture because they are to big to display in the list view. For this reason I setup a list with a hyperlink to the image.

Besides it would look terrible having a list that displays all these large pictures in a list view. For that reason, I have been trying to find some way to display an image from a list when a user hovers over a row or column.


IMG
Add this code to a CEWP below the list view, and it will add a hover function to all hyperlinks including an image (add more types to the array “arrOfImageTypes”):
[javascript]
<script type="text/javascript" src="../../Javascript/jquery-1.4.2.min.js"></script>
<script type="text/javascript">

function imagePreview(){
arrOfImageTypes = [‘jpg’,’jpeg’,’gif’,’png’];
$("table.ms-listviewtable td.ms-vb2 a").hover(function(e){
var href = this.href;
var img = href.substring(href.lastIndexOf(‘.’)+1).toLowerCase();
if(href.indexOf(‘http’)==0 && $.inArray(img,arrOfImageTypes)>-1){
$("body").append("<img id=’preview’ src=’"+ this.href +"’ alt=’Image preview’ />");
}
var obj = $("#preview");
var offset = $(this).offset();
var winHeight = $(window).height();
var winWidth = $(window).width();
var scrollLeft = $(window).scrollLeft();
var scrollTop = $(window).scrollTop();
var objHeight = obj.outerHeight();
var objWidth = obj.width()+15;
if(((winWidth+scrollLeft)-offset.left)<objWidth){
offset.left=((winWidth+scrollLeft)-objWidth);
}
var maxHeight = (winHeight+scrollTop)-offset.top;
if(objHeight>maxHeight){
if(offset.top-scrollTop>objHeight){
offset.top=offset.top-objHeight-20;
}
height = (objHeight<winHeight)?objHeight:winHeight;
}
obj.css({"position":"absolute","top":(offset.top+20)+"px","left":offset.left+20})
.fadeIn("fast");
},
function(){
$("#preview").remove();
});
};

// Call the script on page load
$(document).ready(function(){
imagePreview();
});
</script>
[/javascript]

Regards
Alexander