1<!-- FLOWBUDDY AUTOMATED SITEMAP MINIFIED JAVASCRIPT -->
2<script src="https://flowbuddy.co.uk/widget/scripts/automated-sitemap/automated-sitemap.js"></script>
3<!-- END FLOWBUDDY AUTOMATED SITEMAP MINIFIED JAVASCRIPT -->
1<!-- FLOWBUDDY AUTOMATED SITEMAP (JAVASCRIPT) -->
2<script>
3(function() {
4 function initSitemap() {
5 // Find the sitemap container element
6 const sitemapContainer = document.querySelector('[flowbuddy="sitemap"]');
7
8 if (!sitemapContainer) {
9 console.warn('FlowBuddy: No element with flowbuddy="sitemap" attribute found');
10 return;
11 }
12
13 console.log('FlowBuddy: Sitemap container found, initializing...');
14
15 // Array of potential sitemap URLs to try
16 const sitemapUrls = [
17 '/sitemap.xml',
18 '/sitemap_index.xml',
19 window.location.origin + '/sitemap.xml',
20 window.location.origin + '/sitemap_index.xml'
21 ];
22
23 /**
24 * Recursively tries to fetch sitemap from different URLs
25 * @param {Array} urls - Array of URLs to try
26 * @param {number} index - Current URL index to try
27 */
28 function tryFetchSitemap(urls, index = 0) {
29 // If we've tried all URLs, show error message
30 if (index >= urls.length) {
31 console.error('FlowBuddy: All sitemap URLs failed');
32 sitemapContainer.innerHTML = '<p>No sitemap found. Tried: ' + urls.join(', ') + '</p>';
33 return;
34 }
35
36 const currentUrl = urls[index];
37 console.log('FlowBuddy: Trying URL:', currentUrl);
38
39 fetch(currentUrl)
40 .then(response => {
41 console.log('FlowBuddy: Response status:', response.status, 'for URL:', currentUrl);
42
43 if (!response.ok) {
44 throw new Error('HTTP ' + response.status + ' - ' + response.statusText);
45 }
46
47 return response.text();
48 })
49 .then(xmlString => {
50 console.log('FlowBuddy: Sitemap XML retrieved, length:', xmlString.length);
51 return new window.DOMParser().parseFromString(xmlString, "text/xml");
52 })
53 .then(xmlData => {
54 // Check for XML parsing errors
55 const parserError = xmlData.querySelector('parsererror');
56 if (parserError) {
57 throw new Error('XML parsing error: ' + parserError.textContent);
58 }
59
60 // Extract all URLs from sitemap
61 const urls = Array.from(xmlData.getElementsByTagName('loc'))
62 .map(loc => loc.textContent.trim());
63
64 console.log('FlowBuddy: Found', urls.length, 'URLs in sitemap');
65
66 if (urls.length === 0) {
67 throw new Error('No URLs found in sitemap');
68 }
69
70 // Filter out checkout and payment pages
71 const filteredUrls = urls.filter(url =>
72 !url.includes('/checkout') &&
73 !url.includes('/paypal-checkout') &&
74 !url.includes('/order-confirmation')
75 );
76
77 console.log('FlowBuddy: After filtering:', filteredUrls.length, 'URLs');
78
79 // Group URLs by category (first path segment)
80 const groupedUrls = {};
81 filteredUrls.forEach(url => {
82 const pathSegments = new URL(url).pathname.split('/');
83
84 if (pathSegments.length > 2) {
85 const category = pathSegments[1] || 'Home';
86 if (!groupedUrls[category]) {
87 groupedUrls[category] = [];
88 }
89 groupedUrls[category].push(url);
90 } else {
91 // Direct pages (root level)
92 if (!groupedUrls['directPages']) {
93 groupedUrls['directPages'] = [];
94 }
95 groupedUrls['directPages'].push(url);
96 }
97 });
98
99 console.log('FlowBuddy: Grouped URLs:', groupedUrls);
100
101 // Clear container and get custom styling classes
102 sitemapContainer.innerHTML = '';
103
104 const customLinkClass = sitemapContainer.getAttribute('flowbuddy-link-class') || '';
105 const customHeadingClass = sitemapContainer.getAttribute('flowbuddy-h3-class') || '';
106 const customParagraphClass = sitemapContainer.getAttribute('flowbuddy-p-class') || '';
107 const customContainerClass = sitemapContainer.getAttribute('flowbuddy-container-class') || '';
108 const customSectionClass = sitemapContainer.getAttribute('flowbuddy-section-class') || '';
109
110 // Apply container class if specified
111 if (customContainerClass) {
112 sitemapContainer.classList.add(customContainerClass);
113 }
114
115 // Generate HTML for each category
116 for (const category in groupedUrls) {
117 let sectionContainer;
118
119 // Create category heading (except for direct pages)
120 if (category !== 'directPages') {
121 const heading = document.createElement('h3');
122 heading.textContent = capitalizeFirstLetter(category.replace(/-/g, ' '));
123
124 if (customHeadingClass) {
125 heading.className = customHeadingClass;
126 } else {
127 // Default styling if no custom class provided
128 heading.style.marginTop = '20px';
129 }
130
131 // If section class is provided, wrap heading and links in a container
132 if (customSectionClass) {
133 sectionContainer = document.createElement('div');
134 sectionContainer.className = customSectionClass;
135 sectionContainer.appendChild(heading);
136 sitemapContainer.appendChild(sectionContainer);
137 } else {
138 sitemapContainer.appendChild(heading);
139 }
140 }
141
142 // Create links for this category
143 groupedUrls[category].forEach(url => {
144 const pageName = extractPageName(url);
145
146 if (pageName) {
147 // Create paragraph wrapper
148 const paragraph = document.createElement('p');
149
150 // Create anchor element
151 const anchor = document.createElement('a');
152 anchor.href = url;
153 anchor.textContent = pageName;
154
155 // Apply custom classes
156 if (customLinkClass) {
157 anchor.className = customLinkClass;
158 }
159
160 if (customParagraphClass) {
161 paragraph.className = customParagraphClass;
162 }
163
164 // Append link to paragraph
165 paragraph.appendChild(anchor);
166
167 // Append to appropriate container
168 if (sectionContainer) {
169 sectionContainer.appendChild(paragraph);
170 } else {
171 sitemapContainer.appendChild(paragraph);
172 }
173 }
174 });
175 }
176
177 console.log('FlowBuddy: Sitemap generated successfully');
178 })
179 .catch(error => {
180 console.error('FlowBuddy: Error with URL', currentUrl + ':', error.message);
181 // Try next URL in the array
182 tryFetchSitemap(urls, index + 1);
183 });
184 }
185
186 // Start the sitemap fetching process
187 tryFetchSitemap(sitemapUrls);
188 }
189
190 /**
191 * Capitalizes the first letter of a string
192 * @param {string} string - String to capitalize
193 * @returns {string} Capitalized string
194 */
195 function capitalizeFirstLetter(string) {
196 return string.charAt(0).toUpperCase() + string.slice(1);
197 }
198
199 /**
200 * Extracts and formats page name from URL
201 * @param {string} url - Full URL
202 * @returns {string|null} Formatted page name or null
203 */
204 function extractPageName(url) {
205 const path = new URL(url).pathname;
206 const segments = path.split('/').filter(Boolean);
207
208 if (segments.length === 0) {
209 return null;
210 }
211
212 const pageName = segments.pop();
213
214 // Convert kebab-case to Title Case
215 return pageName
216 .replace(/-/g, ' ')
217 .replace(/\b\w/g, function(char) {
218 return char.toUpperCase();
219 });
220 }
221
222 // Initialize when DOM is ready or immediately if already loaded
223 if (document.readyState === 'loading') {
224 document.addEventListener('DOMContentLoaded', initSitemap);
225 } else {
226 initSitemap();
227 }
228
229})();
230</script>
231<!-- END FLOWBUDDY AUTOMATED SITEMAP (JAVASCRIPT) -->
1<!-- FLOWBUDDY AUTOMATED SITEMAP (JQUERY) -->
2<script>
3(function($) {
4 'use strict';
5
6 // Ensure jQuery is available
7 if (typeof $ === 'undefined') {
8 console.error('FlowBuddy: jQuery is required but not found. Please load jQuery before this script.');
9 return;
10 }
11
12 function initSitemap() {
13 // Find the sitemap container element
14 const $sitemapContainer = $('[flowbuddy="sitemap"]');
15
16 if ($sitemapContainer.length === 0) {
17 console.warn('FlowBuddy: No element with flowbuddy="sitemap" attribute found');
18 return;
19 }
20
21 console.log('FlowBuddy: Sitemap container found, initializing...');
22
23 // Array of potential sitemap URLs to try
24 const sitemapUrls = [
25 '/sitemap.xml',
26 '/sitemap_index.xml',
27 window.location.origin + '/sitemap.xml',
28 window.location.origin + '/sitemap_index.xml'
29 ];
30
31 /**
32 * Recursively tries to fetch sitemap from different URLs using jQuery AJAX
33 * @param {Array} urls - Array of URLs to try
34 * @param {number} index - Current URL index to try
35 */
36 function tryFetchSitemap(urls, index = 0) {
37 // If we've tried all URLs, show error message
38 if (index >= urls.length) {
39 console.error('FlowBuddy: All sitemap URLs failed');
40 $sitemapContainer.html('<p>No sitemap found. Tried: ' + urls.join(', ') + '</p>');
41 return;
42 }
43
44 const currentUrl = urls[index];
45 console.log('FlowBuddy: Trying URL:', currentUrl);
46
47 // Use jQuery AJAX to fetch sitemap
48 $.ajax({
49 url: currentUrl,
50 type: 'GET',
51 dataType: 'xml',
52 timeout: 10000, // 10 second timeout
53 success: function(xmlData) {
54 console.log('FlowBuddy: Sitemap XML retrieved successfully');
55
56 try {
57 processSitemapData(xmlData, $sitemapContainer);
58 } catch (error) {
59 console.error('FlowBuddy: Error processing sitemap:', error.message);
60 // Try next URL
61 tryFetchSitemap(urls, index + 1);
62 }
63 },
64 error: function(xhr, status, error) {
65 console.error('FlowBuddy: AJAX error for URL', currentUrl + ':', status, error);
66 // Try next URL
67 tryFetchSitemap(urls, index + 1);
68 }
69 });
70 }
71
72 /**
73 * Processes the XML sitemap data and generates HTML
74 * @param {XMLDocument} xmlData - Parsed XML sitemap data
75 * @param {jQuery} $container - jQuery container element
76 */
77 function processSitemapData(xmlData, $container) {
78 // Extract all URLs from sitemap using jQuery
79 const urls = [];
80 $(xmlData).find('loc').each(function() {
81 const url = $(this).text().trim();
82 if (url) {
83 urls.push(url);
84 }
85 });
86
87 console.log('FlowBuddy: Found', urls.length, 'URLs in sitemap');
88
89 if (urls.length === 0) {
90 throw new Error('No URLs found in sitemap');
91 }
92
93 // Filter out checkout and payment pages
94 const filteredUrls = urls.filter(url =>
95 !url.includes('/checkout') &&
96 !url.includes('/paypal-checkout') &&
97 !url.includes('/order-confirmation')
98 );
99
100 console.log('FlowBuddy: After filtering:', filteredUrls.length, 'URLs');
101
102 // Group URLs by category (first path segment)
103 const groupedUrls = {};
104 $.each(filteredUrls, function(index, url) {
105 const pathSegments = new URL(url).pathname.split('/');
106
107 if (pathSegments.length > 2) {
108 const category = pathSegments[1] || 'Home';
109 if (!groupedUrls[category]) {
110 groupedUrls[category] = [];
111 }
112 groupedUrls[category].push(url);
113 } else {
114 // Direct pages (root level)
115 if (!groupedUrls['directPages']) {
116 groupedUrls['directPages'] = [];
117 }
118 groupedUrls['directPages'].push(url);
119 }
120 });
121
122 console.log('FlowBuddy: Grouped URLs:', groupedUrls);
123
124 // Clear container and get custom styling classes
125 $container.empty();
126
127 const customLinkClass = $container.attr('flowbuddy-link-class') || '';
128 const customHeadingClass = $container.attr('flowbuddy-h3-class') || '';
129 const customParagraphClass = $container.attr('flowbuddy-p-class') || '';
130 const customContainerClass = $container.attr('flowbuddy-container-class') || '';
131 const customSectionClass = $container.attr('flowbuddy-section-class') || '';
132
133 // Apply container class if specified
134 if (customContainerClass) {
135 $container.addClass(customContainerClass);
136 }
137
138 // Generate HTML for each category
139 $.each(groupedUrls, function(category, categoryUrls) {
140 let $sectionContainer;
141
142 // Create category heading (except for direct pages)
143 if (category !== 'directPages') {
144 const $heading = $('<h3>')
145 .text(capitalizeFirstLetter(category.replace(/-/g, ' ')));
146
147 if (customHeadingClass) {
148 $heading.addClass(customHeadingClass);
149 } else {
150 // Default styling if no custom class provided
151 $heading.css('margin-top', '20px');
152 }
153
154 // If section class is provided, wrap heading and links in a container
155 if (customSectionClass) {
156 $sectionContainer = $('<div>').addClass(customSectionClass);
157 $sectionContainer.append($heading);
158 $container.append($sectionContainer);
159 } else {
160 $container.append($heading);
161 }
162 }
163
164 // Create links for this category
165 $.each(categoryUrls, function(index, url) {
166 const pageName = extractPageName(url);
167
168 if (pageName) {
169 // Create paragraph wrapper
170 const $paragraph = $('<p>');
171
172 // Create anchor element
173 const $anchor = $('<a>')
174 .attr('href', url)
175 .text(pageName);
176
177 // Apply custom classes
178 if (customLinkClass) {
179 $anchor.addClass(customLinkClass);
180 }
181
182 if (customParagraphClass) {
183 $paragraph.addClass(customParagraphClass);
184 }
185
186 // Append link to paragraph
187 $paragraph.append($anchor);
188
189 // Append to appropriate container
190 if ($sectionContainer) {
191 $sectionContainer.append($paragraph);
192 } else {
193 $container.append($paragraph);
194 }
195 }
196 });
197 });
198
199 console.log('FlowBuddy: Sitemap generated successfully');
200 }
201
202 // Start the sitemap fetching process
203 tryFetchSitemap(sitemapUrls);
204 }
205
206 /**
207 * Capitalizes the first letter of a string
208 * @param {string} string - String to capitalize
209 * @returns {string} Capitalized string
210 */
211 function capitalizeFirstLetter(string) {
212 return string.charAt(0).toUpperCase() + string.slice(1);
213 }
214
215 /**
216 * Extracts and formats page name from URL
217 * @param {string} url - Full URL
218 * @returns {string|null} Formatted page name or null
219 */
220 function extractPageName(url) {
221 const path = new URL(url).pathname;
222 const segments = path.split('/').filter(Boolean);
223
224 if (segments.length === 0) {
225 return null;
226 }
227
228 const pageName = segments.pop();
229
230 // Convert kebab-case to Title Case
231 return pageName
232 .replace(/-/g, ' ')
233 .replace(/\b\w/g, function(char) {
234 return char.toUpperCase();
235 });
236 }
237
238 // Initialize when document is ready
239 $(document).ready(function() {
240 initSitemap();
241 });
242
243})(jQuery);
244</script>
245<!-- END FLOWBUDDY AUTOMATED SITEMAP (JQUERY) -->