Friday, June 14, 2013

How To: SharePoint Suggestions Box using Search Service and the Client Side Object Model

I recently had the requirement to have a suggestion box beneath the Title field on a new item form for a SharePoint list. 



Basically when a user submits a new item to a list, as he is typing in the title we want to display all other items in that list with similar names. Reason for this being people are submitting Ideas to the system and we don't want multiple ideas with the same purpose.

I initially used the standard SharePoint Lists service to get a list of items and then tried to find matching words - but I found that using the SharePoint search service does a better job in matching results.

I found a blog post on an async search box which does most of what I need by Jan Tielens (http://weblogs.asp.net/jan) and just made some adjustments. Below is the code I used on my page:

HTML---------------------------------------------------------------------------------------
<div id="quickSearchResults" style="display: none;"></div>

<style type="text/css">
#quickSearchResultsHeader{
line-height:20px;
font-size:16px;
}
#quickSearchResults{
border:solid 1px gray;
padding:10px;
line-height:20px;
}
</style>

JavaScript
---------------------------------------------------------------------------------------
// *** Customizable parameters ***
var quickSearchConfig = {
    delay: 500, // time to wait before executing the query (in ms)
    minCharacters: 3, // minimum nr of characters to enter before search
    numberOfResults: 5, // number of results to show
    resultsAnimation: 200, // animation time (in ms) of the search results
    resultAnimation: 0 // animation time (in ms) of individual result (when selected)
};
var quickSearchTimer;
var quickSearchSelectedDivIndex = -1;
var searchingTextBox = $("input[title='Title']");
$(document).ready(function () {
    searchingTextBox.keyup(function (event) {
        var previousSelected = quickSearchSelectedDivIndex;
        // catch some keys
        switch (event.keyCode) {
            case 13: // enter
                var selectedDiv = $("#quickSearchResults>div:eq(" + quickSearchSelectedDivIndex + ") a");
                if (selectedDiv.length == 1)
                    window.location = selectedDiv.attr("href");
                break;
            case 38: // key up
                quickSearchSelectedDivIndex--;
                break;
            case 40: // key down
                quickSearchSelectedDivIndex++;
                break;
        }
        // check bounds
        if (quickSearchSelectedDivIndex != previousSelected) {
            if (quickSearchSelectedDivIndex < 0)
                quickSearchSelectedDivIndex = 0;
            if (quickSearchSelectedDivIndex >= $("#quickSearchResults>div").length - 1)
                quickSearchSelectedDivIndex = $("#quickSearchResults>div").length - 2;
        }
        // select new div, unselect the previous selected
        if (quickSearchSelectedDivIndex > -1) {
            if (quickSearchSelectedDivIndex != previousSelected) {
                unSelectDiv($("#quickSearchResults>div:eq(" + previousSelected + ")"));
                selectDiv($("#quickSearchResults>div:eq(" + quickSearchSelectedDivIndex + ")"));
            }
        }
        // if the query is different from the previous one, search again
        if (searchingTextBox.data("query") != searchingTextBox.val()) {
            if (quickSearchTimer != null) // cancel the delayed event
                clearTimeout(quickSearchTimer);
            quickSearchTimer = setTimeout(function () { // delay the searching
                $("#quickSearchResults").fadeOut(200, initSearch);
            }, quickSearchConfig.delay);
        }
    });
});
function unSelectDiv(div) {
    // first stop all animations still in progress
    $("#quickSearchResults>div>div").stop(true, true);
    div.removeClass("quickSearchResultDivSelected").addClass("quickSearchResultDivUnselected");
    $("#details", div).hide();
}
function selectDiv(div) {
    div.addClass("quickSearchResultDivSelected");
    $("#details", div).slideDown(quickSearchConfig.resultAnimation);
}
function initSearch() {
    // first store query in data
    searchingTextBox.data("query", searchingTextBox.val());
    // clear the results
    $("#quickSearchResults").empty();
    // start the search
    var query = searchingTextBox.val();
    if (query.length >= quickSearchConfig.minCharacters) {
        search(query);
    }
}
function search(query) {
    quickSearchSelectedDivIndex = -1;
    var queryXML =
"<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'> \
<Query domain='QDomain'> \
<SupportedFormats><Format>urn:Microsoft.Search.Response.Document.Document</Format></SupportedFormats> \
<Context> \
<QueryText language='en-US' type='STRING' >contenttype:Idea " + query + "</QueryText> \
</Context> \
<SortByProperties><SortByProperty name='Rank' direction='Descending' order='1'/></SortByProperties> \
<Range><StartAt>1</StartAt><Count>" + quickSearchConfig.numberOfResults + "</Count></Range> \
<EnableStemming>false</EnableStemming> \
<TrimDuplicates>true</TrimDuplicates> \
<IgnoreAllNoiseQuery>true</IgnoreAllNoiseQuery> \
<ImplicitAndBehavior>true</ImplicitAndBehavior> \
<IncludeRelevanceResults>true</IncludeRelevanceResults> \
<IncludeSpecialTermResults>true</IncludeSpecialTermResults> \
<IncludeHighConfidenceResults>true</IncludeHighConfidenceResults> \
</Query></QueryPacket>";
    var soapEnv =
"<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/'> \
<soap:Body> \
<Query xmlns='urn:Microsoft.Search'> \
<queryXml>" + escapeHTML(queryXML) + "</queryXml> \
</Query> \
</soap:Body> \
</soap:Envelope>";
    $.ajax({
        url: "/_vti_bin/search.asmx",
        type: "POST",
        dataType: "xml",
        data: soapEnv,
        complete: processResult,
        contentType: "text/xml; charset=\"utf-8\""
    });
    function processResult(xData, status) {
        var xml = "<xml>" + $(xData.responseText).find("QueryResult").text() + "</xml>";
        var xmldoc = new ActiveXObject("Microsoft.XMLDOM");
        xmldoc.loadXML(xml);
        var itemElements = xmldoc.getElementsByTagName("Document");
        var srHtml = "";
        if (itemElements.length > 0) {
            srHtml = "<div id=\"quickSearchResultsHeader\">Similar Ideas</div><div>";
            for (var i = 0; i < itemElements.length; i++) {
                var srUrl = $(itemElements[i].getElementsByTagName("LinkUrl")).text();
                if (srUrl.indexOf('/Lists/Ideas') > 0) { // limits the results to one specific list
                    var srTitle = $(itemElements[i].getElementsByTagName("Title")).text();
                    srHtml = srHtml + "<a href=\"" + srUrl + "\">" + srTitle + "</a><br/>";
                }
            }
            srHtml = srHtml + "</div>";
            $("#quickSearchResults").html(srHtml);
            $("#quickSearchResults").slideDown(200);
        }
    }
}
function escapeHTML(str) {
    return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}



