PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.9
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.9
4.4.9 4.4.8 4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 All 140 releases
← All changes | assets/src/js/admin/addons.js +492 -341 4.3.9 → 4.4.9 View file →
@@ -1,341 +1,492 @@
1 -/**
2 - * Script handle admin notices.
3 - *
4 - * @since 4.1.7.3.2
5 - * @version 1.0.1
6 - */
7 -import API from '../api';
8 -let elAddonsPage;
9 -let dataHtml;
10 -let dataAddons;
11 -let elLPAddons;
12 -const queryString = window.location.search;
13 -const urlParams = new URLSearchParams( queryString );
14 -const tab = urlParams.get( 'tab' );
15 -let elNotifyActionWrapper;
16 -const isHandling = [];
17 -
18 -// API get list addons.
19 -const getAddons = ( set = '' ) => {
20 - const params = tab ? `?tab=${ tab }` : `?${ set }`;
21 - fetch( API.admin.apiAddons + params, {
22 - method: 'GET',
23 - headers: {
24 - 'X-WP-Nonce': lpDataAdmin.nonce,
25 - },
26 - } ).then( ( res ) =>
27 - res.json()
28 - ).then( ( res ) => {
29 - // console.log(data);
30 - const { status, message, data } = res;
31 - if ( status === 'success' ) {
32 - dataHtml = data.html;
33 - dataAddons = data.addons;
34 - } else {
35 - dataHtml = message;
36 - }
37 - } ).catch( ( err ) => {
38 - console.log( err );
39 - } );
40 -};
41 -// API send action install, update, activate, deactivate.
42 -const addonsAction = ( data, callBack ) => {
43 - const addonSlug = data.addon.slug;
44 -
45 - if ( isHandling.indexOf( addonSlug ) !== -1 ) {
46 - return;
47 - }
48 - isHandling.push( addonSlug );
49 -
50 - fetch( API.admin.apiAddonAction, {
51 - method: 'POST',
52 - headers: {
53 - 'Content-Type': 'application/json',
54 - 'X-WP-Nonce': lpDataAdmin.nonce,
55 - },
56 - body: JSON.stringify( { ...data } ),
57 - } ).then( ( res ) =>
58 - res.json()
59 - ).then( ( res ) => {
60 - const indexAddonHanding = isHandling.indexOf( addonSlug );
61 - if ( indexAddonHanding !== -1 ) {
62 - isHandling.splice( indexAddonHanding, 1 );
63 - }
64 -
65 - const { status, message, data } = res;
66 -
67 - if ( callBack ) {
68 - callBack( status, message, data );
69 - }
70 -
71 - handleNotify( status, message );
72 - } ).catch( ( err ) => {
73 - handleNotify( 'error', `error js: ${ err }` );
74 - console.log( err );
75 - } );
76 -};
77 -// Show notify.
78 -const handleNotify = ( status, message ) => {
79 - const elNotifyAction = elNotifyActionWrapper.querySelector( '.lp-notify-action' );
80 - const elNotifyActionNew = elNotifyAction.cloneNode( true );
81 - elNotifyActionNew.classList.remove( 'clone' );
82 - elNotifyActionWrapper.insertBefore( elNotifyActionNew, elNotifyActionWrapper[ 0 ] );
83 - const elSuccess = elNotifyActionNew.querySelector( `.${ elNotifyActionNew.classList.value }__success` );
84 - const elFailed = elNotifyActionNew.querySelector( `.${ elNotifyActionNew.classList.value }__error` );
85 -
86 - if ( status === 'success' ) {
87 - elSuccess.classList.add( 'show' );
88 - elSuccess.querySelector( '.message' ).innerHTML = message;
89 - } else {
90 - elFailed.classList.add( 'show' );
91 - elFailed.querySelector( '.message' ).innerHTML = message;
92 - }
93 -
94 - elNotifyActionWrapper.classList.add( 'show' );
95 - setTimeout( () => {
96 - elNotifyActionNew.remove();
97 -
98 - const elNotifyAction = elNotifyActionWrapper.querySelectorAll( '.lp-notify-action' );
99 - if ( elNotifyAction.length === 1 ) {
100 - elNotifyActionWrapper.classList.remove( 'show' );
101 - }
102 - }, status === 'success' ? 3000 : 4000 );
103 -};
104 -// Get addons when js loaded.
105 -getAddons();
106 -// Search Addons.
107 -const searchAddons = ( name ) => {
108 - const elAddonItems = elAddonsPage.querySelectorAll( '.lp-addon-item' );
109 - let totalItems = 0;
110 -
111 - elAddonItems.forEach( ( elAddonItem ) => {
112 - const addonName = elAddonItem.querySelector( 'a' ).textContent;
113 - if ( elAddonItem.classList.contains( 'hide' ) ) {
114 - return;
115 - }
116 -
117 - if ( addonName.toLowerCase().includes( name.toLowerCase() ) ) {
118 - elAddonItem.classList.remove( 'search-not-found' );
119 - totalItems++;
120 - } else {
121 - elAddonItem.classList.add( 'search-not-found' );
122 - }
123 - } );
124 -
125 - setGridItems( totalItems );
126 -};
127 -// Set grid style items.
128 -const setGridItems = ( totalItems ) => {
129 - if ( totalItems < 4 ) {
130 - elLPAddons.classList.add( 'max-3-items' );
131 - } else {
132 - elLPAddons.classList.remove( 'max-3-items' );
133 - }
134 -};
135 -// Check element loaded and data API returned.
136 -const loadElData = setInterval( () => {
137 - if ( ! elAddonsPage || ! elNotifyActionWrapper ) {
138 - elAddonsPage = document.querySelector( '.lp-addons-page' );
139 - elNotifyActionWrapper = document.querySelector( '.lp-notify-action-wrapper' );
140 - } else if ( dataHtml && elAddonsPage && elNotifyActionWrapper ) {
141 - elAddonsPage.innerHTML = dataHtml;
142 - elLPAddons = elAddonsPage.querySelector( '#lp-addons' );
143 - const elNavTabWrapper = document.querySelector( '.lp-nav-tab-wrapper' );
144 - if ( ! elNavTabWrapper ) {
145 - clearInterval( loadElData );
146 - return;
147 - }
148 -
149 - const elNavTabWrapperClone = elNavTabWrapper.cloneNode( true );
150 - elAddonsPage.insertBefore( elNavTabWrapperClone, elAddonsPage.children[ 0 ] );
151 - elNavTabWrapperClone.style.display = 'flex';
152 - elNavTabWrapper.remove();
153 - const elNavActive = elNavTabWrapperClone.querySelector( '.nav-tab.nav-tab-active span' );
154 - setGridItems( parseInt( elNavActive.textContent ) );
155 -
156 - clearInterval( loadElData );
157 - }
158 -}, 1 );
159 -
160 -document.addEventListener( 'DOMContentLoaded', ( e ) => {
161 -} );
162 -
163 -/*** Events ***/
164 -document.addEventListener( 'click', ( e ) => {
165 - let el = e.target;
166 - const tagName = el.tagName.toLowerCase();
167 - if ( tagName === 'span' ) {
168 - e.preventDefault();
169 - const elBtnAction = el.closest( '.btn-addon-action' );
170 - if ( elBtnAction ) {
171 - elBtnAction.click();
172 - }
173 - }
174 -
175 - // Events actions: activate, deactivate.
176 - /*if ( el.classList.contains( 'lp-toggle-switch-label' ) ) {
177 - //e.preventDefault();
178 -
179 - const elAddonItem = el.closest( '.lp-addon-item' );
180 - const idLabel = el.getAttribute( 'for' );
181 - const elInput = document.querySelector( `#${ idLabel }` );
182 - const action = elInput.getAttribute( 'data-action' );
183 - const addon = dataAddons[ elAddonItem.dataset.slug ];
184 - const addonSlug = addon.slug;
185 - const parent = el.closest( '.lp-toggle-switch' );
186 - const label = parent.querySelector( `label[for=${ idLabel }]` );
187 - const dashicons = parent.querySelector( '.dashicons-update' );
188 - dashicons.style.display = 'inline-block';
189 - label.style.display = 'none';
190 - const data = { action, addon };
191 - addonsAction( data, function( status, message, data ) {
192 - const elAddon = document.querySelector( `#${ addonSlug }` );
193 - if ( elAddon ) {
194 - const parent = elAddon.closest( '.lp-toggle-switch' );
195 - if ( parent ) {
196 - const dashicons = parent.querySelector( '.dashicons-update' );
197 - dashicons.style.display = 'none';
198 - if ( action === 'deactivate' ) {
199 - elAddon.setAttribute( 'data-action', 'activate' );
200 - } else if ( action === 'activate' ) {
201 - elAddon.setAttribute( 'data-action', 'deactivate' );
202 - }
203 - const label = parent.querySelector( `label[for=${ addonSlug }]` );
204 - label.style.display = 'inline-flex';
205 - }
206 - }
207 -
208 - if ( status === 'success' ) {
209 - if ( action === 'deactivate' ) {
210 - elAddonItem.classList.remove( 'activated' );
211 - }
212 - if ( action === 'activate' ) {
213 - elAddonItem.classList.add( 'activated' );
214 - }
215 - }
216 - } );
217 - }*/
218 -
219 - // Events actions: install, update, delete.
220 - if ( el.closest( '.btn-addon-action' ) ) {
221 - el = el.closest( '.btn-addon-action' );
222 - e.preventDefault();
223 - el.classList.add( 'handling' );
224 - let purchaseCode = '';
225 - const elAddonItem = el.closest( '.lp-addon-item' );
226 - const addon = dataAddons[ elAddonItem.dataset.slug ];
227 - const action = el.dataset.action;
228 - const elItemPurchase = elAddonItem.querySelector( '.lp-addon-item__purchase' );
229 - //const elToggleSwitchInput = elAddonItem.querySelector( '.lp-toggle-switch-input' );
230 -
231 - if ( action === 'purchase' ) {
232 - elItemPurchase.style.display = 'block';
233 - elItemPurchase.querySelector( '.purchase-install' ).style.display = 'flex';
234 - return;
235 - } else if ( action === 'update-purchase-code' ) {
236 - elItemPurchase.querySelector( '.purchase-update' ).style.display = 'flex';
237 - elItemPurchase.style.display = 'block';
238 - return;
239 - } else if ( action === 'buy' ) {
240 - const link = el.dataset.link;
241 - window.open( link, '_blank' );
242 - return;
243 - } else if ( action === 'cancel' ) {
244 - elItemPurchase.style.display = 'none';
245 - return;
246 - } else if ( action === 'install' ) {
247 - if ( el.dataset.link ) {
248 - el.classList.remove( 'handling' );
249 - const link = el.dataset.link;
250 - window.open( link, '_blank' );
251 - return;
252 - }
253 - }
254 -
255 - // Send request to server.
256 - if ( elItemPurchase ) {
257 - purchaseCode = elItemPurchase.querySelector( 'input[name=purchase-code]' ).value;
258 - }
259 -
260 - const data = { purchase_code: purchaseCode, action, addon };
261 - addonsAction( data, function( status, message, data ) {
262 - if ( status === 'success' ) {
263 - if ( action === 'install' ) {
264 - elAddonItem.classList.add( 'installed', 'activated' );
265 - elAddonItem.classList.remove( 'not_installed' );
266 - elItemPurchase.style.display = 'none';
267 - /*elToggleSwitchInput.setAttribute( 'checked', 'checked' );
268 - elToggleSwitchInput.setAttribute( 'data-action', 'deactivate' );*/
269 - const elNavInstalled = document.querySelector( '.nav-tab[data-tab=installed] span' );
270 - elNavInstalled.textContent = parseInt( elNavInstalled.textContent ) + 1;
271 - const elNavNoInstalled = document.querySelector( '.nav-tab[data-tab=not_installed] span' );
272 - elNavNoInstalled.textContent = parseInt( elNavNoInstalled.textContent ) - 1;
273 - elItemPurchase.querySelector( '.purchase-install' ).style.display = 'none';
274 - elItemPurchase.querySelector( '.purchase-update' ).querySelector( '.enter-purchase-code' ).value = purchaseCode;
275 - } else if ( action === 'update' ) {
276 - const elAddonVersionCurrent = elAddonItem.querySelector( '.addon-version-current' );
277 - elAddonVersionCurrent.innerHTML = addon.version;
278 - elAddonItem.classList.remove( 'update' );
279 - } else if ( action === 'activate' ) {
280 - elAddonItem.classList.add( 'activated' );
281 - } else if ( action === 'deactivate' ) {
282 - elAddonItem.classList.remove( 'activated' );
283 - } else if ( action === 'update-purchase' ) {
284 - elItemPurchase.style.display = 'none';
285 - }
286 - }
287 -
288 - el.classList.remove( 'handling' );
289 - } );
290 - }
291 -
292 - if ( el.classList.contains( 'nav-tab' ) ) {
293 - e.preventDefault();
294 - const elTabs = document.querySelectorAll( '.nav-tab' );
295 - elTabs.forEach( function( elTab ) {
296 - elTab.classList.remove( 'nav-tab-active' );
297 - } );
298 - el.classList.add( 'nav-tab-active' );
299 - const tabName = el.dataset.tab;
300 - const elAddonItems = elAddonsPage.querySelectorAll( '.lp-addon-item' );
301 - const elSearch = elAddonsPage.querySelector( '#lp-search-addons__input' );
302 - elSearch.value = '';
303 -
304 - urlParams.set( 'tab', tabName );
305 - window.history.pushState( {}, '', `${ window.location.pathname }?${ urlParams.toString() }` );
306 - let totalItems = 0;
307 - elAddonItems.forEach( ( elAddonItem ) => {
308 - elAddonItem.classList.remove( 'search-not-found' );
309 - if ( 'all' === tabName || elAddonItem.classList.contains( tabName ) ) {
310 - elAddonItem.classList.remove( 'hide' );
311 - totalItems++;
312 - } else {
313 - elAddonItem.classList.add( 'hide' );
314 - }
315 - } );
316 -
317 - setGridItems( totalItems );
318 - }
319 -} );
320 -
321 -/*** Event search addons. ***/
322 -document.addEventListener( 'input', ( e ) => {
323 - const el = e.target;
324 -
325 - if ( 'lp-search-addons__input' === el.id ) {
326 - const keyword = el.value;
327 - searchAddons( keyword );
328 - }
329 -
330 - // Events change input purchase code.
331 - if ( el.classList.contains( 'enter-purchase-code' ) ) {
332 - e.preventDefault();
333 - const purchaseCode = el.value;
334 - const elItemPurchase = el.closest( '.lp-addon-item__purchase' );
335 -
336 - if ( elItemPurchase ) {
337 - const input = elItemPurchase.querySelector( 'input[name=purchase-code]' );
338 - input.value = purchaseCode;
339 - }
340 - }
341 -} );
1 +/**
2 + * Admin Add-ons page JS handler as a class.
3 + *
4 + * Add-ons list is rendered server-side via AdminAddonsPage::render_addons()
5 + * through TemplateAJAX::load_content_via_ajax(). This class only handles
6 + * UI interactions and addon actions via window.lpAJAXG.
7 + *
8 + * @since 4.4.7
9 + * @version 1.0.0
10 + */
11 +import * as lpToastify from 'lpAssetsJsPath/lpToastify.js';
12 +import * as lpUtils from 'lpAssetsJsPath/utils.js';
13 +
14 +class AdminAddons {
15 + constructor() {
16 + this.elAddonsPage = null;
17 + this.urlParams = new URLSearchParams( window.location.search );
18 + }
19 +
20 + static selectors = {
21 + elAddonsPage: '.lp-addons-page',
22 + elLPAddons: '#lp-addons',
23 + elAddonItem: '.lp-addon-item',
24 + elBtnAction: '.btn-addon-action',
25 + elNavTab: '.nav-tab',
26 + elNavTabActive: '.nav-tab.nav-tab-active',
27 + elCategory: '.lp-addons-category',
28 + elCategoryActive: '.lp-addons-category.active',
29 + elSearchInput: '#lp-search-addons__input',
30 + elItemPurchase: '.lp-addon-item__purchase',
31 + elPurchaseInstall: '.purchase-install',
32 + elPurchaseCode: '.enter-purchase-code',
33 + elLicense: '.lp-addon-license',
34 + };
35 +
36 + init() {
37 + // Content is injected via AJAX into .lp-target, wait for #lp-addons.
38 + lpUtils.lpOnElementReady( AdminAddons.selectors.elLPAddons, () => {
39 + this.elAddonsPage = document.querySelector(
40 + AdminAddons.selectors.elAddonsPage
41 + );
42 + if ( ! this.elAddonsPage ) {
43 + return;
44 + }
45 +
46 + this.filterAddons();
47 + this.events();
48 + } );
49 + }
50 +
51 + /**
52 + * Register events.
53 + *
54 + * @return void
55 + */
56 + events() {
57 + lpUtils.eventHandlers( 'click', [
58 + {
59 + selector: AdminAddons.selectors.elBtnAction,
60 + class: this,
61 + callBack: this.handleBtnAction.name,
62 + },
63 + {
64 + selector: AdminAddons.selectors.elNavTab,
65 + class: this,
66 + callBack: this.handleTabClick.name,
67 + },
68 + {
69 + selector: AdminAddons.selectors.elCategory,
70 + class: this,
71 + callBack: this.handleCategoryClick.name,
72 + },
73 + {
74 + selector: AdminAddons.selectors.elPurchaseCode,
75 + class: this,
76 + callBack: this.handlePurchaseCodeClick.name,
77 + },
78 + ] );
79 + lpUtils.eventHandlers( 'input', [
80 + {
81 + selector: AdminAddons.selectors.elSearchInput,
82 + class: this,
83 + callBack: this.handleSearchInput.name,
84 + },
85 + {
86 + selector: AdminAddons.selectors.elPurchaseCode,
87 + class: this,
88 + callBack: this.handlePurchaseCodeInput.name,
89 + },
90 + ] );
91 + }
92 +
93 + /**
94 + * Get addon data embedded on the item element.
95 + *
96 + * @param {Element} elAddonItem Addon item element.
97 + * @return {Object|null}
98 + */
99 + getAddonData( elAddonItem ) {
100 + try {
101 + return JSON.parse( elAddonItem.dataset.addon );
102 + } catch ( e ) {
103 + return null;
104 + }
105 + }
106 +
107 + /**
108 + * Send addon action to server via lpAJAXG.
109 + *
110 + * @param {Object} data { action, addon, purchase_code, el, elAddonItem }.
111 + * @return void
112 + */
113 + addonsAction( data ) {
114 + const { action, addon, el, elAddonItem } = data;
115 + const selectors = AdminAddons.selectors;
116 +
117 + const params = {
118 + action: 'addon_action',
119 + action_type: action,
120 + addon,
121 + purchase_code: data.purchase_code || '',
122 + id_url: 'addon-action',
123 + };
124 +
125 + window.lpAJAXG.fetchAJAX( params, {
126 + success: ( response ) => {
127 + const { status, message, data: resData } = response;
128 +
129 + lpToastify.show( message, status );
130 +
131 + if ( status === 'success' && resData?.html ) {
132 + elAddonItem.outerHTML = resData.html;
133 + } else if (
134 + 'install' === action ||
135 + 'update-purchase' === action
136 + ) {
137 + const elPurchasePanel = el.closest(
138 + selectors.elPurchaseInstall
139 + );
140 + const elPurchaseCode = elPurchasePanel
141 + ? elPurchasePanel.querySelector( selectors.elPurchaseCode )
142 + : null;
143 + if ( elPurchaseCode ) {
144 + elPurchaseCode.classList.add( 'is-error' );
145 + elPurchaseCode.setAttribute( 'aria-invalid', 'true' );
146 + }
147 + }
148 +
149 + //this.filterAddons();
150 + lpUtils.lpSetLoadingEl( el, 0 );
151 + },
152 + error: ( error ) => {
153 + lpUtils.lpSetLoadingEl( el, 0 );
154 + lpToastify.show( `error js: ${ error }`, 'error' );
155 + },
156 + } );
157 + }
158 +
159 + /**
160 + * Filter add-ons by status, category, and search query.
161 + *
162 + * @return void
163 + */
164 + filterAddons() {
165 + const selectors = AdminAddons.selectors;
166 + const elAddonItems = this.elAddonsPage.querySelectorAll(
167 + selectors.elAddonItem
168 + );
169 +
170 + // Count items for each tab
171 + this.updateTabCounts( elAddonItems );
172 + const elActiveTab = this.elAddonsPage.querySelector(
173 + selectors.elNavTabActive
174 + );
175 + const elActiveCategory = this.elAddonsPage.querySelector(
176 + selectors.elCategoryActive
177 + );
178 + const elSearch = this.elAddonsPage.querySelector(
179 + selectors.elSearchInput
180 + );
181 + const tabName = elActiveTab ? elActiveTab.dataset.tab : 'all';
182 + const category = elActiveCategory
183 + ? elActiveCategory.dataset.category
184 + : 'all';
185 + const keyword = elSearch ? elSearch.value.trim().toLowerCase() : '';
186 + const searchTerms = keyword.split( /\s+/ ).filter( Boolean );
187 +
188 + // Count items for category by tab
189 + this.updateCategoryCounts( elAddonItems, tabName );
190 +
191 + elAddonItems.forEach( ( elAddonItem ) => {
192 + const addonInfo = JSON.parse( elAddonItem.dataset.addon );
193 +
194 + const addonName = elAddonItem
195 + .querySelector( 'a' )
196 + .textContent.toLowerCase();
197 + const textCompare = addonName + addonInfo.description;
198 +
199 + const addonCategories = ( elAddonItem.dataset.category || '' )
200 + .split( /\s+/ )
201 + .filter( Boolean );
202 + const matchesTab =
203 + 'all' === tabName || elAddonItem.classList.contains( tabName );
204 + const matchesCategory =
205 + 'all' === category || addonCategories.includes( category );
206 + const matchesSearch = searchTerms.every( ( term ) => textCompare.includes( term ) );
207 + const isVisible = matchesTab && matchesCategory && matchesSearch;
208 +
209 + elAddonItem.classList.toggle( 'hide', ! matchesTab );
210 + elAddonItem.classList.toggle(
211 + 'search-not-found',
212 + ! isVisible
213 + );
214 + } );
215 + }
216 +
217 + updateCategoryCounts( elAddonItems, tabName ) {
218 + const categoryCounts = { all: 0 };
219 +
220 + elAddonItems.forEach( ( elAddonItem ) => {
221 + const matchesTab =
222 + 'all' === tabName || elAddonItem.classList.contains( tabName );
223 +
224 + if ( ! matchesTab ) {
225 + return;
226 + }
227 +
228 + categoryCounts.all++;
229 + ( elAddonItem.dataset.category || '' )
230 + .split( /\s+/ )
231 + .filter( Boolean )
232 + .forEach( ( addonCategory ) => {
233 + categoryCounts[ addonCategory ] =
234 + ( categoryCounts[ addonCategory ] || 0 ) + 1;
235 + } );
236 + } );
237 +
238 + this.elAddonsPage
239 + .querySelectorAll( AdminAddons.selectors.elCategory )
240 + .forEach( ( elCategory ) => {
241 + const count =
242 + categoryCounts[ elCategory.dataset.category ] || 0;
243 + const elCount = elCategory.querySelector(
244 + '.lp-addons-category__count'
245 + );
246 +
247 + if ( elCount ) {
248 + elCount.textContent = `(${ count })`;
249 + }
250 +
251 + elCategory.hidden =
252 + 'all' !== elCategory.dataset.category && 0 === count;
253 + } );
254 + }
255 +
256 + updateTabCounts( elAddonItems ) {
257 + this.elAddonsPage
258 + .querySelectorAll( AdminAddons.selectors.elNavTab )
259 + .forEach( ( elNavTab ) => {
260 + const tabName = elNavTab.dataset.tab;
261 + const count = Array.from( elAddonItems ).filter(
262 + ( elAddonItem ) =>
263 + 'all' === tabName ||
264 + elAddonItem.classList.contains( tabName )
265 + ).length;
266 + const elCount = elNavTab.querySelector( 'span' );
267 +
268 + if ( elCount ) {
269 + elCount.textContent = count;
270 + }
271 + } );
272 + }
273 +
274 + /**
275 + * Handle click events.
276 + *
277 + * @param {Object} args Click event arguments.
278 + * @return void
279 + */
280 + handlePurchaseCodeClick( args ) {
281 + const { target: el } = args;
282 +
283 + if ( ! el.value.startsWith( '***' ) ) {
284 + return;
285 + }
286 +
287 + el.value = '';
288 + const elItemPurchase = el.closest(
289 + AdminAddons.selectors.elItemPurchase
290 + );
291 + if ( elItemPurchase ) {
292 + elItemPurchase.querySelector( 'input[name=purchase-code]' ).value = '';
293 + }
294 + }
295 +
296 + /**
297 + * Handle addon action button click.
298 + *
299 + * @param {Object} args Click event arguments.
300 + * @return void
301 + */
302 + handleBtnAction( args ) {
303 + const { e, target } = args;
304 + const selectors = AdminAddons.selectors;
305 + const el = target.closest( selectors.elBtnAction );
306 +
307 + // Ignore button type link
308 + if ( 'A' === el.tagName ) {
309 + return;
310 + }
311 +
312 + e.preventDefault();
313 + lpUtils.lpSetLoadingEl( el, 1 );
314 +
315 + let purchaseCode = '';
316 + const elAddonItem = el.closest( selectors.elAddonItem );
317 + const addon = this.getAddonData( elAddonItem );
318 + const action = el.dataset.action;
319 + const elItemPurchase = elAddonItem.querySelector(
320 + selectors.elItemPurchase
321 + );
322 +
323 + if ( ! addon ) {
324 + lpUtils.lpSetLoadingEl( el, 0 );
325 + return;
326 + }
327 +
328 + if ( action === 'purchase' || action === 'update-purchase-code' ) {
329 + const elPurchaseInstall = elItemPurchase.querySelector(
330 + selectors.elPurchaseInstall
331 + );
332 + const elPurchaseCode = elPurchaseInstall.querySelector(
333 + selectors.elPurchaseCode
334 + );
335 + const elPurchaseSubmit = elPurchaseInstall.querySelector(
336 + '.lp-addon-purchase__submit'
337 + );
338 +
339 + if ( action === 'update-purchase-code' ) {
340 + const elLicense = elAddonItem.querySelector( selectors.elLicense );
341 + elPurchaseCode.value = elLicense
342 + ? elLicense.dataset.purchaseCodeMasked || ''
343 + : '';
344 + elPurchaseSubmit.dataset.action = 'update-purchase';
345 + } else {
346 + elPurchaseCode.value = '';
347 + elPurchaseSubmit.dataset.action = 'install';
348 + }
349 +
350 + elPurchaseCode.classList.remove( 'is-error' );
351 + elPurchaseCode.removeAttribute( 'aria-invalid' );
352 + elItemPurchase.querySelector( 'input[name=purchase-code]' ).value =
353 + '';
354 + elPurchaseInstall.style.display = 'flex';
355 + elItemPurchase.style.display = 'block';
356 + elPurchaseCode.focus();
357 + lpUtils.lpSetLoadingEl( el, 0 );
358 + return;
359 + } else if ( action === 'cancel' ) {
360 + elItemPurchase.style.display = 'none';
361 + elItemPurchase.querySelector(
362 + selectors.elPurchaseInstall
363 + ).style.display = 'none';
364 + lpUtils.lpSetLoadingEl( el, 0 );
365 + return;
366 + }
367 +
368 + // Send request to server.
369 + if ( elItemPurchase ) {
370 + purchaseCode = elItemPurchase.querySelector(
371 + 'input[name=purchase-code]'
372 + ).value;
373 + }
374 +
375 + this.addonsAction( {
376 + purchase_code: purchaseCode,
377 + action,
378 + addon,
379 + el,
380 + elAddonItem,
381 + } );
382 + }
383 +
384 + /**
385 + * Handle tab click.
386 + *
387 + * @param {Object} args Click event arguments.
388 + * @return void
389 + */
390 + handleTabClick( args ) {
391 + const { e, target } = args;
392 + const selectors = AdminAddons.selectors;
393 + const el = target.closest( selectors.elNavTab );
394 + e.preventDefault();
395 +
396 + const elTabs = this.elAddonsPage.querySelectorAll( selectors.elNavTab );
397 + elTabs.forEach( ( elTab ) => {
398 + elTab.classList.remove( 'nav-tab-active' );
399 + elTab.setAttribute( 'aria-pressed', 'false' );
400 + } );
401 + el.classList.add( 'nav-tab-active' );
402 + el.setAttribute( 'aria-pressed', 'true' );
403 +
404 + const tabName = el.dataset.tab;
405 + const elSearch = this.elAddonsPage.querySelector(
406 + selectors.elSearchInput
407 + );
408 + elSearch.value = '';
409 + this.elAddonsPage
410 + .querySelectorAll( selectors.elCategory )
411 + .forEach( ( elCategory ) => {
412 + const isActive = 'all' === elCategory.dataset.category;
413 + elCategory.classList.toggle( 'active', isActive );
414 + elCategory.setAttribute(
415 + 'aria-pressed',
416 + isActive ? 'true' : 'false'
417 + );
418 + } );
419 +
420 + this.urlParams.set( 'tab', tabName );
421 + window.history.pushState(
422 + {},
423 + '',
424 + `${ window.location.pathname }?${ this.urlParams.toString() }`
425 + );
426 + this.filterAddons();
427 + }
428 +
429 + /**
430 + * Handle category click.
431 + *
432 + * @param {Object} args Click event arguments.
433 + * @return void
434 + */
435 + handleCategoryClick( args ) {
436 + const { e, target } = args;
437 + const elCategory = target.closest(
438 + AdminAddons.selectors.elCategory
439 + );
440 + e.preventDefault();
441 +
442 + this.elAddonsPage
443 + .querySelectorAll( AdminAddons.selectors.elCategory )
444 + .forEach( ( item ) => {
445 + const isActive = item === elCategory;
446 + item.classList.toggle( 'active', isActive );
447 + item.setAttribute(
448 + 'aria-pressed',
449 + isActive ? 'true' : 'false'
450 + );
451 + } );
452 + this.filterAddons();
453 + }
454 +
455 + /**
456 + * Handle search input.
457 + *
458 + * @return void
459 + */
460 + handleSearchInput() {
461 + this.filterAddons();
462 + }
463 +
464 + /**
465 + * Handle purchase code input.
466 + *
467 + * @param {Object} args Input event arguments.
468 + * @return void
469 + */
470 + handlePurchaseCodeInput( args ) {
471 + const { e, target: el } = args;
472 + e.preventDefault();
473 + el.classList.remove( 'is-error' );
474 + el.removeAttribute( 'aria-invalid' );
475 + const purchaseCode = el.value;
476 + const elItemPurchase = el.closest(
477 + AdminAddons.selectors.elItemPurchase
478 + );
479 +
480 + if ( elItemPurchase ) {
481 + const input = elItemPurchase.querySelector(
482 + 'input[name=purchase-code]'
483 + );
484 + input.value = purchaseCode;
485 + }
486 + }
487 +}
488 +
489 +const adminAddons = new AdminAddons();
490 +adminAddons.init();
491 +
492 +export default AdminAddons;