PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2
Jetpack – WP Security, Backup, Speed, & Growth v16.2
16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 All 502 releases
jetpack / _inc / jetpack-modules.models.js

jetpack-modules.models.js in Jetpack – WP Security, Backup, Speed, & Growth 16.2, at _inc/jetpack-modules.models.js

165 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 window.jetpackModules = window.jetpackModules || {};
2
3 window.jetpackModules.models = ( function ( window, $, Backbone ) {
4 'use strict';
5
6 var models = {};
7
8 models.Modules = Backbone.Model.extend( {
9 visibles: {},
10
11 /**
12 * Applies the active availability, tag, state/purpose and search filters
13 * to the raw module set and returns the matching items (unsorted).
14 *
15 * @param {Object} [options] Filtering options.
16 * @param {boolean} [options.ignoreTag] When true, skip the "Show" tag
17 * facet so counts can be computed independently of the current tag
18 * selection (faceted counting).
19 * @param {boolean} [options.ignorePurpose] When true, skip the "Purpose"
20 * facet so its own faceted counts can be computed.
21 * @return {Array} Filtered module items.
22 */
23 apply_filters: function ( options ) {
24 options = options || {};
25
26 var subsubsub = $( '.subsubsub .current a' ),
27 purpose = $( '.purpose-filter .current a' ),
28 items = Object.values( this.get( 'raw' ) ),
29 m_availability = $( '.button-group.availability-filter .active' ),
30 m_search = $( '#srch-term-search-input' ).val().toLowerCase();
31
32 // Offline-mode top-level filter: when "Hide unavailable" is selected,
33 // drop every module that isn't available offline, regardless of the
34 // other filters below it.
35 if ( 'hide-unavailable' === m_availability.data( 'availability' ) ) {
36 items = items.filter( item => item.available );
37 }
38
39 // Tag facet ("Show"). Skipped when computing that facet's own counts.
40 if ( ! options.ignoreTag && ! subsubsub.closest( 'li' ).hasClass( 'all' ) ) {
41 items = items.filter( item => item.module_tags.includes( subsubsub.data( 'title' ) ) );
42 }
43
44 // Purpose facet. Skipped when computing that facet's own counts.
45 if (
46 ! options.ignorePurpose &&
47 purpose.length &&
48 ! purpose.closest( 'li' ).hasClass( 'all' )
49 ) {
50 items = items.filter( item => item.product_group === purpose.data( 'group' ) );
51 }
52
53 // State facet (segmented buttons). "All" carries no data-filter-by.
54 $( '.button-group.module-filter .active' ).each( function () {
55 var by = $( this ).data( 'filter-by' ),
56 value = $( this ).data( 'filter-value' );
57
58 if ( by ) {
59 items = items.filter( item => item[ by ] === value );
60 }
61 } );
62
63 if ( m_search.length ) {
64 items = items.filter( function ( item ) {
65 var search_text =
66 item.name +
67 ' ' +
68 item.description +
69 ' ' +
70 item.long_description +
71 ' ' +
72 item.search_terms +
73 ' ' +
74 item.module_tags;
75 return -1 !== search_text.toLowerCase().indexOf( m_search );
76 } );
77 }
78
79 return items;
80 },
81
82 /**
83 * Computes, for the "Show" tag list, how many modules match every active
84 * filter except the tag facet itself. This powers faceted counts that
85 * stay meaningful as the user narrows the other filters.
86 *
87 * @return {Object} Map of translated tag title to count, plus an
88 * `__all__` key holding the total across all tags.
89 */
90 tag_counts: function () {
91 var base = this.apply_filters( { ignoreTag: true } ),
92 counts = { __all__: base.length };
93
94 base.forEach( function ( item ) {
95 ( item.module_tags || [] ).forEach( function ( tag ) {
96 counts[ tag ] = ( counts[ tag ] || 0 ) + 1;
97 } );
98 } );
99
100 return counts;
101 },
102
103 /**
104 * Computes, for the "Purpose" list, how many modules fall in each product
105 * group under every active filter except the Purpose facet itself. Mirrors
106 * tag_counts() so the two facet lists behave identically.
107 *
108 * @return {Object} Map of product-group slug to count, plus an `__all__`
109 * key holding the total across all groups.
110 */
111 purpose_counts: function () {
112 var base = this.apply_filters( { ignorePurpose: true } ),
113 counts = { __all__: base.length };
114
115 base.forEach( function ( item ) {
116 if ( item.product_group ) {
117 counts[ item.product_group ] = ( counts[ item.product_group ] || 0 ) + 1;
118 }
119 } );
120
121 return counts;
122 },
123
124 /**
125 * Updates modules.items dataset to be a reflection of both the current
126 * modules.raw data, as well as any filters or sorting that may be in effect.
127 */
128 filter_and_sort: function () {
129 var items = this.apply_filters();
130
131 // Default alphabetical order by name. The product-group sort below is
132 // stable, so this ordering is preserved within each group.
133 items.sort( ( a, b ) => a.name.localeCompare( b.name ) );
134
135 // Group by product group (primary), then push unavailable modules to
136 // the end of their group. Any user-selected sort above is preserved
137 // within a group because Array.prototype.sort is stable in the
138 // browsers we support.
139 var groupOrder = { growth: 0, performance: 1, security: 2, other: 3 };
140 items.sort( function ( a, b ) {
141 var ga = groupOrder[ a.product_group ] !== undefined ? groupOrder[ a.product_group ] : 99;
142 var gb = groupOrder[ b.product_group ] !== undefined ? groupOrder[ b.product_group ] : 99;
143
144 if ( ga !== gb ) {
145 return ga - gb;
146 }
147
148 return ( b.available ? 1 : 0 ) - ( a.available ? 1 : 0 );
149 } );
150
151 // Now shove it back in.
152 this.set( 'items', items );
153
154 return this;
155 },
156
157 initialize: function () {
158 var items = this.get( 'items' );
159 this.set( 'raw', items );
160 },
161 } );
162
163 return models;
164 } )( window, jQuery, Backbone );
165