Friday, January 13, 2012

SharePoint 2010 Forms Based Authentication on an Extended Web Application



The following will explain how to create an extended web application in your SharePoint 2010 environment and how to set up Forms Based Authentication with its own database on it.

We first create our ASP.Net membership database, then configure our providers for Central Admin and the Secure Store Web Service, then extend the web application, then finally we configure our extended web application and the default web site.

(Note that Forms Based Authentication can only be added onto web applications that have Claims Authentication enabled. You can activate this using a PowerShell command in section 5 of this walkthrough.)
1.       Create the database for the ASP.Net Membership and Role Providers:

a.       Using Microsoft SQL Server Management Studio, create a new empty database on your server called aspnetdb_claim. 
b.      Open Command Prompt and browse to the following location:
                         i.      C:\Windows\Microsoft.NET\Framework64\v2.0.50727
c.       Run the following command:
aspnet_regsql -S <SPSSERVER> -E -A mr –d aspnetdb_claim
·   -S Specifies the server.
·   -E Specifies that Windows authentication should be used to connect to SQL Server.
·   -A mr Specifies that the membership and role feature should be added.
·   -d Specifies the database name.
d.      Give your web application AppPool account full permissions on the database
2.       Create FBAMembershipProvider and FBARoleProvider providers in IIS for Central Admin

