Warning: SetCookie changes implementation in SharePoint 2013

I was recently working on verifying some of my custom code worked in SharePoint 2013.  I had some older code where I needed to set cookies and use them to remember user preferences.  I was debugging my code and I noticed that all of my cookies were coming back either true or false instead of the value I put into them.  I had been using a SharePoint JavaScript method called SetCookie.  Below is the implementation that has existed in SharePoint for the previous 3 versions (2003, 2007, and 2010).

function SetCookie(name, value, path)
{
	document.cookie=name+"="+value+";path="+path;
}

But for some reason Microsoft decided to change the implementation of this method in SharePoint 2013 to the following:

function SetCookie(sName, value) {
    SetCookieEx(sName, value, false, window);
}
function SetCookieEx(sName, value, isGlobal, wnd) {
    var c = sName + (value ? "=true" : "=false");
    var p = isGlobal ? ";path=/" : "";

    wnd.document.cookie = c + p;
}

Notice this new method tries to evaluate the value into a boolean and then forcefully sets the cookie to true or false.  So if you were storing anything other than boolean values in your cookies and used this method in previous versions of SharePoint, you will now need to update your code.  For this you have two options:

Option 1:  I found there is another method in SharePoint 2013 which still implements the SetCookie the same was as previous versions of SharePoint.

function SetMtgCookie(cookieName, value, path) {
    document.cookie = cookieName + "=" + value + ";path=" + path;

}

So you can easily do a search and replace for SetCookie to be replaced with SetMtgCookie and you’ll be good to go.

Option 2:  This is the option I opted for.  I decided that as tempting as doing a quick search and replace like I suggested in option 1 sounded, I wanted to remove my dependency on SharePoint’s implementation in case it changes again in the future.  Since the older SetCookie is only one line, I just replaced my method call to that line:

document.cookie=name+"="+value+";path="+path;

It was a pretty simple change, only slightly more time taken than option 1 and I removed a dependency.

Anyway, I hope this helps someone else out when troubleshooting code migration to SharePoint 2013.

Option to Show the SharePoint 2010 ribbon when hidden by default

At work we’ve been working on a new intranet project with SharePoint 2010 being our platform of choice. This site is mainly a view only type of site but we also wanted users to have the ability to create alerts and other similar activities. One of the requirements of this project was to remove the ribbon on every page. This brought complications for SP 2010 because so much is done in the ribbon. We decided to hide the ribbon by default and show a caret (or chevron) at the upper right of the screen to allow the ribbon to be shown and hidden by all users.

There are many solutions I’ve seen on the interwebs around hiding the ribbon for some and not hiding it for others so they were a good starting point for us but I couldn’t find anyone talking about having the ribbon being optional for everyone. This brought a few challenges such as keeping the ribbon shown on postbacks and displaying the ribbon when clicking on a list webpart.

The first challenge was easy to accomplish. Using a cookie to set whether the user currently has the ribbon open or closed allows the ribbon to “remember” that setting as a user is browsing and editing items in SP.

 var val = 'False';
 try { val = GetCookie('ShowHideRibbon'); } catch (err) { }
 var showRibbon = document.getElementById('ShowRibbon');
 var hideRibbon = document.getElementById('HideRibbon');
 var ribbon = document.getElementById('s4-ribbonrow');

 if (val == 'True') {
  ribbon.style.display = 'block'; ;
  if (showRibbon != null)
   showRibbon.style.display = 'none';
  if (hideRibbon != null)
   hideRibbon.style.display = 'block';
 }
 else {
  if (hideRibbon != null)
   hideRibbon.style.display = 'none';
  if (showRibbon != null)
   showRibbon.style.display = 'block';
 }

The second issue was more difficult and wasn’t initially obvious. The issue was that in SP 2010, when you click on a list view webpart it automatically activates the ribbon. This is great when using the checkboxes to select items or documents. Normal behavior is when the ribbon is activated it will hide the site level breadcrumbs / title bar and the top navigation to use that space for the ribbon itself. But with the ribbon being hidden, this causes the whole top part of the page to disappear including our caret to reopen the ribbon.

