<!DOCTYPE html>
<%
	// CREATE ALIASES
	const activity = locals.metadata.page.activity
	const publicpage = locals.metadata.page.public || activity === 'preview'
	const modules = locals.metadata.site.modules
	const metafields = locals.metadata.site.metafields
	const space = locals.metadata.page.space
	const object = locals.metadata.page.object
	const query = locals.metadata.page.query
	const languages = locals.metadata.site.languages
	const language = locals.metadata.page.language
	const rights = locals.metadata.user.rights
	const pagedisplay = locals.metadata.page.display
	const pages = locals.metadata.page.count
	const source = locals.metadata.page.source
	const sourceurl = locals.metadata.site.apps_in_suite.find(d => d.key === locals.metadata.page.source)?.baseurl

	let mapscale = locals.metadata.page.mscale
	// THIS IS FOR PUBLIC PINBOARDS: TO ENSURE THAT THE DEFINED MAP SCALE IS PRESERVED
	if (space === 'pinned' && (!locals.pinboard?.editable || activity === 'preview') && locals.pinboard?.display_map) {
		if (locals.pinboard?.display_fullscreen) mapscale = 'full-screen'
		else mapscale = 'contain'
	}

	const display_content = Object.keys(query)?.filter(key => !['status', 'source', 'mapscale', 'display'].includes(key)).length > 0

%>
<html>
<head>

<%- include ('../../partials/head.ejs') %>

</head>

<body translate="no">

<% include ('../../partials/translations.ejs') %>

<%- include ('../../partials/menu-languages.ejs') %>
<%- include ('../../partials/modals.ejs') %>

<%- include ('../modules/download.ejs') %>


<% const welcome_module = locals.metadata.site.welcome_module %>

<% if (publicpage && object === 'pads' && space !== 'pinned' && mapscale !== 'full-screen' && locals.metadata.page.id === 1 && !display_content) { %>

<div class='public-page-header'>
	<button class='scroll-nav' onclick='scrollToPad(this)'><label class='rotate'>&rsaquo;</label></button>
	<script type='text/javascript'>
		function scrollToPad (node) {
			let next = d3.select('div.public-page-header h1.site-title').node()
			window.scrollTo({
				top: next.offsetTop - 60,
				left: 0,
				behavior: 'smooth'
			})
		}
		window.addEventListener('scroll', function (e) {
			d3.select('button.scroll-nav').classed('hide', document.documentElement.scrollTop > 60)
		})
	</script>

	<h1 class='site-title'><%- locals.metadata.site.title %></h1>
	<% if (true) { // FIXME find a good condition to show it up here %>
	<div class="toppage">
		<h4>Contribute to:</h4>
		<!-- TO DO: TRANSLATE -->
		<menu>
			<% locals.metadata.site.apps_in_suite.forEach(d => { %>
				<a href='<%- d.forwardURL %>' target='_blank'>
					<li><h3><%- d.name %></h3></li>
				</a>
			<% }) %>

			<a href='/en/browse/blog/preview' >
				<li><h3>Blog</h3></li>
			</a>
		</menu>
</div>
	<% } // page links %>
	<% if (locals.metadata.page.instance_title) { %><h2><%- locals.metadata.page.instance_title %></h2><% } %>
	<% if (locals.metadata.site.description?.[language]) { %><p class='lead'><%- locals.metadata.site.description[language] %></p><% } %>
</div>
<% } %>

<header <% if (!display_content) { %>class='center'<% } %>>
	<div class='inner'>
		<%- include ('../../partials/menu-logo.ejs') %>
	</div>
</header>

<div class='browse <%- pagedisplay %><% if (publicpage) { %> public<% } %><% if (!display_content) { %> flex<% } %>' translate="no">

	<main>
		<div class='inner'>

			<!-- SEARCH AND FILTER MENU -->
			<% if (
				!(space === 'pinned'
					&& (!locals.pinboard?.editable || activity === 'preview')
					&& !locals.pinboard?.display_filters)
				&& pagedisplay !== 'slideshow'
			) { %>
				<%- include('./filter') %>
			<% } %>

			<!-- MAP -->
			<% if (
				display_content
				&& (locals.metadata.page.map)
			) { %>
				<%- include ('../modules/map') %>
			<% } %>


				<section class="container pads">
					<div class="layout" >
						<% locals.data[1]['searchResults'].forEach(result => { %>
							<article class="pad status-3" style="padding: 12px;" >
							  <div class="inner">
								<div class="head">
								  <div class="meta meta-group">
									<div class="meta meta-data">
									  <div class="meta meta-contributor">
										<a class="contributor-name" >
										  <%= result.country || 'Unkown country' %>
										</a>
									  </div>
									  <div class="meta meta-date">
										<%= result.posted_date_str || new Date(result.created_at).toLocaleDateString('en-US', {
											month: 'long',
											day: 'numeric',
											year: 'numeric'
										  }) %>
									  </div>
									</div>
								  </div>
								</div>
								<div class="body" style="min-width: 100% !important;">
								  <div class="media media-title">
									<a class="pad-link" href="<%= result.url %>" target="_blank">
									  <h1>
										<img src="/imgs/icons/i-pad.svg">
										<span>
										  <%= result.title || 'Unknown title' %>
										</span>
									  </h1>
									</a>
								  </div>
								  <div class="media media-txt" style="margin-top: 20px; white-space: inherit !important;">
									<a class="pad-link" target="_blank" style="width: 100% !important; ">
										<%
										  function findMatchingText(largeText) {

											largeText.trim().replace(/^\n+|\n+$/g, '');

											var searchText = locals?.metadata?.page?.query?.['search'] || ""

											var escapedSearchText = searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
											var pattern = new RegExp(`[^.!?]*${escapedSearchText}[^.!?]*[.!?]`, 'g');
											var matches = largeText.match(pattern);

											if (matches && matches.length > 0) {
											  var firstSentence = matches[0];
											  var secondSentence = matches[1];
											  var lastSentence = matches[matches.length - 1];
											  return firstSentence + " " + `${secondSentence || ''}`;
											} else {
											  return largeText?.split(' ')?.slice(0, 100)?.join(' ') || null;
											}
										  }

										  var extractedText = findMatchingText(result?.content || result?.all_html_content || '');
										%>

										<%= extractedText || 'We are unable to extract content from the original website. Please click to read content on the original website.' %> ... <a target="_blank" href="<%= result.url %>" style="cursor: pointer; color: cornflowerblue;">read more</a>
									  </a>
								  </div>
								  <div class="meta tag-group">
									<div class="meta meta-tags">
										<a class="tag-link" >
										  <div class="tag">
											<label><%= result?.article_type?.capitalize() %></label>
										  </div>
										</a>
									</div>
								  </div>
								</div>
							  </div>
							</article>
						  <% }); %>

					</div>
				</section>

			<!-- STATISTICS IN MOBILE VIEW -->

			<div class='xs'>
				<%- include ('./statistics.ejs') %>
			</div>

		</div>
		<div class='lds-ellipsis hide'><div></div><div></div><div></div><div></div></div>
	</main>
</div>

<% if (display_content && pagedisplay !== 'slideshow' && mapscale !== 'full-screen') { %>
	<div class='sidebar lg'>
		<div class='inner'>
			<%- include ('./statistics.ejs') %>
			<%- include ('../../partials/filters.ejs') %>
		</div>
	</div>
<% } %>


<footer>
	<nav class='pagination xs sm m'>
		<% if (activity === 'browse') { %><%- include('../modules/pagination') %><% } %>
	</nav>
	<h4>Contribute to:</h4>
	<!-- TO DO: TRANSLATE -->
	<menu>
		<% locals.metadata.site.apps_in_suite.forEach(d => { %>
			<a href='<%- d.forwardURL %>' target='_blank'>
				<li><h3><%- d.name %></h3></li>
			</a>
		<% }) %>

		<a href='/en/browse/blog/preview' >
			<li><h3>Blog</h3></li>
		</a>
	</menu>
	<% if (pagedisplay === 'slideshow') { %>
		<div class='inner'>
			<div class='dots'></div>
		</div>
	<% } %>
	<% if (display_content && pages > 1) { %>
		<div class='outer'>
				<p class='nav-info'>&larr; <%- vocabulary['navigate here'][language] %></p>
		</div>
	<% } %>

</footer>