a.       On your application server, select the Central Administration v4 website in IIS
b.      Open Connection Strings and click Add in the right hand pane. Then add a connection string to your previously created database and call it FBADB

c.       Select the Central Admin site again, open Providers, select .NET Roles from the Feature dropdown at the top and click Add in the right hand pane.

a.       Set the Type as SqlRoleProvider
b.      Name the provider FBARoleProvider
c.       Set the ConnectionStringName property to FBADB
d.      Set the ApplicationName property to /

d.      Select the Central Admin site again, open Providers, select .NET Users from the Feature dropdown at the top and click Add in the right hand pane.

a.       Set the Type as SqlMembershipProvider
b.      Name the provider FBAMembershipProvider
c.       Set the EnablePasswordReset property to True
d.      Set the ConnectionStringName property to FBADB
e.      Set the ApplicationName property to /

3.       Configure Secure Store Web Service and Default Website
The default website will be your site running on port 80 by default e.g. (SharePoint – 80). The Secure Store web site can be found by expanding the SharePoint Web Services site and selecting the SecurityTokenServiceApplication
a.       Repeat Section 2, steps A to D, to create the Membership and Role Providers for both of these websites.
4.       Extend the Web Application

a.       Open Central Administration and select Application Management from the left hand pane
b.      Select Manage Web Applications and click on the default SharePoint application (SharePoint – 80) so that it is highlighted
c.       Click on Extend in the Ribbon controls
d.      Give the new extended application name (e.g. SharePoint – 81)
e.      Set the port number (e.g. 81)
f.        Uncheck the Enable Windows Authentication box and check Enable Forms Based Authentication (FBA) (If this is not available, see next section)
g.       Set the ASP.NET Membership provider name property to the Membership provider name created previously (i.e. FBAMembershipProvider)
h.      Set the ASP.NET Role manager name to the Role Provider name created previously (i.e. FBARoleProvider)
i.         Click OK


5.       Activate Claims authentication if Forms Based Authentication is not available

·         NOTE: This only has to be done if the Forms Based Authentication (FBA) option is not available when extending the web application:
o   Open PowerShell on the application server and run the following commands:
§ $webapp = Get-SPWebApplication http://<spsserver>/ $webapp.UseClaimsAuthentication = $true $webapp.Update()
6.       Configure Extended Web Application
Note: This has to be done on all application- and web front end servers
a.       On your application server, select the Extended website in IIS (e.g. SharePoint – 81)
b.      Repeat Section 2, steps A to D, to create the Membership and Role Providers
c.       Select the Extended website again and open the .Net Roles section
o   You will probably get the following error:

o   There is another configuration file that contains a list of all the trusted providers and for some reason the default claims providers are not in there. This does however not break anything and can be ignored.
d.      Click on Set Default Provider, then select the FBARoleProvider and click OK. This will allow you to add a new administrator role for your administrator user.
e.      On the right hand pane, click on Add, type in administrator as the name and click ok. Your new Role should appear on screen. 
f.        Select the Extended website again and open the .Net Roles section
o   You will get the same error as before and can just ignore it again.
g.       Click on Set Default Provider, then select the FBAMembershipProvider and click OK. This will allow you to add a new administrator user for your FBA site.
h.      On the right hand pane and click on Add. Type in your Username, email, password and click Next.
i.         Now you can tick the user group called administrator that was created in the previous step and click Finish.

