Entries in JavaScript (13)

Tuesday
Jan152013

A Fresh Visualization for SharePoint Calendars with SPServices

This is a set of scripts that can be dropped into a SharePoint 2010 deployment to allow for an alternate view of calendar lists.

There was also a request to color cancelled items that the user had on the calendar so I've left that code in there. The widget display start and end times as well as alter the behavior for those events that span days. I also added CAML support so you can provide your own query to it if you want or just you the built-in default. There is a separate CSS file to do your own styling and a few images that I use. One is optional and controlled by the arguments to the script.

I chose to set this up to live in our farm's common repository and be called relatively easily from any page so you see 2 files here, one being the LoadCalendarWidget function and the other being the code we drop into a page to create the calendar control. Options/args you pass to the function are:

  1. Site's URL, no end '/' character
  2. List name
  3. Title to appear for the webpart
  4. GUID of your calendar list
  5. # of results to show
  6. Optional CAML query to use
  7. Indicator to show icon or use 'Add to Outlook' text instead

If you want to keep it simple and not place file directly on your farm's servers, the zipped package below has all of the files needed and is just about ready-to-use for a dirt-simple deployment.  There is a PDF included that contains instructions on use that are a little more detailed than in the posting that follows.

Download all code and images used here.

This is the code we call to create the control. I house this in a text file in a library and add to the page in a CEWP as the 'Content Link'.

1
2
3
4
5
6
7
8
9
10
11
12
<!-- jQuery -->
<script type="text/javascript" src="https://myfarm/_layouts/1033/jquery.SPServices-0.7.2.min.js"></script>
<script type="text/javascript" src="https://myfarm/_layouts/1033/jquery.dateFormat.js"></script>
<!-- Calendar Widget -->
<script type="text/javascript" src="https://myfarm/_layouts/1033/jquery.SPS.Calendar_Widget-1.0.js"></script>
<link href="/_layouts/1033/styles/Calendar_Widget.css" type="text/css" rel="stylesheet" />
<script language = "javascript">
    $(document).ready(function() {     
        LoadCalendarWidget("https://myfarm/hr/", "Upcoming HR Events Calendar", "Event Calendar", "{B7FE9372-9724-4504-9699-DEDD445B1BA3}", 8, ,true); 
    });
</script>
<div id="cw_TopContainer" style="margin-bottom:15px;"><div id="cw_HeaderContainer"><table style="width:100%;"><tr><td><span id="cw_HeaderTitle"></span></td><td style="text-align:right;"><a id="cw_HeaderLink" href="#" >Calendar View</a></td></tr></table></div><div id="cw_ItemsContainer" /></div>

And this is the function. I put it in our 14 hive for access across the farm, you put it where you want, just make sure the script source in the code above points to where you dropped this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// REQUIRES:
// - jquery-1.8.0.js or higher
// - jquery.SPServices-0.7.2.min.js or higher
// - jquery.dateFormat.js
 
