PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.25
Advanced Ads – Ad Manager & AdSense v2.0.25
2.0.25 2.0.24 2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / admin / assets / js / termination.js
advanced-ads / admin / assets / js Last commit date
adblocker-image-data.js 1 year ago admin-global.js 1 year ago admin.js 2 days ago advertisement.js 3 months ago conditions.js 1 year ago dialog-advads-modal.js 1 year ago termination.js 1 year ago ui.js 3 years ago
termination.js
275 lines
1 // phpcs:disable Generic.WhiteSpace.ScopeIndent.IncorrectExact -- PHPCS can't handle es5 short functions
2 function Advads_Termination( element ) {
3 /**
4 * Function to reset the changed nodes to default values.
5 *
6 * @constructor
7 */
8 function FormValues() {
9 this.addedNodes = [];
10 this.removedNodes = [];
11 };
12
13 this.initialFormValues = new FormValues();
14 this.changedFormValues = new FormValues();
15
16 const blocklist = [
17 'active_post_lock'
18 ];
19
20 this.observers = {
21 list: [],
22
23 push: item => {
24 this.observers.list.push( item );
25 },
26
27 disconnect: () => {
28 this.observers.list.forEach( observer => {
29 observer.disconnect();
30 } );
31 this.observers.list = [];
32 }
33 };
34
35 /**
36 * Set an initial form value.
37 * Can be used e.g. when a field is updated after an AJAX call.
38 *
39 * @param {String} key The key of the value that should be changed in the initial form value array.
40 * @param {Node} input The input field node.
41 *
42 * @returns {void}
43 */
44 this.setInitialValue = ( key, input ) => {
45 if ( ! input || ! input.value ) {
46 return;
47 }
48 this.initialFormValues[key] = input.value;
49 };
50
51 /**
52 * Collect input values.
53 * Checkboxes are true/false, unless they are part of a group.
54 * Radio buttons have a boolean value on the saved value, only the checked one will be collected.
55 *
56 * @param {FormValues} object
57 * @param {Node} input
58 * @return {FormValues}
59 */
60 const collectInputValue = function ( object, input ) {
61 /**
62 * Collect checkbox group values.
63 * If there are multiple checkboxes with the same `nome` attribute, collect all values for this group.
64 *
65 * @param {NodeList} group Iterable of inputs with the same `name` attribute.
66 * @return {FormValues}
67 */
68 const collectCheckboxGroup = ( group ) => {
69
70 object[group[0].name] = [];
71 group.forEach( input => {
72 if ( input.checked ) {
73 object[input.name].push( input.value );
74 }
75 } );
76
77 return object;
78 };
79
80 if ( input.type === 'checkbox' ) {
81 const checkboxGroup = element.querySelectorAll( '[name="' + input.name + '"]' );
82 if ( checkboxGroup.length > 1 ) {
83 return collectCheckboxGroup( checkboxGroup, input );
84 }
85
86 object[input.name] = input.checked;
87
88 return object;
89 }
90
91 // if a radio button is not checked, don't collect it.
92 if ( input.type === 'radio' && ! input.checked ) {
93 return object;
94 }
95
96 object[input.name] = input.value;
97
98 return object;
99 };
100
101 /**
102 * Setup a mutationobserver to check for added and removed form fields.
103 * This especially applies to conditions.
104 *
105 * @type {MutationObserver}
106 */
107 const addedRemovedObserver = new MutationObserver( mutations => {
108 for ( const mutation of mutations ) {
109 for ( const removedNode of mutation.removedNodes ) {
110 const nodes = document.createTreeWalker( removedNode, NodeFilter.SHOW_ELEMENT );
111 while ( nodes.nextNode() ) {
112 if ( nodes.currentNode.tagName === 'INPUT' || nodes.currentNode.tagName === 'SELECT' ) {
113 const index = this.changedFormValues.addedNodes.indexOf( nodes.currentNode.name );
114 if ( index > - 1 ) {
115 this.changedFormValues.addedNodes.splice( index, 1 );
116 } else {
117 this.changedFormValues.removedNodes.push( nodes.currentNode.name );
118 }
119 }
120 }
121 }
122 for ( const addedNode of mutation.addedNodes ) {
123 if ( addedNode.nodeType === Node.TEXT_NODE ) {
124 continue;
125 }
126
127 const nodes = document.createTreeWalker( addedNode, NodeFilter.SHOW_ELEMENT );
128 while ( nodes.nextNode() ) {
129 if ( nodes.currentNode.tagName === 'INPUT' || nodes.currentNode.tagName === 'SELECT' ) {
130 if ( nodes.currentNode.name === '' ) {
131 continue;
132 }
133 this.changedFormValues.addedNodes.push( nodes.currentNode.name );
134 }
135 }
136 }
137 }
138 } );
139
140 // attach the mutation observer to the passed element.
141 addedRemovedObserver.observe( element, {childList: true, subtree: true} );
142 this.observers.push( addedRemovedObserver );
143
144 /**
145 * Check if there are inputs that have been changed and if their value is different.
146 *
147 * @param {Object} reference The initial values when the modal loaded, indexed by name attribute.
148 * @param {Object} changed The input values that were changed, indexed by name.
149 *
150 * @return {boolean}
151 */
152 this.hasChanged = ( reference, changed ) => {
153 for ( const name in changed ) {
154 if ( ! reference.hasOwnProperty( name ) || reference[name].toString() !== changed[name].toString() ) {
155 return true;
156 }
157 }
158
159 return false;
160 };
161
162 /**
163 * If the modal is associated with a form and any values have changed, ask for confirmation to navigate away.
164 * Returns true if the user agrees with termination, false otherwise.
165 *
166 * @param {boolean} reload Whether to reload the page on added and removed nodes (needed for the modal). Default false.
167 *
168 * @return {boolean}
169 */
170 this.terminationNotice = ( reload = false ) => {
171 if ( ! this.hasChanged( this.initialFormValues, this.changedFormValues ) ) {
172 return true;
173 }
174
175 // ask user for confirmation.
176 if ( window.confirm( window.advadstxt.confirmation ) ) {
177 // if we have added or removed nodes, we might need to reload the page.
178 if ( this.changedFormValues.addedNodes.length || this.changedFormValues.removedNodes.length ) {
179 if ( reload ) {
180 window.location.reload();
181 }
182 return true;
183 }
184
185 // otherwise, we'll replace the values with the previous values.
186 for ( const name in this.changedFormValues ) {
187 const input = element.querySelector( '[name="' + name + '"]' );
188 if ( input === null ) {
189 continue;
190 }
191
192 if ( input.type === 'checkbox' ) {
193 input.checked = this.initialFormValues[name];
194 } else if ( input.type === 'radio' ) {
195 let value = (this.initialFormValues[name] !== null && this.initialFormValues[name] !== undefined) ? this.initialFormValues[name] : input.value;
196 element.querySelector( '[name="' + name + '"][value="' + value + '"]' ).checked = true;
197 } else {
198 input.value = this.initialFormValues[name];
199 }
200 }
201
202 return true;
203 }
204
205 return false;
206 };
207
208 /**
209 * Set the initial values to the current ones, then reset the changed form values
210 */
211 this.resetInitialValues = () => {
212 if ( this.changedFormValues.addedNodes.length ) {
213 for ( const name in this.changedFormValues.addedNodes ) {
214 this.initialFormValues[name] = this.changedFormValues.addedNodes[name];
215 }
216 }
217 if ( this.changedFormValues.removedNodes.length ) {
218 for ( const name in this.changedFormValues.removedNodes ) {
219 if ( this.initialFormValues[name] !== undefined ) {
220 delete ( this.initialFormValues[name] );
221 }
222 }
223 }
224 for ( const name in this.changedFormValues ) {
225 if ( 'removedNodes' === name || 'addedNodes' === name ) {
226 continue;
227 }
228 if ( this.initialFormValues[name] !== undefined ) {
229 this.initialFormValues[name] = this.changedFormValues[name];
230 }
231 }
232 this.changedFormValues = new FormValues();
233 };
234
235 /**
236 * Collect inputs in this modal and save their initial and changed values (if any).
237 */
238 this.collectValues = () => {
239 const isDialog = element.tagName === 'DIALOG';
240
241 element.querySelectorAll( 'input, select, textarea' ).forEach( input => {
242 if ( ! input.name.length || blocklist.includes( input.id ) || blocklist.includes( input.name ) ) {
243 return;
244 }
245
246 // if the element itself is not a dialog but the input is within a dialog, ignore it. This accounts for split forms, e.g. the placements page where some inputs are hidden in a modal dialog.
247 if ( ! isDialog && input.closest( 'dialog' ) ) {
248 return;
249 }
250
251 this.initialFormValues = collectInputValue( this.initialFormValues, input );
252
253 // if the input is `hidden` no change event gets triggered. Use MutationObservers to check for changes in the value attribute.
254 if ( input.type === 'hidden' ) {
255 const hiddenObserver = new MutationObserver( function ( mutations, observer ) {
256 mutations.forEach( mutation => {
257 if ( mutation.attributeName === 'value' ) {
258 mutation.target.dispatchEvent( new Event( 'input' ) );
259 }
260 } );
261 } );
262 hiddenObserver.observe( element, {
263 attributes: true,
264 subtree: true
265 } );
266 this.observers.push( hiddenObserver );
267 }
268
269 input.addEventListener( 'input', event => {
270 this.changedFormValues = collectInputValue( this.changedFormValues, input );
271 } );
272 } );
273 };
274 };
275