7.       Set up site collection administrators
a.       From Central Administration, click on Application management and select Change site collection administrators
b.      For the secondary site collection administrator, add the user created previously in the .NET Users in IIS for the extended web application
c.       Click OK
8.       Reset the default providers
The default providers have to be set back to the claims authentication providers before the site can work. The providers are only changed temporarily in the set up to allow for adding the admin users.
a.       Open IIS and select the extended web application on the left (i.e. SharePoint – 81)
b.      Open the .NET Users and set the default provider to i
c.       Open the .NET Roles and set the default provider to c

Friday, November 11, 2011

SharePoint 2010: Programmatically send a file to the records center

If you want to send a file to a Records Center, whether it's a sub site or a seperate site collection, you can use the following code:

string additionalInformation = string.Empty;

SPOfficialFileHost ofh = new SPOfficialFileHost();
ofh.Action = SPOfficialFileAction.Move;
ofh.OfficialFileName = item.File.Name;
ofh.OfficialFileUrl = new Uri("http://yourserver/sites/RecordsCenter");

OfficialFileResult returnValue = item.File.SendToOfficialFile("Content-type", ofh, "", SPOfficialFileSubmissionMode.ExpirationPolicy, out additionalInformation);

if (returnValue != OfficialFileResult.Success)
{
throw new Exception("Error sending file to records center: " + additionalInformation);
}

Friday, October 28, 2011

How To: Programmatically generate a report and save the results as a PDF to SharePoint.

I had a requirement this week to run a report from within one of our WCF services, get it as a PDF file and then save it to a SharePoint library. To do this you have to use the reporting services ReportExecutionService to run the report and get the file, then use the SharePoint Copy service to store it in the library. Here's how:

1. Add a web reference to the service 
1.1 Right click on your project and click on Add Service Reference
 1.2 Click on the Advanced button in the bottom left
 1.3 Click on Add Web Reference
 1.4 In the URL box add the reference to
http://yourReportServer/reportserver/reportexecution2005.asmx?wsdl
 1.5 Give your service a name and click on Add Reference

2. Create the service call method

// create the service
ReportExecutionService rs = new ReportExecutionService();
rs.Credentials = EcmServices.CreateNetworkCredential();
rs.Url = ConfigurationSettings.AppSettings["ReportServerUrl"];


// Prepare report parameter.
ParameterValue[] parameters = new ParameterValue[1];
parameters[0] = new ParameterValue();
parameters[0].Name = "ID";
parameters[0].Value = id;  // this is a value I pass to the method


// render arguments
byte[] result = null;
string reportPath = "/My Reports/Report";
string format = "PDF";
string historyID = null;
string devInfo = @"<DeviceInfo><Toolbar>False</Toolbar></DeviceInfo>";
string encoding;
string mimeType;
string extension;
Warning[] warnings = null;
string[] streamIDs = null;


// create execution info
ExecutionInfo execInfo = new ExecutionInfo();
ExecutionHeader execHeader = new ExecutionHeader();
rs.ExecutionHeaderValue = execHeader;
execInfo = rs.LoadReport(reportPath, historyID);
rs.SetExecutionParameters(parameters, "en-us");
String SessionId = rs.ExecutionHeaderValue.ExecutionID;


// execute the report and get the rendered file as a byte array
result = rs.Render(format, devInfo, out extension, out encoding, out mimeType, out warnings, out streamIDs);
execInfo = rs.GetExecutionInfo();


if(result != null)
{
 SubmitFileToSharePoint(result, id);
}


As above, the results return as a byte[] and this can be used in the SharePoint Copy service to store the file.