function LoadCalendarWidget(cw_SiteUrl, cw_ListName, cw_ListNameForHeader, cw_EncodedListGuid, cw_ReturnRowCount, cw_CAML , cw_ShowIcon)
{
    // USAGE: --------------
    // cw_SiteUrl = "https://myfarm/hr/"; // Site's URL, no end '/' character
    // cw_ListName = "HR Events Calendar"; // List name
    // cw_ListNameForHeader = "Upcoming HR Events";  // what will appear in webpart
    // cw_EncodedListGuid = "{B7FE9372-9724-4504-9699-DEDD445B1BA3}";  // GUID of your calendar list
    // cw_ReturnRowCount = 15;  // # of results
    // cw_CAML = Your optional CAML query
    // cw_ShowIcon = true;  // false will use 'Add to Outlook'
 
    var cw_ListUrlForHeader = cw_SiteUrl + '/lists/' + cw_ListName;
    var cw_IcsEventPath = cw_SiteUrl + "/_vti_bin/owssvr.dll?CS=109&Cmd=Display&List=" + cw_EncodedListGuid + "&CacheControl=1&Using=event.ics&ID=";
    var cw_ListItemDetailPath = cw_SiteUrl + "/Lists/" + cw_ListName + "/DispForm.aspx?ID=";
    var cw_CamlQuery = "";
    if (cw_CAML.length < 2) {
        cw_CamlQuery = "<Query><Where><Geq><FieldRef Name='EventDate' /><Value IncludeTimeValue='TRUE' Type='DateTime'><Today/></Value></Geq></Where></Query>";}
    else { cw_CamlQuery = cw_CAML; }
 
    // format header info
    $('#cw_HeaderTitle').html(cw_ListNameForHeader);  // title
    $('#cw_HeaderLink').attr('href',cw_ListUrlForHeader); // link
     
   $().SPServices({
        operation: "GetListItems",
        webURL: cw_SiteUrl,
        listName: cw_ListName,
        async: false,
        CAMLRowLimit: cw_ReturnRowCount,
        CAMLQuery: cw_CamlQuery,    
      
        completefunc: function (xData, Status) {                           
           $(xData.responseXML).SPFilterNode("z:row").each(function() {            
                var cw_id = ($(this).attr("ows_ID"));
                var cw_title = ($(this).attr("ows_Title"));
                    if(cw_title.length == 0) {cw_title = 'No event title.';}
                     
                // By request, if an item is canceled, make that fact stand out.
                cw_title = cw_title.replace("Deleted:","<span class='cw_canceled'>DELETED:</span> ");
                cw_title = cw_title.replace("DELETED:","<span class='cw_canceled'>DELETED:</span> ");
                cw_title = cw_title.replace("Cancelled:","<span class='cw_canceled'>CANCELLED:</span> ");
                cw_title = cw_title.replace("CANCELLED:","<span class='cw_canceled'>CANCELLED:</span> ");
                 
                // Clean up date formats               
                var cw_startdateday =       $.format.date(($(this).attr("ows_EventDate")), "M/d/yyyy");
                var cw_startdatetime =      $.format.date(($(this).attr("ows_EventDate")), "h:mm a");
                var cw_startdate =          cw_startdateday + ' - ' + cw_startdatetime;
                var cw_enddateday =         $.format.date(($(this).attr("ows_EndDate")), "M/d/yyyy");
                var cw_enddatedaytime =     $.format.date(($(this).attr("ows_EndDate")), "h:mm a");
                var cw_enddate =            cw_enddateday + ' - ' + cw_enddatedaytime
                 
                var cw_CX = '<div class="cw_item">';
                if(cw_startdateday == cw_enddateday)  // single day event
                {
                    cw_CX += '<table width="100%" cellpadding="0">';
                    cw_CX += '<tr><td colspan="2"><a href="' + cw_ListItemDetailPath + cw_id + '" target="_new" class="cw_title">' + cw_title + '</a></td></tr>';                  
                    cw_CX += '<tr><td><span class="cw_time">' + cw_startdateday + ' ' + cw_startdatetime + ' to ' + cw_enddatedaytime +'</span></td>';
                    cw_CX += '<td align="right" valign="top"><a href="' + cw_IcsEventPath + cw_id + '" target="_new">';                
                    if(cw_ShowIcon) {
                        cw_CX += '<img src="FARM/Images/add_item.png" class="cw_addIcon" title="Add to Outlook Calendar" /></a></td></tr>';
                    }
                    else { cw_CX += '[Add to Outlook]</a></td></tr>'; }                      
                    cw_CX += '</table>';
                }
                else // multi day event
                {
                    cw_CX += '<table width="100%" cellpadding="0">';
                    cw_CX += '<tr><td colspan="2"><a href="' + cw_ListItemDetailPath + cw_id + '" target="_new" class="cw_title">' + cw_title + '</a></td></tr>';                  
                    cw_CX += '<tr><td><span class="cw_time">Start:' + cw_startdate + '</span></td><td align="right" rowspan="2" valign="top"><a href="' + cw_IcsEventPath + cw_id + '" target="_new">';
                     
                    if(cw_ShowIcon) {
                        cw_CX += '<img src="https://cbhq.cbh.com/_layouts/images/myelite2/cal/add_item.png" class="cw_addIcon" title="Add to Outlook Calendar" /></a></td></tr>';
                    }
                    else { cw_CX += '[<a href="' + cw_IcsEventPath + cw_id + '" target="_new">Add to Outlook]</a></td></tr>'; }                    
                    cw_CX += '<tr><td><span class="cw_time">End:' + cw_enddate + '</span></td></tr>';
                    cw_CX += '</table>';             
                }              
                cw_CX += '</div>';
                $("#cw_ItemsContainer").append(cw_CX); // insert into DOM                      
           }); // -- $(xData.responseXML).find  
        } // -- completefunc
   }); // -- $().SPServices({
} // -- LoadCalendarWidget()
Friday
Dec212012

