-
Notifications
You must be signed in to change notification settings - Fork 51
/
acm.js
396 lines (353 loc) · 11.4 KB
/
acm.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
( function( window, $, undefined ) {
var document = window.document;
var AdCodeManager = function() {
/**
* A reference to the AdCodeManager object so we avoid confusion with `this` later on
*
* @type {*}
*/
var SELF = this;
/**
* Container for cached UI elements
*
* @type {Object}
*/
var UI = {};
/**
* Used for storing the currently edited ACM ID
* @type {Boolean}
*/
var EDIT_ID = false;
/**
* Initializes the AdCodeManager when the object is instantiated. This script must always run from the footer
* after the DOM elements are rendered
*
* @private
*/
var _init = function() {
_cacheElements();
_bindEvents();
};
/**
* Caches useful DOM elements so we can easily reference them later without the extra lookup
*
* @private
*/
var _cacheElements = function() {
UI.addMoreButton = document.getElementById( 'conditional-tpl' ).querySelector( '.add-more-conditionals' );
UI.theList = document.getElementById( 'the-list' );
UI.theNew = document.getElementById( 'add-adcode' );
};
/**
* Handles binding our events for the page to work
*
* @private
*/
var _bindEvents = function() {
_addEvent( 'click', UI.addMoreButton, _addConditional );
_addEvent( 'click', UI.theList, _delegateListClicks );
_addEvent( 'click', UI.theNew, _delegateNewAdClicks );
_addEvent( 'keydown', UI.theList, _delegateListKeyEvents );
};
/**
* Registers a DOM event for the specified element
*
* @param event The event we're hooking to
* @param element The element we want to monitor for the event
* @param callback The callback to be fired when the event is triggered
* @private
*/
var _addEvent = function( event, element, callback ) {
if( window.addEventListener ) {
element.addEventListener( event, callback, false );
}
else {
element.attachEvent( 'on' + event, callback );
}
};
/**
* Handles adding a new conditional row to the UI for the user
*
* @param e The event object
* @private
*/
var _addConditional = function( e ) {
e = e || window.event;
var target = e.srcElement || e.target;
var parent = target.parentNode.parentNode.querySelector( '.form-new-row' );
_addInlineConditionalRow( parent );
_killEvent( e );
};
/**
* Kills the passed event and prevents it from bubbling up the DOM
*
* @param e The event we're killing
* @private
*/
var _killEvent = function( e ) {
e.returnValue = false;
e.cancelBubble = true;
if( e.stopPropagation ) {
e.stopPropagation();
}
if( e.preventDefault ) {
e.preventDefault();
}
};
/**
* Handles checking delegated events for the add ad code area
*
* @param e The event object
* @private
*/
var _delegateNewAdClicks = function( e ) {
e = e || window.event;
var target = e.srcElement || e.target;
// check for remove conditional call
if( _hasClass( target, 'acm-remove-conditional' ) === true ) {
_removeInlineConditionalRow( target );
_killEvent( e );
}
};
/**
* Handles checking delegated key events for the inline editor and table rows
* @param e
* @private
*/
var _delegateListKeyEvents = function( e ) {
e = e || window.event;
var key = e.which || e.keyCode;
// 13 is Enter, which avoids the default form on the page from saving
if ( key === 13 ) {
_saveInlineEditorChanges();
_killEvent( e );
}
};
/**
* Handles checking delegated events for the inline editor and table rows
*
* @param e The event object
* @private
*/
var _delegateListClicks = function( e ) {
e = e || window.event;
var target = e.srcElement || e.target;
// check for ajax edit click
if( _hasClass( target, 'acm-ajax-edit' ) === true ) {
// close other editors
if( EDIT_ID !== false ) {
if( confirm( 'Are you sure you want to do this? Any unsaved data will be lost.' ) === false ) {
_killEvent( e );
return;
}
_toggleInlineEdit( false );
}
EDIT_ID = parseInt( target.id.replace( 'acm-edit-', '' ), 10 );
_toggleInlineEdit( true );
_killEvent( e );
}
// check for cancel button
else if( _hasClass( target, 'cancel' ) === true && EDIT_ID !== false ) {
_toggleInlineEdit( false );
EDIT_ID = false;
}
// check for remove conditional call
else if( _hasClass( target, 'acm-remove-conditional' ) === true ) {
_removeInlineConditionalRow( target );
_killEvent( e );
}
// check for save button
else if( _hasClass( target, 'save' ) === true ) {
_toggleLoader( true );
_saveInlineEditorChanges();
_killEvent( e );
}
// check for add more conditionals
else if( _hasClass( target, 'add-more-conditionals' ) === true ) {
_addInlineConditionalRow( UI.theList.querySelector( '#ad-code-' + EDIT_ID + ' .acm-editor-row .acm-conditional-fields .form-new-row' ) );
_killEvent( e );
}
};
/**
* Saves any inline editor changes that occurred
*
* @private
*/
var _saveInlineEditorChanges = function() {
$.post( window.ajaxurl, _getFormData(), function( result ) {
if( result ) {
if( result.indexOf( '<tr' ) > -1 ) {
$( document.getElementById( 'ad-code-' + EDIT_ID ) ).before( result).remove();
EDIT_ID = false;
}
else {
_showError( result );
}
}
else {
_showError( inlineEditL10n.error );
}
} );
};
/**
* Shows the error for this ad code if it exists
*
* @param html
* @private
*/
var _showError = function( html ) {
var errorContainer = document.getElementById( 'ad-code-' + EDIT_ID ).querySelector( '.acm-editor-row .inline-edit-save .error' );
errorContainer.innerHTML = html;
errorContainer.style.display = 'block';
};
/**
* Custom serialization function based off of $.serializeArray() - slimmed down to exactly what we need
*
* @return {Array}
* @private
*/
var _getFormData = function() {
var data = [];
var fields = document.getElementById( 'ad-code-' + EDIT_ID ).querySelector( '.acm-editor-row fieldset' );
var elements = fields.querySelectorAll( 'input, select, textarea' ), element, name;
for( var i = 0, len = elements.length; i < len; i++ ) {
element = elements[ i ];
name = element.name.replace( /^\s+|\s+$/i, '' );
if( name === '' ) {
continue;
}
data.push( { name : name, value : element.value } );
}
return data;
};
/**
* Removes the conditional row from the perspective of the button clicked
*
* @param target The `remove` button that was clicked.
* @private
*/
var _removeInlineConditionalRow = function( target ) {
var row = target.parentNode.parentNode;
var parent = row.parentNode;
parent.removeChild( row );
};
/**
* Add a new inline editor conditional row for the current ad-code
*
* @private
*/
var _addInlineConditionalRow = function( parent ) {
// create a new element
var newConditional = document.createElement( 'div' );
newConditional.className = 'conditional-single-field';
newConditional.innerHTML = document.getElementById( 'conditional-single-field-master' ).innerHTML;
newConditional.querySelector( '.conditional-arguments' ).innerHTML += '<a href="#" class="acm-remove-conditional">Remove</a>';
parent.appendChild( newConditional );
};
/**
* Toggles the loader for the form. This should only be used when the save button is clicked and we have a current
* EDIT_ID available
*
* @param showing Indicates whether the loader should be showing or not
* @private
*/
var _toggleLoader = function( showing ) {
var loader = document.querySelector( '#ad-code-' + EDIT_ID + ' .acm-editor-row .inline-edit-save .waiting' );
loader.style.display = ( showing === true ) ? 'block' : 'none';
};
/**
* Lightweight utility function that handles checking an element to see if it contains a class
*
* @param element The element we're checking against
* @param className The class name we're looking for
* @return {Boolean}
* @private
*/
var _hasClass = function( element, className ) {
return ( ' ' + element.className + ' ' ).indexOf( ' ' + className + ' ' ) > -1;
};
/**
* Handles toggling the inline editor. We assume EDIT_ID is being handled correctly for this to work.
*
* @param visible Indicates whether we are hiding/showing the inline editor.
* @private
*/
var _toggleInlineEdit = function( visible ) {
var row = document.getElementById( 'ad-code-' + EDIT_ID );
if( visible === true ) {
_toggleTableChildrenDisplay( row, false );
_createNewInlineRow( EDIT_ID, row );
}
else {
_toggleTableChildrenDisplay( row, true );
_removeEditInlineRow( row );
}
};
/**
* Toggles all the table children of the parent.
*
* @param parent The parent table row we're toggling td's for
* @param display Indicates whether or not the row children should be shown or not
* @private
*/
var _toggleTableChildrenDisplay = function( parent, display ) {
display = ( display === true ) ? 'table-cell' : 'none';
var children = parent.children;
for( var i = 0, len = children.length; i < len; i++ ) {
children[ i ].style.display = display;
}
};
/**
* Handles creating the new inline editor row with all of the necessary UI controls. Notice that we do not rebind
* events because they are already handled by the delegation technique in `_delegateListClicks`
*
* @param id The ID of the ad-code you are editing
* @param parentToBe The DOM element that the newRow will be inserted into
* @private
*/
var _createNewInlineRow = function( id, parentToBe ) {
var newRow = document.createElement( 'td' );
newRow.setAttribute( 'colspan', ( parentToBe.children.length - 1 ) );
newRow.className = 'acm-editor-row';
newRow.innerHTML = document.getElementById( 'inline-edit' ).innerHTML;
// fill in the rows with existing HTML here
var data = _getDataFromRow( id );
newRow.querySelector( 'input[name="id"]' ).value = id;
newRow.querySelector( '.acm-conditional-fields' ).innerHTML = data.conditionalFields;
newRow.querySelector( '.acm-column-fields' ).innerHTML = data.columnFields;
newRow.querySelector( '.acm-priority-field' ).innerHTML = data.priority;
newRow.querySelector( '.acm-operator-field' ).innerHTML = data.operator;
parentToBe.appendChild( newRow );
};
/**
* Removes the editor inline row if it exists
*
* @param parent The parent DOM element where we are removing the oldRow from
* @private
*/
var _removeEditInlineRow = function( parent ) {
var oldRow = parent.querySelector( '.acm-editor-row' );
parent.removeChild( oldRow );
parent.querySelector( '.column-id' ).style.display = 'none';
};
/**
* Builds an object literal containing HTML for the new Row based off of existing DOM elements and their HTML
*
* @param id The ID of the ad-code we're retrieving information from.
* @return {Object}
* @private
*/
var _getDataFromRow = function( id ) {
var dataParent = document.getElementById( 'inline_' + id );
return {
conditionalFields : dataParent.querySelector( '.acm-conditional-fields' ).innerHTML,
columnFields : dataParent.querySelector( '.acm-column-fields' ).innerHTML,
priority : dataParent.querySelector( '.acm-priority-field' ).innerHTML,
operator : dataParent.querySelector( '.acm-operator-field' ).innerHTML
};
};
// fire our initialization method
_init();
};
window.AdCodeManager = new AdCodeManager();
} )( window, jQuery );