3. Store the file using the SharePoint Copy service
You need to add a web reference to the SharePoint Copy service in the same way as you did for the ReportExecutionService. The URL for this service is: http://yourSharepointServer/_vti_bin/Copy.asmx
public static void SubmitFileToSharePoint(byte[] fileBytes, int id){
 // create the copy service
 Copy copyWS = new Copy();
 copyWS.Url = "
http://yourSharepointServer/_vti_bin/Copy.asmx";
 copyWS.Credentials = System.Net.NetworkCredential("username", "password", "domain");


 // create the file url
 string documentLibraryUrl = "
http://yourSharepointServer/Documents/Report_" + id + ".pdf";
 string[] destinationUrls = { documentLibraryUrl };


 // set metadata values on the file
 FieldInformation info1 = new FieldInformation();
 info1.DisplayName = "Report ID";
 info1.Value = id.ToString();
 FieldInformation[] info = { info1 };


 // create the result variable
 var copyResult = new CopyResult();
 CopyResult[] copyResults = { copyResult };


 // copy the file
 copyWS.CopyIntoItems(documentLibraryUrl, destinationUrls, info, fileBytes, out copyResults);
 copyWS.Dispose();

 if (copyResults[0].ErrorCode != CopyErrorCode.Success)
 {
  throw new SPException(string.Format("Error copying file to {0}. {1}", copyResults[0].DestinationUrl, copyResults[0].ErrorMessage));
 }
}

Thursday, October 13, 2011

How To: Stretch an iFrame to the size of its contents

I had a requirement to put a PageViewerControl inside a SharePoint 2007 page (which is rendered out as an iFrame) and the height of it must be set to the height of its contents. Now most browsers won't allow you to do this at all for security reasons and I would suggest finding another way to build this, but during my development I did get this little script working on a lot of the more common browsers. Not sure how it will do on IE8 or IE9 though...

<script type="text/javascript">
 // get the iframe
 var oFrame = document.getElementsByTagName("iframe")[0];

 function SetFrameHeight_FireFox(){ 
  try{
   // remove border and scrolling
   oFrame.setAttribute("frameborder","0");
   oFrame.setAttribute("scrolling","no");
 
   // set height and width
   var oBody = oFrame.contentWindow.document.body;
   document.getElementById("ifrm").style.height = oBody.scrollHeight + "px";
   document.getElementsByTagName("iframe")[0].style.width = "960px";
  }
  catch(e){
   window.status =  'Error: ' + e.number + '; ' + e.description;
  }
 }

 function SetFrameHeight_IE(){
  try{
   // set height and width
   var oBody = oFrame.document.body;
   document.getElementById("ifrm").style.height = ifrm.document.body.scrollHeight + (ifrm.document.body.offsetHeight - ifrm.document.body.clientHeight);
   document.getElementsByTagName("iframe")[0].style.width = "960px";
  
   // remove scrolling
   ifrm.document.body.setAttribute('scroll', 'no');
  }
  catch(e){
   window.status =  'Error: ' + e.number + '; ' + e.description;
  }
 }

 // check for browser type and call function when frame has finished loading.
 if(document.implementation && document.implementation.createDocument)
 {
  oFrame.setAttribute("onLoad","SetFrameHeight_FireFox()");
 }
 else
 {
  oFrame.setAttribute("onload","SetFrameHeight_IE()");
 
 }
</script>

Thursday, October 6, 2011

How To: SharePoint 2010 Exchange Rate Web Part

This is another web part which our clients frequently require which is not always available on the web, and when it is it's not what you're looking for. The workings of this is going to be really simple so you will be able to customize it easily for your needs. The web part will for now only display selected exchange rates. The RSS feed is provided by TheMoneyConverter.com



1. Create a Visual Web Part project
In Visual Studio, click on File > New > Project and select the Visual Web Part template from the SharePoint 2010 installed templates. Give your project a name e.g. CurrencyWebPart, click Ok and click Finish.



In your solution explorer you can rename the web part and its code files to suit your needs. I have renamed mine to CurrencyWebPart. Remember to update the properties in the .webpart file if you rename your solution elements otherwise you will get an issue with the SafeControl entries when trying to add the web part to a page.

2. Add the front-end controls

The following is the code for the user control .ascx file
<style type="text/css">
    .erBox
    {
        text-align:center;
        width:300px;
    }
    .erTitle, erItem, erDate
    {
        font-size:8pt;
        font-family:Verdana, Arial, Helvetica, Sans-Serif;
        color:#676767;
    }
   
    .erTitle
    {
        font-weight:bold;
        margin-bottom:5px;
    }
   
    .erLastUpdate
    {
        margin-top:5px;
        font-weight:bold;
    }
   
    .erDate, .erLastUpdate
    {
        font-style:italic;
    }