Add a Little Holiday Wonder to SharePoint: Cost $3, Time 10 Minutes

I've been wanting to see some snow here for the holiday season and there has been a sad lack of even cold weather, so I took a short break from an ocean of CSS and re-branding this morning to add a snow-effect web part to our SharePoint farm for site admins to use. It's cheap, easy and people are pretty excited about it so I thought I might throw the instructions out for you to implement in just a few minutes.

 

Step 1: Cheat, spend $3 and grab the excellent javascript snow effect package from codecanyon from here (I have no affiliation with that site or the snow effect author) http://codecanyon.net/item/jsized-snow-effect/75580. I did the math and writing my own script was not economically viable with this at 3 bucks. That and, well this is a little frivolous, so we gotta make it all pretty brief.

Step 2: Pop the files on your farm. We'll assume that you do not have access to add files to ye olde 14 hive so we'll keep this simple, though I added to our 14 hive for easier cross site collection availability. Create a folder in a doc library and drop the files from that snow-effect package there. You really just have one .js file and 5 .gif files (the flakes).

Step 3: Create the file to kick the effect off. Create a text file in that same folder, I'll name mine s.txt to save on typing. Put the following text in that text file, replacing the url in caps there to your library file's urls.

1
2
<script src="http://YOUR_SP_URL/FOLDERS/jsized.snow/jsized.snow.min.js" type="text/javascript"></script>       
<script>createSnow('http://YOUR_SP_URL/FOLDERS/jsized.snow/', 30);</script>

Here was mine:

Step 4: On some page on your SP site, add a Content Editor web part, setting the url to your freshly minted text file as the Content Link, giving it a name and setting the Chrome Type to none.

Optional: Export your new web part from that page as a .dwp file and import it into your site collection's web-part library for easy re-use. This may depend on your admin privileges and may not be an option. If not just repeating step 4 above will also work to add wherever you want.

Friday
Nov162012

Easy Scroll-Aware Hovering HTML Container with jQuery

I needed a quick and easy way to have a container hover on the side of a page and update with details as the user selected options. This is a simple, jQuery-based approach to doing so. Note that the portion at the end is included if you are needing to fire the positioning from a repost in asp.net, in this instance I needed to do so as the floating container was only displayed and filled via an async event when the user selected their first item.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<!DOCTYPE HTML>
<style>
    #oHeader {height:200px;width:100%;background:lightyellow;margin-bottom:20px;} /* Not needed */
    #oAnchorObject {height:2000px;width:450px;background:lightgreen;} /* Not needed */
    #oSidebar {position:fixed;left:500px;border:1px solid #ff0000;padding:40px} /* Position and left or right value needed */
</style>
 
<script src="jquery.js" type="text/javascript"></script>
<script type="text/javascript">
    $(document).ready(function () {
        RepositionCard();
    });
     
    $(window).scroll(function () {
        RepositionCard();
    });
     
    function RepositionCard() {
        try{
            var eTop = $('#oAnchorObject').offset().top;
            if (eTop - $(window).scrollTop() <= 1) {
                $("#oSidebar").css("top", 0);
            }
            else {
                $("#oSidebar").css("top", eTop - $(window).scrollTop());
            }
        }
        catch(err) {}
    }
 
