/* CustomBranding 2.0.0 - safe Classic SharePoint runtime */ (function (global) { 'use strict'; var COMPONENT_ID = '035ba968-6488-4d42-86b3-0470ffcc95b9'; var OWNER = 'CustomBranding.Classic'; var MAX_DEPTH = 8; var MAX_ELEMENTS = 200; var allowedTags = ['div', 'span', 'p', 'a', 'button', 'img', 'h1', 'h2', 'h3', 'strong', 'em', 'nav', 'section']; var allowedStyles = ('align-items background background-color border border-bottom border-color border-left border-radius border-right border-style border-top border-width box-sizing color display flex flex-basis flex-direction flex-grow flex-shrink flex-wrap font-family font-size font-style font-weight gap grid-template-columns height justify-content line-height margin margin-bottom margin-left margin-right margin-top max-height max-width min-height min-width opacity overflow padding padding-bottom padding-left padding-right padding-top text-align text-decoration text-transform white-space width').split(' '); var state = { debug: false, hosts: [], css: [] }; function log(message, data) { if (state.debug && global.console && console.log) { console.log('[CustomBranding Classic] ' + message, data || ''); } } function strings() { var german = global._spPageContextInfo && Number(_spPageContextInfo.currentLanguage) === 1031; return german ? { loadError: 'Die CustomBranding-Konfiguration konnte nicht geladen werden.', renderError: 'Das konfigurierte Branding konnte nicht dargestellt werden.' } : { loadError: 'The CustomBranding configuration could not be loaded.', renderError: 'The configured branding could not be rendered.' }; } function siteUrl() { return global._spPageContextInfo ? _spPageContextInfo.siteAbsoluteUrl : ''; } function sanitizeUrl(value, allowMailto) { var raw = String(value || '').replace(/^\s+|\s+$/g, ''); if (!raw || /[\u0000-\u001f\u007f]/.test(raw)) { return null; } var resolved = raw.toLowerCase().indexOf('~sitecollection') === 0 ? siteUrl().replace(/\/+$/, '') + raw.substring('~sitecollection'.length) : raw; var match = resolved.match(/^([a-z][a-z0-9+.-]*):/i); if (!match) { return resolved; } var protocol = match[1].toLowerCase(); return protocol === 'http' || protocol === 'https' || (allowMailto && protocol === 'mailto') ? resolved : null; } function normalizeHosts(value) { var result = []; if (!Array.isArray(value)) { return result; } for (var i = 0; i < value.length; i++) { var host = String(value[i] || '').toLowerCase().replace(/^\s+|\s+$/g, '').replace(/^https?:\/\//, '').replace(/\/.*$/, ''); if (/^[a-z0-9.-]+(?::\d+)?$/.test(host) && result.indexOf(host) < 0) { result.push(host); } } return result; } function sanitizeCssUrl(value, hosts) { var resolved = sanitizeUrl(value, false); if (!resolved) { return null; } var absolute = resolved.match(/^(https?):\/\/([^/]+)/i); if (!absolute) { return resolved; } var current = siteUrl().match(/^(https?):\/\/([^/]+)/i); var protocol = absolute[1].toLowerCase(); var host = absolute[2].toLowerCase(); if (current && protocol === current[1].toLowerCase() && host === current[2].toLowerCase()) { return resolved; } return protocol === 'https' && hosts.indexOf(host) >= 0 ? resolved : null; } function sanitizeStyle(name, value) { var property = String(name || '').toLowerCase().replace(/^\s+|\s+$/g, ''); var styleValue = String(value || '').replace(/^\s+|\s+$/g, ''); if (allowedStyles.indexOf(property) < 0 || !styleValue || styleValue.length > 512) { return null; } if (/[\u0000-\u001f\u007f]/.test(styleValue) || /(url\s*\(|expression\s*\(|javascript\s*:|@import|behavior\s*:|-moz-binding)/i.test(styleValue)) { return null; } return styleValue; } function allowedAttribute(tag, name) { if (['id', 'class', 'title', 'role', 'aria-label', 'aria-hidden', 'aria-current', 'aria-live'].indexOf(name) >= 0) { return true; } if (tag === 'a') { return ['href', 'target'].indexOf(name) >= 0; } if (tag === 'img') { return ['src', 'alt', 'width', 'height'].indexOf(name) >= 0; } if (tag === 'button') { return ['type', 'disabled', 'aria-expanded', 'aria-controls'].indexOf(name) >= 0; } return false; } function createElement(config, depth, counter) { if (!config || typeof config !== 'object' || depth > MAX_DEPTH || counter.value >= MAX_ELEMENTS) { return null; } var tag = String(config.type || '').toLowerCase(); if (allowedTags.indexOf(tag) < 0) { return null; } counter.value++; var element = document.createElement(tag); var attributes = config.attributes && typeof config.attributes === 'object' ? config.attributes : {}; var hasAlt = false; for (var rawName in attributes) { if (!Object.prototype.hasOwnProperty.call(attributes, rawName)) { continue; } var name = String(rawName).toLowerCase(); var value = String(attributes[rawName] === undefined ? '' : attributes[rawName]).substring(0, 2048); if (name.indexOf('on') === 0 || !allowedAttribute(tag, name)) { continue; } if (name === 'href' || name === 'src') { var safeUrl = sanitizeUrl(value, name === 'href'); if (safeUrl) { element.setAttribute(name, safeUrl); } } else if (name === 'target') { if (value === '_blank' || value === '_self') { element.setAttribute(name, value); } } else if ((name === 'id' || name === 'class') && !/^[a-z0-9 _-]{1,256}$/i.test(value)) { continue; } else if ((name === 'width' || name === 'height') && !/^\d{1,4}$/.test(value)) { continue; } else { element.setAttribute(name, value); } if (name === 'alt') { hasAlt = true; } } if (tag === 'img' && !hasAlt) { return null; } if (tag === 'button') { element.setAttribute('type', 'button'); } if (tag === 'a' && element.getAttribute('target') === '_blank') { element.setAttribute('rel', 'noopener noreferrer'); } var styles = config.styles && typeof config.styles === 'object' ? config.styles : {}; for (var styleName in styles) { if (!Object.prototype.hasOwnProperty.call(styles, styleName)) { continue; } var safeStyle = sanitizeStyle(styleName, styles[styleName]); if (safeStyle) { element.style.setProperty(String(styleName).toLowerCase(), safeStyle); } } if (config.content !== undefined && config.content !== null) { element.appendChild(document.createTextNode(String(config.content).substring(0, 4000))); } if (tag !== 'img' && Array.isArray(config.children)) { for (var i = 0; i < config.children.length; i++) { var child = createElement(config.children[i], depth + 1, counter); if (child) { element.appendChild(child); } } } if ((tag === 'a' || tag === 'button') && !element.textContent && !element.getAttribute('aria-label')) { return null; } return element; } function clear(element) { while (element && element.firstChild) { element.removeChild(element.firstChild); } } function renderHost(id, parent, elements, beforeNode) { var host = document.getElementById(id); if (!host) { host = document.createElement('div'); host.id = id; host.setAttribute('data-custom-branding-owner', OWNER); if (beforeNode) { parent.insertBefore(host, beforeNode); } else { parent.appendChild(host); } } clear(host); var counter = { value: 0 }; for (var i = 0; i < elements.length; i++) { var element = createElement(elements[i], 1, counter); if (element) { host.appendChild(element); } } state.hosts.push(host); } function showStatus(message) { var host = document.getElementById('CustomBrandingClassicTopHost') || document.createElement('div'); host.id = 'CustomBrandingClassicTopHost'; clear(host); var status = document.createElement('div'); status.setAttribute('role', 'status'); status.textContent = message; host.appendChild(status); if (!host.parentNode) { document.body.insertBefore(host, document.body.firstChild); } } function loadCss(files, hosts) { var seen = {}; for (var i = 0; i < files.length && i < 20; i++) { var path = sanitizeCssUrl(files[i] && files[i].path, hosts); var key = String(path || '').toLowerCase(); if (!path || seen[key]) { continue; } seen[key] = true; var link = document.createElement('link'); link.rel = 'stylesheet'; link.href = path; link.media = files[i].media && /^[a-z0-9 (),.:\/-]{1,80}$/i.test(files[i].media) ? files[i].media : 'all'; link.setAttribute('data-custom-branding-owner', OWNER); document.getElementsByTagName('head')[0].appendChild(link); state.css.push(link); } } function normalize(raw) { raw = raw && typeof raw === 'object' ? raw : {}; return { enabled: raw.enabled !== false, debug: raw.debug === true, allowedCssHosts: normalizeHosts(raw.allowedCssHosts), cssfiles: Array.isArray(raw.cssfiles) ? raw.cssfiles : [], top: raw.placeholdertop && Array.isArray(raw.placeholdertop.elements) ? raw.placeholdertop.elements : (Array.isArray(raw.elements) ? raw.elements : []), bottom: raw.placeholderbottom && Array.isArray(raw.placeholderbottom.elements) ? raw.placeholderbottom.elements : [] }; } function cleanup() { for (var i = 0; i < state.hosts.length; i++) { if (state.hosts[i].parentNode) { state.hosts[i].parentNode.removeChild(state.hosts[i]); } } for (var j = 0; j < state.css.length; j++) { if (state.css[j].parentNode) { state.css[j].parentNode.removeChild(state.css[j]); } } state.hosts = []; state.css = []; } function render(config) { cleanup(); state.debug = config.debug; if (!config.enabled) { return; } loadCss(config.cssfiles, config.allowedCssHosts); var titleRow = document.getElementById('s4-titlerow'); var topParent = titleRow && titleRow.parentNode ? titleRow.parentNode : document.body; var topBefore = titleRow ? titleRow.nextSibling : document.body.firstChild; renderHost('CustomBrandingClassicTopHost', topParent, config.top, topBefore); var workspace = document.getElementById('s4-workspace') || document.body; renderHost('CustomBrandingClassicBottomHost', workspace, config.bottom, null); log('Branding rendered.'); } function readConfiguration(callback, errorCallback) { var url = siteUrl() + "/_api/site/UserCustomActions?$filter=ClientSideComponentId eq guid'" + COMPONENT_ID + "'&$select=ClientSideComponentProperties"; var request = new XMLHttpRequest(); request.open('GET', url, true); request.setRequestHeader('Accept', 'application/json;odata=verbose'); request.onreadystatechange = function () { if (request.readyState !== 4) { return; } if (request.status < 200 || request.status >= 300) { errorCallback(); return; } try { var data = JSON.parse(request.responseText); var actions = data && data.d && data.d.results ? data.d.results : []; var serialized = actions.length && actions[0].ClientSideComponentProperties ? actions[0].ClientSideComponentProperties : '{}'; if (serialized.length > 100000) { throw new Error('Configuration exceeds the maximum size.'); } var properties = JSON.parse(serialized); callback(normalize(properties)); } catch (error) { errorCallback(error); } }; request.send(); } function init() { readConfiguration(render, function (error) { log('Configuration load failed.', error); showStatus(strings().loadError); }); } function start() { if (!global._spPageContextInfo) { global.setTimeout(start, 100); return; } init(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', start); } else { global.setTimeout(start, 0); } global.CustomBrandingClassic = { init: init, reload: init, dispose: cleanup }; }(window));