PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.1.1
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.1.1
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 6.0.4 6.0.5 All 36 releases
mlsimport / admin / js / mlsimport-customizer-controls.js

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

194 lines 7.2 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.
61 var known = control.state.active.concat( control.state.inactive );
62 control.catalog.forEach( function ( c ) {
63 if ( known.indexOf( c.slug ) === -1 ) {
64 control.state.active.push( c.slug );
65 }
66 } );
67
68 control.renderLists();
69 },
70
71 /**
72 * Re-render both lists from current state and re-bind their buttons.
73 */
74 renderLists: function () {
75 var control = this;
76 // Empty both list containers before repopulating.
77 var $active = control.container.find( '.mlsimport-arranger__active' ).empty();
78 var $inactive = control.container.find( '.mlsimport-arranger__inactive' ).empty();
79
80 // Append one list item per slug in each list, tracking index + length.
81 control.state.active.forEach( function ( slug, i ) {
82 $active.append( control.itemHtml( slug, 'active', i, control.state.active.length ) );
83 } );
84 control.state.inactive.forEach( function ( slug, i ) {
85 $inactive.append( control.itemHtml( slug, 'inactive', i, control.state.inactive.length ) );
86 } );
87
88 control.wire();
89 },
90
91 /**
92 * Build the HTML for one arranger row.
93 *
94 * @param {string} slug Section slug.
95 * @param {string} list Which list this row belongs to ('active'/'inactive').
96 * @param {number} i Row index within its list.
97 * @param {number} len Total rows in the list (to decide up/down buttons).
98 * @return {string} List-item markup.
99 */
100 itemHtml: function ( slug, list, i, len ) {
101 // Human label (fall back to the slug), and the enable/disable toggle glyph.
102 var label = this.labelFor[ slug ] || slug;
103 var moveLabel = 'active' === list ? '✗' : '✓'; // ✗ disable / ✓ enable
104 // Up button only when not first; down button only when not last.
105 var up = i > 0 ? '<button type="button" class="button-link mlsimport-arranger__up" aria-label="Move up">↑</button>' : '';
106 var down = i < len - 1 ? '<button type="button" class="button-link mlsimport-arranger__down" aria-label="Move down">↓</button>' : '';
107 return '<li class="mlsimport-arranger__item" data-slug="' + slug + '" data-list="' + list + '">'
108 + '<span class="mlsimport-arranger__label">' + label + '</span>'
109 + '<span class="mlsimport-arranger__actions">' + up + down
110 + '<button type="button" class="button-link mlsimport-arranger__move" aria-label="Toggle">' + moveLabel + '</button>'
111 + '</span></li>';
112 },
113
114 /**
115 * Bind up/down/toggle click handlers on every rendered row.
116 */
117 wire: function () {
118 var control = this;
119 control.container.find( '.mlsimport-arranger__item' ).each( function () {
120 // Read the row's slug and which list it lives in from data attributes.
121 var $item = $( this );
122 var slug = $item.data( 'slug' );
123 var list = $item.data( 'list' );
124
125 // Move up one position within the same list.
126 $item.find( '.mlsimport-arranger__up' ).on( 'click', function () {
127 control.move( list, slug, -1 );
128 } );
129 // Move down one position within the same list.
130 $item.find( '.mlsimport-arranger__down' ).on( 'click', function () {
131 control.move( list, slug, 1 );
132 } );
133 // Toggle the row between the active and inactive lists.
134 $item.find( '.mlsimport-arranger__move' ).on( 'click', function () {
135 control.flip( list, slug );
136 } );
137 } );
138 },
139
140 /**
141 * Reorder a slug within its list by delta positions, then sync.
142 *
143 * @param {string} list List key ('active'/'inactive').
144 * @param {string} slug Slug to move.
145 * @param {number} delta -1 to move up, +1 to move down.
146 */
147 move: function ( list, slug, delta ) {
148 var arr = this.state[ list ];
149 var idx = arr.indexOf( slug );
150 var to = idx + delta;
151 // Ignore if the slug is missing or the target index is out of bounds.
152 if ( idx === -1 || to < 0 || to >= arr.length ) {
153 return;
154 }
155 // Remove from the old index and re-insert at the target index.
156 arr.splice( idx, 1 );
157 arr.splice( to, 0, slug );
158 this.sync();
159 },
160
161 /**
162 * Move a slug from one list to the other (enable <-> disable), then sync.
163 *
164 * @param {string} from Source list key ('active'/'inactive').
165 * @param {string} slug Slug to flip.
166 */
167 flip: function ( from, slug ) {
168 // Destination is the opposite list.
169 var to = 'active' === from ? 'inactive' : 'active';
170 var idx = this.state[ from ].indexOf( slug );
171 // Nothing to do if the slug isn't in the source list.
172 if ( idx === -1 ) {
173 return;
174 }
175 // Remove from source, append to destination.
176 this.state[ from ].splice( idx, 1 );
177 this.state[ to ].push( slug );
178 this.sync();
179 },
180
181 /**
182 * Write current state back to the Customizer setting and re-render.
183 */
184 sync: function () {
185 // A fresh object so the Customizer sees the value as changed.
186 this.setting.set( {
187 active: this.state.active.slice(),
188 inactive: this.state.inactive.slice()
189 } );
190 this.renderLists();
191 }
192 } );
193 } )( window.wp, window.jQuery );
194