</style>

<div class="erBox">
    <div class="erTitle"><asp:Label runat="server" ID="lblTitle"></asp:Label></div>
    <div class="erItem">1 USD = <asp:Label runat="server" ID="lblUSD"></asp:Label> ZAR</div>
    <div class="erItem">1 GBP = <asp:Label runat="server" ID="lblGBP"></asp:Label> ZAR</div>
    <div class="erItem">1 EUR = <asp:Label runat="server" ID="lblEUR"></asp:Label> ZAR</div>
    <div class="erLastUpdate">Last update on:</div>
    <div class="erDate"><asp:Label runat="server" ID="lblDate"></asp:Label></div>
</div>


3. Add the code for rendering the page

The following code will get the Xml document from the URL and transform the data for the web part.

public partial class CurrencyUserControl : UserControl
{
 public const string _rssUrl = "
http://themoneyconverter.com/ZAR/rss.xml";
 protected void Page_Load(object sender, EventArgs e)
 {
  // get the nodes
  XmlDocument xdoc = CreateXmlDoc();
  string title = xdoc.SelectSingleNode("/rss/channel/title").InnerText;
  string buildDate = xdoc.SelectSingleNode("/rss/channel/lastBuildDate").InnerText;
  XmlNodeList nodeList = xdoc.SelectNodes("/rss/channel/item");

  DisplayExchangeRates(nodeList);
  lblTitle.Text = title;
  lblDate.Text = buildDate;
 }

 private void DisplayExchangeRates(XmlNodeList nodeList)
 {
  foreach (XmlNode node in nodeList)
  {
   string title = GetTitle(node.SelectSingleNode("title").InnerText);
   double value = GetValue(node.SelectSingleNode("description").InnerText);

   if (title.Equals("USD"))
    lblUSD.Text = value.ToString("##.00");
   else if (title.Equals("GBP"))
    lblGBP.Text = value.ToString("##.00");
   else if(title.Equals("EUR"))
    lblEUR.Text = value.ToString("##.00");
  }
 }

 private string GetTitle(string title)
 {
  string val = title;
  val = val.Substring(0, val.IndexOf("/"));
  return val;
 }

 private Double GetValue(string value)
 {
  // seeing that the value is in a description we have to hack it out there... yes I know it's nasty
  string val = value;
  val = val.Substring(val.IndexOf("=") + 2);
  val = val.Substring(0, val.IndexOf(" "));
  return 1 / double.Parse(val);
 }

 private XmlDocument CreateXmlDoc()
 {
  // create a reader and populate the document
  XmlReader reader = XmlReader.Create(_rssUrl);
  XmlDocument doc = new XmlDocument();
  doc.Load(reader);
  return doc;
 }
}


4. Deploy the solution

The solution can be deployed by right clicking the project in your solution explorer and clicking Deploy.

Wednesday, October 5, 2011

How To: SharePoint 2010 News Scroller Web Part

In this tutorial I will show you how to create a dynamic news scroller that will take the top news headlines and make them scroll upwards after a short period of time. This was done with a SharePoint 2010 Visual Web Part and some JavaScript from DynamicDrive.com.


For the purpose of this tutorial I will be hard-coding a URL from News24.com and using their latest articles. If you want the web part to be more dynamic in letting users choose their region or a different news site you can look at adding custom properties for the web part which can be set later on. View my How To: SharePoint 2010 Weather Web Part blog article to find out more on how this is done.

1. Create a new Visual Web Part project

Click on File > New > Project and select the Visual Web Part project template from the SharePoint 2010 Templates and click Ok.
Deploying as a Farm solution should be your only option so just click on Finish.
(I have renamed my web part to NewsScrollerWebPart for this tutorial)


