Here's a small JS solution I created:
Simply modify the settings
object to your needs.
It does not come with any url's, since you did not specify if you need one, but this can be added without much trouble.
(function () {
'use strict';
var settings = {
listUrl: 'https://sharepoint/Lists/Assets/',
lookupFieldInternalName: 'Department',
targetControlSelector: '#sideNavBox .ms-core-listMenu-verticalBox',
replaceContent: true
};
var template = {
Container:
'<ul id="zz15_RootAspMenu" class="root ms-core-listMenu-root static">{Departments}</ul>',
Department:
'<li class="static">' +
'<a class="static menu-item ms-core-listMenu-item ms-displayInline ms-navedit-linkNode" tabindex="0" href="#">' +
'<span class="additional-background ms-navedit-flyoutArrow">' +
'<span class="menu-item-text">{Department}</span>' +
'</span>' +
'</a>' +
'<ul class="static">' +
'{Assets}' +
'</ul>' +
'</li>',
Asset:
'<li class="static">' +
'<a class="static menu-item ms-core-listMenu-item ms-displayInline ms-navedit-linkNode" tabindex="0" title="Wiki" href="#">' +
'<span class="additional-background ms-navedit-flyoutArrow">' +
'<span class="menu-item-text">{Asset}</span>' +
'</span>' +
'</a>' +
'</li>'
};
//some utilities for working with dom elements
var utilities = {
focusNextElement: function () {
//add all elements we want to include in our selection
var focussableElements = 'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled])';
if (document.activeElement && document.activeElement.form) {
var focussable = Array.prototype.filter.call(document.activeElement.form.querySelectorAll(focussableElements),
function (element) {
//check for visibility while always inlcude the current activeElement
return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement
});
var index = focussable.indexOf(document.activeElement);
if (focussable[index + 1]) {
focussable[index + 1].focus();
}
}
},
getElementByHTML: function (html) {
var elem = document.createElement('div');
elem.innerHTML = html;
return elem.firstChild;
},
hasClass: function (el, className) {
if (el.classList)
return el.classList.contains(className);
else
return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
},
addClass: function (el, className) {
if (el.classList)
el.classList.add(className);
else if (!this.hasClass(el, className)) el.className += ' ' + className;
},
removeClass: function (el, className) {
if (el.classList)
el.classList.remove(className);
else if (this.hasClass(el, className)) {
var reg = new RegExp('(\\s|^)' + className + '(\\s|$)');
el.className = el.className.replace(reg, ' ');
}
},
guid: function () {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
},
isNullOrUndefined: function (obj) {
return typeof obj === 'undefined' || obj === null;
},
getTableRowByHTML: function (html) {
//fix MS Internet Exploder’s lameness
var temp = document.createElement('div');
temp.innerHTML = '<table>' + html + '</table>';
return temp.firstChild;
},
log: function (message) {
if (settings.debugging && window.console && window.console.log) {
// console is available
console.log(message);
}
},
runAsync: function (functionToRun, callback) {
setTimeout(function () {
functionToRun();
if (callback) { callback(); }
}, 0);
},
format: function (str, replaceWith) {
for (var key in replaceWith) { // jshint ignore:line
str = utilities.replaceAll(str, '{' + key + '}', replaceWith[key]);
}
return str;
},
replaceAll: function (str, find, replace) {
return str.replace(new RegExp(find.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'g'), replace);
}
};
//helper to get all listItems even when the list contains > 5000 items
var listUtility = {
batch: {
getListItemsByBatch: function (batchSuccess, success, error) {
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(
'<View Scope="RecursiveAll">' +
'<ViewFields>' +
'<FieldRef Name="ID"/>' +
'<FieldRef Name="Title"/>' +
'<FieldRef Name="' + settings.lookupFieldInternalName + '"/>' +
'</ViewFields>' +
'<QueryOptions>' +
'<QueryThrottleMode>Override</QueryThrottleMode>' +
'</QueryOptions>' +
'<RowLimit>5000</RowLimit>' +
'<OrderBy Override="true" UseIndexForOrderBy="true" />' +
'</View>');
listUtility.batch.executeItemsRequest(camlQuery, batchSuccess, success, error);
},
executeItemsRequest: function (camlQuery, batchSuccess, success, error) {
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var lookupList = web.getList(settings.listUrl);
var itemCollection = lookupList.getItems(camlQuery);
ctx.load(itemCollection, 'ListItemCollectionPosition', 'Include(ID, Title, ' + settings.lookupFieldInternalName + ')');
ctx.executeQueryAsync(
function () {
batchSuccess(itemCollection);
//Iterate if more items needs to be fetched
if (itemCollection.get_listItemCollectionPosition()) {
camlQuery.set_listItemCollectionPosition(itemCollection.get_listItemCollectionPosition());
listUtility.batch.executeItemsRequest(camlQuery, batchSuccess, success, error);
} else {
success();
}
},
function (sender, args) {
error(args);
});
}
}
};
var init = function () {
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function () {
var allItems = [];
listUtility.batch.getListItemsByBatch(
function (items) {
//got batch
allItems = allItems.concat(items.get_data());
},
function () {
//got all batches
//--> reformat data
var formatted = {};
for (var i = 0; i < allItems.length; i++) {
var itemTitle = allItems[i].get_fieldValues().Title;
var itemDepartment = allItems[i].get_fieldValues().Department.get_lookupValue();
formatted[itemDepartment] = formatted[itemDepartment] || [];
formatted[itemDepartment].push({
title: itemTitle
});
}
var departmentsHtml = '';
var departments = Object.keys(formatted);
for (var i = 0; i < departments.length; i++) {
var department = departments[i];
var assetHtml = '';
for (var j = 0; j < formatted[department].length; j++) {
var asset = formatted[department][j];
assetHtml += utilities.format(template.Asset, { Asset: asset.title });
}
departmentsHtml += utilities.format(template.Department, { Department: department, Assets: assetHtml });
}
var container = document.createElement('div');
container.innerHTML = utilities.format(template.Container, { Departments: departmentsHtml });
var target = document.querySelector(settings.targetControlSelector);
if (settings.replaceContent) {
target.innerHTML = '';
}
target.appendChild(container);
},
function (a) {
//error
console.log('error while getting the list items - ', a.get_message());
});
});
};
init();
})();