/* * input.js * * All javascript needed for ACF to work * * @type awesome * @date 1/08/13 * * @param N/A * @return N/A */ var acf = { // vars ajaxurl : '', admin_url : '', wp_version : '', post_id : 0, nonce : '', l10n : null, o : null, // helper functions helpers : { get_atts : null, version_compare : null, uniqid : null, sortable : null, add_message : null, is_clone_field : null, url_to_object : null }, // modules validation : null, conditional_logic : null, media : null, // fields fields : { date_picker : null, color_picker : null, Image : null, file : null, wysiwyg : null, gallery : null, relationship : null } }; (function($){ /* * acf.helpers.isset * * description * * @type function * @date 20/07/13 * * @param {mixed} arguments * @return {boolean} */ acf.helpers.isset = function(){ var a = arguments, l = a.length, c = null, undef; if (l === 0) { throw new Error('Empty isset'); } c = a[0]; for (i = 1; i < l; i++) { if (a[i] === undef || c[ a[i] ] === undef) { return false; } c = c[ a[i] ]; } return true; }; /* * acf.helpers.get_atts * * description * * @type function * @date 1/06/13 * * @param {el} $el * @return {object} atts */ acf.helpers.get_atts = function( $el ){ var atts = {}; $.each( $el[0].attributes, function( index, attr ) { if( attr.name.substr(0, 5) == 'data-' ) { atts[ attr.name.replace('data-', '') ] = attr.value; } }); return atts; }; /** * Simply compares two string version values. * * Example: * versionCompare('1.1', '1.2') => -1 * versionCompare('1.1', '1.1') => 0 * versionCompare('1.2', '1.1') => 1 * versionCompare('2.23.3', '2.22.3') => 1 * * Returns: * -1 = left is LOWER than right * 0 = they are equal * 1 = left is GREATER = right is LOWER * And FALSE if one of input versions are not valid * * @function * @param {String} left Version #1 * @param {String} right Version #2 * @return {Integer|Boolean} * @author Alexey Bass (albass) * @since 2011-07-14 */ acf.helpers.version_compare = function(left, right) { if (typeof left + typeof right != 'stringstring') return false; var a = left.split('.') , b = right.split('.') , i = 0, len = Math.max(a.length, b.length); for (; i < len; i++) { if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) { return 1; } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) { return -1; } } return 0; }; /* * Helper: uniqid * * @description: * @since: 3.5.8 * @created: 17/01/13 */ acf.helpers.uniqid = function() { var newDate = new Date; return newDate.getTime(); }; /* * Helper: url_to_object * * @description: * @since: 4.0.0 * @created: 17/01/13 */ acf.helpers.url_to_object = function( url ){ // vars var obj = {}, pairs = url.split('&'); for( i in pairs ) { var split = pairs[i].split('='); obj[decodeURIComponent(split[0])] = decodeURIComponent(split[1]); } return obj; }; /* * Sortable Helper * * @description: keeps widths of td's inside a tr * @since 3.5.1 * @created: 10/11/12 */ acf.helpers.sortable = function(e, ui) { ui.children().each(function(){ $(this).width($(this).width()); }); return ui; }; /* * is_clone_field * * @description: * @since: 3.5.8 * @created: 17/01/13 */ acf.helpers.is_clone_field = function( input ) { if( input.attr('name') && input.attr('name').indexOf('[acfcloneindex]') != -1 ) { return true; } return false; }; /* * acf.helpers.add_message * * @description: * @since: 3.2.7 * @created: 10/07/2012 */ acf.helpers.add_message = function( message, div ){ var message = $('

' + message + '

'); div.prepend( message ); setTimeout(function(){ message.animate({ opacity : 0 }, 250, function(){ message.remove(); }); }, 1500); }; /* * Exists * * @description: returns true / false * @created: 1/03/2011 */ $.fn.exists = function() { return $(this).length>0; }; /* * 3.5 Media * * @description: * @since: 3.5.7 * @created: 16/01/13 */ acf.media = { div : null, frame : null, render_timout : null, clear_frame : function(){ // validate if( !this.frame ) { return; } // detach this.frame.detach(); this.frame.dispose(); // reset var this.frame = null; }, type : function(){ // default var type = 'thickbox'; // if wp exists if( typeof wp !== 'undefined' ) { type = 'backbone'; } // return return type; }, init : function(){ // validate if( this.type() !== 'backbone' ) { return false; } // validate prototype if( ! acf.helpers.isset(wp, 'media', 'view', 'AttachmentCompat', 'prototype') ) { return false; } // vars var _prototype = wp.media.view.AttachmentCompat.prototype; // orig _prototype.orig_render = _prototype.render; _prototype.orig_dispose = _prototype.dispose; // update class _prototype.className = 'compat-item acf_postbox no_box'; // modify render _prototype.render = function() { // reference var _this = this; // validate if( _this.ignore_render ) { return this; } // run the old render function this.orig_render(); // add button setTimeout(function(){ // vars var $media_model = _this.$el.closest('.media-modal'); // is this an edit only modal? if( $media_model.hasClass('acf-media-modal') ) { return; } // does button already exist? if( $media_model.find('.media-frame-router .acf-expand-details').exists() ) { return; } // create button var button = $([ '', '', '' + acf.l10n.core.expand_details + '', '' + acf.l10n.core.collapse_details + '', '' ].join('')); // add events button.on('click', function( e ){ e.preventDefault(); if( $media_model.hasClass('acf-expanded') ) { $media_model.removeClass('acf-expanded'); } else { $media_model.addClass('acf-expanded'); } }); // append $media_model.find('.media-frame-router').append( button ); }, 0); // setup fields // The clearTimout is needed to prevent many setup functions from running at the same time clearTimeout( acf.media.render_timout ); acf.media.render_timout = setTimeout(function(){ $(document).trigger( 'acf/setup_fields', [ _this.$el ] ); }, 50); // return based on the origional render function return this; }; // modify dispose _prototype.dispose = function() { // remove $(document).trigger('acf/remove_fields', [ this.$el ]); // run the old render function this.orig_dispose(); }; // override save _prototype.save = function( event ) { var data = {}, names = {}; if ( event ) event.preventDefault(); _.each( this.$el.serializeArray(), function( pair ) { // initiate name if( pair.name.slice(-2) === '[]' ) { // remove [] pair.name = pair.name.replace('[]', ''); // initiate counter if( typeof names[ pair.name ] === 'undefined'){ names[ pair.name ] = -1; //console.log( names[ pair.name ] ); } names[ pair.name ]++ pair.name += '[' + names[ pair.name ] +']'; } data[ pair.name ] = pair.value; }); this.ignore_render = true; this.model.saveCompat( data ); }; } }; /* * Conditional Logic Calculate * * @description: * @since 3.5.1 * @created: 15/10/12 */ acf.conditional_logic = { items : [], init : function(){ // reference var _this = this; // events $(document).on('change', '.field input, .field textarea, .field select', function(){ // preview hack if( $('#acf-has-changed').exists() ) { $('#acf-has-changed').val(1); } _this.change( $(this) ); }); $(document).on('acf/setup_fields', function(e, el){ //console.log('acf/setup_fields calling acf.conditional_logic.refresh()'); _this.refresh( $(el) ); }); //console.log('acf.conditional_logic.init() calling acf.conditional_logic.refresh()'); _this.refresh(); }, change : function( $el ){ //console.log('change %o', $el); // reference var _this = this; // vars var $field = $el.closest('.field'), key = $field.attr('data-field_key'); // loop through items and find rules where this field key is a trigger $.each(this.items, function( k, item ){ $.each(item.rules, function( k2, rule ){ // compare rule against the changed $field if( rule.field == key ) { _this.refresh_field( item ); } }); }); }, refresh_field : function( item ){ //console.log( 'refresh_field: %o ', item ); // reference var _this = this; // vars var $targets = $('.field_key-' + item.field); // may be multiple targets (sub fields) $targets.each(function(){ //console.log('target %o', $(this)); // vars var show = true; // if 'any' was selected, start of as false and any match will result in show = true if( item.allorany == 'any' ) { show = false; } // vars var $target = $(this), hide_all = true; // loop through rules $.each(item.rules, function( k2, rule ){ // vars var $toggle = $('.field_key-' + rule.field); // are any of $toggle a sub field? if( $toggle.hasClass('sub_field') ) { // toggle may be a sibling sub field. // if so ,show an empty td but keep the column $toggle = $target.siblings('.field_key-' + rule.field); hide_all = false; // if no toggle was found, we need to look at parent sub fields. // if so, hide the entire column if( ! $toggle.exists() ) { // loop through all the parents that could contain sub fields $target.parents('tr').each(function(){ // attempt to update $toggle to this parent sub field $toggle = $(this).find('.field_key-' + rule.field) // if the parent sub field actuallly exists, great! Stop the loop if( $toggle.exists() ) { return false; } }); hide_all = true; } } // if this sub field is within a flexible content layout, hide the entire column because // there will never be another row added to this table var parent = $target.parent('tr').parent().parent('table').parent('.layout'); if( parent.exists() ) { hide_all = true; if( $target.is('th') && $toggle.is('th') ) { $toggle = $target.closest('.layout').find('td.field_key-' + rule.field); } } // if this sub field is within a repeater field which has a max row of 1, hide the entire column because // there will never be another row added to this table var parent = $target.parent('tr').parent().parent('table').parent('.repeater'); if( parent.exists() && parent.attr('data-max_rows') == '1' ) { hide_all = true; if( $target.is('th') && $toggle.is('th') ) { $toggle = $target.closest('table').find('td.field_key-' + rule.field); } } var calculate = _this.calculate( rule, $toggle, $target ); if( item.allorany == 'all' ) { if( calculate == false ) { show = false; // end loop return false; } } else { if( calculate == true ) { show = true; // end loop return false; } } }); // $.each(item.rules, function( k2, rule ){ // clear classes $target.removeClass('acf-conditional_logic-hide acf-conditional_logic-show acf-show-blank'); // hide / show field if( show ) { // remove "disabled" $target.find('input, textarea, select').removeAttr('disabled'); $target.addClass('acf-conditional_logic-show'); // hook $(document).trigger('acf/conditional_logic/show', [ $target, item ]); } else { // add "disabled" $target.find('input, textarea, select').attr('disabled', 'disabled'); $target.addClass('acf-conditional_logic-hide'); if( !hide_all ) { $target.addClass('acf-show-blank'); } // hook $(document).trigger('acf/conditional_logic/hide', [ $target, item ]); } }); }, refresh : function( $el ){ // defaults $el = $el || $('body'); // reference var _this = this; // loop through items and find rules where this field key is a trigger $.each(this.items, function( k, item ){ $.each(item.rules, function( k2, rule ){ // is this field within the $el // this will increase performance by ignoring conditional logic outside of this newly appended element ($el) if( ! $el.find('.field[data-field_key="' + item.field + '"]').exists() ) { return; } _this.refresh_field( item ); }); }); }, calculate : function( rule, $toggle, $target ){ // vars var r = false; // compare values if( $toggle.hasClass('field_type-true_false') || $toggle.hasClass('field_type-checkbox') || $toggle.hasClass('field_type-radio') ) { var exists = $toggle.find('input[value="' + rule.value + '"]:checked').exists(); if( rule.operator == "==" ) { if( exists ) { r = true; } } else { if( ! exists ) { r = true; } } } else { // get val and make sure it is an array var val = $toggle.find('input, textarea, select').last().val(); if( ! $.isArray(val) ) { val = [ val ]; } if( rule.operator == "==" ) { if( $.inArray(rule.value, val) > -1 ) { r = true; } } else { if( $.inArray(rule.value, val) < 0 ) { r = true; } } } // return return r; } }; /* * Document Ready * * @description: * @since: 3.5.8 * @created: 17/01/13 */ $(document).ready(function(){ // conditional logic acf.conditional_logic.init(); // fix for older options page add-on $('.acf_postbox > .inside > .options').each(function(){ $(this).closest('.acf_postbox').addClass( $(this).attr('data-layout') ); }); // Remove 'field_123' from native custom field metabox $('#metakeyselect option[value^="field_"]').remove(); }); /* * window load * * @description: * @since: 3.5.5 * @created: 22/12/12 */ $(window).on('load', function(){ // init acf.media.init(); setTimeout(function(){ // Hack for CPT without a content editor try { // post_id may be string (user_1) and therefore, the uploaded image cannot be attached to the post if( $.isNumeric(acf.o.post_id) ) { wp.media.view.settings.post.id = acf.o.post_id; } } catch(e) { // one of the objects was 'undefined'... } // setup fields $(document).trigger('acf/setup_fields', [ $('#poststuff') ]); }, 10); }); /* * Gallery field Add-on Fix * * Gallery field v1.0.0 required some data in the acf object. * Now not required, but older versions of gallery field need this. * * @type object * @date 1/08/13 * * @param N/A * @return N/A */ acf.fields.gallery = { add : function(){}, edit : function(){}, update_count : function(){}, hide_selected_items : function(){}, text : { title_add : "Select Images" } }; })(jQuery);