PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.2.3
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.2.3
7.2.3 7.2.2 7.2.1 7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 All 38 releases
mlsimport / admin / js / mlsimport-customizer-controls.js

mlsimport-customizer-controls.js in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.2.3, at admin/js/mlsimport-customizer-controls.js

198 lines 7.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Customizer control JS for the MLSImport "arrange sections" fields.
3 *
4 * Registers the wp.customize.controlConstructor for the PHP control type
5 * 'mlsimport_sections'. Renders an Enabled and a Disabled list; each item can move
6 * up/down within its list or flip between lists. Every change writes the same
7 * { active:[...], inactive:[...] } object back to the setting, which is the shared
8 * mlsimport_standalone_options[...] key. See class-mlsimport-customize-sections-control.php.
9 */
10 ( function ( wp, $ ) {
11 'use strict';
12
13 // Bail if the Customizer API is not present (script loaded out of context).
14 if ( ! wp || ! wp.customize ) {
15 return;
16 }
17
18 // Land the preview on the listings archive so accent + card style are visible
19 // on open. Only redirect away from the home page, and only once, so it never
20 // fights the user navigating the preview to a property/agent page.
21 wp.customize.bind( 'ready', function () {
22 // Nothing to do without a configured archive URL.
23 var cfg = window.mlsimportCustomizer || {};
24 if ( ! cfg.archiveUrl ) {
25 return;
26 }
27 var previewer = wp.customize.previewer;
28 // Current preview URL and the site home URL, trailing slash ignored.
29 var current = previewer.previewUrl.get();
30 var home = ( wp.customize.settings && wp.customize.settings.url && wp.customize.settings.url.home ) || '';
31 // Only redirect when the preview is sitting on the home page.
32 if ( home && current.replace( /\/$/, '' ) === home.replace( /\/$/, '' ) ) {
33 previewer.previewUrl.set( cfg.archiveUrl );
34 }
35 } );
36
37 // Custom control constructor for the PHP 'mlsimport_sections' control type.
38 wp.customize.controlConstructor.mlsimport_sections = wp.customize.Control.extend( {
39 /**
40 * Initialise control state from params and render the two lists.
41 */
42 ready: function () {
43 var control = this;
44
45 // Build a slug -> label lookup from the catalog of available sections.
46 control.catalog = control.params.catalog || [];
47 control.labelFor = {};
48 control.catalog.forEach( function ( c ) {
49 control.labelFor[ c.slug ] = c.label;
50 } );
51
52 // Seed working state from the saved value (copied so we never mutate params).
53 var value = control.params.mlsValue || { active: [], inactive: [] };
54 control.state = {
55 active: ( value.active || [] ).slice(),
56 inactive: ( value.inactive || [] ).slice()
57 };
58
59 // Surface any catalog choice the saved value never mentions (e.g. a
60 // section added after the user last saved) so it is never hidden. It
61 // lands where the field's default puts it — Disabled for the slugs in
62 // defaultInactive (the Tabs/Accordion containers), Enabled otherwise —
63 // which is where the PHP sanitizer will put it on save.
64 var known = control.state.active.concat( control.state.inactive );
65 var off = control.params.defaultInactive || [];
66 control.catalog.forEach( function ( c ) {
67 if ( known.indexOf( c.slug ) === -1 ) {
68 control.state[ off.indexOf( c.slug ) === -1 ? 'active' : 'inactive' ].push( c.slug );
69 }
70 } );
71
72 control.renderLists();
73 },
74
75 /**
76 * Re-render both lists from current state and re-bind their buttons.
77 */
78 renderLists: function () {
79 var control = this;
80 // Empty both list containers before repopulating.
81 var $active = control.container.find( '.mlsimport-arranger__active' ).empty();
82 var $inactive = control.container.find( '.mlsimport-arranger__inactive' ).empty();
83
84 // Append one list item per slug in each list, tracking index + length.
85 control.state.active.forEach( function ( slug, i ) {
86 $active.append( control.itemHtml( slug, 'active', i, control.state.active.length ) );
87 } );
88 control.state.inactive.forEach( function ( slug, i ) {
89 $inactive.append( control.itemHtml( slug, 'inactive', i, control.state.inactive.length ) );
90 } );
91
92 control.wire();
93 },
94
95 /**
96 * Build the HTML for one arranger row.
97 *
98 * @param {string} slug Section slug.
99 * @param {string} list Which list this row belongs to ('active'/'inactive').
100 * @param {number} i Row index within its list.
101 * @param {number} len Total rows in the list (to decide up/down buttons).
102 * @return {string} List-item markup.
103 */
104 itemHtml: function ( slug, list, i, len ) {
105 // Human label (fall back to the slug), and the enable/disable toggle glyph.
106 var label = this.labelFor[ slug ] || slug;
107 var moveLabel = 'active' === list ? '✗' : '✓'; // ✗ disable / ✓ enable
108 // Up button only when not first; down button only when not last.
109 var up = i > 0 ? '<button type="button" class="button-link mlsimport-arranger__up" aria-label="Move up">↑</button>' : '';
110 var down = i < len - 1 ? '<button type="button" class="button-link mlsimport-arranger__down" aria-label="Move down">↓</button>' : '';
111 return '<li class="mlsimport-arranger__item" data-slug="' + slug + '" data-list="' + list + '">'
112 + '<span class="mlsimport-arranger__label">' + label + '</span>'
113 + '<span class="mlsimport-arranger__actions">' + up + down
114 + '<button type="button" class="button-link mlsimport-arranger__move" aria-label="Toggle">' + moveLabel + '</button>'
115 + '</span></li>';
116 },
117
118 /**
119 * Bind up/down/toggle click handlers on every rendered row.
120 */
121 wire: function () {
122 var control = this;
123 control.container.find( '.mlsimport-arranger__item' ).each( function () {
124 // Read the row's slug and which list it lives in from data attributes.
125 var $item = $( this );
126 var slug = $item.data( 'slug' );
127 var list = $item.data( 'list' );
128
129 // Move up one position within the same list.
130 $item.find( '.mlsimport-arranger__up' ).on( 'click', function () {
131 control.move( list, slug, -1 );
132 } );
133 // Move down one position within the same list.
134 $item.find( '.mlsimport-arranger__down' ).on( 'click', function () {
135 control.move( list, slug, 1 );
136 } );
137 // Toggle the row between the active and inactive lists.
138 $item.find( '.mlsimport-arranger__move' ).on( 'click', function () {
139 control.flip( list, slug );
140 } );
141 } );
142 },
143
144 /**
145 * Reorder a slug within its list by delta positions, then sync.
146 *
147 * @param {string} list List key ('active'/'inactive').
148 * @param {string} slug Slug to move.
149 * @param {number} delta -1 to move up, +1 to move down.
150 */
151 move: function ( list, slug, delta ) {
152 var arr = this.state[ list ];
153 var idx = arr.indexOf( slug );
154 var to = idx + delta;
155 // Ignore if the slug is missing or the target index is out of bounds.
156 if ( idx === -1 || to < 0 || to >= arr.length ) {
157 return;
158 }
159 // Remove from the old index and re-insert at the target index.
160 arr.splice( idx, 1 );
161 arr.splice( to, 0, slug );
162 this.sync();
163 },
164
165 /**
166 * Move a slug from one list to the other (enable <-> disable), then sync.
167 *
168 * @param {string} from Source list key ('active'/'inactive').
169 * @param {string} slug Slug to flip.
170 */
171 flip: function ( from, slug ) {
172 // Destination is the opposite list.
173 var to = 'active' === from ? 'inactive' : 'active';
174 var idx = this.state[ from ].indexOf( slug );
175 // Nothing to do if the slug isn't in the source list.
176 if ( idx === -1 ) {
177 return;
178 }
179 // Remove from source, append to destination.
180 this.state[ from ].splice( idx, 1 );
181 this.state[ to ].push( slug );
182 this.sync();
183 },
184
185 /**
186 * Write current state back to the Customizer setting and re-render.
187 */
188 sync: function () {
189 // A fresh object so the Customizer sees the value as changed.
190 this.setting.set( {
191 active: this.state.active.slice(),
192 inactive: this.state.inactive.slice()
193 } );
194 this.renderLists();
195 }
196 } );
197 } )( window.wp, window.jQuery );
198