</script>
 
<div id="oHeader">
    Here is some header content to show how the locking works
</div>
 
<div id="oAnchorObject">
    Here is your large content block to which the sidebar will orient
</div>
 
<div id="oSidebar">
    This will hover/stick
</div>

Optional for firing the client event after ajax postback from C#, use in page load where needed:

1
ScriptManager.RegisterClientScriptBlock(Page, typeof(Page), "MyScript", "RepositionCard();", true);
Thursday
May242012

Making SharePoint look like a million bucks on a shoestring budget - Part II

AKA the sexy, animated, customizable alerts bar you can have for 6 bucks.

In the second installment of our using jQuery, SPS and some inexpensive UI controls we implement a beautiful, customizable alerts/notification bar for SharePoint, driven off of a SharePoint list that'll set you back $6 and take your lunch hour to implement.  Very little configuration is needed to get you up and rolling here and, staying true to a tennant of this series, there is no requirement to install any invasive files (solutions) on your server or farm.  

As shown here it will handle multiple alerts being active at the same time and will default to a generic theme if there is more than one alert.  What you do not see here is the slick behavior of this notification bar and the options it exposes, see a demo of all that the bar does here.

 
First a few Resources needed:
  • The notification bar ($6), officially named 'FooBar - A jQuery Notification Bar'
  • jQuery (consider putting this reference in your master page)
  • SPServices (also consider putting this reference in your master page) 
SharePoint List:
I created a simple custom list (System Alerts) with the following columns, if you use other names just reflect that in lines 36-38, if you don't know the ows_X field name uncomment line 23 to see.
  • IsActive - a yes/no data type
  • Theme - String datatype, required - I made this a choice and provided canned options
  • Message - Multiple lines of text datatype, plaintext or not
The code below:
This I dropped into a text file and just added to a page via a CEWP letting me choose on which pages this is displayed, in our case just the home page.  Note the mention of an image as part of the theme, you can whip your own up or just remove the text in line 85 (and comma in 84 then).  The screenshot above shows how nice this looks with a good image though.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<style> <!-- A few canned styles, go nuts here -->
    .foobar_darktext{ font-weight:bold; color:black; }
    .foobar_lighttext{ font-weight:bold; color:white; }
</style>
<!-- For jQuery -->
    <script type="text/javascript" src="/_layouts/1033/jquery.SPServices.min.js"></script>
    <script type="text/javascript" src="/_layouts/1033/jquery.min.js"></script>
<!-- For Foobar -->
    <script type='text/javascript' src='/_layouts/1033/jquery.foobar.min.js'></script>
    <link href='/_layouts/1033/styles/jquery.foobar.css' type='text/css' rel='stylesheet' />
 
