PluginProbe
MainWP Dashboard: Self-hosted WordPress Management for Agencies / 6.2.2
MainWP Dashboard: Self-hosted WordPress Management for Agencies v6.2.2
6.2.2 6.2.1 6.2 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1 6.0.12 6.0.11 4.6.0.1 5.0 5.0.1 5.0.2 5.0.3 5.0.3.1 5.0.3.2 5.1 5.1.1 5.2 5.2.1 All 155 releases
← All changes | assets/js/mainwp-theme.js +224 -242 5.0 → 6.2.2 View file →
@@ -1,14 +1,14 @@
1 1 /* eslint complexity: ["error", 100] */
2 2 // current complexity is the only way to achieve desired results, pull request solutions appreciated.
3 3
4 4 /* global _mainwpThemeSettings, confirm */
5 -window.wp = window.wp || {};
5 +globalThis.wp = globalThis.wp || {};
6 6
7 7 (function ($) {
8 8
9 9 // Set up our namespace...
10 - var themes, l10n;
10 + let themes, l10n;
11 11 themes = wp.themes = wp.themes || {};
12 12
13 13 // Store the theme data and settings for organized and quick access
14 14 // themes.data.settings, themes.data.themes, themes.data.l10n
@@ -24,9 +24,9 @@
24 24 themes.Model = Backbone.Model.extend({
25 25 // Adds attributes to the default data coming through the .org themes api
26 26 // Map `id` to `slug` for shared code
27 27 initialize: function () {
28 - var description;
28 + let description;
29 29
30 30 // If theme is already installed, set an attribute.
31 31 if (_.indexOf(themes.data.installedThemes, this.get('slug')) !== -1) {
32 32 this.set({ installed: true });
@@ -52,9 +52,9 @@
52 52 themes.view.Appearance = wp.Backbone.View.extend({
53 53
54 54 el: '#wpbody-content .mainwp-browse-themes',
55 55
56 - window: $(window),
56 + window: $(globalThis.window),
57 57 // Pagination instance
58 58 page: 0,
59 59
60 60 // Sets up a throttler for binding to 'scroll'
@@ -91,18 +91,15 @@
91 91
92 92 // Search input and view
93 93 // for current theme collection
94 94 search: function () {
95 - var view,
96 - self = this;
97 -
98 95 // Don't render the search if there is only one theme
99 96 if (themes.data.themes.length === 1) {
100 97 return;
101 98 }
102 99
103 - view = new this.SearchView({
104 - collection: self.collection,
100 + const view = new this.SearchView({
101 + collection: this.collection,
105 102 parent: this
106 103 });
107 104
108 105 // Render and append after screen title
@@ -114,13 +111,10 @@
114 111
115 112 // Checks when the user gets close to the bottom
116 113 // of the mage and triggers a theme:scroll event
117 114 scroller: function () {
118 - var self = this,
119 - bottom, threshold;
120 -
121 - bottom = this.window.scrollTop() + self.window.height();
122 - threshold = self.$el.offset().top + self.$el.outerHeight(false) - self.window.height();
115 + const bottom = this.window.scrollTop() + this.window.height();
116 + let threshold = this.$el.offset().top + this.$el.outerHeight(false) - this.window.height();
123 117 threshold = Math.round(threshold * 0.9);
124 118
125 119 if (bottom > threshold) {
126 120 this.trigger('theme:scroll');
@@ -168,27 +162,27 @@
168 162
169 163 // Performs a search within the collection
170 164 // @uses RegExp
171 165 search: function (term) {
172 - var match, results, haystack, name, description, author;
166 + let match, results, haystack, name, description, author;
173 167
174 168 // Start with a full collection
175 169 this.reset(themes.data.themes, { silent: true });
176 170
177 171 // Escape the term string for RegExp meta characters
178 - term = term.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
172 + term = term.replaceAll(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); // NOSONAR - safe pattern.
179 173
180 174 // Consider spaces as word delimiters and match the whole string
181 175 // so matching terms can be combined
182 - term = term.replace(/ /g, ')(?=.*');
176 + term = term.replaceAll(' ', ')(?=.*');
183 177 match = new RegExp('^(?=.*' + term + ').+', 'i');
184 178
185 179 // Find results
186 180 // _.filter and .test
187 181 results = this.filter(function (data) {
188 - name = data.get('name').replace(/(<([^>]+)>)/ig, '');
189 - description = data.get('description').replace(/(<([^>]+)>)/ig, '');
190 - author = data.get('author').replace(/(<([^>]+)>)/ig, '');
182 + name = data.get('name').replace(/(<([^>]+)>)/ig, ''); // NOSONAR - safe pattern from WP.
183 + description = data.get('description').replace(/(<([^>]+)>)/ig, ''); // NOSONAR - safe pattern from WP.
184 + author = data.get('author').replace(/(<([^>]+)>)/ig, ''); // NOSONAR - safe pattern from WP.
191 185
192 186 haystack = _.union(name, data.get('id'), description, author, data.get('tags'));
193 187
194 188 if (match.test(data.get('author')) && term.length > 2) {
@@ -208,17 +202,15 @@
208 202 },
209 203
210 204 // Paginates the collection with a helper method
211 205 // that slices the collection
212 - paginate: function (instance) {
213 - var collection = this;
214 - instance = instance || 0;
215 -
216 - // Themes per instance are set at 20
217 - collection = _(collection.rest(20 * instance));
206 + paginate: function (instance = 0) {
207 + // Themes per instance are set at 20
208 + let collection = _(this.rest(20 * instance));
218 209 collection = _(collection.first(20));
219 210
220 211 return collection;
212 +
221 213 },
222 214
223 215 count: false,
224 216
@@ -227,88 +219,83 @@
227 219 //
228 220 // When we are missing a cache object we fire an apiCall()
229 221 // which triggers events of `query:success` or `query:fail`
230 222 query: function (request) {
231 - /**
232 - * @static
233 - * @type Array
234 - */
235 - var queries = this.queries,
236 - self = this,
237 - query, isPaginated, count;
238 223
224 + let queries = this.queries;
225 + let query, isPaginated, count;
226 +
239 227 // Store current query request args
240 - // for later use with the event `theme:end`
241 228 this.currentQuery.request = request;
242 229
243 230 // Search the query cache for matches.
244 - query = _.find(queries, function (query) {
231 + query = _.find(queries, (query) => {
245 232 return _.isEqual(query.request, request);
246 233 });
247 234
248 - // If the request matches the stored currentQuery.request
249 - // it means we have a paginated request.
250 235 isPaginated = _.has(request, 'page');
251 236
252 - // Reset the internal api page counter for non paginated queries.
253 237 if (!isPaginated) {
254 238 this.currentQuery.page = 1;
255 239 }
256 240
257 - // Otherwise, send a new API call and add it to the cache.
258 241 if (!query && !isPaginated) {
259 - query = this.apiCall(request).done(function (data) {
260 242
261 - // Update the collection with the queried data.
262 - if (data.themes) {
263 - self.reset(data.themes);
264 - count = data.info.results;
265 - // Store the results and the query request
266 - queries.push({ themes: data.themes, request: request, total: count });
267 - }
243 + this.apiCall(request)
244 + .done((data) => {
268 245
269 - // Trigger a collection refresh event
270 - // and a `query:success` event with a `count` argument.
271 - self.trigger('update');
272 - self.trigger('query:success', count);
246 + if (data.themes) {
247 + this.reset(data.themes);
248 + count = data.info.results;
273 249
274 - if (data.themes && data.themes.length === 0) {
275 - self.trigger('query:empty');
276 - }
250 + queries.push({
251 + themes: data.themes,
252 + request: request,
253 + total: count
254 + });
255 + }
277 256
278 - }).fail(function () {
279 - self.trigger('query:fail');
280 - });
257 + this.trigger('update');
258 + this.trigger('query:success', count);
259 +
260 + if (data?.themes?.length === 0) {
261 + this.trigger('query:empty');
262 + }
263 +
264 + })
265 + .fail(() => {
266 + this.trigger('query:fail');
267 + });
268 +
281 269 } else {
282 - // If it's a paginated request we need to fetch more themes...
270 +
283 271 if (isPaginated) {
284 - return this.apiCall(request, isPaginated).done(function (data) {
285 - // Add the new themes to the current collection
286 - // @todo update counter
287 - self.add(data.themes);
288 - self.trigger('query:success');
272 + return this.apiCall(request, isPaginated)
273 + .done((data) => {
289 274
290 - // We are done loading themes for now.
291 - self.loadingThemes = false;
275 + this.add(data.themes);
276 + this.trigger('query:success');
292 277
293 - }).fail(function () {
294 - self.trigger('query:fail');
295 - });
278 + this.loadingThemes = false;
279 +
280 + })
281 + .fail(() => {
282 + this.trigger('query:fail');
283 + });
296 284 }
297 285
298 286 if (query.themes.length === 0) {
299 - self.trigger('query:empty');
287 + this.trigger('query:empty');
300 288 } else {
301 289 $('body').removeClass('no-results');
302 290 }
303 291
304 - // Only trigger an update event since we already have the themes
305 - // on our cached object
306 292 if (_.isNumber(query.total)) {
307 293 this.count = query.total;
308 294 }
309 295
310 296 this.reset(query.themes);
297 +
311 298 if (!query.total) {
312 299 this.count = this.length;
313 300 }
314 301
@@ -353,13 +340,13 @@
353 340 $('body').addClass('loading-content').removeClass('no-results');
354 341 }
355 342 }
356 343 }).done(function (response) {
357 - if (response && response.themes) {
358 - var favThemes = jQuery('#mainwp-favorites-themes').attr('favorites-themes');
344 + if (response?.themes) {
345 + let favThemes = jQuery('#mainwp-favorites-themes').attr('favorites-themes');
359 346 if ('' !== favThemes) {
360 347 try {
361 - var decoded_favThemes = JSON.parse(favThemes);
348 + let decoded_favThemes = JSON.parse(favThemes);
362 349 response.themes.forEach(function (part, index) {
363 350 if (decoded_favThemes.hasOwnProperty(this[index].slug)) {
364 351 this[index].added_fav = 1;
365 352 } else {
@@ -365,9 +352,9 @@
365 352 } else {
366 353 this[index].added_fav = 0;
367 354 }
368 355 }, response.themes); // use second param as this.
369 - } catch (e) {
356 + } catch {
370 357 console.log('Invalid favorites themes data.');
371 358 }
372 359 }
373 360 }
@@ -402,9 +389,9 @@
402 389
403 390 touchDrag: false,
404 391
405 392 render: function () {
406 - var data = this.model.toJSON();
393 + let data = this.model.toJSON();
407 394 // Render themes using the html template
408 395 this.$el.html(this.html(data)).attr({
409 396 tabindex: 0,
410 397 'aria-describedby': data.id + '-action ' + data.id + '-name'
@@ -431,9 +418,9 @@
431 418 },
432 419
433 420 // Add class of focus to the theme we are focused on.
434 421 addFocus: function () {
435 - var $themeToFocus = ($(':focus').hasClass('theme')) ? $(':focus') : $(':focus').parents('.theme');
422 + let $themeToFocus = ($(':focus').hasClass('theme')) ? $(':focus') : $(':focus').parents('.theme');
436 423
437 424 $('.theme.focus').removeClass('focus');
438 425 $themeToFocus.addClass('focus');
439 426 },
@@ -440,11 +427,10 @@
440 427
441 428 // Single theme overlay screen
442 429 // It's shown when clicking a theme
443 430 expand: function (event) {
444 - var self = this;
445 431
446 - event = event || window.event;
432 + event = event || window.event; // NOSONAR - compatible.
447 433
448 434 // 'enter' and 'space' keys expand the details view when a theme is :focused
449 435 if (event.type === 'keydown' && (event.which !== 13 && event.which !== 32)) {
450 436 return;
@@ -451,9 +437,10 @@
451 437 }
452 438
453 439 // Bail if the user scrolled on a touch device
454 440 if (this.touchDrag === true) {
455 - return this.touchDrag = false;
441 + this.touchDrag = false;
442 + return this.touchDrag;
456 443 }
457 444
458 445 // Prevent the modal from showing when the user clicks
459 446 // one of the direct action buttons
@@ -463,9 +450,9 @@
463 450
464 451 // Set focused theme to current element
465 452 themes.focusedTheme = this.$el;
466 453
467 - this.trigger('theme:expand', self.model.cid);
454 + this.trigger('theme:expand', this.model.cid);
468 455 },
469 456
470 457 preventExpand: function () {
471 458 this.touchDrag = true;
@@ -471,14 +458,14 @@
471 458 this.touchDrag = true;
472 459 },
473 460
474 461 preview: function (event) {
475 - var self = this,
476 - current, preview;
462 + let current, preview;
477 463
478 464 // Bail if the user scrolled on a touch device
479 465 if (this.touchDrag === true) {
480 - return this.touchDrag = false;
466 + this.touchDrag = false;
467 + return this.touchDrag;
481 468 }
482 469
483 470 // Allow direct link path to installing a theme.
484 471 if ($(event.target).hasClass('button-primary')) {
@@ -503,9 +490,9 @@
503 490 }
504 491
505 492 event.preventDefault();
506 493
507 - event = event || window.event;
494 + event = event || window.event; // NOSONAR - compatible.
508 495
509 496 // Set focus to current theme.
510 497 themes.focusedTheme = this.$el;
511 498
@@ -529,28 +516,29 @@
529 516 $('div.mainwp-content-wrap').append(preview.el);
530 517
531 518 // Listen to our preview object
532 519 // for `theme:next` and `theme:previous` events.
533 - this.listenTo(preview, 'theme:next', function () {
520 + this.listenTo(preview, 'theme:next', () => {
534 521
535 522 // Keep local track of current theme model.
536 - current = self.model;
523 + current = this.model;
537 524
538 525 // If we have ventured away from current model update the current model position.
539 - if (!_.isUndefined(self.current)) {
540 - current = self.current;
526 + if (!_.isUndefined(this.current)) {
527 + current = this.current;
541 528 }
542 529
543 530 // Get next theme model.
544 - self.current = self.model.collection.at(self.model.collection.indexOf(current) + 1);
531 + this.current = this.model.collection.at(this.model.collection.indexOf(current) + 1);
545 532
546 533 // If we have no more themes, bail.
547 - if (_.isUndefined(self.current)) {
548 - self.options.parent.parent.trigger('theme:end');
549 - return self.current = current;
534 + if (_.isUndefined(this.current)) {
535 + this.options.parent.parent.trigger('theme:end');
536 + this.current = current;
537 + return this.current;
550 538 }
551 539
552 - preview.model = self.current;
540 + preview.model = this.current;
553 541
554 542 // Render and append.
555 543 preview.render();
556 544 this.setNavButtonsState();
@@ -555,32 +543,32 @@
555 543 preview.render();
556 544 this.setNavButtonsState();
557 545 $('.next-theme').trigger('focus');
558 546 })
559 - .listenTo(preview, 'theme:previous', function () {
547 + .listenTo(preview, 'theme:previous', () => {
560 548
561 549 // Keep track of current theme model.
562 - current = self.model;
550 + current = this.model;
563 551
564 552 // Bail early if we are at the beginning of the collection
565 - if (self.model.collection.indexOf(self.current) === 0) {
553 + if (this.model.collection.indexOf(this.current) === 0) {
566 554 return;
567 555 }
568 556
569 557 // If we have ventured away from current model update the current model position.
570 - if (!_.isUndefined(self.current)) {
571 - current = self.current;
558 + if (!_.isUndefined(this.current)) {
559 + current = this.current;
572 560 }
573 561
574 562 // Get previous theme model.
575 - self.current = self.model.collection.at(self.model.collection.indexOf(current) - 1);
563 + this.current = this.model.collection.at(this.model.collection.indexOf(current) - 1);
576 564
577 565 // If we have no more themes, bail.
578 - if (_.isUndefined(self.current)) {
566 + if (_.isUndefined(this.current)) {
579 567 return;
580 568 }
581 569
582 - preview.model = self.current;
570 + preview.model = this.current;
583 571
584 572 // Render and append.
585 573 preview.render();
586 574 this.setNavButtonsState();
@@ -586,16 +574,16 @@
586 574 this.setNavButtonsState();
587 575 $('.previous-theme').trigger('focus');
588 576 });
589 577
590 - this.listenTo(preview, 'preview:close', function () {
591 - self.current = self.model;
578 + this.listenTo(preview, 'preview:close', () => {
579 + this.current = this.model;
592 580 });
593 581 },
594 582
595 583 // Handles .disabled classes for previous/next buttons in theme installer preview
596 584 setNavButtonsState: function () {
597 - var $themeInstaller = $('.theme-install-overlay'),
585 + let $themeInstaller = $('.theme-install-overlay'),
598 586 current = _.isUndefined(this.current) ? this.model : this.current;
599 587
600 588 // Disable previous at the zero position
601 589 if (0 === this.model.collection.indexOf(current)) {
@@ -626,9 +614,9 @@
626 614 // The HTML template for the theme overlay
627 615 html: themes.template('theme-single'),
628 616
629 617 render: function () {
630 - var data = this.model.toJSON();
618 + let data = this.model.toJSON();
631 619 this.$el.html(this.html(data));
632 620 // Renders active theme styles
633 621 this.activeTheme();
634 622 // Set up navigation events
@@ -647,9 +635,9 @@
647 635 },
648 636
649 637 // Keeps :focus within the theme details elements
650 638 containFocus: function ($el) {
651 - var $target;
639 + let $target;
652 640
653 641 // Move focus to the primary action
654 642 _.delay(function () {
655 643 $('.theme-wrap a.button-primary:visible').trigger('focus');
@@ -676,13 +664,11 @@
676 664
677 665 // Single theme overlay screen
678 666 // It's shown when clicking a theme
679 667 collapse: function (event) {
680 - var self = this,
681 - scroll;
668 + let scroll;
669 + event = event || window.event; // NOSONAR - compatible.
682 670
683 - event = event || window.event;
684 -
685 671 // Prevent collapsing detailed view when there is only one theme available
686 672 if (themes.data.themes.length === 1) {
687 673 return;
688 674 }
@@ -695,13 +681,13 @@
695 681 // Add a temporary closing class while overlay fades out
696 682 $('body').addClass('closing-overlay');
697 683
698 684 // With a quick fade out animation
699 - this.$el.fadeOut(130, function () {
685 + this.$el.fadeOut(130, () => {
700 686 // Clicking outside the modal box closes the overlay
701 687 $('body').removeClass('closing-overlay');
702 688 // Handle event cleanup
703 - self.closeOverlay();
689 + this.closeOverlay();
704 690
705 691 // Get scroll position to avoid jumping to the top
706 692 scroll = document.body.scrollTop;
707 693
@@ -725,9 +711,9 @@
725 711 // Disable Left/Right when at the start or end of the collection
726 712 if (this.model.cid === this.model.collection.at(0).cid) {
727 713 this.$el.find('.left').addClass('disabled');
728 714 }
729 - if (this.model.cid === this.model.collection.at(this.model.collection.length - 1).cid) {
715 + if (this.model.cid === this.model.collection.at(-1).cid) {
730 716 this.$el.find('.right').addClass('disabled');
731 717 }
732 718 },
733 719
@@ -745,16 +731,14 @@
745 731 return confirm(themes.data.settings.confirmDelete);
746 732 },
747 733
748 734 nextTheme: function () {
749 - var self = this;
750 - self.trigger('theme:next', self.model.cid);
735 + this.trigger('theme:next', this.model.cid);
751 736 return false;
752 737 },
753 738
754 739 previousTheme: function () {
755 - var self = this;
756 - self.trigger('theme:previous', self.model.cid);
740 + this.trigger('theme:previous', this.model.cid);
757 741 return false;
758 742 },
759 743
760 744 // Checks if the theme screenshot is the old 300px width version
@@ -759,9 +743,9 @@
759 743
760 744 // Checks if the theme screenshot is the old 300px width version
761 745 // and adds a corresponding class if it's true
762 746 screenshotCheck: function (el) {
763 - var screenshot, image;
747 + let screenshot, image;
764 748
765 749 screenshot = el.find('.screenshot img');
766 750 image = new Image();
767 751 image.src = screenshot.attr('src');
@@ -791,9 +775,9 @@
791 775 // The HTML template for the theme preview
792 776 html: themes.template('theme-preview'),
793 777
794 778 render: function () {
795 - var data = this.model.toJSON();
779 + let data = this.model.toJSON();
796 780
797 781 this.$el.html(this.html(data));
798 782
799 783 themes.router.navigate(themes.router.baseUrl(themes.router.themePath + this.model.get('id')), { replace: true });
@@ -821,9 +805,9 @@
821 805 return false;
822 806 },
823 807
824 808 collapse: function (event) {
825 - var $button = $(event.currentTarget);
809 + let $button = $(event.currentTarget);
826 810 if ('true' === $button.attr('aria-expanded')) {
827 811 $button.attr({ 'aria-expanded': 'false', 'aria-label': l10n.expandSidebar });
828 812 } else {
829 813 $button.attr({ 'aria-expanded': 'true', 'aria-label': l10n.collapseSidebar });
@@ -854,9 +838,9 @@
854 838 // Controls the rendering of div.themes,
855 839 // a wrapper that will hold all the theme elements
856 840 themes.view.Themes = wp.Backbone.View.extend({
857 841
858 - className: 'themes ui four cards',
842 + className: 'themes ui three cards',
859 843 $overlay: $('div.theme-overlay'),
860 844
861 845 // Number to keep track of scroll position
862 846 // while in theme-overlay mode
@@ -868,9 +852,8 @@
868 852 // The live themes count
869 853 liveThemeCount: 0,
870 854
871 855 initialize: function (options) {
872 - var self = this;
873 856
874 857 // Set up parent
875 858 this.parent = options.parent;
876 859
@@ -877,61 +860,61 @@
877 860 // Set current view to [grid]
878 861 this.setView('grid');
879 862
880 863 // Move the active theme to the beginning of the collection
881 - self.currentTheme();
864 + this.currentTheme();
882 865
883 866 // When the collection is updated by user input...
884 - this.listenTo(self.collection, 'update', function () {
885 - self.parent.page = 0;
886 - self.currentTheme();
887 - self.render(this);
867 + this.listenTo(this.collection, 'update', () => {
868 + this.parent.page = 0;
869 + this.currentTheme();
870 + this.render(this);
888 871 });
889 872
890 873 // Update theme count to full result set when available.
891 - this.listenTo(self.collection, 'query:success', function (count) {
874 + this.listenTo(this.collection, 'query:success', (count) => {
892 875 if (_.isNumber(count)) {
893 - self.count.text(count);
894 - self.announceSearchResults(count);
876 + this.count.text(count);
877 + this.announceSearchResults(count);
895 878 } else {
896 - self.count.text(self.collection.length);
897 - self.announceSearchResults(self.collection.length);
879 + this.count.text(this.collection.length);
880 + this.announceSearchResults(this.collection.length);
898 881 }
899 882 });
900 883
901 - this.listenTo(self.collection, 'query:empty', function () {
884 + this.listenTo(this.collection, 'query:empty', () => {
902 885 $('body').addClass('no-results');
903 886 });
904 887
905 - this.listenTo(this.parent, 'theme:scroll', function () {
906 - self.renderThemes(self.parent.page);
888 + this.listenTo(this.parent, 'theme:scroll', () => {
889 + this.renderThemes(this.parent.page);
907 890 });
908 891
909 - this.listenTo(this.parent, 'theme:close', function () {
910 - if (self.overlay) {
911 - self.overlay.closeOverlay();
892 + this.listenTo(this.parent, 'theme:close', () => {
893 + if (this.overlay) {
894 + this.overlay.closeOverlay();
912 895 }
913 896 });
914 897
915 898 // Bind keyboard events.
916 - $('body').on('keyup', function (event) {
917 - if (!self.overlay) {
899 + $('body').on('keyup', (event) => {
900 + if (!this.overlay) {
918 901 return;
919 902 }
920 903
921 904 // Pressing the right arrow key fires a theme:next event
922 905 if (event.keyCode === 39) {
923 - self.overlay.nextTheme();
906 + this.overlay.nextTheme();
924 907 }
925 908
926 909 // Pressing the left arrow key fires a theme:previous event
927 910 if (event.keyCode === 37) {
928 - self.overlay.previousTheme();
911 + this.overlay.previousTheme();
929 912 }
930 913
931 914 // Pressing the escape key fires a theme:collapse event
932 915 if (event.keyCode === 27) {
933 - self.overlay.collapse(event);
916 + this.overlay.collapse(event);
934 917 }
935 918 });
936 919 },
937 920
@@ -973,15 +956,13 @@
973 956
974 957 // Iterates through each instance of the collection
975 958 // and renders each theme module
976 959 renderThemes: function (page) {
977 - var self = this;
978 960
979 - self.instance = self.collection.paginate(page);
961 + this.instance = this.collection.paginate(page);
980 962
981 963 // If we have no more themes bail
982 - if (self.instance.length === 0) {
983 - // Fire a no-more-themes event.
964 + if (this.instance.length === 0) {
984 965 this.parent.trigger('theme:end');
985 966 return;
986 967 }
987 968
@@ -990,27 +971,35 @@
990 971 $('.add-new-theme').remove();
991 972 }
992 973
993 974 // Loop through the themes and setup each theme view
994 - self.instance.each(function (theme) {
995 - self.theme = new themes.view.Theme({
975 + this.instance.each((theme) => {
976 +
977 + this.theme = new themes.view.Theme({
996 978 model: theme,
997 - parent: self
979 + parent: this
998 980 });
999 981
1000 - // Render the views...
1001 - self.theme.render();
1002 - // and append them to div.themes
1003 - self.$el.append(self.theme.el);
982 + // Render the view
983 + this.theme.render();
1004 984
1005 - // Binds to theme:expand to show the modal box
1006 - // with the theme details
1007 - self.listenTo(self.theme, 'theme:expand', self.expand, self);
985 + // Append to container
986 + this.$el.append(this.theme.el);
987 +
988 + // Bind expand event
989 + this.listenTo(this.theme, 'theme:expand', this.expand);
1008 990 });
1009 991
1010 992 // 'Add new theme' element shown at the end of the grid
1011 993 if (themes.data.settings.canInstall) {
1012 - this.$el.append('<div class="theme add-new-theme"><a href="' + themes.data.settings.installURI + '"><div class="theme-screenshot"><span></span></div><h3 class="theme-name">' + l10n.addNew + '</h3></a></div>');
994 + this.$el.append(
995 + '<div class="theme add-new-theme">' +
996 + '<a href="' + themes.data.settings.installURI + '">' +
997 + '<div class="theme-screenshot"><span></span></div>' +
998 + '<h3 class="theme-name">' + l10n.addNew + '</h3>' +
999 + '</a>' +
1000 + '</div>'
1001 + );
1013 1002 }
1014 1003
1015 1004 this.parent.page++;
1016 1005 },
@@ -1016,17 +1005,14 @@
1016 1005 },
1017 1006
1018 1007 // Grabs current theme and puts it at the beginning of the collection
1019 1008 currentTheme: function () {
1020 - var self = this,
1021 - current;
1009 + const current = this.collection.findWhere({ active: true });
1022 1010
1023 - current = self.collection.findWhere({ active: true });
1024 -
1025 1011 // Move the active theme to the beginning of the collection
1026 1012 if (current) {
1027 - self.collection.remove(current);
1028 - self.collection.add(current, { at: 0 });
1013 + this.collection.remove(current);
1014 + this.collection.add(current, { at: 0 });
1029 1015 }
1030 1016 },
1031 1017
1032 1018 // Sets current view
@@ -1036,12 +1022,10 @@
1036 1022
1037 1023 // Renders the overlay with the ThemeDetails view
1038 1024 // Uses the current model data
1039 1025 expand: function (id) {
1040 - var self = this;
1041 -
1042 1026 // Set the current theme model
1043 - this.model = self.collection.get(id);
1027 + this.model = this.collection.get(id);
1044 1028
1045 1029 // Trigger a route update for the current model
1046 1030 themes.router.navigate(themes.router.baseUrl(themes.router.themePath + this.model.id));
1047 1031
@@ -1050,9 +1034,9 @@
1050 1034 $('body').addClass('modal-open');
1051 1035
1052 1036 // Set up the theme details view
1053 1037 this.overlay = new themes.view.Details({
1054 - model: self.model
1038 + model: this.model
1055 1039 });
1056 1040
1057 1041 this.overlay.render();
1058 1042 this.$overlay.html(this.overlay.el);
@@ -1061,16 +1045,15 @@
1061 1045 // triggered by the arrow keys
1062 1046 //
1063 1047 // Keep track of the current model so we
1064 1048 // can infer an index position
1065 - this.listenTo(this.overlay, 'theme:next', function () {
1049 + this.listenTo(this.overlay, 'theme:next', () => {
1066 1050 // Renders the next theme on the overlay
1067 - self.next([self.model.cid]);
1068 -
1051 + this.next([this.model.cid]);
1069 1052 })
1070 - .listenTo(this.overlay, 'theme:previous', function () {
1053 + .listenTo(this.overlay, 'theme:previous', () => {
1071 1054 // Renders the previous theme on the overlay
1072 - self.previous([self.model.cid]);
1055 + this.previous([this.model.cid]);
1073 1056 });
1074 1057 },
1075 1058
1076 1059 // This method renders the next theme on the overlay modal
@@ -1076,15 +1059,14 @@
1076 1059 // This method renders the next theme on the overlay modal
1077 1060 // based on the current position in the collection
1078 1061 // @params [model cid]
1079 1062 next: function (args) {
1080 - var self = this,
1081 - model, nextModel;
1063 + let model, nextModel;
1082 1064
1083 1065 // Get the current theme
1084 - model = self.collection.get(args[0]);
1066 + model = this.collection.get(args[0]);
1085 1067 // Find the next model within the collection
1086 - nextModel = self.collection.at(self.collection.indexOf(model) + 1);
1068 + nextModel = this.collection.at(this.collection.indexOf(model) + 1);
1087 1069
1088 1070 // Sanity check which also serves as a boundary test
1089 1071 if (nextModel !== undefined) {
1090 1072
@@ -1092,9 +1074,9 @@
1092 1074 // Close the overlay
1093 1075 this.overlay.closeOverlay();
1094 1076
1095 1077 // Trigger a route update for the current model
1096 - self.theme.trigger('theme:expand', nextModel.cid);
1078 + this.theme.trigger('theme:expand', nextModel.cid);
1097 1079
1098 1080 }
1099 1081 },
1100 1082
@@ -1101,15 +1083,14 @@
1101 1083 // This method renders the previous theme on the overlay modal
1102 1084 // based on the current position in the collection
1103 1085 // @params [model cid]
1104 1086 previous: function (args) {
1105 - var self = this,
1106 - model, previousModel;
1087 + let model, previousModel;
1107 1088
1108 1089 // Get the current theme
1109 - model = self.collection.get(args[0]);
1090 + model = this.collection.get(args[0]);
1110 1091 // Find the previous model within the collection
1111 - previousModel = self.collection.at(self.collection.indexOf(model) - 1);
1092 + previousModel = this.collection.at(this.collection.indexOf(model) - 1);
1112 1093
1113 1094 if (previousModel !== undefined) {
1114 1095
1115 1096 // We have a new theme...
@@ -1116,9 +1097,9 @@
1116 1097 // Close the overlay
1117 1098 this.overlay.closeOverlay();
1118 1099
1119 1100 // Trigger a route update for the current model
1120 - self.theme.trigger('theme:expand', previousModel.cid);
1101 + this.theme.trigger('theme:expand', previousModel.cid);
1121 1102
1122 1103 }
1123 1104 },
1124 1105
@@ -1133,9 +1114,8 @@
1133 1114 });
1134 1115
1135 1116 // Search input view controller.
1136 1117 themes.view.Search = wp.Backbone.View.extend({
1137 -
1138 1118 tagName: 'input',
1139 1119 className: 'wp-filter-search fluid prompt',
1140 1120 id: 'wp-filter-search-input',
1141 1121 searching: false,
@@ -1141,9 +1121,9 @@
1141 1121 searching: false,
1142 1122
1143 1123
1144 1124 attributes: {
1145 - placeholder: __('Search themes...'),
1125 + placeholder: 'Search themes...',
1146 1126 type: 'text',
1147 1127 'aria-describedby': 'live-search-desc'
1148 1128 },
1149 1129
@@ -1176,9 +1156,9 @@
1176 1156 },
1177 1157
1178 1158 // Runs a search on the theme collection.
1179 1159 doSearch: _.debounce(function (event) {
1180 - var options = {};
1160 + let options = {};
1181 1161
1182 1162 this.collection.doSearch(event.target.value);
1183 1163
1184 1164 // if search is initiated and key is not return
@@ -1196,9 +1176,9 @@
1196 1176 }
1197 1177 }, 500),
1198 1178
1199 1179 pushState: function (event) {
1200 - var url = themes.router.baseUrl('');
1180 + let url = themes.router.baseUrl('');
1201 1181
1202 1182 if (event.target.value) {
1203 1183 url = themes.router.baseUrl(themes.router.searchPath + event.target.value);
1204 1184 }
@@ -1270,27 +1250,25 @@
1270 1250 pushState: true,
1271 1251 hashChange: false
1272 1252 });
1273 1253 },
1254 + routes: function () {
1274 1255
1275 - routes: function () {
1276 - var self = this;
1277 - // Bind to our global thx object
1278 - // so that the object is available to sub-views
1256 + // Bind to our global themes object
1279 1257 themes.router = new themes.Router();
1280 1258
1281 1259 // Handles theme details route event
1282 - themes.router.on('route:theme', function (slug) {
1283 - self.view.view.expand(slug);
1260 + themes.router.on('route:theme', (slug) => {
1261 + this.view.view.expand(slug);
1284 1262 });
1285 1263
1286 - themes.router.on('route:themes', function () {
1287 - self.themes.doSearch('');
1288 - self.view.trigger('theme:close');
1264 + themes.router.on('route:themes', () => {
1265 + this.themes.doSearch('');
1266 + this.view.trigger('theme:close');
1289 1267 });
1290 1268
1291 1269 // Handles search route event
1292 - themes.router.on('route:search', function () {
1270 + themes.router.on('route:search', () => {
1293 1271 $('.wp-filter-search').trigger('keyup');
1294 1272 });
1295 1273
1296 1274 this.extraRoutes();
@@ -1295,8 +1273,9 @@
1295 1273
1296 1274 this.extraRoutes();
1297 1275 },
1298 1276
1277 +
1299 1278 extraRoutes: function () {
1300 1279 return false;
1301 1280 }
1302 1281 };
@@ -1327,9 +1306,9 @@
1327 1306 this.doSearch(event.target.value);
1328 1307 },
1329 1308
1330 1309 doSearch: _.debounce(function (value) {
1331 - var request = {};
1310 + let request = {};
1332 1311
1333 1312 request.search = value;
1334 1313
1335 1314 // Intercept an [author] search.
@@ -1378,9 +1357,8 @@
1378 1357 },
1379 1358
1380 1359 // Initial render method
1381 1360 render: function () {
1382 - var self = this;
1383 1361
1384 1362 this.search();
1385 1363 this.uploader();
1386 1364
@@ -1386,22 +1364,22 @@
1386 1364
1387 1365 this.collection = new themes.Collection();
1388 1366
1389 1367 // Bump `collection.currentQuery.page` and request more themes if we hit the end of the page.
1390 - this.listenTo(this, 'theme:end', function () {
1368 + this.listenTo(this, 'theme:end', () => {
1391 1369
1392 1370 // Make sure we are not already loading
1393 - if (self.collection.loadingThemes) {
1371 + if (this.collection.loadingThemes) {
1394 1372 return;
1395 1373 }
1396 1374
1397 1375 // Set loadingThemes to true and bump page instance of currentQuery.
1398 - self.collection.loadingThemes = true;
1399 - self.collection.currentQuery.page++;
1376 + this.collection.loadingThemes = true;
1377 + this.collection.currentQuery.page++;
1400 1378
1401 1379 // Use currentQuery.page to build the themes request.
1402 - _.extend(self.collection.currentQuery.request, { page: self.collection.currentQuery.page });
1403 - self.collection.query(self.collection.currentQuery.request);
1380 + _.extend(this.collection.currentQuery.request, { page: this.collection.currentQuery.page });
1381 + this.collection.query(this.collection.currentQuery.request);
1404 1382 });
1405 1383
1406 1384 this.listenTo(this.collection, 'query:success', function () {
1407 1385 $('body').removeClass('loading-content');
@@ -1442,9 +1420,9 @@
1442 1420 },
1443 1421
1444 1422 // Sorting navigation
1445 1423 onSort: function (event) {
1446 - var $el = $(event.target),
1424 + let $el = $(event.target),
1447 1425 sort = $el.data('sort');
1448 1426
1449 1427 event.preventDefault();
1450 1428
@@ -1471,9 +1449,9 @@
1471 1449 },
1472 1450
1473 1451 // Filters and Tags
1474 1452 onFilter: function (event) {
1475 - var request,
1453 + let request,
1476 1454 $el = $(event.target),
1477 1455 filter = $el.data('filter');
1478 1456
1479 1457 // Bail if this is already active
@@ -1504,9 +1482,9 @@
1504 1482 },
1505 1483
1506 1484 // Applying filters triggers a tag request
1507 1485 applyFilters: function (event) {
1508 - var name,
1486 + let name,
1509 1487 tags = this.filtersChecked(),
1510 1488 request = { tag: tags },
1511 1489 filteringBy = $('.filtered-by .tags');
1512 1490
@@ -1530,9 +1508,9 @@
1530 1508
1531 1509 // Get the checked filters
1532 1510 // @return {array} of tags or false
1533 1511 filtersChecked: function () {
1534 - var items = $('.filter-group').find(':checkbox'),
1512 + let items = $('.filter-group').find(':checkbox'),
1535 1513 tags = [];
1536 1514
1537 1515 _.each(items.filter(':checked'), function (item) {
1538 1516 tags.push($(item).prop('value'));
@@ -1542,9 +1520,9 @@
1542 1520 if (tags.length === 0) {
1543 1521 $('.filter-drawer .apply-filters').find('span').text('');
1544 1522 $('.filter-drawer .clear-filters').hide();
1545 1523 $('body').removeClass('filters-applied');
1546 - return false;
1524 + return [];
1547 1525 }
1548 1526
1549 1527 $('.filter-drawer .apply-filters').find('span').text(tags.length);
1550 1528 $('.filter-drawer .clear-filters').css('display', 'inline-block');
@@ -1560,20 +1538,23 @@
1560 1538
1561 1539 uploader: function () {
1562 1540 $('a.upload').on('click', function (event) {
1563 1541 event.preventDefault();
1564 - $('body').addClass('show-upload-theme');
1542 + $('.mainwp-bulk-install-showhide-content').hide();
1543 + $('.mainwp-upload-theme').show();
1565 1544 themes.router.navigate(themes.router.baseUrl('&upload'), { replace: true });
1566 - $(this).addClass('mainwp_action_down');
1567 - $('a.browse-themes').removeClass('mainwp_action_down');
1545 + $('a.mainwp-bulk-install-tabs-header-btn').removeClass('green');
1546 + $(this).addClass('green');
1568 1547 $('#mainwp_theme_bulk_install_btn').attr('bulk-action', 'upload');
1569 1548 });
1570 1549 $('a.browse-themes').on('click', function (event) {
1571 1550 event.preventDefault();
1572 - $('body').removeClass('show-upload-theme');
1551 + $('.mainwp-bulk-install-showhide-content').hide();
1552 + $('#mainwp-search-themes-input-container').show();
1553 + $('#theme-filter').show();
1573 1554 themes.router.navigate(themes.router.baseUrl(''), { replace: true });
1574 - $(this).addClass('mainwp_action_down');
1575 - $('a.upload').removeClass('mainwp_action_down');
1555 + $('a.mainwp-bulk-install-tabs-header-btn').removeClass('green');
1556 + $(this).addClass('green');
1576 1557 $('#mainwp_theme_bulk_install_btn').attr('bulk-action', 'install');
1577 1558 });
1578 1559 },
1579 1560
@@ -1599,16 +1580,14 @@
1599 1580
1600 1581 // Clears all the checked filters
1601 1582 // @uses filtersChecked()
1602 1583 clearFilters: function (event) {
1603 - var items = $('.filter-group').find(':checkbox'),
1604 - self = this;
1605 -
1584 + let items = $('.filter-group').find(':checkbox')
1606 1585 event.preventDefault();
1607 1586
1608 - _.each(items.filter(':checked'), function (item) {
1587 + _.each(items.filter(':checked'), (item) => {
1609 1588 $(item).prop('checked', false);
1610 - return self.filtersChecked();
1589 + return this.filtersChecked();
1611 1590 });
1612 1591 },
1613 1592
1614 1593 backToFilters: function (event) {
@@ -1681,10 +1660,9 @@
1681 1660 });
1682 1661 },
1683 1662
1684 1663 routes: function () {
1685 - var self = this,
1686 - request = {};
1664 + let request = {};
1687 1665
1688 1666 // Bind to our global `wp.themes` object
1689 1667 // so that the router is available to sub-views
1690 1668 themes.router = new themes.InstallerRouter();
@@ -1690,22 +1668,23 @@
1690 1668 themes.router = new themes.InstallerRouter();
1691 1669
1692 1670 // Handles `theme` route event
1693 1671 // Queries the API for the passed theme slug
1694 - themes.router.on('route:preview', function (slug) {
1672 + themes.router.on('route:preview', (slug) => {
1695 1673 request.theme = slug;
1696 - self.view.collection.query(request);
1674 + this.view.collection.query(request);
1697 1675 });
1698 1676
1699 1677 // Handles sorting / browsing routes
1700 1678 // Also handles the root URL triggering a sort request
1701 1679 // for `featured`, the default view
1702 - themes.router.on('route:sort', function (sort) {
1680 + themes.router.on('route:sort', (sort) => {
1703 1681 if (!sort) {
1704 1682 sort = 'featured';
1705 1683 }
1706 - self.view.sort(sort);
1707 - self.view.trigger('theme:close');
1684 +
1685 + this.view.sort(sort);
1686 + this.view.trigger('theme:close');
1708 1687 });
1709 1688
1710 1689 // Support the `upload` route by going straight to upload section
1711 1690 themes.router.on('route:upload', function () {
@@ -1740,31 +1719,34 @@
1740 1719
1741 1720 })(jQuery);
1742 1721
1743 1722 // Align theme browser thickbox
1744 -var tb_position;
1745 -jQuery(document).ready(function ($) {
1746 - tb_position = function () {
1747 - var tbWindow = $('#TB_window'),
1748 - width = $(window).width(),
1749 - H = $(window).height(),
1750 - W = (1040 < width) ? 1040 : width,
1723 +jQuery(function($) {
1724 +
1725 + globalThis.tb_position = function () {
1726 + let tbWindow = $('#TB_window'),
1727 + width = $(globalThis).width(),
1728 + H = $(globalThis).height(),
1729 + W = Math.min(1040, width),
1751 1730 adminbar_height = 0;
1752 1731
1753 1732 if ($('#wpadminbar').length) {
1754 - adminbar_height = parseInt($('#wpadminbar').css('height'), 10);
1733 + adminbar_height = Number.parseInt($('#wpadminbar').css('height'), 10);
1755 1734 }
1756 1735
1757 1736 if (tbWindow.length) {
1758 1737 tbWindow.width(W - 50).height(H - 45 - adminbar_height);
1759 1738 $('#TB_iframeContent').width(W - 50).height(H - 75 - adminbar_height);
1760 - tbWindow.css({ 'margin-left': '-' + parseInt(((W - 50) / 2), 10) + 'px' });
1761 - if (typeof document.body.style.maxWidth !== 'undefined') {
1762 - tbWindow.css({ 'top': 20 + adminbar_height + 'px', 'margin-top': '0' });
1739 + tbWindow.css({ 'margin-left': '-' + Number.parseInt(((W - 50) / 2), 10) + 'px' });
1740 + if ('maxWidth' in document.body.style) {
1741 + tbWindow.css({
1742 + top: `${20 + adminbar_height}px`,
1743 + marginTop: 0
1744 + });
1763 1745 }
1764 1746 }
1765 1747 };
1766 1748
1767 - $(window).on('resize',function () {
1768 - tb_position();
1749 + $(globalThis).on('resize', function () {
1750 + globalThis.tb_position();
1769 1751 });
1770 1752 });