<script type='text/javascript'>
	if (!mediaSize) var mediaSize = getMediaSize()
	const main = d3.select('div.browse main')
	const layout = main.select('div.inner')
	const overview = layout.select('section.overview')

	let page = <% if (!isNaN(locals.metadata.page.id)) { %><%- locals.metadata.page.id %><% } else if (![null, undefined].includes(locals.metadata.page.id)) { %>'<%- locals.metadata.page.id %>'<% } else { %>undefined<% } %>;
	var lazyloading = false


	function renderSections (_data) {
		const sections = layout.addElems('section', 'container pads', _data)

		sections.addElems('div', 'layout')
		.each(function (d) {
			const section = d3.select(this);
			// THIS IS WHERE THE GROUPING SHOULD HAPPEN FOR FOLLOW UPS AND FORWARDS
			if (mediaSize === 'xs') {
				section.classed('columns', true)
				d?.data?.forEach(c => section.call(renderVignette, { data: c, display: 'columns' }))
			} else {
				section.classed('<%- pagedisplay %>', true)
				console.log(d)
				d?.data?.forEach(c => section.call(renderVignette, { data: c, display: '<%- pagedisplay %>' }))
			}
		})

		<% if (pagedisplay === 'slideshow') { %>initSlideshow()<% } %>
	}

	const Entry = function (_kwargs) {
		const { parent, data, display, language } = _kwargs

		this.language = language || 'en'
		this.data = data[1]['searchResults']

		this.container = parent.addElem('article', 'pad')
			.datum(data)
		.each(function (d) {
			d3.select(this).classed(`status-${d.status}`, true)
		})

		this.inner = this.container.addElems('div', 'inner')
			.classed('centered', d => {
				return (display === 'slideshow'
				&& d.img?.length === 0
				&& d.items === undefined
				&& d.contributors === undefined
				&& d.private_associated_pads === undefined
				&& d.associated_pads === undefined
				&& d.ongoing_associated_mobilizations === undefined
				&& d.past_associated_mobilizations === undefined)
			})
		this.outer = this.container.addElems('div', 'outer')
			.addElems('div', 'inner')

		this.head = this.inner.addElems('div', 'head')
		this.body = this.inner.addElems('div', 'body')
			.classed('full-width', d => {
				return (display !== 'columns'
				&& d.img?.length === 0
				&& d.items === undefined
				&& d.contributors === undefined
				&& d.private_associated_pads === undefined
				&& d.associated_pads === undefined
				&& d.ongoing_associated_mobilizations === undefined
				&& d.past_associated_mobilizations === undefined)
			})
		this.foot = this.inner.addElems('div', 'foot', d => {
			if (display === 'columns'
				|| d.img?.length > 0
				|| d.items !== undefined
				|| d.contributors !== undefined
				|| d.private_associated_pads !== undefined
				|| d.associated_pads !== undefined
				|| d.ongoing_associated_mobilizations !== undefined
				|| d.past_associated_mobilizations !== undefined)
			{ return [d]
			} else return []
		})

		if (display !== 'columns') this.metagroup = this.head.addElems('div', 'meta meta-group')

		this.render = {
			owner: function (_sel) {
				const metainfo = _sel.addElems('div', 'meta meta-data')
				metainfo.addElems('div', 'meta meta-contributor')
					.addElems('a', 'contributor-name')
					.attr('href', d => {
						<% if ((['pads', 'templates', 'files'].includes(object) && ['private', 'curated'].includes(space))
							|| object === 'mobilizations')
						{ %>
							return `?contributors=${d.owner}`
						<% } else if (object === 'reviews') { %>
							return d.is_review ? `?contributors=${d.owner}` : '#'
						<% } else { %>
							return `?countries=${d.iso3}`
						<% } %>
					}).html(d => {
						<% if ((['pads', 'templates', 'files'].includes(object) && ['private', 'curated'].includes(space))
							|| object === 'mobilizations')
						{ %>
							return d.ownername || '<%- vocabulary["anonymous contributor"][language] %>'
						<% } else if (object === 'reviews' && space === 'private') { %>
							return d.is_review ? d.ownername : '<%- vocabulary["blinded for review"][language] %>'
						<% } else { %>
							if (d.iso3 === 'NUL') return '<%- vocabulary["global"][language] %>'
							return d.country || '<%- vocabulary["anonymous contributor"][language] %>'
						<% } %>
					})
				metainfo.addElems('div', 'meta meta-date')
					.html(d => {
						if (d.date) return Function(`"use strict";return (<%- vocabulary["date"][language] %>)`)()(d.date)
						else if (d.start_date) {
							<% if (object === 'contributors') { %>
								const start = Function(`"use strict";return (<%- vocabulary["date"][language] %>)`)()({ date: d.start_date })
								let str = Function(`"use strict";return (<%- vocabulary["joined on"][language] %>)`)()(start)

								if (d.end_date) {
									const end = Function(`"use strict";return (<%- vocabulary["date"][language] %>)`)()({ date: d.end_date })
									str += ', ' + Function(`"use strict";return (<%- vocabulary["left on"][language] %>)`)()(end).toLowerCase()
								}
								return str

							<% } else { %>
								if (d.end_date) {
									return Function(`"use strict";return (<%- vocabulary["date"][language] %>)`)()(d.start_date) + ' – ' + Function(`"use strict";return (<%- vocabulary["date"][language] %>)`)()(d.end_date)
								} else return Function(`"use strict";return (<%- vocabulary["date"][language] %>)`)()(d.start_date)
							<% } %>
						}
						else return d.email
					})
			}.bind(this),
			actions: function (_sel) {
				<% if (rights >= (modules.find(d => d.type === object)?.rights.write ?? Infinity)) { %>
					_sel.addElems('div', 'btn-group')
					.addElems('form', 'actions', d => {
						const opts = []

						<% if (['private', 'pinned'].includes(space) && object !== 'reviews') { %>
							if (d.editable) opts.push({ node: 'button', type: 'button', classname: 'delete', label: '<%- vocabulary["delete"][language] %>', fn: deleteArticles })
						<% } else if (space === 'curated') { %>
							if (d.editable && !d.owner) opts.push({ node: 'button', type: 'button', classname: 'delete', label: '<%- vocabulary["delete"][language] %>', fn: deleteArticles })
						<% } %>
						<% if (object === 'pads') { %>
							opts.push({ node: 'button', type: 'button', classname: 'download', label: '<%- vocabulary["download"][language] %>', name: 'pads', value: d.id, disabled: d.status < 2, fn: setDownloadOptions })
						<% } %>
						// CHANGED THE LOGIC HERE FOR THE PUBLICATION LIMIT
						<% if (['files', 'pads'].includes(object)) { %>
							if (d.editable) {
								const publish_dropdown = []
								<% if (modules.some(d => d.type === 'reviews')) { %>
									if (d.status === 1) publish_dropdown.push({ name: 'status', value: 2, classname: 'preprint', label: '<%- vocabulary["object status"][language](object, 2) %>' })
									if (d.status <= 2 && d.review_status === 0) publish_dropdown.push({ name: 'review_status', value: 1, classname: 'review',label: '<%- vocabulary["submit for review"][language] %>', fn: selectReviewLanguage })
								<% } else { %>
									if (d.status === 1) publish_dropdown.push({ name: 'status', value: 2, classname: 'internally', label: '<%- vocabulary["internally"][language] %>' })
									// TO DO: MAKE SURE THIS IS ONLY POSSIBLE WHEN ALL META FIELDS ARE FILLED OUT
									if (d.status <= 2 && d.publishable) publish_dropdown.push({ name: 'status', value: 3, classname: 'externally', label: '<%- vocabulary["externally"][language] %>' })
								<% } %>

								if (publish_dropdown.length > 0) opts.push({ action: '/publish/<%- object %>', method: 'GET', node: 'button', type: 'button', value: d.id, disabled: !(d.editable && [1, 2].includes(d.status) && (d.available_publications === undefined || d.available_publications > 0)), classname: 'publish', label: '<%- vocabulary["publish"][language] %>', dropdown: publish_dropdown, inputs: [{ name: 'id', value: d.id }, { name: 'title', value: d.title }] })

								// if (d.status > 1) opts.push({ action: '/publish/<%- object %>', method: 'GET', node: 'button', type: 'submit', value: d.id, disabled: !d.editable, classname: 'unpublish', label: '<%- vocabulary["unpublish"][language] %>', inputs: [{ name: 'status', value: 1 }] })

								if (d.status > 1) opts.push({ node: 'button', type: 'button', value: d.id, disabled: !d.editable, classname: 'unpublish', label: '<%- vocabulary["unpublish"][language] %>', fn: unpublishArticles })
							}
						<% } else if (object === 'templates') { %>
							if (d.editable) {
								if (d.status === 1) opts.push({ action: '/publish/<%- object %>', method: 'GET', node: 'button', type: 'submit', value: d.id, disabled: !d.editable, classname: 'publish', label: '<%- vocabulary["publish"][language] %>', inputs: [{ name: 'status', value: 2 }] })

								<% if (space !== 'reviews') { %>
									// if (d.status > 1 && d.retractable) opts.push({ action: '/publish/<%- object %>', method: 'GET', node: 'button', type: 'submit', value: d.id, disabled: !d.editable, classname: 'unpublish', label: '<%- vocabulary["unpublish"][language] %>', inputs: [{ name: 'status', value: 1 }] })

									if (d.status > 1 && d.retractable) opts.push({ node: 'button', type: 'button', value: d.id, disabled: !d.editable, classname: 'unpublish', label: '<%- vocabulary["unpublish"][language] %>', fn: unpublishArticles })
								<% } %>
							}
						<% } else if (object === 'reviews') { %>
							<% if (space === 'pending') { %>
								if (!d.is_review) {
									if (d.reviewers < <%- modules.find(d => d.type === 'reviews')?.reviewers %>) {
										opts.push({
											action: '/accept/review',
											method: 'GET',
											node: 'button',
											type: 'submit',
											classname: 'accept',
											label: '<%- vocabulary["accept"][language] %>',
											value: d.id,
											inputs: [{ name: 'template', value: d.review_template }]
										})
										if (!d.required) {
											opts.push({
												action: '/decline/review',
												method: 'GET',
												node: 'button',
												type: 'submit',
												classname: 'decline',
												label: '<%- vocabulary["decline"][language] %>',
												value: d.id
											})
										}
									}
								} else {
									if (d.editable) {
										if (d.status === 1) opts.push({ action: '/publish/<%- object %>', method: 'GET', node: 'button', type: 'submit', value: d.id, disabled: !(d.editable && [1, 2].includes(d.status) && d.is_review), classname: 'publish', label: '<%- vocabulary["publish"][language] %>', inputs: [{ name: 'status', value: 2 }, { name: 'source', value: d.source }] })
									}
								}
							<% } %>
						<% } else if (object === 'mobilizations') { %>
							if (d.status === 1) {
								opts.push({ node: 'button', type: 'button', classname: 'copy', label: '<%- vocabulary["copy link"][language] %>', value: d.id, inputs: [{ name: 'template', value: d.template }, { name: 'language', value: d.language }], fn: copyLink })
							}
							opts.push({ node: 'button', type: 'button', disabled: d.pads === 0, classname: 'download', label: '<%- vocabulary["download"][language] %>', name: 'mobilizations', value: d.id, fn: setDownloadOptions }) // TO DO: CHECK IF THIS WORKS
							if (d.status === 1) opts.push({ action: '/unpublish/<%- object %>', method: 'GET', node: 'button', type: 'submit', disabled: !(d.editable && d.status === 1), classname: 'demobilize', label: '<%- vocabulary["demobilize"][language] %>', value: d.id })
							if (d.status === 2) opts.push({ action: `../../mobilize/cohort`, method: 'GET', node: 'button', type: 'submit', disabled: !(d.editable && d.status === 2 && d.pads !== 0 && !d.target_id), classname: 'followup', label: '<%- vocabulary["follow up"][language](true) %>', value: d.id })
							if (d.status === 2) opts.push({ action: `../../mobilize/cohort`, method: 'GET', node: 'button', type: 'submit', disabled: !(d.editable && d.status === 2), classname: 'copy', label: '<%- vocabulary["copy"][language](true) %>', value: d.id, inputs: [{ name: 'copy', value: true }] })
						<% } else if (object === 'contributors') { %>
							<% if (space === 'invited') { %>
								opts.push({ node: 'button', type: 'button', classname: 'revoke', label: '<%- vocabulary["revoke"][language] %>', name: 'contributor', value: d.id, disabled: ![null, undefined].includes(d.end_date), fn: revoke })
							<% } %>
						<% } %>

						return opts
					}).attrs({
						'action': d => d.action,
						'method': d => d.method,
						'target': d => d.target || null
					}).each(function (d) {
						const sel = d3.select(this)
						sel.addElems('input', 'get-value', d.inputs || [])
							.attrs({
								'type': 'hidden',
								'name': c => c.name,
								'value': c => c.value
							})

						sel.addElems(d.node, 'btn-overlay', [d])
							.classed(d.classname.toLowerCase(), true)
							.attrs({
								'type': c => c.type,
								'name': c => c.name || 'id',
								'value': c => c.value,
								'disabled': c => c.disabled || null
							}).html(c => c.label)
						.on('click', function (c) {
							c.fn?.call(this) || null
							this.focus()
						}).on('focus', _ => {
							const dropdown = sel.select('.dropdown')
							if (dropdown.node()) {
								if (dropdown.node().style.maxHeight) dropdown.node().style.maxHeight = null
								else dropdown.node().style.maxHeight = `${Math.min(dropdown.node().scrollHeight, 300)}px`
								dropdown.selectAll('button').on('mousedown', _ => d3.event.preventDefault())
							}
						}).on('blur', _ => {
							const dropdown = sel.select('.dropdown')
							if (dropdown.node()) dropdown.node().style.maxHeight = null
						})

						if (d.dropdown?.length) {
							const dropdown = sel.addElems('div', 'dropdown')
								.addElems('menu', 'opts')
							.addElems('li', 'opt', d.dropdown)
							.addElems('button')
								.classed('highlight', c => c.highlight || false)
								.attrs({
									'type': c => c.fn ? 'button' : 'submit',
									'name': c => c.name,
									'value': c => c.value,
									'disabled': c => (c.disabled && !c.highlight) || null
								}).html(c => c.label)
							.on('click', function (c) { c.fn?.call(this, d) || null })
						}
					})

					async function selectReviewLanguage (datum) {
						const { inputs } = datum
						const { name, value } = d3.select(this).datum()

						// const target_opts = <%- JSON.stringify(languages.filter(d => locals.metadata.menu.review_templates?.some(c => c.language === d.language))?.map(d => { return { label: d.name, value: d.language, type: 'radio', required: true } })) %>
						const target_opts = <%- JSON.stringify(locals.metadata.menu.review_templates.map(d => { return { label: d.name, value: d.language, count: d.count, disabled: { value: d.disabled, label: vocabulary["missing reviewers"][language] }, type: 'radio', required: true } })) %>

						// TO DO: FILTER THIS BASED ON USER RIGHTS > review.rights.write
						const reviewers = await POST('/<%- language %>/browse/contributors/all', { limit: null })
						const reviewer_opts = reviewers.data.map(d => {
							d.secondary_languages.push(d.language)
							return { label: d.name, value: d.id, type: 'checkbox', classname: d.secondary_languages.join(' ') }
						}) // TO DO: IMPROVE THIS
						const formdata = { action: '/request/review',  method: 'POST' }
						const message = '<%- vocabulary["select review language"][language] %>'

						const opts = []
						opts.push({ node: 'select', name: 'language', label: '<%- vocabulary["select language"][language](false) %>', options: target_opts, fn: updateReviewerList })
						opts.push({ node: 'select', name: 'reviewers', label: 'Select specific reviewers', options: reviewer_opts, classname: 'reviewer-list' }) // TO DO: TRANSLATE

						inputs.forEach(d => {
							opts.push({ node: 'input', type: 'hidden', name: d.name, value: d.value })
						})

						opts.push({ node: 'button', type: 'submit', name: name, value: value, label: '<%- vocabulary["submit for review"][language] %>' })
						const new_constraint = await renderFormModal({ message, formdata, opts })
					}

					function updateReviewerList (d) {
						const { value: language } = d
						const menu = d3.select(this).findAncestor('opts')
						const reviewer_list = menu.select('.reviewer-list .dropdown menu li')
						reviewer_list
						// .classed('hide', function () {
						// 	return !d3.select(this).classed(language)
						// }) // THIS CONFLICTS WITH THE SEARCH OPERATIONS (BECAUSE BOTH USE THE class='hide')
						.select('input')
						.each(function () {
							this.disabled = !d3.select(this.parentNode).classed(language)
						})


					}

					function copyLink (datum) {
						// INSPIRED BY: https://www.w3schools.com/howto/howto_js_copy_clipboard.asp
						const sel = d3.select(this)
						const { value, inputs } = sel.datum()
						const template = inputs.find(d => d.name === 'template').value
						const language = inputs.find(d => d.name === 'language').value

						const url = new URL(window.location)
						const queryparams = new URLSearchParams()
						queryparams.set('mobilization', value)
						queryparams.set('template', template)

						const link = `${url.origin}/${language}/contribute/pad?${queryparams.toString()}`
						navigator.clipboard.writeText(link)

						sel.classed('active', true)
						.html('<%- vocabulary["copied"][language] %>')
						setTimeout(_ => {
							sel.classed('active', false)
							.html('<%- vocabulary["copy link"][language] %>')
						}, 1000)
					}
					async function revoke () {
						const sel = d3.select(this)
						const { value } = sel.datum()

						// SET END DATE
						const formdata = { action: '/delete/<%- object %>',  method: 'GET' }
						const message = '<%- vocabulary["set user end date"][language] %>'
						const opts = []

						const today = new Date()
						const dd = String(today.getDate()).padStart(2, '0')
						const mm = String(today.getMonth() + 1).padStart(2, '0')
						const yyyy = today.getFullYear()

						// opts.push({ node: 'input', type: 'date', name: 'invite', value: `${yyyy}-${mm}-${dd}` })
						opts.push({ node: 'input', type: 'date', name: 'date', value: `${yyyy}-${mm}-${dd}` })
						// opts.push({ node: 'input', type: 'email', name: 'email' })

						// SET WHETHER TO EXCLUDE OR TO REMOVE RIGHTS
						// opts.push({ node: 'input', type: 'radio', name: 'type', value: 'revoke', placeholder: 'Remove user rights', checked: true, default: true }) // TO DO: TRANSLATE
						// opts.push({ node: 'input', type: 'radio', name: 'type', value: 'delete', placeholder: 'Exclude from platform and suite', checked: false, default: true }) // TO DO: TRANSLATE

						opts.push({ node: 'input', type: 'hidden', name: 'id', value: value })

						opts.push({ node: 'button', type: 'submit', label: '<%- vocabulary["revoke"][language] %>' })
						const new_constraint = await renderFormModal({ message, formdata, opts })
					}
				<% } %>
			}.bind(this),
			img: function (_sel) {
				_sel.addElems('div', 'media media-img', d => d.img?.length ? [d] : [])
					.addElems('a', 'pad-link')
					.attrs({
						'href': d => {
							const queryparams = new URLSearchParams()
							if (d.id) queryparams.set('id', d.id)
							if (d.source) queryparams.set('source', d.source)
							if (d.is_followup || d.is_review) queryparams.set('display', 'adjacent-source')
							if ((d.is_review) && d.review_template) queryparams.set('template', d.review_template)

							<% if (object === 'reviews') { %>
								if (!d.is_review && d.reviews?.length > 0) {
									queryparams.set('display', 'adjacent-reviews')
									return `../../view/pad?${queryparams.toString()}`
								}
							<% } %>
							// if (d.editable) return `../../edit/<%- object.slice(0, -1) %>?${queryparams.toString()}`
							// else return `../../view/<%- object.slice(0, -1) %>?${queryparams.toString()}`

							return d.link
						}, 'target': '_blank'
					})
				.addElems('img', 'vignette')
					.attrs({ 'loading': 'lazy', 'alt': _ => '<%- vocabulary["missing image"][language] %>' })
				.each(function (d) {
					const node = this
					const img = new Image()
					img.onload = function () { node.src = this.src }
					if (display === 'slideshow') img.src = `<%- sourceurl %>/${d.img[0].replace('uploads/sm/', 'uploads/')}`
					else img.src = `<%- sourceurl %>/${d.img[0]}`


					// BELOW IS IN CASE WE MAKE A CALL FOR THE IMAGES VIA API
					// POST('/call/api', { uri: encodeURI(`https://www.sdg-innovation-commons.org/api/file?filepath=${d}`), method: 'GET', key: 'SM_PLATFORM_TOKEN', expect: 'image' }, false)
					// .then(res => res.blob())
					// .then(data => {
					// 	// const node = this
					// 	// const imgdata = URL.createObjectURL(data)

					// 	// const img = new Image()
					// 	// img.onload = function () {
					// 	// 	node.src = this.src
					// 	// }
					// 	// img.onerror = function (err) {
					// 	// 	if (err) console.log(err)
					// 	// }
					// 	// img.src = imgdata
					// }).catch(err => console.log(err))
				})
			}.bind(this),
			stats: function (_sel) {
				const stats = _sel.addElems('div', 'meta meta-group', d => {
					<% if (['pads', 'files'].includes(object)) { %>return []
					<% } else { %>return !d.img?.length ? [d] : []<% } %>
				})
				.addElems('div', 'meta meta-stats', d => {
					const data = []
					if (d.items !== undefined) {
						const obj = {}
						obj.type = 'item'
						// obj.label = vocabulary['item']['<%- language %>'](d.items !== 1)
						obj.label = Function(`"use strict";return (<%- vocabulary["item"][language] %>)`)()(d.items !== 1)
						obj.count = d.items
						data.push(obj)
					}
					if (![undefined, null].includes(d.contributors) && ![undefined, null].includes(d.participants)) {
						const obj = {}
						obj.type = 'contributor'
						// obj.label = vocabulary['contributor']['<%- language %>'](d.count !== 1)
						obj.label = Function(`"use strict";return (<%- vocabulary["contributor"][language] %>)`)()(d.participants !== 1)
						obj.count = d.contributors
						obj.text = `${d.contributors} <small class='total'>/ ${d.participants}</small>`
						data.push(obj)
					}
					if (d.private_associated_pads !== undefined) {
						const obj = {}
						obj.type = 'pad'
						// obj.label = `${vocabulary['status']['<%- language %>'](0, d.count !== 1)} ${vocabulary['pad']['<%- language %>'](d.count !== 1).toLowerCase()}`
						obj.label = `${Function(`"use strict";return (<%- vocabulary["object status"][language] %>)`)()('pad', 0, d.private_associated_pads !== 1)} ${Function(`"use strict";return (<%- vocabulary["pad"][language] %>)`)()(d.private_associated_pads !== 1).toLowerCase()}`
						obj.count = d.private_associated_pads
						obj.href = `../../browse/pads/curated?<%- object %>=${d.id}&status=0&status=1`
						data.push(obj)
					}
					if (d.associated_pads !== undefined) {
						const obj = {}
						obj.type = 'pad'
						// obj.label = `${vocabulary['status']['<%- language %>'](2, d.count !== 1)} ${vocabulary['pad']['<%- language %>'](d.count !== 1).toLowerCase()}`
						obj.label = `${Function(`"use strict";return (<%- vocabulary["object status"][language] %>)`)()('pad', 2, d.associated_pads !== 1)} ${Function(`"use strict";return (<%- vocabulary["pad"][language] %>)`)()(d.associated_pads !== 1).toLowerCase()}`
						obj.count = d.associated_pads
						obj.href = `../../browse/pads/shared?<%- object %>=${d.id}`
						data.push(obj)
					}
					if (d.ongoing_associated_mobilizations !== undefined) {
						const obj = {}
						obj.type = 'mobilization'
						// obj.label = vocabulary['mobilization']['<%- language %>'](d.count !== 1).toLowerCase()
						obj.label = Function(`"use strict";return (<%- vocabulary["mobilization"][language] %>)`)()(d.count !== 1).toLowerCase()
						obj.count = d.ongoing_associated_mobilizations
						obj.href = `../../browse/mobilizations/ongoing?<%- object %>=${d.id}`
						data.push(obj)
					}
					if (d.past_associated_mobilizations !== undefined) {
						const obj = {}
						obj.type = 'mobilization'
						// obj.label = vocabulary['mobilization']['<%- language %>'](d.count !== 1).toLowerCase()
						obj.label = Function(`"use strict";return (<%- vocabulary["mobilization"][language] %>)`)()(d.count !== 1).toLowerCase()
						obj.count = d.past_associated_mobilizations
						obj.href = `../../browse/mobilizations/ongoing?<%- object %>=${d.id}`
						data.push(obj)
					}
					return data
				}).addElems('a')
				 	.classed('disabled', d => !d.href)
				 	.attr('href', d => d.href)
				 .addElems('h1')
					.html(d => d.text || d.count)
				stats.addElems('small', 'light')
					.html(d => d.label)
			}.bind(this),
			title: function (_sel) {
				const title = _sel.addElems('div', 'media media-title')
					.addElems('a', 'pad-link')
					.attrs({
						'href': d => {
							const queryparams = new URLSearchParams()
							if (d.id) queryparams.set('id', d.id)
							if (d.source) queryparams.set('source', d.source)
							if (d.is_followup || d.is_review) queryparams.set('display', 'adjacent-source')
							if (d.is_review && d.review_template) queryparams.set('template', d.review_template)

							<% if (object === 'reviews') { %>
								if (!d.is_review && d.reviews?.length > 0) {
									queryparams.set('display', 'adjacent-reviews')
									return `../../view/pad?${queryparams.toString()}`
								}
							<% } %>
							// if (d.editable) return `../../edit/<%- object.slice(0, -1) %>?${queryparams.toString()}`
							// else return `../../view/<%- object.slice(0, -1) %>?${queryparams.toString()}`

							return d.link
						}, 'target': '_blank'
					}).addElems('h1')
				title.addElems('img')
					.attr('src', d => {
						if (d.is_followup) return '/imgs/icons/i-followup-<%- object.slice(0, -1) %>.svg'
						else if (d.is_forward) return '/imgs/icons/i-forward-<%- object.slice(0, -1) %>.svg'
						else {
							<% if (object === 'reviews') { %>return '/imgs/icons/i-pad.svg'
							<% } else { %>return '/imgs/icons/i-<%- object.slice(0, -1) %>.svg'<% } %>
						}
					})
				title.addElems('span')
					.html(d => {
						if (d.is_review) return `[<%- vocabulary["review"][language](false, false, true) %>] ${d.source_title}`
						else return d.title || d.name || '[<%- vocabulary[`untitled ${object.slice(0, -1)}`]?.[language] %>]'
					})
			}.bind(this),
			txt: function (_sel) {
				_sel.addElems('div', 'media media-txt', d => {
					if (d.txt) {
						if (!Array.isArray(d.txt)) d.txt = [d.txt]
						return d.txt.map(c => {
							return {
								id: d.id,
								editable: d.editable,
								source: d.source,
								is_followup: d.is_followup,
								is_review: d.is_review,
								reviews: d.reviews,
								txt: c.split(/\n\n+/)[0]
							}
						})
					} else return []
				}).addElems('a', 'pad-link')
					.attrs({
						'href': d => {
							const queryparams = new URLSearchParams()
							if (d.id) queryparams.set('id', d.id)
							if (d.source) queryparams.set('source', d.source)
							if (d.is_followup || d.is_review) queryparams.set('display', 'adjacent-source')
							if ((d.is_review) && d.review_template) queryparams.set('template', d.review_template)

							<% if (object === 'reviews') { %>
								if (!d.is_review && d.reviews?.length > 0) {
									queryparams.set('display', 'adjacent-reviews')
									return `../../view/pad?${queryparams.toString()}`
								}
							<% } %>

							// if (d.editable) return `../../edit/<%- object.slice(0, -1) %>?${queryparams.toString()}`
							// else return `../../view/<%- object.slice(0, -1) %>?${queryparams.toString()}`

							return d.link
						}, 'target': '_blank'
					}).html(d => {
						if (d.txt.length > 500) return `${d.txt.replace(/\n/g, ' ').replace(/\s+/, ' ').slice(0, 500)}… <span class='read-more'>[<%- vocabulary["read more"][language] %>]</span>`
						else return d.txt.replace(/\n/g, ' ').replace(/\s+/, ' ')
					})
			}.bind(this),
			tags: function (_sel) {
				// TO DO: UPDATE THIS WITH metafields
				const taggroup = _sel.addElems('div', 'meta tag-group')
				const sdgs = taggroup.addElems('div', 'meta meta-sdgs', d => d.sdgs?.length ? [d.sdgs] : [])
				sdgs.addElems('span')
					.html(d => Function(`"use strict";return (<%- vocabulary["sdg"][language] %>)`)()((d?.length || 0) > 1))
				sdgs.addElems('a', 'sdg-link', d => d)
					.attr('href', d => `?sdgs=${d.key || d}`)
					.html(d => d.key || d)
				taggroup.addElems('div', 'meta meta-tags', d => d.tags?.length ? [d.tags] : [])
					.addElems('a', 'tag-link', d => d)
					.attr('href', d => `?${d.type}=${d.id}`)
				.addElems('div', 'tag')
					.addElems('label')
					.html(d => d.name.capitalize())
			}.bind(this),
			metainfo: function (_sel) {
				_sel.addElems('div', 'meta meta-followup', d => d.source && d.is_followup ? [d] : [])
					.attr('title', d => d.source_title)
					.html('<i><%- vocabulary["follow up"][language]() %>:</i> ')
				.addElems('a')
					.attrs({
						'href': d => {
							if (d.editable) return `../../edit/<%- object.slice(0, -1) %>?id=${d.source}`
							else return `../../view/<%- object.slice(0, -1) %>?id=${d.source}`
						}//, 'target': '_blank'
					}).html(d => d.source_title)

				_sel.addElems('div', 'meta meta-forward', d => d.source && d.is_forward ? [d] : [])
					.attr('title', d => d.source_title)
					.html('<i><%- vocabulary["forwarded from"][language] %>:</i> ')
				.addElems('a')
					.attrs({
						'href': d => {
							if (d.editable) return `../../edit/<%- object.slice(0, -1) %>?id=${d.source}`
							else return `../../view/<%- object.slice(0, -1) %>?id=${d.source}`
						}//, 'target': '_blank'
					}).html(d => d.source_title)

				_sel.addElems('div', 'meta meta-copy', d => d.source && d.is_copy ? [d] : [])
					.attr('title', d => d.source_title)
					.html('<i><%- vocabulary["copy"][language](false, true) %>:</i> ')
				.addElems('a')
					.attrs({
						'href': d => {
							if (d.editable) return `../../edit/<%- object.slice(0, -1) %>?id=${d.source}`
							else return `../../view/<%- object.slice(0, -1) %>?id=${d.source}`
						}//, 'target': '_blank'
					}).html(d => d.source_title)

				_sel.addElems('div', 'meta meta-child', d => d.source && d.is_child ? [d] : [])
					.attr('title', d => d.source_title)
					.html('<i><%- vocabulary["expansion"][language] %>:</i> ')
				.addElems('a')
					.attrs({
						'href': d => {
							if (d.editable) return `../../edit/<%- object.slice(0, -1) %>?id=${d.source}`
							else return `../../view/<%- object.slice(0, -1) %>?id=${d.source}`
						}//, 'target': '_blank'
					}).html(d => d.source_title)

				_sel.addElems('div', 'meta meta-mobilization', d => d.mobilization ? [d] : [])
					.attr('title', d => d.mobilization_title)
					.html('<i><%- vocabulary["mobilization"][language](false) %>:</i> ')
				.addElems('a')
					.attr('href', d => `?mobilizations=${d.mobilization}`)
					.html(d => d.mobilization_title?.length > 25 ? `${d.mobilization_title.slice(0, 25)}…` : d.mobilization_title)

				<% if (object === 'mobilizations') { %>
					_sel.addElems('div', 'meta meta-public', d => d.public ? [d] : [])
						.html('<i><%- vocabulary["public mobilization"][language] %></i> ')
				<% } %>

				_sel.addElems('div', 'meta meta-template', d => d.template ? [d] : [])
					.attr('title', d => d.template_title)
					.html('<i><%- vocabulary["template"][language](false, false) %>:</i> ')
				.addElems('a')
					.attrs({
						'href': d => `../../view/template?id=${d.template}`
						//'target': '_blank'
					}).html(d => d.template_title?.length > 25 ? `${d.template_title.slice(0, 25)}…` : d.template_title)

				<% if (object === 'reviews' && space === 'private') { %>
					_sel.addElems('div', 'meta meta-reviewers', d => {
						return !d.reviewer_pooled && ![null, undefined].includes(d.reviewers) ? [d] : []
					}).html(d => {
						return Function(`"use strict";return (<%- vocabulary["reviewers accepted"][language] %>)`)()(d.reviewers, <%- modules.find(d => d.type === 'reviews').reviewers %>)
					})
				<% } %>
			}.bind(this),
			followup: function (_sel) {
				if (this.data.editable && (this.data.followups?.length > 0 || this.data.forwards?.length > 0)) {
					const group = _sel.addElems('div', 'meta forward-group')

					if (this.data.followups?.length) {
						const btn = group.addElems('div', 'create follow-up')
						btn.addElems('button')
							.html('<%- vocabulary["follow up"][language](true) %>')
						.on('click', function () { this.focus() })
						.on('focus', _ => {
							const dropdown = btn.select('.dropdown')
							if (dropdown.node()) {
								if (dropdown.node().style.maxHeight) dropdown.node().style.maxHeight = null
								else dropdown.node().style.maxHeight = `${Math.min(dropdown.node().scrollHeight, 300)}px`
								dropdown.selectAll('button').on('mousedown', _ => d3.event.preventDefault())
							}
						}).on('blur', _ => {
							const dropdown = btn.select('.dropdown')
							if (dropdown.node()) dropdown.node().style.maxHeight = null
						})

						btn.addElems('div', 'dropdown')
							.addElems('menu')
						.addElems('li', 'follow-up-option', d => d.followups)
						.addElems('a')
							.attrs({ 'href': d => `../../contribute/<%- object.slice(0, -1) %>?source=${d.source}&template=${d.template}&mobilization=${d.id}&display=adjacent-source` })
							.addElems('button')
							.html(d => d.title.length > 40 ? `${d.title.slice(0, 40)}…` : d.title)
						.on('click', function (d) {
							const sel = d3.select(this)
							const li = sel.findAncestor('follow-up-option')
							const button = li.findAncestor('follow-up')
							if (d.count + 1 >= d.max) li.remove()
							if (button.selectAll('li').size() === 0) button.remove()
						})
					}

					if (this.data.forwards?.length) {
						const btn = group.addElems('div', 'create forward')
							btn.addElems('button')
								.html('<%- vocabulary["forward"][language] %>')
							.on('click', function () { this.focus() })
							.on('focus', _ => {
								const dropdown = btn.select('.dropdown')
								if (dropdown.node()) {
									if (dropdown.node().style.maxHeight) dropdown.node().style.maxHeight = null
									else dropdown.node().style.maxHeight = `${Math.min(dropdown.node().scrollHeight, 300)}px`
									dropdown.selectAll('button').on('mousedown', _ => d3.event.preventDefault())
								}
							}).on('blur', _ => {
								const dropdown = btn.select('.dropdown')
								if (dropdown.node()) dropdown.node().style.maxHeight = null
							})
							btn.addElems('div', 'dropdown')
								.addElems('menu')
							.addElems('li', 'forward-option', d => d.forwards)
							.addElems('a')
								.attrs({ 'href': d => `/forward/<%- object.slice(0, -1) %>?id=${d.source}&mobilization=${d.id}` }) // TO DO: CHECK THIS WORKS
								.addElems('button')
								.html(d => d.title)
							.on('click', function (d) {
								const sel = d3.select(this)
								const li = sel.findAncestor('forward-option')
								const button = li.findAncestor('forward')
								if (d.count + 1 >= d.max) li.remove()
								if (button.selectAll('li').size() === 0) button.remove()
							})
					}
				}
			}.bind(this),
			engagement: function (_sel) {
				<% if (!['mobilizations', 'contributors', 'reviews'].includes(object)
					&& !publicpage
					&& locals.metadata.site.engagementtypes?.length)
				{ %>
					const engagementgroup = _sel.addElems('div', 'engagement-group', d => d.status > 1 ? [d] : [])

					<% locals.metadata.site.engagementtypes.forEach(e => { %>

						engagementgroup.addElems('button', 'engagement engagement-<%- e %>')
						.each(function (d) {
							<% if (e === 'like') { %>
								this.disabled = d.disliked
							<% } else if (e === 'dislike') { %>
								this.disabled = d.liked
							<% } %>
							const sel = d3.select(this)
							sel.classed('active', d['<%- e.charAt(e.length - 1) !== "e" ? `${e}ed` : `${e}d` %>'])

							sel.addElems('img')
							.attr('src', d => {
								if (!sel.classed('active')) return `/imgs/icons/i-<%- e %>-outline${mediaSize === 'xs' ? '-sm' : ''}.svg`
								else return `/imgs/icons/i-<%- e %>${mediaSize === 'xs' ? '-sm' : ''}.svg`
							})
							sel.addElems('span', 'count')
							.html(d => d['<%- `${e}s` %>'])

						}).on('click', async function (d) {
							const sel = d3.select(this)

							const res = await POST('/engage', { object: '<%- object.slice(0, -1) %>', id: d.id, type: '<%- e %>', action: !sel.classed('active') ? 'insert' : 'delete', source: '<%- source %>' })

							if (res.status === 200) {
								sel.toggleClass('active')
								sel.select('img').attr('src', c => {
									if (!sel.classed('active')) return `/imgs/icons/i-<%- e %>-outline${mediaSize === 'xs' ? '-sm' : ''}.svg`
									else return `/imgs/icons/i-<%- e %>${mediaSize === 'xs' ? '-sm' : ''}.svg`
								})
								let count = +sel.select('span').node().innerText
								if (res.active === true) count ++
								else count --
								sel.select('span').html(count)

								<% if (e === 'like') { %>
									engagementgroup.select('button.engagement-dislike').attr('disabled', res.active)
								<% } else if (e === 'dislike') { %>
									engagementgroup.select('button.engagement-like').attr('disabled', res.active)
								<% } %>
							}
						})
					<% }) %>
				<% } %>
			}.bind(this),
			pin: function (_sel) {
				<% if ((object === 'pads'
					&& modules.some(d => d.type === 'pinboards' && rights >= d.rights.write)
					) || (
					object === 'contributors'
					&& modules.some(d => d.type === 'teams' && rights >= d.rights.write)
					)
				) { %>
					const pinboards = <%- JSON.stringify(locals.pinboards_list || []) %>
					const source = '<%- source %>'

					function renderPins (_group, _data) {
						const pin = _group.addElems('div', 'pin tag', d => {
							return _data.map(c => Object.assign(Object.assign({}, c), { object_id: d.id }))
						})
						pin.addElems('label', 'name')
							.attr('title', d => d.title)
							.html(d => {
								if (d.title.length > 25) return `${d.title.slice(0, 25)}…`
								else return d.title
							})
						pin.addElems('label', 'close')
						.on('click', async function (d) {
							const res = await POST('/pin', { board_id: d.id, object_id: { id: d.object_id, source }, action: 'delete', object: '<%- object.slice(0, -1) %>' })
							if (res.status === 200) {
								<% if (space === 'pinned') { %>
									location.reload()
								<% } else { %>
									pins.call(renderPins, res.pins)
									d3.selectAll('.pinboard-group .dropdown')
										.selectAll('.pinboard')
									.filter(c => c.id === d.id)
									.each(function (c) {
										// c.count -= 1
										const sel = d3.select(this)
										if (c.count === 0) { // IF NOT ASSOCIATED TO A MOBILIZATION
											sel.remove()
											// ADD THE NEW PINBOARD TO LIST IN MAIN NAV AT THE TOP
											renderPinNavigation(res.pinboards_list)
										} else {
											if (d.object_id === c.object_id) {
												d.checked = null
												sel.select('input').attr('checked', d.checked)
												sel.select('input').node().checked = false
											}
											// sel.select('.count').html(c.count)
										}
									})

									// DECREMENT THE NUMBER OF PINNED PADS FOR THE SELECTED BOARD
									pinboards.find(b => b.id === d.id).count -= 1
									// RESET THE MENU
									d3.select(this).findAncestor('pinboard-group')
									.each(function (b) {
										b.pinboards = b.pinboards.filter(a => a.id !== d.id)
									})
								<% } %>
							}
						})
					}
					function renderPinOptions (_input, _data) {
						const dropdown = _input.addElems('div', 'dropdown')
							.addElems('menu')
						const opts = dropdown.addElems('li', 'pinboard', _data)
						opts.addElems('input')
							.attrs({
								'id': d => `board-${d.id}-object-${d.object_id}`,
								'type': 'checkbox',
								'checked': d => d.checked
							})
						.on('change', async function (d) {
							const sel = d3.select(this)
							const pins = sel.findAncestor('pinboard-group').select('.pins')

							if (this.checked) {
								const res = await POST('/pin', { board_id: d.id, object_id: { id: d.object_id, source }, action: 'insert', object: '<%- object.slice(0, -1) %>' })
								if (res.status === 200) {
									pins.call(renderPins, res.pins)

									pinboards.find(b => b.id === d.id).count += 1
									d3.selectAll('.pinboard-group .dropdown')
										.selectAll('.pinboard .count')
										.filter(c => c.id === d.id)
									.html(c => c.count += 1)

									sel.findAncestor('pinboard-group')
									.each(function (c) {
										c.pinboards.push({ id: d.id, title: d.title })
									})
								}
							} else {
								const res = await POST('/pin', { board_id: d.id, object_id: { id: d.object_id, source }, action: 'delete', object: '<%- object.slice(0, -1) %>' })
								if (res.status === 200) {
									pins.call(renderPins, res.pins)
									d3.selectAll('.pinboard-group .dropdown')
										.selectAll('.pinboard').filter(c => c.id === d.id)
									.each(function (c) {
										c.count -= 1
										pinboards.find(b => b.id === d.id).count -= 1

										const sel = d3.select(this)
										if (c.count === 0) {
											sel.remove()
											// ADD THE NEW PINBOARD TO LIST IN MAIN NAV AT THE TOP
											renderPinNavigation(res.pinboards_list)
										} else {
											if (d.object_id === c.object_id) {
												d.checked = null
												sel.select('input').attr('checked', d.checked)
												sel.select('input').node().checked = false
											}
											sel.select('.count').html(c.count)
										}
									})
								}
							}
						})
						opts.addElems('label', 'title')
							.attr('for', d => `board-${d.id}-object-${d.object_id}`)
							.html(d => d.title)
						.addElems('span', 'count')
							.html(d => d.count)
					}
					function renderPinNavigation (_data) {
						d3.select('#pinboards-list menu')
							.addElems('li', null, _data)
						.addElems('a')
							.attr('href', d => `./pinned?pinboard=${d.id}`)
							.html(d => d.title)
					}

					const pingroup = _sel.addElems('div', 'pinboard-group')
					const pins = pingroup.addElems('div', 'pins')
					.each(function (d) { d3.select(this).call(renderPins, d.pinboards) })

					const newpin = pingroup.addElems('div', 'add filter')
					newpin.addElems('input')
						.attrs({ 'type': 'text', 'id': 'new-pinboard' })
					.on('keyup', function (d) {
						const evt = d3.event
						const node = this
						const dropdown = d3.select(node).findAncestor('filter').select('.dropdown')
						dropdown.selectAll('menu li')
							.classed('hide', function () {
								return !this.textContent.trim().toLowerCase()
								.includes(node.value.trim().toLowerCase())
							})

						if (evt.code === 'Enter' || evt.keyCode === 13) {
							evt.preventDefault()
							newpin.select('button').node().click()
						}
					}).on('focus', function (d) {
						const data = pinboards.map(c => Object.assign(Object.assign({}, c), { object_id: d.id, checked: d.pinboards.map(b => b.id).includes(c.id) ? true : null }))
						// RENDER THE DROPDOWN
						newpin.call(renderPinOptions, data)

						const dropdown = d3.select(this).findAncestor('filter').select('.dropdown')
						dropdown.node().style.maxHeight = `${Math.min(dropdown.node().scrollHeight, 300)}px`

						dropdown.selectAll('li').on('mousedown', function () {
							d3.event.preventDefault()
						})
					}).on('blur', function () {
						const dropdown = d3.select(this).findAncestor('filter').select('.dropdown')
						dropdown.node().style.maxHeight = null
						fixLabel(this)
					})

					newpin.addElems('label')
						.attr('for', 'new-pinboard')
						.html('<%- vocabulary["add to collection"][language] %>')
					newpin.addElems('button')
						.attr('type', 'button')
						.on('click', async function (d) {
							const node = newpin.select('input[type=text]').node()

							if (node.value.trim().length) {
								const dropdown = d3.select(node).findAncestor('filter').select('.dropdown')

								const existingBoard = dropdown.selectAll('menu li:not(.hide) .title').filter(function () {
									return this.textContent.trim().toLowerCase() === node.value.trim().toLowerCase()
								})

								if (existingBoard.node()) { // SIMPLY ADD THE OBJECT TO AN EXISTING BOARD
									const res = await POST('/pin', { board_id: existingBoard.datum().id, object_id: { id: d.id, source }, action: 'insert', object: '<%- object.slice(0, -1) %>' })
									if (res.status === 200) pins.call(renderPins, res.pins)
									d3.selectAll('.pinboard-group .dropdown').selectAll('.pinboard .count')
										.filter(c => c.id === d.id)
										.html(c => c.count += 1)
								} else { // CREATE A NEW BOARD AND ADD THE OBJECT TO IT
									const res = await POST('/pin', { board_title: node.value.trim(), object_id: { id: d.id, source }, action: 'insert', object: '<%- object.slice(0, -1) %>' })
									if (res.status === 200) {
										// d.pinboards = res.pins
										// // ADD THE NEW PIN TO THE OBJECT
										// pins.call(renderPins, res.pins)
										// // ADD THE NEW PINBOARD TO ALL LISTS
										// d3.selectAll('.pinboard-group').selectAll('.add.filter')
										// .each(function (c) {
										// 	const data = res.pinboards_list.map(b => Object.assign(Object.assign({}, b), { object_id: c.id, checked: c.pinboards.map(a => a.id).includes(b.id) ? true : null }))
										// 	d3.select(this).call(renderPinOptions, data)
										// })
										// // ADD THE NEW PINBOARD TO LIST IN MAIN NAV AT THE TOP
										// renderPinNavigation(res.pinboards_list)

										// RELOAD THE PAGE
										location.reload()
									}

								}
								// RESET DROPDOWN
								this.value = ''
								dropdown.selectAll('menu li').classed('hide', false)
							}
						})
				<% } %>
			}.bind(this),
			unpublish: function (_sel) {
				const form = _sel.addElems('form', 'unpublish hide')
					.attrs({
						'method': 'GET',
						'action': '/publish/<%- object %>'
					})
				form.addElems('input', 'pad-id')
					.attrs({
						'type': 'hidden',
						'name': 'id',
						'value': d => d.id
					})
				form.addElems('input', 'pad-status')
					.attrs({
						'type': 'hidden',
						'name': 'status',
						'value': 1
					})

				form.addElems('button', 'opt', d => {
					const opts = []
					opts.push({ type: 'button', class: '<%- vocabulary["confirm"]["en"] %>', label: '<%- vocabulary["confirm"][language] %>', fn: confirmRemoval })
					opts.push({ type: 'button', class: '<%- vocabulary["cancel"]["en"] %>', label: '<%- vocabulary["cancel"][language] %>', fn: unpublishArticles })
					return opts
				}).attrs({
					'class': d => d.class.toLowerCase(),
					'type': d => d.type,
					'value': d => d.value
				}).html(d => d.label)
					.on('click', function (d) { d.fn ? d.fn.call(this, 'unpublish') : null })
			}.bind(this),
			delete: function (_sel) {
				const form = _sel.addElems('form', 'delete hide')
					.attrs({
						'method': 'GET',
						'action': '/delete/<%- object %>'
					})
				form.addElems('input', 'pad-id')
					.attrs({
						'type': 'hidden',
						'name': 'id',
						'value': d => d.id
					})

				form.addElems('button', 'opt', d => {
					const opts = []
					opts.push({ type: 'button', class: '<%- vocabulary["confirm"]["en"] %>', label: '<%- vocabulary["confirm"][language] %>', fn: confirmRemoval })
					opts.push({ type: 'button', class: '<%- vocabulary["cancel"]["en"] %>', label: '<%- vocabulary["cancel"][language] %>', fn: deleteArticles })
					return opts
				}).attrs({
					'class': d => d.class.toLowerCase(),
					'type': d => d.type,
					'value': d => d.value
				}).html(d => d.label)
					.on('click', function (d) { d.fn ? d.fn.call(this, 'delete') : null })
			}.bind(this),
			contributor: function (_sel) { // THIS DOES NOT SEEM TO BE USED
				const name = _sel.addElems('div', 'media media-name')
					.addElems('a', 'pad-link')
					.attrs({
						'href': d => {
							let query = `id=${d.uuid}`
							if (d.editable) return `../../edit/<%- object.slice(0, -1) %>?${query}`
							else return `../../view/<%- object.slice(0, -1) %>?${query}`
						}//, 'target': '_blank'
					}).addElems('h1')
				name.addElems('img')
					.attr('src', d => {
						if (d.is_followup) return '/imgs/icons/i-followup-<%- object.slice(0, -1) %>.svg'
						else if (d.is_forward) return '/imgs/icons/i-forward-<%- object.slice(0, -1) %>.svg'
						else return '/imgs/icons/i-<%- object.slice(0, -1) %>.svg'
					})
				name.addElems('span')
					.html(d => d.name || '[<%- vocabulary[`untitled ${object.slice(0, -1)}`]?.[language] %>]')


				_sel.addElems('div', 'meta meta-email')
				.html(d => d.email)

				_sel.addElems('div', 'meta meta-position')
				.html(d => d.position)

				_sel.addElems('div', 'meta meta-country')
				.html(d => d.countryname)
			}.bind(this)
		}
	}

	function renderVignette (_section, _kwargs) {
		const { data, display } = _kwargs
		console.log("data vvvd ", _kwargs)

		const entry = new Entry({
			parent: _section,
			data: data,
			display: display,
			language: '<%- language %>'
		})
		// CREATE ALIAS FOR render
		const render = entry.render

		if (display === 'columns') {
			render.img(entry.head)
			render.actions(entry.head)
			// if (mediaSize !== 'xs') render.stats(entry.head)
			// render.tags(entry.body)
			render.title(entry.body)
			render.owner(entry.body)
			if (data.img?.length === 0) render.txt(entry.body)
			<% if (!publicpage) { %>
				render.metainfo(entry.body)
				render.followup(entry.body)
			<% } %>
			render.tags(entry.body)
			// if (mediaSize === 'xs') render.stats(entry.foot)
			if (mediaSize !== 'xs') render.stats(entry.foot)
			render.engagement(entry.foot)
			render.pin(entry.foot)
			render.delete(entry.outer)
			render.unpublish(entry.outer)
		} else if (display === 'slideshow') {
			render.img(entry.head)
			render.owner(entry.metagroup)
			render.title(entry.body)
			render.txt(entry.body)
			render.tags(entry.body)
			render.stats(entry.foot)
		} else {
			render.owner(entry.metagroup)
			render.actions(entry.metagroup)
			render.title(entry.body)
			render.txt(entry.body)
			<% if (!publicpage) { %>
				render.metainfo(entry.body)
				render.followup(entry.body)
			<% } %>
			render.tags(entry.body)
			render.engagement(entry.body)
			render.img(entry.foot)
			render.stats(entry.foot)
			render.pin(entry.inner)
			render.delete(entry.outer)
			render.unpublish(entry.outer)
		}
	}

	function initSlideshow () {
		const slideshow = d3.select('div.layout.slideshow')
		const slides = slideshow.selectAll('.slide')

		d3.select('div.browse')
		.addElems('button', 'slide-nav', [{ label: '&lsaquo;', class: 'prev' }, { label: '&rsaquo;', class: 'next' }])
		.each(function (d) { d3.select(this).classed(d.class, true) })
			.classed('hide', d => {
				const sel = d3.select(this)
				let focus_id = 0
				d3.selectAll('.slide').each(function (c, i) {
					if (d3.select(this).classed('slide-in-view')) focus_id = i
				})
				if (d.class === 'prev' && focus_id === 0) {
					<% if (pages && locals.metadata.page.id === 1) { %>
						return true
					<% } else { %>
						return false
					<% } %>
				} else if (d.class === 'next' && focus_id === slides.size() - 1) {
					<% if (pages && locals.metadata.page.id === pages) { %>
						return true
					<% } else { %>
						return false
					<% } %>
				} else return false
			}).html(d => d.label)
		.on('click', d => {
			if (d.class === 'prev') switchslide(idx - 1)
			else if (d.class === 'next') switchslide(idx + 1)
		}).on('mouseup', function () {
			d3.event.stopPropagation()
			// LOSE FOCUS OF THIS BUTTON TO RE-ENABLE KEYBOARD NAVIGATION
			this.blur()
		})
		// ADD DOTS
		d3.select('footer .dots').addElems('div', 'dot', new Array(slides.size()).fill(0))
		.classed('highlight', (d, i) => i === 0)
		.on('click', (d, i) => { switchslide(i) })

		let idx = 0
		const slidewidth = slides.node().clientWidth || slides.node().offsetWidth || slides.node().scrollWidth

		function switchslide (i) {
			slideshow.node().scrollTo({
				top: 0,
				left: i * slidewidth,
				behavior: 'smooth'
			})

			if (i > slides.size() - 1) {
				<% if (pages && locals.metadata.page.id < pages) { %>
					const url = new URL(window.location)
					const queryparams = new URLSearchParams(url.search)
					queryparams.set('page', page + 1)
					window.location = `${url.pathname}?${queryparams.toString()}`
				<% } %>
			} else if (i < 0) {
				<% if (pages && locals.metadata.page.id > 1) { %>
					// WE KEEP THE pages AT THE BEGINNING TO MAKE SURE THE PAGINATION SCHEMA IS NUMERIC (FOR EXAMPLE, IN THE CASE OF CONTRIBUTORS, IT IS ALPHABETIC)
					const url = new URL(window.location)
					const queryparams = new URLSearchParams(url.search)
					queryparams.set('page', page - 1)
					window.location = `${url.pathname}?${queryparams.toString()}`
				<% } %>
			}

			return idx = i
		}

		slideshow.on('scroll', function () {
			if (this.scrollLeft % slidewidth === 0) {
				idx = Math.round(this.scrollLeft / slidewidth)
				d3.selectAll('.dot').classed('highlight', (d, i) => i === idx)
				d3.selectAll('button.slide-nav').classed('hide', d => {
					if (d.class === 'prev' && idx === 0) {
						<% if (pages && locals.metadata.page.id === 1) { %>
							return true
						<% } else { %>
							return false
						<% } %>
					} else if (d.class === 'next' && idx === slides.size() - 1) {
						<% if (pages && locals.metadata.page.id === pages) { %>
							return true
						<% } else { %>
							return false
						<% } %>
					} else return false
				})
			}
		})
	}


	// THIS IS THE LAZY LOADING MECHANISM
	if (<%- JSON.parse(locals.metadata.page.lazyload) %>) {
		window.onscroll = async function (ev) {
			if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight && !lazyloading) {
				console.log('hit the bottom')
				main.select('.lds-ellipsis').classed('hide', false)

				if (!isNaN(page)) page ++
				lazyloading = true

				const url = new URL(window.location)
				const queryparams = new URLSearchParams(url.search)
				queryparams.set('page', page)

				const response = await GET(`?${queryparams.toString()}`) // NO TARGET NEEDED SINCE SAME AS CURRENT PAGE

				d3.selectAll('section.container div.layout')
				.each(function (d) {
					const section = d3.select(this)
					response.sections.find(s => s.status === d.status).data.forEach(c => section.call(renderVignette, { data: c, display: '<%- pagedisplay %>' }))
				})

				if (response.count < <%- locals.stats?.filtered || 0 %>) lazyloading = false
				else main.select('.lds-ellipsis').classed('hide', true)
			}
		}
	} else { // PAGINATION MECHANISM
		d3.selectAll('.pagination a.page-link:not(.static)').each(function () {
			const sel = d3.select(this)
			const url = new URL(window.location)
			const queryparams = new URLSearchParams(url.search)
			queryparams.set('page', sel.attr('data-page'))
			sel.attr('href', `?${queryparams.toString()}`)
		})
	}

	function deleteArticles () {
		const sel = d3.select(this)
		const article = sel.findAncestor('article')
		article.selectAll('button.delete')
			.toggleClass('active')
		const outer = article.select('div.outer').toggleClass('expand')
		outer.select('form.unpublish').classed('hide', true)
		outer.select('form.delete').toggleClass('hide')
	}
	function unpublishArticles () {
		const sel = d3.select(this)
		const article = sel.findAncestor('article')
		article.selectAll('button.unpublish')
			.toggleClass('active')
		const outer = article.select('div.outer').toggleClass('expand')
		outer.select('form.delete').classed('hide', true)
		outer.select('form.unpublish').toggleClass('hide')
	}
	async function confirmRemoval (action) {
		const sel = d3.select(this)
		const datum = d3.select(this.parentNode).datum()
		const form = this.form
		const flagged = d3.selectAll('article .outer.expand')

		if (flagged.size() === 1) form.submit()
		else {
			let message = ''
			if (action === 'delete') message = '<%- vocabulary["what pads to delete"][language] %>'
			else if (action === 'unpublish') message = '<%- vocabulary["what pads to unpublish"][language] %>'

			const opts = [
				{ node: 'button', type: 'button', label: '<%- vocabulary["all selected"][language] %>', resolve: _ => d3.selectAll('article .outer.expand').data().map(d => d.id) },
				{
					node: 'button',
					type: 'button',
					label: Function(`"use strict";return (<%- vocabulary["only pad"][language] %>)`)()(datum.title),
					resolve: [ datum.id ]
				}
			]
			const removal = await renderPromiseModal({ message, opts })

			d3.select(form)
			.addElems('input', 'pad-id', removal)
				.attrs({
					'type': 'hidden',
					'name': 'id',
					'value': d => d
				})
			form.submit()
		}
	}

	<% if (pagedisplay === 'slideshow') { %>
		window.addEventListener('keydown', function (e) {
			// SET THE LEFT/RIGHT KEYBOARD NAVIGATION IF IN SLIDESHOW DISPLAY
			if (document.activeElement === document.body) { // NOTHING IS IN FOCUS/ BEING EDITED
				if (e.key === 'ArrrowRight' || e.keyCode === 39) {
					d3.select('button.slide-nav.next:not(.hide)').node()?.click()
				}
				if (e.key === 'ArrowLeft' || e.keyCode === 37) {
					d3.select('button.slide-nav.prev:not(.hide)').node()?.click()
				}
			}
		})
	<% } %>

	window.addEventListener('keyup', function (e) {
		e = e || event
		if (e.key === 'Escape' || e.keyCode === 27) {
			d3.selectAll('.modal').remove()

			<% if (pagedisplay === 'slideshow') { %>
				const url = new URL(window.location)
				const queryparams = new URLSearchParams(url.search)
				queryparams.delete('display')
				window.location = `${url.pathname}?${queryparams.toString()}`
			<% } %>
		}
	})

	window.onload = function () {
		<% if (display_content && (!mapscale || mapscale === 'contain')) { %>
			renderSections(<%- JSON.stringify(locals.sections) %>)
		<% } %>
	}

</script>

</body>
</html>