<script language = "javascript">
$(document).ready(function()
{
    $().SPServices(
    {
        operation: "GetListItems",
        webURL: "https://YOUR_SHAREPOINT_SITE",   
        async: false,
        listName: "YOUR_LIST_NAME",
        completefunc: function (xData, Status)
        {
            //alert("Response from server: " + xData.responseText); // helpful for debug
            var output = "";
            $("#WSOutput").html(output);
             
            var iCounter = 0;
            var arrPayload = [];
            var szBackHexColor = '';
            var szAppliedStyle = '';
            var szPathAlertImage = 'https://SOME_SHAREPOINT_PATH/AlertImages/';
            var szFullPathAlertImage = '';
             
            $(xData.responseXML).find("[nodeName='z:row']").each(function() {
                 
                var szContent = ($(this).attr("ows_Message"));             
                var bIsActive = ($(this).attr("ows_IsActive"));
                var szTheme = ($(this).attr("ows_Theme"));             
                 
                if(bIsActive == 1)
                {              
                    arrPayload.push(szContent);
                             
                    switch(szTheme) // set theme options
                    {
                        case 'Theme A':
                            szBackHexColor = '#ffcc00';
                            szAppliedStyle = 'foobar_darktext';
                            szFullPathAlertImage = szPathAlertImage + 'Achtung.png';                           
                            break;
                        case 'Theme B':
                            szBackHexColor = '#CAF87A';
                            szAppliedStyle = 'foobar_darktext';
                            szFullPathAlertImage = szPathAlertImage + 'Danger Will Robinson.png';  
                            break;                         
                    }                  
                    szFullPathAlertImage = '<img style=\' padding:0px 0 0 7px; \' src= \'' + szFullPathAlertImage + '\' />';
                    iCounter++;  // increment
                }
            });
             
            if(iCounter > 1) // to prevent conflicting themes, make this a generic visual
            {
                szBackHexColor = '#ffcc00';
                szAppliedStyle = 'foobar_darktext';
                szFullPathAlertImage = szPathAlertImage + 'Generic Alert.png';         
            }
             
            if(iCounter > 0)
            {
                $.foobar({
                    "positioning" : "fixed",
                    "display" : "delayed",
                    "displayDelay" : 2000,
                    "messagesDelay" : 5000,
                    "messagesScrollSpeed": 50,
                    "messagesScrollDelay": 2000,
                    "messageClass" : szAppliedStyle,
                    "fontColor" : szFontHexColor,
                    "backgroundColor" : szBackHexColor,
                    "buttonTheme" : "long-arrow",
                    "height" : 35,
                    "enableShadow" : false,
                    "messages" : arrPayload,
                    "leftHtml": szFullPathAlertImage
                });
            }
        }
    });
});
</script>
<div id="x_container" />
Further fiddling:
Initially I set this up so that the user could specify the background color, the text color, select an image or provide a URL, etc. All of which are done the same way as the theme logic but we decided to simplify the user experince with themes rather than give them so many choices.  You can easily expand on this though.
Consider throwing a few bucks at the jQuery and/or SPServices teams via the donate links on their sites, they do good work and deserve some help. I am not affiliated with any of the groups/sites I steer you toward in these posts...

 

 

Thursday
May102012

Using SPServices, jQuery and an $8 Tab Plugin to Create a Stunning SharePoint List Visualization

AKA: Making SharePoint look like a million bucks on a shoestring budget - Part I

I'd been wanting to spend some time getting familiar with the brilliant open source jQuery library for SharePoint's web services, SPServices, and had the opportunity this past week.  We had a SharePoint site under construction that needed to have a list of links and a little explanation to accompany them  and initially just hosted a standard grouping mechanism on the list view which looks terrible.  I thought that this might be a chance to offer a better way to visualize list data using an inexpensive tabbing control in my library.  The result is shown below, with each 'tab' here linking to 3 fields from a custom list, the title, the sub-title and the content.  This stays in our needed model of users interacting with the standard SP forms that they understand and is leaps and bounds a better user experience.  If this takes you more than a lunch break to implement you might need longer lunch breaks, this is an easy setup and looks great, even responding to mouse-wheel scrolling through the tabs.

 

To do this I used a stunning jQuery plugin called 'Sliding Tabs' that I bought from CodeCanyon.net for a dirt cheap 8 bucks (authored by 360north).  You can see/buy it here and it is one of the many ridiculous bargains on that site that I'll be extending in future posts.  That 8 dollars is all this costs and it looks like $1,000,008 when done.  You'll also need to grab the jQuery file and the SPServices file, we have these hosted on our farm and embedded in the master page for ease of use as seen in the includes in the code.  All includes that are needed but the core jQuery and SPServices files come with the Sliding Tabs package.

With the code in hand from them I created a new list with the aforementioned title, subtitle and content (being a rich content type) list and hooked it up.  Full code follows below.

Notice in the code that you have to specify your site and list names (lines 13 and 15) and if you name your columns something other than I did, you'll need to change the mappings to the 'ows_XXX' moniker that matches your fields (lines 26, 28 and 30).  If you are unsure of what your field's names are you can uncomment line 20 and see for yourself.