2. Add the JavaScript for the scroller

Right click your project and click Add > SharePoint Mapped Folder and choose the TEMPLATES/LAYOUTS folder. Add a new normal folder inside with the name of your web part e.g. NewsScrollerWebPart. Create a new JScript file inside and paste the following code into it:





/***********************************************
* Pausing up-down scroller- © Dynamic Drive (
www.dynamicdrive.com)
* Visit
http://www.dynamicdrive.com/ for this script and 100s more.
***********************************************/

function pausescroller(content, divId, delay) {
    this.content = content //message array content
    this.tickerid = divId //ID of ticker div to display information
    this.delay = delay //Delay between msg change, in miliseconds.
    this.mouseoverBol = 0 //Boolean to indicate whether mouse is currently over scroller (and pause it if it is)
    this.hiddendivpointer = 1 //index of message array for hidden div
    document.write('<div id="' + divId + '" style="position: relative; overflow: hidden"><div class="innerDiv" style="position: absolute; width: 100%" id="' + divId + '1">' + content[0] + '</div><div class="innerDiv" style="position: absolute; width: 100%; visibility: hidden" id="' + divId + '2">' + content[1] + '</div></div>')
    var scrollerinstance = this
    if (window.addEventListener) //run onload in DOM2 browsers
        window.addEventListener("load", function () { scrollerinstance.initialize() }, false)
    else if (window.attachEvent) //run onload in IE5.5+
        window.attachEvent("onload", function () { scrollerinstance.initialize() })
    else if (document.getElementById) //if legacy DOM browsers, just start scroller after 0.5 sec
        setTimeout(function () { scrollerinstance.initialize() }, 500)
}

// -------------------------------------------------------------------
// initialize()- Initialize scroller method.
// -Get div objects, set initial positions, start up down animation
// -------------------------------------------------------------------

pausescroller.prototype.initialize = function () {
    this.tickerdiv = document.getElementById(this.tickerid)
    this.visiblediv = document.getElementById(this.tickerid + "1")
    this.hiddendiv = document.getElementById(this.tickerid + "2")
    this.visibledivtop = parseInt(pausescroller.getCSSpadding(this.tickerdiv))
    //set width of inner DIVs to outer DIV's width minus padding (padding assumed to be top padding x 2)
    this.visiblediv.style.width = this.hiddendiv.style.width = this.tickerdiv.offsetWidth - (this.visibledivtop * 2) + "px"
    this.getinline(this.visiblediv, this.hiddendiv)
    this.hiddendiv.style.visibility = "visible"
    var scrollerinstance = this
    document.getElementById(this.tickerid).onmouseover = function () { scrollerinstance.mouseoverBol = 1 }
    document.getElementById(this.tickerid).onmouseout = function () { scrollerinstance.mouseoverBol = 0 }
    if (window.attachEvent) //Clean up loose references in IE
        window.attachEvent("onunload", function () { scrollerinstance.tickerdiv.onmouseover = scrollerinstance.tickerdiv.onmouseout = null })
    setTimeout(function () { scrollerinstance.animateup() }, this.delay)
}


// -------------------------------------------------------------------
// animateup()- Move the two inner divs of the scroller up and in sync
// -------------------------------------------------------------------

pausescroller.prototype.animateup = function () {
    var scrollerinstance = this
    if (parseInt(this.hiddendiv.style.top) > (this.visibledivtop + 5)) {
        this.visiblediv.style.top = parseInt(this.visiblediv.style.top) - 5 + "px"
        this.hiddendiv.style.top = parseInt(this.hiddendiv.style.top) - 5 + "px"
        setTimeout(function () { scrollerinstance.animateup() }, 50)
    }
    else {
        this.getinline(this.hiddendiv, this.visiblediv)
        this.swapdivs()
        setTimeout(function () { scrollerinstance.setmessage() }, this.delay)
    }
}

