(function($, undefined){ /** * acf.newSelect2 * * description * * @date 13/1/18 * @since 5.6.5 * * @param type $var Description. Default. * @return type Description. */ acf.newSelect2 = function( $select, props ){ // defaults props = acf.parseArgs(props, { allowNull: false, placeholder: '', multiple: false, field: false, ajax: false, ajaxAction: '', ajaxData: function( data ){ return data; }, ajaxResults: function( json ){ return json; }, }); // initialize if( getVersion() == 4 ) { var select2 = new Select2_4( $select, props ); } else { var select2 = new Select2_3( $select, props ); } // actions acf.doAction('new_select2', select2); // return return select2; }; /** * getVersion * * description * * @date 13/1/18 * @since 5.6.5 * * @param type $var Description. Default. * @return type Description. */ function getVersion() { // v4 if( acf.isset(window, 'jQuery', 'fn', 'select2', 'amd') ) { return 4; } // v3 if( acf.isset(window, 'Select2') ) { return 3; } // return return false; } /** * Select2 * * description * * @date 13/1/18 * @since 5.6.5 * * @param type $var Description. Default. * @return type Description. */ var Select2 = acf.Model.extend({ setup: function( $select, props ){ $.extend(this.data, props); this.$el = $select; }, initialize: function(){ }, selectOption: function( value ){ var $option = this.getOption( value ); if( !$option.prop('selected') ) { $option.prop('selected', true).trigger('change'); } }, unselectOption: function( value ){ var $option = this.getOption( value ); if( $option.prop('selected') ) { $option.prop('selected', false).trigger('change'); } }, getOption: function( value ){ return this.$('option[value="' + value + '"]'); }, addOption: function( option ){ // defaults option = acf.parseArgs(option, { id: '', text: '', selected: false }); // vars var $option = this.getOption( option.id ); // append if( !$option.length ) { $option = $(''); $option.html( option.text ); $option.attr('value', option.id); $option.prop('selected', option.selected); this.$el.append($option); } // chain return $option; }, getValue: function(){ // vars var val = []; var $options = this.$el.find('option:selected'); // bail early if no selected if( !$options.exists() ) { return val; } // sort by attribute $options = $options.sort(function(a, b) { return +a.getAttribute('data-i') - +b.getAttribute('data-i'); }); // loop $options.each(function(){ var $el = $(this); val.push({ $el: $el, id: $el.attr('value'), text: $el.text(), }); }); // return return val; }, mergeOptions: function(){ }, getChoices: function(){ // callback var crawl = function( $parent ){ // vars var choices = []; // loop $parent.children().each(function(){ // vars var $child = $(this); // optgroup if( $child.is('optgroup') ) { choices.push({ text: $child.attr('label'), children: crawl( $child ) }); // option } else { choices.push({ id: $child.attr('value'), text: $child.text() }); } }); // return return choices; }; // crawl return crawl( this.$el ); }, decodeChoices: function( choices ){ // callback var crawl = function( items ){ items.map(function( item ){ item.text = acf.decode( item.text ); if( item.children ) { item.children = crawl( item.children ); } return item; }); return items; }; // crawl return crawl( choices ); }, getAjaxData: function( params ){ // vars var ajaxData = { action: this.get('ajaxAction'), s: params.term || '', paged: params.page || 1 }; // field helper var field = this.get('field'); if( field ) { ajaxData.field_key = field.get('key'); } // callback var callback = this.get('ajaxData'); if( callback ) { ajaxData = callback.apply( this, [ajaxData, params] ); } // filter ajaxData = acf.applyFilters( 'select2_ajax_data', ajaxData, this.data, this.$el, (field || false), this ); // return return acf.prepareForAjax(ajaxData); }, getAjaxResults: function( json, params ){ // defaults json = acf.parseArgs(json, { results: false, more: false, }); // decode if( json.results ) { json.results = this.decodeChoices(json.results); } // callback var callback = this.get('ajaxResults'); if( callback ) { json = callback.apply( this, [json, params] ); } // filter json = acf.applyFilters( 'select2_ajax_results', json, params, this ); // return return json; }, processAjaxResults: function( json, params ){ // vars var json = this.getAjaxResults( json, params ); // change more to pagination if( json.more ) { json.pagination = { more: true }; } // merge together groups setTimeout($.proxy(this.mergeOptions, this), 1); // return return json; }, destroy: function(){ // destroy via api if( this.$el.data('select2') ) { this.$el.select2('destroy'); } // destory via HTML (duplicating HTML does not contain data) this.$el.siblings('.select2-container').remove(); } }); /** * Select2_4 * * description * * @date 13/1/18 * @since 5.6.5 * * @param type $var Description. Default. * @return type Description. */ var Select2_4 = Select2.extend({ initialize: function(){ // vars var $select = this.$el; var options = { width: '100%', allowClear: this.get('allowNull'), placeholder: this.get('placeholder'), multiple: this.get('multiple'), data: [], escapeMarkup: function( m ){ return m; } }; // multiple if( options.multiple ) { // reorder options this.getValue().map(function( item ){ item.$el.detach().appendTo( $select ); }); } // remove conflicting atts $select.removeData('ajax'); $select.removeAttr('data-ajax'); // ajax if( this.get('ajax') ) { options.ajax = { url: acf.get('ajaxurl'), delay: 250, dataType: 'json', type: 'post', cache: false, data: $.proxy(this.getAjaxData, this), processResults: $.proxy(this.processAjaxResults, this), }; } // filter for 3rd party customization //options = acf.applyFilters( 'select2_args', options, $select, this ); var field = this.get('field'); options = acf.applyFilters( 'select2_args', options, $select, this.data, (field || false), this ); // add select2 $select.select2( options ); // get container (Select2 v4 does not return this from constructor) var $container = $select.next('.select2-container'); // multiple if( options.multiple ) { // vars var $ul = $container.find('ul'); // sortable $ul.sortable({ stop: function( e ) { // loop $ul.find('.select2-selection__choice').each(function() { // vars var $option = $( $(this).data('data').element ); // detach and re-append to end $option.detach().appendTo( $select ); }); // trigger change on input (JS error if trigger on select) $select.trigger('change'); } }); // on select, move to end $select.on('select2:select', this.proxy(function( e ){ this.getOption( e.params.data.id ).detach().appendTo( this.$el ); })); } // add class $container.addClass('-acf'); // action for 3rd party customization acf.doAction('select2_init', $select, options, this.data, (field || false), this); }, mergeOptions: function(){ // vars var $prevOptions = false; var $prevGroup = false; // loop $('.select2-results__option[role="group"]').each(function(){ // vars var $options = $(this).children('ul'); var $group = $(this).children('strong'); // compare to previous if( $prevGroup && $prevGroup.text() === $group.text() ) { $prevOptions.append( $options.children() ); $(this).remove(); return; } // update vars $prevOptions = $options; $prevGroup = $group; }); }, }); /** * Select2_3 * * description * * @date 13/1/18 * @since 5.6.5 * * @param type $var Description. Default. * @return type Description. */ var Select2_3 = Select2.extend({ initialize: function(){ // vars var $select = this.$el; var value = this.getValue(); var multiple = this.get('multiple'); var options = { width: '100%', allowClear: this.get('allowNull'), placeholder: this.get('placeholder'), separator: '||', multiple: this.get('multiple'), data: this.getChoices(), escapeMarkup: function( m ){ return m; }, dropdownCss: { 'z-index': '999999999' }, initSelection: function( element, callback ) { if( multiple ) { callback( value ); } else { callback( value.shift() ); } } }; // get hidden input var $input = $select.siblings('input'); if( !$input.length ) { $input = $(''); $select.before( $input ); } // set input value inputValue = value.map(function(item){ return item.id }).join('||'); $input.val( inputValue ); // multiple if( options.multiple ) { // reorder options value.map(function( item ){ item.$el.detach().appendTo( $select ); }); } // remove blank option as we have a clear all button if( options.allowClear ) { options.data = options.data.filter(function(item){ return item.id !== ''; }); } // remove conflicting atts $select.removeData('ajax'); $select.removeAttr('data-ajax'); // ajax if( this.get('ajax') ) { options.ajax = { url: acf.get('ajaxurl'), quietMillis: 250, dataType: 'json', type: 'post', cache: false, data: $.proxy(this.getAjaxData, this), results: $.proxy(this.processAjaxResults, this), }; } // filter for 3rd party customization var field = this.get('field'); options = acf.applyFilters( 'select2_args', options, $select, this.data, (field || false), this ); // add select2 $input.select2( options ); // get container var $container = $input.select2('container'); // helper to find this select's option var getOption = $.proxy(this.getOption, this); // multiple if( options.multiple ) { // vars var $ul = $container.find('ul'); // sortable $ul.sortable({ stop: function() { // loop $ul.find('.select2-search-choice').each(function() { // vars var data = $(this).data('select2Data'); var $option = getOption( data.id ); // detach and re-append to end $option.detach().appendTo( $select ); }); // trigger change on input (JS error if trigger on select) $select.trigger('change'); } }); } // on select, create option and move to end $input.on('select2-selecting', function( e ){ // vars var item = e.choice; var $option = getOption( item.id ); // create if doesn't exist if( !$option.length ) { $option = $(''); } // detach and re-append to end $option.detach().appendTo( $select ); }); // add class $container.addClass('-acf'); // action for 3rd party customization acf.doAction('select2_init', $select, options, this.data, (field || false), this); // change $input.on('change', function(){ var val = $input.val(); if( val.indexOf('||') ) { val = val.split('||'); } $select.val( val ).trigger('change'); }); // hide select $select.hide(); }, mergeOptions: function(){ // vars var $prevOptions = false; var $prevGroup = false; // loop $('#select2-drop .select2-result-with-children').each(function(){ // vars var $options = $(this).children('ul'); var $group = $(this).children('.select2-result-label'); // compare to previous if( $prevGroup && $prevGroup.text() === $group.text() ) { $prevGroup.append( $options.children() ); $(this).remove(); return; } // update vars $prevOptions = $options; $prevGroup = $group; }); }, getAjaxData: function( term, page ){ // create Select2 v4 params var params = { term: term, page: page } // return return Select2.prototype.getAjaxData.apply(this, [params]); }, }); // manager var select2Manager = new acf.Model({ priority: 5, wait: 'prepare', initialize: function(){ // vars var locale = acf.get('locale'); var rtl = acf.get('rtl'); var l10n = acf.get('select2L10n'); var version = getVersion(); // bail ealry if no l10n if( !l10n ) { return false; } // bail early if 'en' if( locale.indexOf('en') === 0 ) { return false; } // initialize if( version == 4 ) { this.addTranslations4(); } else if( version == 3 ) { this.addTranslations3(); } }, addTranslations4: function(){ // vars var l10n = acf.get('select2L10n'); var locale = acf.get('locale'); // modify local to match html[lang] attribute (used by Select2) locale = locale.replace('_', '-'); // select2L10n var select2L10n = { errorLoading: function () { return l10n.load_fail; }, inputTooLong: function (args) { var overChars = args.input.length - args.maximum; if( overChars > 1 ) { return l10n.input_too_long_n.replace( '%d', overChars ); } return l10n.input_too_long_1; }, inputTooShort: function( args ){ var remainingChars = args.minimum - args.input.length; if( remainingChars > 1 ) { return l10n.input_too_short_n.replace( '%d', remainingChars ); } return l10n.input_too_short_1; }, loadingMore: function () { return l10n.load_more; }, maximumSelected: function( args ) { var maximum = args.maximum; if( maximum > 1 ) { return l10n.selection_too_long_n.replace( '%d', maximum ); } return l10n.selection_too_long_1; }, noResults: function () { return l10n.matches_0; }, searching: function () { return l10n.searching; } }; // append jQuery.fn.select2.amd.define('select2/i18n/' + locale, [], function(){ return select2L10n; }); }, addTranslations3: function(){ // vars var l10n = acf.get('select2L10n'); var locale = acf.get('locale'); // modify local to match html[lang] attribute (used by Select2) locale = locale.replace('_', '-'); // select2L10n var select2L10n = { formatMatches: function( matches ) { if( matches > 1 ) { return l10n.matches_n.replace( '%d', matches ); } return l10n.matches_1; }, formatNoMatches: function() { return l10n.matches_0; }, formatAjaxError: function() { return l10n.load_fail; }, formatInputTooShort: function( input, min ) { var remainingChars = min - input.length; if( remainingChars > 1 ) { return l10n.input_too_short_n.replace( '%d', remainingChars ); } return l10n.input_too_short_1; }, formatInputTooLong: function( input, max ) { var overChars = input.length - max; if( overChars > 1 ) { return l10n.input_too_long_n.replace( '%d', overChars ); } return l10n.input_too_long_1; }, formatSelectionTooBig: function( maximum ) { if( maximum > 1 ) { return l10n.selection_too_long_n.replace( '%d', maximum ); } return l10n.selection_too_long_1; }, formatLoadMore: function() { return l10n.load_more; }, formatSearching: function() { return l10n.searching; } }; // ensure locales exists $.fn.select2.locales = $.fn.select2.locales || {}; // append $.fn.select2.locales[ locale ] = select2L10n; $.extend($.fn.select2.defaults, select2L10n); } }); })(jQuery);