You can change the size, shape, orientation and other options with the tab control with the options section starting at line 58.  It does not handle overflow well (um, not at all really, it just gets clipped) but for what we needed it was perfect.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<script type="text/javascript" src="_layouts/1033/jquery.min.js"></script>
<script type="text/javascript" src="_layouts/1033/jquery.SPServices.min.js"></script>
<script type="text/javascript" src="_layouts/1033/jquery.mousewheel.js"></script>
<script type="text/javascript" src="_layouts/1033/jquery.slidingtabs.pack.js"></script>
<script type="text/javascript" src="_layouts/1033/jquery.easing.js"></script>
 
<link href="_layouts/1033/styles/slidingtabs-vertical.css" rel="stylesheet" type="text/css" media="screen"/>
 
<script language = "javascript">
$(document).ready(function() {
    $().SPServices({
        operation: "GetListItems",
        webURL: "https://MY_SHAREPOINT_ROOT/SOME_SITE",   
        async: false,
        listName: "SIMPLE DEMO LIST",
        completefunc: function (xData, Status) {
 
            //alert("Status of XML message reaching Sharepoint webservice: " + Status); // handy for debug
            //alert("Response from server: " + xData.responseText); // also handy for debug
            var output = "";
            $("#WSOutput").html(output);
            var TabCounter = 0; // to keep track of tabs/containers
 
            $(xData.responseXML).find("[nodeName='z:row']").each(function() {
 
                var title = ($(this).attr("ows_Title"));
                    if(title.length == 0) {title = ' ';}
                var subtitle = ($(this).attr("ows_Subtitle"));
                    if(subtitle.length == 0) {subtitle = ' ';}
                var content = ($(this).attr("ows_Content"));
                    if(content.length == 0) {content = ' ';}
                  
                var TabClass = 'st_tab';
                var ContainerClass = 'st_tab_view';
                         
                if(TabCounter==0) // our first items get special 'active' class styling
                {              
                    TabClass = 'st_tab st_tab_active';
                    ContainerClass = 'st_tab_view st_first_tab_view';
                }
                // Create new tab element as <li>
                var TabHeader = '<li><a href="#stv_content_' + TabCounter + '" rel="v_tab_' + TabCounter + '" class="' + TabClass + '">'
                    TabHeader += title + '<span>' + subtitle + '</span></a></li>';
                $("#tablisting").append(TabHeader); // insert into DOM
 
                // Create new lined content container as <div>
                var stv_content = '<div id="stv_content_' + TabCounter + '" class="' + ContainerClass + '" style="overflow:auto;">';
                    stv_content +='<h2 class="sc_title">' + title + '</h2><div class="text"><p>' + content + '</p></div></div>';         
                $("#st_tabs_container").append(stv_content); // insert into DOM
 
                TabCounter++;  // increment counter
            });
        }
    });
        
    // Instantiate Vertical Sliding Tabs
    $('div#st_vertical').slideTabs({           
        // Options
        contentAnim: 'slideH',
        contentAnimTime: 600,
        contentEasing: 'easeInOutExpo',
        orientation: 'vertical',
        tabsAnimTime: 300,
        totalWidth: '660',     
        tabScroll: true            
    });
});
</script>
        
<!-- Start HTML - Vertical tabs -->
<div id="st_vertical" class="st_vertical">
    <div class="st_tabs_container">  
        <a href="#prev" class="st_prev"></a>
        <a href="#next" class="st_next"></a>                                                                   
        <div class="st_slide_container">           
            <ul class="st_tabs" id="tablisting"></ul><!-- tabs inserted here -->              
        </div> <!-- /.st_slide_container -->
    </div> <!-- /.st_tabs_container -->                              
    <div class="st_view_container">                      
        <div class="st_view" id="st_tabs_container"></div> <!-- content blocks inserted here -->                       
    </div> <!-- /.st_view_container -->
</div> <!-- /#st_vertical -->
<!-- End HTML - Vertical tabs -->
More jQuery visual enhancements to come...