balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

redux-extension-customizer.js (15802B)


      1 /* global jQuery, document, redux, redux_change:true, wp */
      2 
      3 /**
      4  * SerializeJSON jQuery plugin.
      5  * https://github.com/marioizquierdo/jquery.serializeJSON
      6  * version 2.6.0 (Apr, 2015)
      7 
      8  * Copyright (c) 2012, 2015 Mario Izquierdo
      9  * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
     10  * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
     11  */
     12 (function( $ ) {
     13 	'use strict';
     14 
     15 	// Usage: jQuery('form').serializeJSON() .
     16 	$.fn.serializeJSON = function( options ) {
     17 		var serializedObject, formAsArray, keys, type, value, f, opts;
     18 		f           = $.serializeJSON;
     19 		opts        = f.setupOpts( options );                       // Calculate values for options {parseNumbers, parseBoolens, parseNulls}.
     20 		formAsArray = this.serializeArray();                        // Array of objects {name, value}.
     21 		f.readCheckboxUncheckedValues( formAsArray, this, opts );   // Add {name, value} of unchecked checkboxes if needed.
     22 
     23 		serializedObject = {};
     24 
     25 		$.each(
     26 			formAsArray,
     27 			function( i, input ) {
     28 				i = null;
     29 
     30 				keys = f.splitInputNameIntoKeysArray( input.name, opts );
     31 				type = keys.pop();                                      // The last element is always the type ('string' by default).
     32 				if ( 'skip' !== type ) {                                // Aasy way to skip a value.
     33 					value = f.parseValue( input.value, type, opts );    // String, number, boolean or null.
     34 					if ( opts.parseWithFunction && '_' === type ) {
     35 						value = opts.parseWithFunction( value, input.name );
     36 					} // Allow for custom parsing.
     37 
     38 					f.deepSet( serializedObject, keys, value, opts );
     39 				}
     40 			}
     41 		);
     42 
     43 		return serializedObject;
     44 	};
     45 
     46 	// Use $.serializeJSON as namespace for the auxiliar functions
     47 	// and to define defaults.
     48 	$.serializeJSON = {
     49 
     50 		defaultOptions: {
     51 			checkboxUncheckedValue: undefined, // To include that value for unchecked checkboxes (instead of ignoring them).
     52 
     53 			parseNumbers: false, // Convert values like '1', '-2.33' to 1, -2.33.
     54 			parseBooleans: false, // Convert 'true', 'false' to true, false.
     55 			parseNulls: false, // Convert 'null' to null.
     56 			parseAll: false, // All of the above.
     57 			parseWithFunction: null, // To use custom parser, a function like: function(val){ return parsed_val; }.
     58 
     59 			customTypes: {}, // Override defaultTypes.
     60 			defaultTypes: {
     61 				string: function( str ) {
     62 					return String( str );
     63 				}, number: function( str ) {
     64 					return Number( str );
     65 				}, boolean: function( str ) {
     66 					return (['false', 'null', 'undefined', '', '0'].indexOf( str ) === - 1 );
     67 				}, null: function( str ) {
     68 					return (['false', 'null', 'undefined', '', '0'].indexOf( str ) !== - 1 ) ? null : str;
     69 				}, array: function( str ) {
     70 					return JSON.parse( str );
     71 				}, object: function( str ) {
     72 					return JSON.parse( str );
     73 				}, auto: function( str ) {
     74 					return $.serializeJSON.parseValue(
     75 						str,
     76 						null,
     77 						{
     78 							parseNumbers: true,
     79 							parseBooleans: true,
     80 							parseNulls: true
     81 						}
     82 					);
     83 				} // Try again with something like "parseAll".
     84 			},
     85 
     86 			useIntKeysAsArrayIndex: false // Name="foo[2]" value="v" => {foo: [null, null, "v"]}, instead of {foo: ["2": "v"]}.
     87 		},
     88 
     89 		// Merge option defaults into the options.
     90 		setupOpts: function( options ) {
     91 			var opt, validOpts, defaultOptions, optWithDefault, parseAll, f;
     92 			f = $.serializeJSON;
     93 
     94 			if ( null === options || undefined === options ) {
     95 				options = {};       // Options ||= {}.
     96 			}
     97 
     98 			defaultOptions = f.defaultOptions || {}; // Default Options.
     99 
    100 			// Make sure that the user didn't misspell an option.
    101 			validOpts = [
    102 				'checkboxUncheckedValue',
    103 				'parseNumbers',
    104 				'parseBooleans',
    105 				'parseNulls',
    106 				'parseAll',
    107 				'parseWithFunction',
    108 				'customTypes',
    109 				'defaultTypes',
    110 				'useIntKeysAsArrayIndex'
    111 			]; // Re-define because the user may override the defaultOptions.
    112 
    113 			for ( opt in options ) {
    114 				if ( validOpts.indexOf( opt ) === - 1 ) {
    115 					throw new Error( 'serializeJSON ERROR: invalid option ' + opt + '. Please use one of ' + validOpts.join( ', ' ) );
    116 				}
    117 			}
    118 
    119 			// Helper to get the default value for this option if none is specified by the user.
    120 			optWithDefault = function( key ) {
    121 				return ( false !== options[key] ) && ( '' !== options[key] ) && ( options[key] || defaultOptions[key] );
    122 			};
    123 
    124 			// Return computed options (opts to be used in the rest of the script).
    125 			parseAll = optWithDefault( 'parseAll' );
    126 			return {
    127 				checkboxUncheckedValue: optWithDefault( 'checkboxUncheckedValue' ),
    128 				parseNumbers: parseAll || optWithDefault( 'parseNumbers' ),
    129 				parseBooleans: parseAll || optWithDefault( 'parseBooleans' ),
    130 				parseNulls: parseAll || optWithDefault( 'parseNulls' ),
    131 				parseWithFunction: optWithDefault( 'parseWithFunction' ),
    132 				typeFunctions: $.extend( {}, optWithDefault( 'defaultTypes' ), optWithDefault( 'customTypes' ) ),
    133 				useIntKeysAsArrayIndex: optWithDefault( 'useIntKeysAsArrayIndex' )
    134 			};
    135 		},
    136 
    137 		// Given a string, apply the type or the relevant "parse" options, to return the parsed value.
    138 		parseValue: function( str, type, opts ) {
    139 			var typeFunction, f;
    140 			f = $.serializeJSON;
    141 
    142 			// Parse with a type if available.
    143 			typeFunction = opts.typeFunctions && opts.typeFunctions[type];
    144 			if ( typeFunction ) {
    145 				return typeFunction( str ); // Use specific type.
    146 			}
    147 
    148 			// Otherwise, check if there is any auto-parse option enabled and use it.
    149 			if ( opts.parseNumbers && f.isNumeric( str ) ) {
    150 				return Number( str );
    151 			} // Auto: number.
    152 
    153 			if ( opts.parseBooleans && ( 'true' === str || 'false' === str ) ) {
    154 				return 'true' === str;
    155 			} // Auto: boolean.
    156 
    157 			if ( opts.parseNulls && 'null' === str ) {
    158 				return null;
    159 			} // Auto: null.
    160 
    161 			// If none applies, just return the str.
    162 			return str;
    163 		},
    164 
    165 		isObject: function( obj ) {
    166 			return obj === Object( obj );
    167 		}, // Is this variable an object?
    168 		isUndefined: function( obj ) {
    169 			return obj === void 0;
    170 		}, // Safe check for undefined values.
    171 		isValidArrayIndex: function( val ) {
    172 			return /^[0-9]+$/.test( String( val ) );
    173 		}, // 1,2,3,4 ... are valid array indexes
    174 		isNumeric: function( obj ) {
    175 			return obj - parseFloat( obj ) >= 0;
    176 		}, // Taken from jQuery.isNumeric implementation. Not using jQuery.isNumeric to support old jQuery and Zepto versions.
    177 
    178 		optionKeys: function( obj ) {
    179 			var keys;
    180 			var key;
    181 
    182 			if ( Object.keys ) {
    183 				return Object.keys( obj );
    184 			} else {
    185 				keys = [];
    186 				for ( key in obj ) {
    187 					if ( obj.hasOwnProperty( key ) ) {
    188 						keys.push( key );
    189 					}
    190 				}
    191 
    192 				return keys;
    193 			}
    194 		}, // Polyfill Object.keys to get option keys in IE<9.
    195 
    196 		// Split the input name in programatically readable keys.
    197 		// The last element is always the type (default "_").
    198 		// Examples:
    199 		// "foo"              => ['foo', '_']
    200 		// "foo:string"       => ['foo', 'string']
    201 		// "foo:boolean"      => ['foo', 'boolean']
    202 		// "[foo]"            => ['foo', '_']
    203 		// "foo[inn][bar]"    => ['foo', 'inn', 'bar', '_']
    204 		// "foo[inn[bar]]"    => ['foo', 'inn', 'bar', '_']
    205 		// "foo[inn][arr][0]" => ['foo', 'inn', 'arr', '0', '_']
    206 		// "arr[][val]"       => ['arr', '', 'val', '_']
    207 		// "arr[][val]:null"  => ['arr', '', 'val', 'null'] .
    208 		splitInputNameIntoKeysArray: function( name, opts ) {
    209 			var keys, nameWithoutType, type, _ref, f;
    210 
    211 			f               = $.serializeJSON;
    212 			_ref            = f.extractTypeFromInputName( name, opts );
    213 			nameWithoutType = _ref[0];
    214 			type            = _ref[1];
    215 
    216 			keys = nameWithoutType.split( '[' ); // Split string into array.
    217 			keys = $.map(
    218 				keys,
    219 				function( key ) {
    220 					return key.replace( /]/g, '' );
    221 				}
    222 			); // Remove closing brackets.
    223 
    224 			if ( '' === keys[0] ) {
    225 				keys.shift();
    226 			} // Ensure no opening bracket ("[foo][inn]" should be same as "foo[inn]").
    227 
    228 			keys.push( type ); // Add type at the end.
    229 			return keys;
    230 		},
    231 
    232 		// Returns [name-without-type, type] from name.
    233 		// "foo"              =>  ["foo",      '_']
    234 		// "foo:boolean"      =>  ["foo",      'boolean']
    235 		// "foo[bar]:null"    =>  ["foo[bar]", 'null'].
    236 		extractTypeFromInputName: function( name, opts ) {
    237 			var match, validTypes, f;
    238 
    239 			match = name.match( /(.*):([^:]+)$/ );
    240 
    241 			if ( match ) {
    242 				f = $.serializeJSON;
    243 
    244 				validTypes = f.optionKeys( opts ? opts.typeFunctions : f.defaultOptions.defaultTypes );
    245 				validTypes.push( 'skip' ); // Skip is a special type that makes it easy to remove.
    246 				if ( validTypes.indexOf( match[2] ) !== - 1 ) {
    247 					return [match[1], match[2]];
    248 				} else {
    249 					throw new Error( 'serializeJSON ERROR: Invalid type ' + match[2] + ' found in input name "' + name + '", please use one of ' + validTypes.join( ', ' ) );
    250 				}
    251 			} else {
    252 				return [name, '_']; // No defined type, then use parse options.
    253 			}
    254 		},
    255 
    256 		// Set a value in an object or array, using multiple keys to set in a nested object or array:
    257 		// jscs:disable requireCapitalizedComments
    258 		//
    259 		// deepSet(obj, ['foo'], v)               // obj['foo'] = v
    260 		// deepSet(obj, ['foo', 'inn'], v)        // obj['foo']['inn'] = v // Create the inner obj['foo'] object, if needed
    261 		// deepSet(obj, ['foo', 'inn', '123'], v) // obj['foo']['arr']['123'] = v //
    262 		//
    263 		// deepSet(obj, ['0'], v)                                   // obj['0'] = v
    264 		// deepSet(arr, ['0'], v, {useIntKeysAsArrayIndex: true})   // arr[0] = v
    265 		// deepSet(arr, [''], v)                                    // arr.push(v)
    266 		// deepSet(obj, ['arr', ''], v)                             // obj['arr'].push(v)
    267 		//
    268 		// arr = [];
    269 		// deepSet(arr, ['', v]          // arr => [v]
    270 		// deepSet(arr, ['', 'foo'], v)  // arr => [v, {foo: v}]
    271 		// deepSet(arr, ['', 'bar'], v)  // arr => [v, {foo: v, bar: v}]
    272 		// deepSet(arr, ['', 'bar'], v)  // arr => [v, {foo: v, bar: v}, {bar: v}].
    273 		deepSet: function( o, keys, value, opts ) {
    274 			var key, nextKey, tail, lastIdx, lastVal, f;
    275 			if ( null === opts ) {
    276 				opts = {};
    277 			}
    278 
    279 			f = $.serializeJSON;
    280 
    281 			if ( f.isUndefined( o ) ) {
    282 				throw new Error( 'ArgumentError: param \'o\' expected to be an object or array, found undefined' );
    283 			}
    284 			if ( ! keys || 0 === keys.length ) {
    285 				throw new Error( 'ArgumentError: param \'keys\' expected to be an array with least one element' );
    286 			}
    287 
    288 			key = keys[0];
    289 
    290 			// Only one key, then it's not a deepSet, just assign the value.
    291 			if ( 1 === keys.length ) {
    292 				if ( '' === key ) {
    293 					o.push( value ); // '' is used to push values into the array (assume o is an array)
    294 				} else {
    295 					o[key] = value; // Other keys can be used as object keys or array indexes.
    296 				}
    297 
    298 				// With more keys is a deepSet. Apply recursively.
    299 			} else {
    300 				nextKey = keys[1];
    301 
    302 				// '' is used to push values into the array,
    303 				// with nextKey, set the value into the same object, in object[nextKey].
    304 				// Covers the case of ['', 'foo'] and ['', 'var'] to push the object {foo, var}, and the case of nested arrays.
    305 				if ( '' === key ) {
    306 					lastIdx = o.length - 1; // asume o is array.
    307 					lastVal = o[lastIdx];
    308 					if ( f.isObject( lastVal ) && ( f.isUndefined( lastVal[nextKey] ) || keys.length > 2 ) ) { // If nextKey is not present in the last object element, or there are more keys to deep set.
    309 						key = lastIdx; // then set the new value in the same object element.
    310 					} else {
    311 						key = lastIdx + 1; // otherwise, point to set the next index in the array.
    312 					}
    313 				}
    314 
    315 				// '' is used to push values into the array "array[]"
    316 				if ( '' === nextKey ) {
    317 					if ( f.isUndefined( o[key] ) || ! $.isArray( o[key] ) ) {
    318 						o[key] = []; // define (or override) as array to push values.
    319 					}
    320 				} else {
    321 					if ( opts.useIntKeysAsArrayIndex && f.isValidArrayIndex( nextKey ) ) { // if 1, 2, 3 ... then use an array, where nextKey is the index.
    322 						if ( f.isUndefined( o[key] ) || ! $.isArray( o[key] ) ) {
    323 							o[key] = []; // define (or override) as array, to insert values using int keys as array indexes.
    324 						}
    325 					} else { // for anything else, use an object, where nextKey is going to be the attribute name.
    326 						if ( f.isUndefined( o[key] ) || ! f.isObject( o[key] ) ) {
    327 							o[key] = {}; // define (or override) as object, to set nested properties.
    328 						}
    329 					}
    330 				}
    331 
    332 				// Recursively set the inner object.
    333 				tail = keys.slice( 1 );
    334 				f.deepSet( o[key], tail, value, opts );
    335 			}
    336 		},
    337 
    338 		// Fill the formAsArray object with values for the unchecked checkbox inputs,
    339 		// using the same format as the jquery.serializeArray function.
    340 		// The value of the unchecked values is determined from the opts.checkboxUncheckedValue
    341 		// and/or the data-unchecked-value attribute of the inputs.
    342 		readCheckboxUncheckedValues: function( formAsArray, $form, opts ) {
    343 			var selector, $uncheckedCheckboxes, $el, dataUncheckedValue, f;
    344 			if ( null === opts ) {
    345 				opts = {};
    346 			}
    347 
    348 			f = $.serializeJSON;
    349 
    350 			selector             = 'input[type=checkbox][name]:not(:checked):not([disabled])';
    351 			$uncheckedCheckboxes = $form.find( selector ).add( $form.filter( selector ) );
    352 			$uncheckedCheckboxes.each(
    353 				function( i, el ) {
    354 					i                  = null;
    355 					$el                = $( el );
    356 					dataUncheckedValue = $el.attr( 'data-unchecked-value' );
    357 					if ( dataUncheckedValue ) { // data-unchecked-value has precedence over option opts.checkboxUncheckedValue.
    358 						formAsArray.push( { name: el.name, value: dataUncheckedValue } );
    359 					} else {
    360 						if ( ! f.isUndefined( opts.checkboxUncheckedValue ) ) {
    361 							formAsArray.push( { name: el.name, value: opts.checkboxUncheckedValue } );
    362 						}
    363 					}
    364 				}
    365 			);
    366 		}
    367 
    368 	};
    369 
    370 }( window.jQuery || window.$ ) );
    371 
    372 (function( $ ) {
    373 	'use strict';
    374 
    375 	redux.customizer = redux.customizer || {};
    376 
    377 	$( document ).ready(
    378 		function() {
    379 			redux.customizer.init();
    380 		}
    381 	);
    382 
    383 	redux.customizer.init = function() {
    384 		var reduxChange;
    385 		var redux_initFields;
    386 
    387 		$( 'body' ).addClass( redux.customizer.body_class );
    388 		$( '.accordion-section.redux-section, .accordion-section.redux-panel, .accordion-section-title' ).on(
    389 			'click',
    390 			function() {
    391 				$.redux.initFields();
    392 			}
    393 		);
    394 
    395 		if ( undefined === redux.optName ) {
    396 			console.log( 'Redux customizer extension failure' );
    397 			return;
    398 		}
    399 
    400 		redux.optName.args.disable_save_warn = true;
    401 		reduxChange                          = redux_change;
    402 		redux_change                         = function( variable ) {
    403 			variable = $( variable );
    404 			reduxChange.apply( this, arguments );
    405 			redux.customizer.save( variable );
    406 		};
    407 
    408 		redux_initFields = $.redux.initFields;
    409 
    410 		$.redux.initFiles = function() {
    411 			redux_initFields();
    412 		};
    413 	};
    414 
    415 	redux.customizer.save = function( $obj ) {
    416 		var $parent = $obj.hasClass( 'redux-field' ) ? $obj : $obj.parents( '.redux-field-container:first' );
    417 		redux.customizer.inputSave( $parent );
    418 	};
    419 
    420 	redux.customizer.inputSave = function( $parent ) {
    421 		var $id;
    422 		var $nData;
    423 		var $key;
    424 		var $control;
    425 
    426 		if ( ! $parent.hasClass( 'redux-field-container' ) ) {
    427 			$parent = $parent.parents( '[class^="redux-field-container"]' );
    428 		}
    429 
    430 		$id = $parent.parent().find( '.redux-customizer-input' ).data( 'id' );
    431 
    432 		if ( ! $id ) {
    433 			$parent = $parent.parents( '.redux-container-repeater:first' );
    434 			$id     = $parent.parent().find( '.redux-customizer-input' ).data( 'id' );
    435 		}
    436 
    437 		// var $nData = $parent.serializeJSON(); .
    438 		$nData = $parent.find( ':input' ).serializeJSON();
    439 
    440 		$.each(
    441 			$nData,
    442 			function( $k, $v ) {
    443 				$k     = null;
    444 				$nData = $v;
    445 			}
    446 		);
    447 
    448 		$key = $parent.parent().find( '.redux-customizer-input' ).data( 'key' );
    449 		if ( $nData[$key] ) {
    450 			$nData = $nData[$key];
    451 		}
    452 
    453 		$control = wp.customize.control( $id );
    454 
    455 		// Customizer hack since they didn't code it to save order...
    456 		if ( JSON.stringify( $control.setting._value ) !== JSON.stringify( $nData ) ) {
    457 			$control.setting._value = null;
    458 		}
    459 
    460 		$control.setting.set( $nData );
    461 	};
    462 })( jQuery );