This resulted in a very bad user experience. I spent a lot of time debugging JavaScript trying to find the right SP JS function to “override” in order to tap into this action and unhide the ribbon first before the ribbon is activated. I got no where asking the google and ended up searching all of the SharePoint JS debug files and finally finding the function OnRibbonMinimizedChanged in init.js.

 function OnRibbonMinimizedChanged(ribbonMinimized)
 {ULSxSy:;
  var ribbonElement=GetCachedElement("s4-ribbonrow");
  var titleElement=GetCachedElement("s4-titlerow");
  if (ribbonElement)
  {
   ribbonElement.className=ribbonElement.className.replace("s4-ribbonrowhidetitle", "");
   if(titleElement)
   {
    titleElement.className=titleElement.className.replace("s4-titlerowhidetitle", "");
    if (ribbonMinimized)
    {
     titleElement.style.display="block";
    }
    else
    {
     titleElement.style.display="none";
    }
   }
  }
...

I overrode the function with my own where I unhide the ribbon and then called the original function in init.js.

 function SetupEventHandler_HideShowRibbon() {
  if (original_functionEvent == null) {
   original_functionEvent = OnRibbonMinimizedChanged;
  }
  OnRibbonMinimizedChanged = OnRibbonMinimizedChanged_Delegate;
 }

 function OnRibbonMinimizedChanged_Delegate(ribbonMinimized) {
  if (IsRibbonHidden_HideShowRibbon() &&
   false == ribbonMinimized) {
   showRibbon__HideShowRibbon();
  }
  original_functionEvent(ribbonMinimized);
 }

I put all of my JS code into a user control which I added to my masterpage inside a SP delegate control. This allows me to use features to swap this code out with something different. For instance you could have a user control which can hide the ribbon by default and only show the caret / chevron when a user has certain permissions.

<%@ Control Language="C#"    compilationMode="Always" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="uc" TagName="ShowHideRibbonCore" src="~/_controltemplates/Custom/ShowHideRibbon.ascx" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<!-- Show / Hide Ribbon -->
<Sharepoint:SPSecurityTrimmedControl ID="SecurityTrimHideShowRibbonCurrentItem" runat="server" PermissionsString="AddAndCustomizePages" PermissionContext="CurrentItem">
    <uc:ShowHideRibbonCore ID="HideShowRibbonCoreCurrentItem" runat="server"/>
</Sharepoint:SPSecurityTrimmedControl>
<Sharepoint:SPSecurityTrimmedControl ID="SecurityTrimHideShowRibbonCurrentWeb" runat="server" PermissionsString="AddAndCustomizePages" PermissionContext="CurrentSite">
    <uc:ShowHideRibbonCore ID="HideShowRibbonCoreCurrentSite" runat="server"/>
</Sharepoint:SPSecurityTrimmedControl>

 

The code that we ended up doing is below. We hide the ribbon using CSS in the user control and then output the javascript.

<%@ Control Language="C#"    compilationMode="Always" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="uc" TagName="ShowHideRibbon" src="~/_controltemplates/Custom/ShowHideRibbon.ascx" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<!-- Hide Ribbon -->
<style type="text/css">
#s4-ribbonrow
{
    display:none;
}
</style>
<uc:ShowHideRibbonCore ID="HideShowRibbon" runat="server"/>

And here is my full code that I put in the user control ShowHideRibbon.ascx

<%@ Control Language="C#"    compilationMode="Always" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<!-- Show / Hide Ribbon Core -->
<script type="text/javascript">
    if (testForRibbon_HideShowRibbon == null) {
        var original_functionEvent = null;

        var testForRibbon_HideShowRibbon = function () {

            if (IsRibbonHidden_HideShowRibbon()) {
                ExecuteOrDelayUntilScriptLoaded(SetupEventHandler_HideShowRibbon, "init.js");

                var val = 'False';
                try { val = GetCookie('ShowHideRibbon'); } catch (err) { }
                var showRibbon = document.getElementById('ShowRibbon');
                var hideRibbon = document.getElementById('HideRibbon');
                var ribbon = document.getElementById('s4-ribbonrow');

                if (val == 'True' ||
                   (IsRibbonHidden_HideShowRibbon() &&
                    IsTitleRowHidden_HideShowRibbon())) {
                    ribbon.style.display = 'block';
                    if (showRibbon != null)
                        showRibbon.style.display = 'none';
                    if (hideRibbon != null)
                        hideRibbon.style.display = 'block';
                }
                else {
                    if (hideRibbon != null)
                        hideRibbon.style.display = 'none';
                    if (showRibbon != null)
                        showRibbon.style.display = 'block';
                }
            }
        }

        function IsRibbonHidden_HideShowRibbon() {
            return IsElementHidden_HideShowRibbon('s4-ribbonrow');
        }

        function IsTitleRowHidden_HideShowRibbon() {
            return IsElementHidden_HideShowRibbon('s4-titlerow');
        }

        function IsElementHidden_HideShowRibbon(elementId) {
            var computedStyle = function (el, style) {
                var cs;
                if (typeof el.currentStyle != 'undefined') {
                    cs = el.currentStyle;
                } else {
                    cs = document.defaultView.getComputedStyle(el, null);
                }
                return cs[style];
            }
            var element = document.getElementById(elementId);
            if (element != null &&
				computedStyle(element, 'display') == 'none') {
                return true;
            }
            return false;
        }

        function SetupEventHandler_HideShowRibbon() {
            if (original_functionEvent == null) {
                original_functionEvent = OnRibbonMinimizedChanged;
            }
            OnRibbonMinimizedChanged = OnRibbonMinimizedChanged_Delegate;
        }

        function OnRibbonMinimizedChanged_Delegate(ribbonMinimized) {
            if (IsRibbonHidden_HideShowRibbon() &&
				false == ribbonMinimized) {
                showRibbon__HideShowRibbon();
            }
            original_functionEvent(ribbonMinimized);
        }

        function makeDoubleDelegate_HideShowRibbon(function1, function2) {
            return function () {
                if (function1)
                    function1();
                if (function2)
                    function2();
            }
        }

        function getDocHeight_HideShowRibbon() {
            var D = document;
            return Math.max(
            Math.max(D.body.scrollHeight, D.documentElement.scrollHeight),
            Math.max(D.body.offsetHeight, D.documentElement.offsetHeight),
            Math.max(D.body.clientHeight, D.documentElement.clientHeight)
        );
        }

        function showRibbon__HideShowRibbon() {
            var newHeight = getDocHeight_HideShowRibbon();
            var ribbon = document.getElementById('s4-ribbonrow');
            ribbon.style.display = 'block';
            try {
                SetCookie('ShowHideRibbon', 'True', '/');
            } catch (err) { };
            document.getElementById('ShowRibbon').style.display = 'none';
            document.getElementById('HideRibbon').style.display = 'block';

            newHeight = newHeight - ribbon.offsetHeight;
            var ver = navigator.appVersion;
            if (ver.indexOf("MSIE") != -1) {
                newHeight = newHeight - 4;
            }
            document.getElementById('s4-workspace').style.height = newHeight + 'px';
        }

        function hideRibbon__HideShowRibbon() {
            document.getElementById('s4-ribbonrow').style.display = 'none';
            try {
                SetCookie('ShowHideRibbon', 'False', '/');
            } catch (err) { };
            document.getElementById('HideRibbon').style.display = 'none';
            document.getElementById('ShowRibbon').style.display = 'block';
            var newHeight = getDocHeight_HideShowRibbon();
            var ver = navigator.appVersion;
            if (ver.indexOf("MSIE") != -1) {
                newHeight = newHeight - 4;
            }
            document.getElementById('s4-workspace').style.height = newHeight + 'px';
        }

        window.onload = makeDoubleDelegate_HideShowRibbon(window.onload, testForRibbon_HideShowRibbon);

        document.write('<a id="ShowRibbon" href="#" onclick="showRibbon__HideShowRibbon(); return false;"><img src="/_layouts/images/downarrow.png" title="Show Ribbon" border="0"/></a>');
        document.write('<a id="HideRibbon" href="#" onclick="hideRibbon__HideShowRibbon(); return false;"><img src="/_layouts/images/uparrow.png" title="Hide Ribbon" border="0"/></a>');
    }
</script>
<style type="text/css">
#ShowRibbon
{
	position:fixed;
	top:0;
	right:15px;
	display: none;
	z-index: 100;
}

#HideRibbon
{
	position:fixed;
	top:45px;
	right:15px;
	display: none;
	z-index:100;
}
</style>

Update 4-6-2012

I realized there are instances when visiting a page (like dispform.aspx) where the ribbon is supposed to be open by default but instead showed the same bad behavior from above. I’ve updated my code above to now check if s4-titlerow is hidden in addtion to the s4-ribbonrow. If so, I unhide the ribbon. But I do not cookie this change. The only time this app “remembers” your setting is if you actually click the up or down arrow at the top right.

Javascript 2 Dimensional QuickSort

Here is some code I found and modified for a Javascript 2 Dimensional Quicksort. I was using a bubble sort but decided to change over to Quicksort considering I was going to have a huge dataset.

———————————————————

function Quicksort(vec, k){
QuicksortHelp(vec, k, 0, vec[k].length-1);
}

function QuicksortHelp(vec, k, loBound, hiBound){
if (debug)
if (!confirm(loBound + " " + hiBound))
return;
var loSwap, hiSwap, temp;
var pivot = new Array(vec.length);
// Two items to sort
if (hiBound - loBound == 1){
if (vec[k][loBound].toLowerCase() > vec[k][hiBound].toLowerCase())
swap(vec,loBound,hiBound);
return;
}
// Three or more items to sort
for (counter=0; counter < vec.length; counter++){
pivot[counter] = vec[counter][parseInt((loBound + hiBound) / 2)];
vec[counter][parseInt((loBound + hiBound) / 2)] = vec[counter][loBound];
vec[counter][loBound] = pivot[counter];
}
loSwap = loBound + 1;
hiSwap = hiBound;

mycount = 0
do {
if (debug)
if (!confirm(loSwap + " YEA " + hiSwap))
return;
// Find the right loSwap
while (loSwap <= hiSwap && vec[k][loSwap].toLowerCase() <= pivot[k].toLowerCase()){
loSwap++;
}

// Find the right hiSwap
while (vec[k][hiSwap].toLowerCase() > pivot[k].toLowerCase()){
hiSwap--;
}

// Swap values if loSwap is less than hiSwap
if (loSwap < hiSwap)
swap(vec,loSwap,hiSwap);
mycount++;
} while (loSwap < hiSwap);

for (counter=0; counter < vec.length; counter++){
vec[counter][loBound] = vec[counter][hiSwap];
vec[counter][hiSwap] = pivot[counter];
}

if (debug)
if (!confirm(loBound + " < " + (hiSwap-1)))
return;
// Recursively call function... the beauty of quicksort

// 2 or more items in first section 
if (loBound < hiSwap - 1)
QuicksortHelp(vec, k, loBound, hiSwap - 1);

if (debug)
if (!confirm((hiSwap+1) + " < " + hiBound))
return;
// 2 or more items in second section
if (hiSwap + 1 < hiBound)
QuicksortHelp(vec, k, hiSwap + 1, hiBound);
}

function swap(arr,p1,p2){
for (k=0; k temp = arr[k][p1];
arr[k][p1] = arr[k][p2];
arr[k][p2] = temp;
}
}

———————————————————

I also added the following code to sort blank entries last. You just need to run this before you call quicksort.

for (i=0; i < vec[k].length; i++){
if (vec[k][i].length <= 0){
vec[k][i] = "" + String.fromCharCode(127);
}
}

And run this after you call quicksort.

for (i=0; i < vec[k].length; i++){
if (vec[k][i] == ("" + String.fromCharCode(127))){
vec[k][i] = "";
}
}