Initial commit
This commit is contained in:
265
classic/megamenu-classic.js
Normal file
265
classic/megamenu-classic.js
Normal file
@@ -0,0 +1,265 @@
|
||||
/**
|
||||
* SharePoint MegaMenu for Classic Pages
|
||||
* Version: 1.0.2
|
||||
*
|
||||
* This version uses standalone services that replicate the SPFx logic
|
||||
* without requiring the SharePoint Framework.
|
||||
*
|
||||
* Prerequisites:
|
||||
* 1. megamenu-services-standalone.js must be loaded first
|
||||
* 2. MegaMenu.css must be included
|
||||
* 3. SP.js and SP.Taxonomy.js must be available
|
||||
*
|
||||
* Usage:
|
||||
* <link rel="stylesheet" href="/SiteAssets/megamenu/MegaMenu.css" />
|
||||
* <script src="/SiteAssets/megamenu/megamenu-services-standalone.js"></script>
|
||||
* <script src="/SiteAssets/megamenu/megamenu-classic.js"></script>
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var MEGAMENU_UCA_ID = 'abc3361f-bb2d-491f-aba3-cd51c19a299b'; // Same as in MegaMenuApplicationCustomizer.ts
|
||||
|
||||
var config = window.MegaMenuConfig || {
|
||||
containerId: 's4-titlerow',
|
||||
debug: false
|
||||
};
|
||||
|
||||
function log(message, data) {
|
||||
if (config.debug && console && console.log) {
|
||||
console.log('[MegaMenu Classic] ' + message, data || '');
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for dependencies: SharePoint JSOM, Taxonomy, and our standalone services
|
||||
function waitForDependencies(callback) {
|
||||
if (typeof SP !== 'undefined' &&
|
||||
SP.SOD &&
|
||||
typeof SP.Taxonomy !== 'undefined' &&
|
||||
typeof window.MegaMenuServices !== 'undefined' &&
|
||||
document.readyState === 'complete') {
|
||||
callback();
|
||||
} else {
|
||||
setTimeout(function() { waitForDependencies(callback); }, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Classic SharePoint context wrapper
|
||||
function createClassicContext() {
|
||||
return {
|
||||
pageContext: {
|
||||
site: {
|
||||
absoluteUrl: _spPageContextInfo.siteAbsoluteUrl
|
||||
},
|
||||
web: {
|
||||
absoluteUrl: _spPageContextInfo.webAbsoluteUrl,
|
||||
permissions: {
|
||||
hasPermission: function(permission) {
|
||||
return _spPageContextInfo.isSiteAdmin === true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Configuration reader using REST API (same logic as MegaMenuSettings.ts)
|
||||
function readMegaMenuConfiguration(callback) {
|
||||
log('Reading MegaMenu configuration from UserCustomAction...');
|
||||
|
||||
var restUrl = _spPageContextInfo.siteAbsoluteUrl +
|
||||
"/_api/site/userCustomActions?$filter=ClientSideComponentId eq guid'" + MEGAMENU_UCA_ID + "'";
|
||||
|
||||
fetch(restUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json;odata=nometadata'
|
||||
}
|
||||
})
|
||||
.then(function(response) {
|
||||
if (!response.ok) {
|
||||
throw new Error('HTTP ' + response.status + ' - ' + response.statusText);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
if (data && data.value && data.value.length > 0) {
|
||||
var uca = data.value[0];
|
||||
try {
|
||||
var props = JSON.parse(uca.ClientSideComponentProperties);
|
||||
log('Configuration found', props);
|
||||
callback(props);
|
||||
} catch (e) {
|
||||
log('Error parsing UserCustomAction properties: ' + e.message);
|
||||
callback({ termSetName: 'Navigation', cssUrl: '' });
|
||||
}
|
||||
} else {
|
||||
log('No UserCustomAction found - using defaults');
|
||||
callback({ termSetName: 'Navigation', cssUrl: '' });
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
log('Error reading configuration: ' + error.message);
|
||||
callback({ termSetName: 'Navigation', cssUrl: '' });
|
||||
});
|
||||
}
|
||||
|
||||
// Load external CSS (same as SPFx version)
|
||||
function loadExternalCSS(cssUrl) {
|
||||
if (!cssUrl) return;
|
||||
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.type = 'text/css';
|
||||
link.href = cssUrl;
|
||||
link.onerror = function() {
|
||||
log('Failed to load external CSS: ' + cssUrl);
|
||||
};
|
||||
link.onload = function() {
|
||||
log('External CSS loaded successfully: ' + cssUrl);
|
||||
};
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
// Main initialization
|
||||
function initMegaMenu() {
|
||||
log('Initializing MegaMenu for classic SharePoint...');
|
||||
|
||||
// Check if services are available
|
||||
if (!window.MegaMenuServices) {
|
||||
console.error('[MegaMenu] Standalone services not found! Please include megamenu-services-standalone.js first.');
|
||||
return;
|
||||
}
|
||||
|
||||
readMegaMenuConfiguration(function(props) {
|
||||
log('Using configuration:', props);
|
||||
|
||||
// Load external CSS if specified
|
||||
if (props.cssUrl) {
|
||||
loadExternalCSS(props.cssUrl);
|
||||
}
|
||||
|
||||
// Create context
|
||||
var context = createClassicContext();
|
||||
|
||||
// Create taxonomy service using standalone implementation
|
||||
var taxonomyService = new window.MegaMenuServices.TaxonomyNavigationService(
|
||||
context,
|
||||
props.termSetName
|
||||
);
|
||||
|
||||
// Load menu items
|
||||
taxonomyService.getMenuItems()
|
||||
.then(function(menuItems) {
|
||||
log('Menu items loaded:', menuItems);
|
||||
|
||||
// Find target container
|
||||
var container = document.getElementById(config.containerId);
|
||||
if (!container) {
|
||||
log('Container not found: ' + config.containerId + '. Creating fallback container.');
|
||||
// Create fallback container at top of page
|
||||
container = document.createElement('div');
|
||||
container.id = 'megamenu-fallback-container';
|
||||
document.body.insertBefore(container, document.body.firstChild);
|
||||
}
|
||||
|
||||
// Create menu wrapper
|
||||
var menuWrapper = document.createElement('div');
|
||||
menuWrapper.className = 'megamenu-classic-container';
|
||||
|
||||
// Insert menu wrapper
|
||||
if (container.nextSibling) {
|
||||
container.parentNode.insertBefore(menuWrapper, container.nextSibling);
|
||||
} else {
|
||||
container.parentNode.appendChild(menuWrapper);
|
||||
}
|
||||
|
||||
// Create renderer using standalone implementation
|
||||
var renderer = new window.MegaMenuServices.MegaMenuRenderer(
|
||||
context,
|
||||
menuItems,
|
||||
function(updatedProps) {
|
||||
log('Properties updated (not persisted in classic mode):', updatedProps);
|
||||
}
|
||||
);
|
||||
|
||||
// Render the menu
|
||||
renderer.render(menuWrapper);
|
||||
|
||||
log('✅ MegaMenu rendered successfully!');
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('[MegaMenu] Error loading menu items:', error);
|
||||
|
||||
// Show error message to user
|
||||
var container = document.getElementById(config.containerId);
|
||||
if (container) {
|
||||
var errorDiv = document.createElement('div');
|
||||
errorDiv.style.cssText = 'background:#ffebee;color:#c62828;padding:10px;border:1px solid #ef5350;margin:5px 0;';
|
||||
errorDiv.innerHTML = '<strong>MegaMenu Error:</strong> ' + error.message +
|
||||
'<br><small>Check browser console for details.</small>';
|
||||
|
||||
if (container.nextSibling) {
|
||||
container.parentNode.insertBefore(errorDiv, container.nextSibling);
|
||||
} else {
|
||||
container.parentNode.appendChild(errorDiv);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Bootstrap function
|
||||
function bootstrap() {
|
||||
log('Bootstrapping MegaMenu...');
|
||||
|
||||
// Wait for SharePoint JSOM to be ready
|
||||
if (typeof SP === 'undefined' || !SP.SOD) {
|
||||
setTimeout(bootstrap, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load required SharePoint libraries
|
||||
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function() {
|
||||
SP.SOD.executeFunc('sp.taxonomy.js', 'SP.Taxonomy.TaxonomySession', function() {
|
||||
// Wait for all dependencies and initialize
|
||||
waitForDependencies(function() {
|
||||
initMegaMenu();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Auto-start based on DOM state
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', bootstrap);
|
||||
} else {
|
||||
// DOM already loaded
|
||||
setTimeout(bootstrap, 100);
|
||||
}
|
||||
|
||||
// Expose public API
|
||||
window.MegaMenuClassic = {
|
||||
init: initMegaMenu,
|
||||
bootstrap: bootstrap,
|
||||
config: config,
|
||||
|
||||
// Utility methods
|
||||
setConfig: function(newConfig) {
|
||||
Object.assign(config, newConfig);
|
||||
},
|
||||
|
||||
reload: function() {
|
||||
// Remove existing menu and reload
|
||||
var existing = document.querySelector('.megamenu-classic-container');
|
||||
if (existing) {
|
||||
existing.remove();
|
||||
}
|
||||
initMegaMenu();
|
||||
}
|
||||
};
|
||||
|
||||
log('MegaMenu Classic wrapper loaded. Waiting for dependencies...');
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user