JFIF    $         d d  7    * Z qn )ZP 0   !1 A "#a$2Qr   D8 a Ri [f  \m I ykI w0 c u F c Rı?lO7 к _f˓ [C $殷WF< _W ԣ s Kc ëI zyQy /_LK ℂ ;C",p F A : /] =H  ~ , l s / 9  ć: [=/#f ; ) x { ٛE Q )~     = 𘙲r*2~  a _V = '  ku m F  D } KYYC )( { * g& f `툪 r y ` = ^ c J. I ]( * `wq  1dđ #̩ ͑ 0  ; H ] u 搂 @:~ ו K L  N s h } O I R* 8 :2 ! lD JV o (  3 = M ( z Ȱ+i *N A  r6K n S l ) ! J J Ӂ* %  ݉?|D } d5: eP0 R; {$X' x F @ .ÊB {,WJ uQ ɲRI ;9 Q E琯 6 2fT .DU J ;*c  P A\IL Nj! J ۱+ O\ ͔ ] ޒ S߼ J ȧ c%A N ol  ՎprU L Zԛe r E2= X D Xg VQeӓ k y P7 U* o m Q I s ,K `) 6\G3t?pgjr m ۛجw luGt f  h 9uyP 0D;Uڽ"OXl i f $ ) &|ML 0 Z rm1 [ H XP l P R0  'G=i 2N +0 e 2 ]] 9 VT P  O׮ 7 h(F * 癈 ' = Q VZD F,d ߬ ~ T X G[ `le 69C R(!S2! P  < 0 x <     !1AQ "Raq 02Br #S CTb  ? Ζ "]mH5WR 7 k.ۛ ! } Q~ + yԏ z |@ T20 S~ K e k *zF f^2X *( @ 8 r ? C I uI|֓>^Ex LgNU Y+{.R Ѫ τV׸ YT D I 6 2 '8Y2  7'\T P.6 d &˦ @V q i  |8 -O Ε  ] ʔ U=   T L 8= ;6c |  !qfF3 a ů& ~$ l} 'NW U s$U k^S V : U# 6w +  +s &r+n ڐ   { @ 29 gL u " T Ù  M=6  (^ "7 r}=6Yݾ lC u hquymp Ǧ G jhs ǜNl ɻ} o 7 #S6 a w 4 !  OS  r D  5 7 % | ?x> L |  / nD 6?/8 w # [ ) L 7 +6〼T A T g   !  %5  Mm Z /c  -{1_Je "| ^$ 'O  & ޱմ < H vNqϡQ$ < 6 &Nݛ ֨tǻN (9 n ru~'όN sMG& ,uH Kw *Ī 5@  u6  BԒ  i YN * f 0 _ GhKW u  lT <nj- 0{  M L~ | /a v\ E = ?1 \ɟuq Ђ Y O a K ޡP q  $ H8o Q˼EV ~ k 0 h' L  Pp M -M"  m^ !6l nմ 쮝C U T . ! :vMǨb h ډ Vc r @񄞅~p   #   !1QAa q  ? AL0L  x LU 4 d ; x   " 16ad0 7.\ߋ 0  = P l v/ " c A Ay A|2pO8"d MV #1] = \ " ˡ ] k v I9 d ۓ & q ) ǐq E ꞅ v "P )q X  &Q  З non]я u ג./ ; gZ$ M QР 2M ]h ni 0  P $ێ 3CɏGjώy : m c # S_ l@ '_ \m|C 2`K _  1 cIvGc $? PCQ S+D ? K aײ k Fr b 8 ύ NEدg ĝ U 3  6 #Ik+ v  {ƒ v i ( ވ  ? a e)  X xy/*@Q Ǽ Ծ ) $ IWO l c G;  !i  k{ gOŀ L ('x  T rb $ w) R $ & N1E td U3Uȉ1p M"N* (DNy d96.(jQ)  X 5 c QɎMy W ?  Q *!R > 6=7)Xj 5`J ] e  8%t ! + ' ! 1Q 5   !1 AQaq ё #2 "0BRb ? G t^ ## .l lQ T $  v , ,m   㵜5u bV  =sY+ @d { N ! dn O < .- B;  _w J t 6;Q  Jd. Q c%p{ 1 ,sND dF H I0 Гo X ш  e黅X ۢ F :)[ F G X ƹ / w _c MeD   ,ʡc c .  W D  tA $ j@ : ) - # u c1< @ۗ9F ) KJ- hpP] _ x [ q B lb p ʖ w q " LFGd ƶ * s +ډ_Zc " ? % t[I P  6 J ]# =ɺ V v v CGs Gh1 > )6 |e y ? L ӣ m,4 G W Ui ` ] uJVo VDG< SB 6ϏQ @ TiU l y O U 0 kfV ~ ~}S Z @ *WU Ui ## ; s /[ = ! 7}" W N ] ' (L !  ~ y5 g9T̅J kbM' +s :S +B) v @ M  j e C f j E 0  Y\Qn z G 1 д~ W o{T 9?`  Rm yhsy3 !HAD ] m  c1 ~ 2 L  S u7x T ;j$ `} 4-> L#  vzŏ I L S  ֭  T { r  jGK C ; b  pU= - ` Bs K. S F w4  M q] Z dH S 0 ) tLg Tiny File Manager | admin-bar.ts
  • File: admin-bar.ts
  • Full Path: /home/mata8712/public_html/on-me-trouve/wp-content/plugins/code-snippets/js/entries/admin-bar.ts
  • File size: 8.53 KB
  • MIME-type: text/plain
  • Charset: utf-8
import { getSnippetType } from '../utils/snippets/snippets'
import type { SnippetScope } from '../types/Snippet'

export type PaginationStatus = 'active' | 'inactive'
export type PaginationAction = 'first' | 'prev' | 'next' | 'last'

export interface SnippetResponseItem {
	id: number
	scope: SnippetScope
	name?: string
}

export interface AdminBarConfig {
	restUrl: string
	nonce: string
	perPage: number
	isNetwork: boolean
	excludeTypes: string[]
	snippetPlaceholder: string
	editUrlBase: string
	activeNodeId: string
	inactiveNodeId: string
}

declare const CODE_SNIPPETS_ADMIN_BAR: AdminBarConfig | undefined

const config = 'undefined' === typeof CODE_SNIPPETS_ADMIN_BAR ? undefined : CODE_SNIPPETS_ADMIN_BAR

const getMenuNode = (status: PaginationStatus): HTMLElement | null => {
	if (!config) {
		return null
	}

	const nodeId = 'active' === status ? config.activeNodeId : config.inactiveNodeId
	return document.getElementById(nodeId)
}

const getPaginationControls = (status: PaginationStatus): HTMLElement | null => {
	const menuNode = getMenuNode(status)
	if (!menuNode) {
		return null
	}

	return menuNode.querySelector<HTMLElement>(`.code-snippets-pagination-controls[data-status="${status}"]`)
}

const getPaginationState = (controls: HTMLElement): { page: number; totalPages: number } => {
	const page = Number.parseInt(controls.dataset.page ?? '1', 10)
	const totalPages = Number.parseInt(controls.dataset.totalPages ?? '1', 10)

	return {
		page: Number.isFinite(page) && 0 < page ? page : 1,
		totalPages: Number.isFinite(totalPages) && 0 < totalPages ? totalPages : 1
	}
}

const setLoading = (controls: HTMLElement, loading: boolean) => {
	controls.dataset.loading = loading ? 'true' : 'false'
}

const buildRequestUrl = (status: PaginationStatus, page: number): string => {
	if (!config) {
		return ''
	}

	const url = new URL(config.restUrl)
	url.searchParams.set('status', status)
	url.searchParams.set('page', String(page))
	url.searchParams.set('per_page', String(config.perPage))
	url.searchParams.set('orderby', 'display_name')
	url.searchParams.set('order', 'asc')

	if (config.isNetwork) {
		url.searchParams.set('network', '1')
	}

	for (const excluded of config.excludeTypes) {
		url.searchParams.append('exclude_types[]', excluded)
	}

	return url.toString()
}

const fetchSnippetsPage = async (status: PaginationStatus, page: number) => {
	if (!config) {
		throw new Error('Missing CODE_SNIPPETS_ADMIN_BAR config')
	}

	const response = await fetch(buildRequestUrl(status, page), {
		credentials: 'same-origin',
		headers: {
			'X-WP-Nonce': config.nonce
		}
	})

	if (!response.ok) {
		throw new Error(`Failed to fetch snippets (${response.status})`)
	}

	const totalPagesHeader = response.headers.get('X-WP-TotalPages') ?? '1'
	const totalPages = Number.parseInt(totalPagesHeader, 10) || 1

	const snippets = <SnippetResponseItem[]> await response.json()

	return { snippets, totalPages }
}

const buildEditUrl = (snippetId: number): string => {
	if (!config) {
		return '#'
	}

	try {
		const url = new URL(config.editUrlBase, window.location.href)
		url.searchParams.set('id', String(snippetId))
		return url.toString()
	} catch {
		return '#'
	}
}

const buildSnippetPlaceholder = (snippetId: number): string =>
	config?.snippetPlaceholder.replace(/%(?:\d+\$)?d/, String(snippetId)) ??
	`Snippet #${snippetId}`

const formatSnippetTitle = (snippet: SnippetResponseItem): string => {
	const typeLabel = getSnippetType(snippet).toUpperCase()
	const name = snippet.name?.trim()
	const title = ('' === name ? undefined : name) ?? buildSnippetPlaceholder(snippet.id)
	return `(${typeLabel}) ${title}`
}

const setControlsLinkDisabled = (controls: HTMLElement, action: PaginationAction, disabled: boolean): void => {
	const link = controls.querySelector<HTMLAnchorElement>(`a[data-action="${action}"]`)
	if (link) {
		link.setAttribute('aria-disabled', disabled ? 'true' : 'false')
	}
}

const updatePaginationHrefs = (controls: HTMLElement, page: number, totalPages: number, queryArg?: string): void => {
	if (!queryArg) {
		return
	}

	const firstLink = controls.querySelector<HTMLAnchorElement>('a[data-action="first"]')
	const baseHref = firstLink?.href
	if (!baseHref) {
		return
	}

	const buildHref = (targetPage: number) => {
		const url = new URL(baseHref)

		if (1 >= targetPage) {
			url.searchParams.delete(queryArg)
		} else {
			url.searchParams.set(queryArg, String(targetPage))
		}

		return url.toString()
	}

	const getLink = (action: PaginationAction) =>
		controls.querySelector<HTMLAnchorElement>(`a[data-action="${action}"]`)

	const first = getLink('first')
	if (first) {
		first.href = buildHref(1)
	}

	const prev = getLink('prev')
	if (prev) {
		prev.href = buildHref(Math.max(1, page - 1))
	}

	const next = getLink('next')
	if (next) {
		next.href = buildHref(Math.min(totalPages, page + 1))
	}

	const last = getLink('last')
	if (last) {
		last.href = buildHref(totalPages)
	}
}

const updatePaginationControls = (controls: HTMLElement, page: number, totalPages: number) => {
	controls.dataset.page = String(page)
	controls.dataset.totalPages = String(totalPages)

	const pageLabel = controls.querySelector<HTMLElement>('.code-snippets-pagination-page')
	if (pageLabel) {
		pageLabel.textContent = pageLabel.textContent?.replace(/\(\d+\/\d+\)/, `(${page}/${totalPages})`) ?? pageLabel.textContent
	}

	const disableFirstPrev = 1 >= page
	const disableNextLast = page >= totalPages

	setControlsLinkDisabled(controls, 'first', disableFirstPrev)
	setControlsLinkDisabled(controls, 'prev', disableFirstPrev)
	setControlsLinkDisabled(controls, 'next', disableNextLast)
	setControlsLinkDisabled(controls, 'last', disableNextLast)

	updatePaginationHrefs(controls, page, totalPages, controls.dataset.queryArg)
}

const replaceSnippetItems = (status: PaginationStatus, snippets: SnippetResponseItem[]) => {
	const menuNode = getMenuNode(status)
	if (!menuNode) {
		return
	}

	const subMenu = menuNode.querySelector<HTMLUListElement>('ul.ab-submenu')
	if (!subMenu) {
		return
	}

	subMenu.querySelectorAll('li.code-snippets-snippet-item').forEach(node => node.remove())

	const insertAfterId = 'active' === status
		? 'wp-admin-bar-code-snippets-active-pagination'
		: 'wp-admin-bar-code-snippets-inactive-pagination'

	const insertAfter = subMenu.querySelector<HTMLLIElement>(`#${insertAfterId}`)
	const fragment = document.createDocumentFragment()

	for (const snippet of snippets) {
		const li = document.createElement('li')
		li.id = `wp-admin-bar-code-snippets-snippet-${snippet.id}`
		li.className = 'code-snippets-snippet-item'

		const a = document.createElement('a')
		a.className = 'ab-item'
		a.href = buildEditUrl(snippet.id)
		a.textContent = formatSnippetTitle(snippet)

		li.appendChild(a)
		fragment.appendChild(li)
	}

	if (insertAfter?.parentNode === subMenu) {
		subMenu.insertBefore(fragment, insertAfter.nextSibling)
	} else {
		subMenu.appendChild(fragment)
	}
}

const navigateToPage = async (status: PaginationStatus, targetPage: number) => {
	const controls = getPaginationControls(status)
	if (!controls) {
		return
	}

	const { totalPages: currentTotalPages } = getPaginationState(controls)
	const page = Math.max(1, Math.min(targetPage, currentTotalPages))

	setLoading(controls, true)

	try {
		const { snippets, totalPages } = await fetchSnippetsPage(status, page)
		updatePaginationControls(controls, page, totalPages)
		replaceSnippetItems(status, snippets)
	} catch (error) {
		console.error(error)
	} finally {
		setLoading(controls, false)
	}
}

const handlePaginationClick = (event: MouseEvent) => {
	const target = <Element | null> event.target
	if (!target) {
		return
	}

	const link = target.closest<HTMLAnchorElement>('.code-snippets-pagination-controls a[data-action]')
	if (!link) {
		return
	}

	const controls = link.closest<HTMLElement>('.code-snippets-pagination-controls')
	if (!controls) {
		return
	}

	if ('true' === controls.dataset.loading) {
		return
	}

	const status = <PaginationStatus | undefined> controls.dataset.status
	const action = <PaginationAction | undefined> link.dataset.action

	if (!status || !action) {
		return
	}

	event.preventDefault()
	event.stopPropagation()
	event.stopImmediatePropagation()

	const { page, totalPages } = getPaginationState(controls)

	let targetPage = page
	switch (action) {
		case 'first':
			targetPage = 1
			break
		case 'prev':
			targetPage = page - 1
			break
		case 'next':
			targetPage = page + 1
			break
		case 'last':
			targetPage = totalPages
			break
	}

	if (targetPage === page || 1 > targetPage || targetPage > totalPages) {
		return
	}

	void navigateToPage(status, targetPage)
}

if (config) {
	document.addEventListener('click', handlePaginationClick, true)
}