// -------------------------------------------------------------------
// swapdivs()- Swap between which is the visible and which is the hidden div
// -------------------------------------------------------------------

pausescroller.prototype.swapdivs = function () {
    var tempcontainer = this.visiblediv
    this.visiblediv = this.hiddendiv
    this.hiddendiv = tempcontainer
}

pausescroller.prototype.getinline = function (div1, div2) {
    div1.style.top = this.visibledivtop + "px"
    div2.style.top = Math.max(div1.parentNode.offsetHeight, div1.offsetHeight) + "px"
}

// -------------------------------------------------------------------
// setmessage()- Populate the hidden div with the next message before it's visible
// -------------------------------------------------------------------

pausescroller.prototype.setmessage = function () {
    var scrollerinstance = this
    if (this.mouseoverBol == 1) //if mouse is currently over scoller, do nothing (pause it)
        setTimeout(function () { scrollerinstance.setmessage() }, 100)
    else {
        var i = this.hiddendivpointer
        var ceiling = this.content.length
        this.hiddendivpointer = (i + 1 > ceiling - 1) ? 0 : i + 1
        this.hiddendiv.innerHTML = this.content[this.hiddendivpointer]
        this.animateup()
    }
}

pausescroller.getCSSpadding = function (tickerobj) { //get CSS padding value, if any
    if (tickerobj.currentStyle)
        return tickerobj.currentStyle["paddingTop"]
    else if (window.getComputedStyle) //if DOM2
        return window.getComputedStyle(tickerobj, "").getPropertyValue("padding-top")
    else
        return 0
}


3. Create the JavaScript Links from the RSS XML

The script file uses a javascript array of links to render. We will basically just pull out the XML from the RSS feed, select the title and link nodes for each news headline and build up html links for the array:


public partial class NewsScrollerUserControl : UserControl
{
 // url of the RSS feed
 public const string _rssUrl = "
http://feeds.news24.com/articles/News24/TopStories/rss";
 protected void Page_Load(object sender, EventArgs e)
 {
  XmlDocument xdoc = CreateXmlDoc();
  XmlNodeList nodeList = xdoc.SelectNodes("/rss/channel/item");
  string script = BuildScript(nodeList);

  // register the script on the page
  ClientScriptManager csm = Page.ClientScript;
  csm.RegisterClientScriptBlock(this.GetType(), "ScrollerScript", script, true);
 }

 private String BuildScript(XmlNodeList nodeList)
 {
  // build the script
  StringBuilder sb = new StringBuilder();
  sb.Append("var pausecontent = new Array();");

  // add a link for each news headline
  int count = 0;
  foreach (XmlNode node in nodeList)
  {
   sb.Append(String.Format("pausecontent[{0}] = '<a target=\"_blank\" href=\"{1}\">{2}</a>'; ", count, node.SelectSingleNode("link").InnerText, node.SelectSingleNode("title").InnerText));
   count++;
  }

  return sb.ToString();
 }

 private XmlDocument CreateXmlDoc()
 {
  // create a reader and populate the document
  XmlReader reader = XmlReader.Create(_rssUrl);
  XmlDocument doc = new XmlDocument();
  doc.Load(reader);
  return doc;
 }
}


4. Add the Styles, JavaScript call method and script reference
This call method will make a call with your rendered links to the JavaScript file and bring back the scrolling HTML.

<script src="/_layouts/NewsScrollerWebPart/Scroller.js" type="text/javascript"></script>
<style type="text/css">
    #pscroller{
        width: 100%;
        height: 15px;
        border: 1px solid #ededed;
        padding: 3px;
        font-family:Verdana, Arial, sans-serif;
        font-size:8pt;
    }

    #pscroller a{
        text-decoration: none;
        color:#676767;
    }
   
    #pscroller a:hover{
        text-decoration: underline;
        color:#676767;
    }
</style>

<script type="text/javascript">
    new pausescroller(pausecontent, "pscroller", 3500);
</script>