PluginProbe
Advanced Custom Fields (ACF®) / 4.4.11
Advanced Custom Fields (ACF®) v4.4.11
6.8.9 6.8.8 6.8.7 6.8.6 6.8.5 6.8.4 6.8.3 6.8.2 6.8.1 5.8.5 5.8.6 5.8.7 5.8.8 5.8.9 5.9.0 5.9.1 5.9.2 5.9.3 5.9.4 5.9.5 5.9.6 5.9.7 5.9.8 5.9.9 6.0.0 All 230 releases
advanced-custom-fields / js / input.js
input.js
4,617 lines 82.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1
2
3 /*
4 * input.js
5 *
6 * All javascript needed for ACF to work
7 *
8 * @type awesome
9 * @date 1/08/13
10 *
11 * @param N/A
12 * @return N/A
13 */
14
15 var acf = {
16
17 // vars
18 ajaxurl : '',
19 admin_url : '',
20 wp_version : '',
21 post_id : 0,
22 nonce : '',
23 l10n : null,
24 o : null,
25
26 // helper functions
27 helpers : {
28 get_atts : null,
29 version_compare : null,
30 uniqid : null,
31 sortable : null,
32 add_message : null,
33 is_clone_field : null,
34 url_to_object : null
35 },
36
37
38 // modules
39 validation : null,
40 conditional_logic : null,
41 media : null,
42
43
44 // fields
45 fields : {
46 date_picker : null,
47 color_picker : null,
48 Image : null,
49 file : null,
50 wysiwyg : null,
51 gallery : null,
52 relationship : null
53 }
54 };
55
56 (function($){
57
58
59 /*
60 * acf.helpers.isset
61 *
62 * description
63 *
64 * @type function
65 * @date 20/07/13
66 *
67 * @param {mixed} arguments
68 * @return {boolean}
69 */
70
71 acf.helpers.isset = function(){
72
73 var a = arguments,
74 l = a.length,
75 c = null,
76 undef;
77
78 if (l === 0) {
79 throw new Error('Empty isset');
80 }
81
82 c = a[0];
83
84 for (i = 1; i < l; i++) {
85
86 if (a[i] === undef || c[ a[i] ] === undef) {
87 return false;
88 }
89
90 c = c[ a[i] ];
91
92 }
93
94 return true;
95
96 };
97
98
99 /*
100 * acf.helpers.get_atts
101 *
102 * description
103 *
104 * @type function
105 * @date 1/06/13
106 *
107 * @param {el} $el
108 * @return {object} atts
109 */
110
111 acf.helpers.get_atts = function( $el ){
112
113 var atts = {};
114
115 $.each( $el[0].attributes, function( index, attr ) {
116
117 if( attr.name.substr(0, 5) == 'data-' )
118 {
119 atts[ attr.name.replace('data-', '') ] = attr.value;
120 }
121 });
122
123 return atts;
124
125 };
126
127
128 /**
129 * Simply compares two string version values.
130 *
131 * Example:
132 * versionCompare('1.1', '1.2') => -1
133 * versionCompare('1.1', '1.1') => 0
134 * versionCompare('1.2', '1.1') => 1
135 * versionCompare('2.23.3', '2.22.3') => 1
136 *
137 * Returns:
138 * -1 = left is LOWER than right
139 * 0 = they are equal
140 * 1 = left is GREATER = right is LOWER
141 * And FALSE if one of input versions are not valid
142 *
143 * @function
144 * @param {String} left Version #1
145 * @param {String} right Version #2
146 * @return {Integer|Boolean}
147 * @author Alexey Bass (albass)
148 * @since 2011-07-14
149 */
150
151 acf.helpers.version_compare = function(left, right)
152 {
153 if (typeof left + typeof right != 'stringstring')
154 return false;
155
156 var a = left.split('.')
157 , b = right.split('.')
158 , i = 0, len = Math.max(a.length, b.length);
159
160 for (; i < len; i++) {
161 if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) {
162 return 1;
163 } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) {
164 return -1;
165 }
166 }
167
168 return 0;
169 };
170
171
172 /*
173 * Helper: uniqid
174 *
175 * @description:
176 * @since: 3.5.8
177 * @created: 17/01/13
178 */
179
180 acf.helpers.uniqid = function()
181 {
182 var newDate = new Date;
183 return newDate.getTime();
184 };
185
186
187 /*
188 * Helper: url_to_object
189 *
190 * @description:
191 * @since: 4.0.0
192 * @created: 17/01/13
193 */
194
195 acf.helpers.url_to_object = function( url ){
196
197 // vars
198 var obj = {},
199 pairs = url.split('&');
200
201
202 for( i in pairs )
203 {
204 var split = pairs[i].split('=');
205 obj[decodeURIComponent(split[0])] = decodeURIComponent(split[1]);
206 }
207
208 return obj;
209
210 };
211
212
213 /*
214 * Sortable Helper
215 *
216 * @description: keeps widths of td's inside a tr
217 * @since 3.5.1
218 * @created: 10/11/12
219 */
220
221 acf.helpers.sortable = function(e, ui)
222 {
223 ui.children().each(function(){
224 $(this).width($(this).width());
225 });
226 return ui;
227 };
228
229
230 /*
231 * is_clone_field
232 *
233 * @description:
234 * @since: 3.5.8
235 * @created: 17/01/13
236 */
237
238 acf.helpers.is_clone_field = function( input )
239 {
240 if( input.attr('name') && input.attr('name').indexOf('[acfcloneindex]') != -1 )
241 {
242 return true;
243 }
244
245 return false;
246 };
247
248
249 /*
250 * acf.helpers.add_message
251 *
252 * @description:
253 * @since: 3.2.7
254 * @created: 10/07/2012
255 */
256
257 acf.helpers.add_message = function( message, div ){
258
259 var message = $('<div class="acf-message-wrapper"><div class="message updated"><p>' + message + '</p></div></div>');
260
261 div.prepend( message );
262
263 setTimeout(function(){
264
265 message.animate({
266 opacity : 0
267 }, 250, function(){
268 message.remove();
269 });
270
271 }, 1500);
272
273 };
274
275
276 /*
277 * Exists
278 *
279 * @description: returns true / false
280 * @created: 1/03/2011
281 */
282
283 $.fn.exists = function()
284 {
285 return $(this).length>0;
286 };
287
288
289 /*
290 * 3.5 Media
291 *
292 * @description:
293 * @since: 3.5.7
294 * @created: 16/01/13
295 */
296
297 acf.media = {
298
299 div : null,
300 frame : null,
301 render_timout : null,
302
303 clear_frame : function(){
304
305 // validate
306 if( !this.frame )
307 {
308 return;
309 }
310
311
312 // detach
313 this.frame.detach();
314 this.frame.dispose();
315
316
317 // reset var
318 this.frame = null;
319
320 },
321 type : function(){
322
323 // default
324 var type = 'thickbox';
325
326
327 // if wp exists
328 if( typeof wp !== 'undefined' )
329 {
330 type = 'backbone';
331 }
332
333
334 // return
335 return type;
336
337 },
338 init : function(){
339
340 // validate
341 if( this.type() !== 'backbone' )
342 {
343 return false;
344 }
345
346
347 // validate prototype
348 if( ! acf.helpers.isset(wp, 'media', 'view', 'AttachmentCompat', 'prototype') )
349 {
350 return false;
351 }
352
353
354 // vars
355 var _prototype = wp.media.view.AttachmentCompat.prototype;
356
357
358 // orig
359 _prototype.orig_render = _prototype.render;
360 _prototype.orig_dispose = _prototype.dispose;
361
362
363 // update class
364 _prototype.className = 'compat-item acf_postbox no_box';
365
366
367 // modify render
368 _prototype.render = function() {
369
370 // reference
371 var _this = this;
372
373
374 // validate
375 if( _this.ignore_render )
376 {
377 return this;
378 }
379
380
381 // run the old render function
382 this.orig_render();
383
384
385 // add button
386 setTimeout(function(){
387
388 // vars
389 var $media_model = _this.$el.closest('.media-modal');
390
391
392 // is this an edit only modal?
393 if( $media_model.hasClass('acf-media-modal') )
394 {
395 return;
396 }
397
398
399 // does button already exist?
400 if( $media_model.find('.media-frame-router .acf-expand-details').exists() )
401 {
402 return;
403 }
404
405
406 // create button
407 var button = $([
408 '<a href="#" class="acf-expand-details">',
409 '<span class="icon"></span>',
410 '<span class="is-closed">' + acf.l10n.core.expand_details + '</span>',
411 '<span class="is-open">' + acf.l10n.core.collapse_details + '</span>',
412 '</a>'
413 ].join(''));
414
415
416 // add events
417 button.on('click', function( e ){
418
419 e.preventDefault();
420
421 if( $media_model.hasClass('acf-expanded') )
422 {
423 $media_model.removeClass('acf-expanded');
424 }
425 else
426 {
427 $media_model.addClass('acf-expanded');
428 }
429
430 });
431
432
433 // append
434 $media_model.find('.media-frame-router').append( button );
435
436
437 }, 0);
438
439
440 // setup fields
441 // The clearTimout is needed to prevent many setup functions from running at the same time
442 clearTimeout( acf.media.render_timout );
443 acf.media.render_timout = setTimeout(function(){
444
445 $(document).trigger( 'acf/setup_fields', [ _this.$el ] );
446
447 }, 50);
448
449
450 // return based on the origional render function
451 return this;
452 };
453
454
455 // modify dispose
456 _prototype.dispose = function() {
457
458 // remove
459 $(document).trigger('acf/remove_fields', [ this.$el ]);
460
461
462 // run the old render function
463 this.orig_dispose();
464
465 };
466
467
468 // override save
469 _prototype.save = function( event ) {
470
471 var data = {},
472 names = {};
473
474 if ( event )
475 event.preventDefault();
476
477
478 _.each( this.$el.serializeArray(), function( pair ) {
479
480 // initiate name
481 if( pair.name.slice(-2) === '[]' )
482 {
483 // remove []
484 pair.name = pair.name.replace('[]', '');
485
486
487 // initiate counter
488 if( typeof names[ pair.name ] === 'undefined'){
489
490 names[ pair.name ] = -1;
491 //console.log( names[ pair.name ] );
492
493 }
494
495
496 names[ pair.name ]++
497
498 pair.name += '[' + names[ pair.name ] +']';
499
500
501 }
502
503 data[ pair.name ] = pair.value;
504 });
505
506 this.ignore_render = true;
507 this.model.saveCompat( data );
508
509 };
510 }
511 };
512
513
514 /*
515 * Conditional Logic Calculate
516 *
517 * @description:
518 * @since 3.5.1
519 * @created: 15/10/12
520 */
521
522 acf.conditional_logic = {
523
524 items : [],
525
526 init : function(){
527
528 // reference
529 var _this = this;
530
531
532 // events
533 $(document).on('change', '.field input, .field textarea, .field select', function(){
534
535 // preview hack
536 if( $('#acf-has-changed').exists() )
537 {
538 $('#acf-has-changed').val(1);
539 }
540
541 _this.change( $(this) );
542
543 });
544
545
546 $(document).on('acf/setup_fields', function(e, el){
547
548 //console.log('acf/setup_fields calling acf.conditional_logic.refresh()');
549 _this.refresh( $(el) );
550
551 });
552
553 //console.log('acf.conditional_logic.init() calling acf.conditional_logic.refresh()');
554 _this.refresh();
555
556 },
557 change : function( $el ){
558
559 //console.log('change %o', $el);
560 // reference
561 var _this = this;
562
563
564 // vars
565 var $field = $el.closest('.field'),
566 key = $field.attr('data-field_key');
567
568
569 // loop through items and find rules where this field key is a trigger
570 $.each(this.items, function( k, item ){
571
572 $.each(item.rules, function( k2, rule ){
573
574 // compare rule against the changed $field
575 if( rule.field == key )
576 {
577 _this.refresh_field( item );
578 }
579
580 });
581
582 });
583
584 },
585
586 refresh_field : function( item ){
587
588 //console.log( 'refresh_field: %o ', item );
589 // reference
590 var _this = this;
591
592
593 // vars
594 var $targets = $('.field_key-' + item.field);
595
596
597 // may be multiple targets (sub fields)
598 $targets.each(function(){
599
600 //console.log('target %o', $(this));
601
602 // vars
603 var show = true;
604
605
606 // if 'any' was selected, start of as false and any match will result in show = true
607 if( item.allorany == 'any' )
608 {
609 show = false;
610 }
611
612
613 // vars
614 var $target = $(this),
615 hide_all = true;
616
617
618 // loop through rules
619 $.each(item.rules, function( k2, rule ){
620
621 // vars
622 var $toggle = $('.field_key-' + rule.field);
623
624
625 // are any of $toggle a sub field?
626 if( $toggle.hasClass('sub_field') )
627 {
628 // toggle may be a sibling sub field.
629 // if so ,show an empty td but keep the column
630 $toggle = $target.siblings('.field_key-' + rule.field);
631 hide_all = false;
632
633
634 // if no toggle was found, we need to look at parent sub fields.
635 // if so, hide the entire column
636 if( ! $toggle.exists() )
637 {
638 // loop through all the parents that could contain sub fields
639 $target.parents('tr').each(function(){
640
641 // attempt to update $toggle to this parent sub field
642 $toggle = $(this).find('.field_key-' + rule.field)
643
644 // if the parent sub field actuallly exists, great! Stop the loop
645 if( $toggle.exists() )
646 {
647 return false;
648 }
649
650 });
651
652 hide_all = true;
653 }
654
655 }
656
657
658 // if this sub field is within a flexible content layout, hide the entire column because
659 // there will never be another row added to this table
660 var parent = $target.parent('tr').parent().parent('table').parent('.layout');
661 if( parent.exists() )
662 {
663 hide_all = true;
664
665 if( $target.is('th') && $toggle.is('th') )
666 {
667 $toggle = $target.closest('.layout').find('td.field_key-' + rule.field);
668 }
669
670 }
671
672 // if this sub field is within a repeater field which has a max row of 1, hide the entire column because
673 // there will never be another row added to this table
674 var parent = $target.parent('tr').parent().parent('table').parent('.repeater');
675 if( parent.exists() && parent.attr('data-max_rows') == '1' )
676 {
677 hide_all = true;
678
679 if( $target.is('th') && $toggle.is('th') )
680 {
681 $toggle = $target.closest('table').find('td.field_key-' + rule.field);
682 }
683
684 }
685
686
687 var calculate = _this.calculate( rule, $toggle, $target );
688
689 if( item.allorany == 'all' )
690 {
691 if( calculate == false )
692 {
693 show = false;
694
695 // end loop
696 return false;
697 }
698 }
699 else
700 {
701 if( calculate == true )
702 {
703 show = true;
704
705 // end loop
706 return false;
707 }
708 }
709
710 });
711 // $.each(item.rules, function( k2, rule ){
712
713
714 // clear classes
715 $target.removeClass('acf-conditional_logic-hide acf-conditional_logic-show acf-show-blank');
716
717
718 // hide / show field
719 if( show )
720 {
721 // remove "disabled"
722 $target.find('input, textarea, select').removeAttr('disabled');
723
724 $target.addClass('acf-conditional_logic-show');
725
726 // hook
727 $(document).trigger('acf/conditional_logic/show', [ $target, item ]);
728
729 }
730 else
731 {
732 // add "disabled"
733 $target.find('input, textarea, select').attr('disabled', 'disabled');
734
735 $target.addClass('acf-conditional_logic-hide');
736
737 if( !hide_all )
738 {
739 $target.addClass('acf-show-blank');
740 }
741
742 // hook
743 $(document).trigger('acf/conditional_logic/hide', [ $target, item ]);
744 }
745
746
747 });
748
749 },
750
751 refresh : function( $el ){
752
753 // defaults
754 $el = $el || $('body');
755
756
757 // reference
758 var _this = this;
759
760
761 // loop through items and find rules where this field key is a trigger
762 $.each(this.items, function( k, item ){
763
764 $.each(item.rules, function( k2, rule ){
765
766 // is this field within the $el
767 // this will increase performance by ignoring conditional logic outside of this newly appended element ($el)
768 if( ! $el.find('.field[data-field_key="' + item.field + '"]').exists() )
769 {
770 return;
771 }
772
773 _this.refresh_field( item );
774
775 });
776
777 });
778
779 },
780
781 calculate : function( rule, $toggle, $target ){
782
783 // vars
784 var r = false;
785
786
787 // compare values
788 if( $toggle.hasClass('field_type-true_false') || $toggle.hasClass('field_type-checkbox') || $toggle.hasClass('field_type-radio') )
789 {
790 var exists = $toggle.find('input[value="' + rule.value + '"]:checked').exists();
791
792
793 if( rule.operator == "==" )
794 {
795 if( exists )
796 {
797 r = true;
798 }
799 }
800 else
801 {
802 if( ! exists )
803 {
804 r = true;
805 }
806 }
807
808 }
809 else
810 {
811 // get val and make sure it is an array
812 var val = $toggle.find('input, textarea, select').last().val();
813
814 if( ! $.isArray(val) )
815 {
816 val = [ val ];
817 }
818
819
820 if( rule.operator == "==" )
821 {
822 if( $.inArray(rule.value, val) > -1 )
823 {
824 r = true;
825 }
826 }
827 else
828 {
829 if( $.inArray(rule.value, val) < 0 )
830 {
831 r = true;
832 }
833 }
834
835 }
836
837
838 // return
839 return r;
840
841 }
842
843 };
844
845
846
847
848
849 /*
850 * Document Ready
851 *
852 * @description:
853 * @since: 3.5.8
854 * @created: 17/01/13
855 */
856
857 $(document).ready(function(){
858
859
860 // conditional logic
861 acf.conditional_logic.init();
862
863
864 // fix for older options page add-on
865 $('.acf_postbox > .inside > .options').each(function(){
866
867 $(this).closest('.acf_postbox').addClass( $(this).attr('data-layout') );
868
869 });
870
871
872 // Remove 'field_123' from native custom field metabox
873 $('#metakeyselect option[value^="field_"]').remove();
874
875
876 });
877
878
879 /*
880 * window load
881 *
882 * @description:
883 * @since: 3.5.5
884 * @created: 22/12/12
885 */
886
887 $(window).on('load', function(){
888
889 // init
890 acf.media.init();
891
892
893 setTimeout(function(){
894
895 // Hack for CPT without a content editor
896 try
897 {
898 // post_id may be string (user_1) and therefore, the uploaded image cannot be attached to the post
899 if( $.isNumeric(acf.o.post_id) )
900 {
901 wp.media.view.settings.post.id = acf.o.post_id;
902 }
903
904 }
905 catch(e)
906 {
907 // one of the objects was 'undefined'...
908 }
909
910
911 // setup fields
912 $(document).trigger('acf/setup_fields', [ $('#poststuff') ]);
913
914 }, 10);
915
916 });
917
918
919 /*
920 * Gallery field Add-on Fix
921 *
922 * Gallery field v1.0.0 required some data in the acf object.
923 * Now not required, but older versions of gallery field need this.
924 *
925 * @type object
926 * @date 1/08/13
927 *
928 * @param N/A
929 * @return N/A
930 */
931
932 acf.fields.gallery = {
933 add : function(){},
934 edit : function(){},
935 update_count : function(){},
936 hide_selected_items : function(){},
937 text : {
938 title_add : "Select Images"
939 }
940 };
941
942
943
944 })(jQuery);
945
946 (function($){
947
948
949 /*
950 * acf.screen
951 *
952 * Data used by AJAX to hide / show field groups
953 *
954 * @type object
955 * @date 1/03/2011
956 *
957 * @param N/A
958 * @return N/A
959 */
960
961 acf.screen = {
962 action : 'acf/location/match_field_groups_ajax',
963 post_id : 0,
964 page_template : 0,
965 page_parent : 0,
966 page_type : 0,
967 post_category : 0,
968 post_format : 0,
969 taxonomy : 0,
970 lang : 0,
971 nonce : 0
972 };
973
974
975 /*
976 * Document Ready
977 *
978 * Updates acf.screen with more data
979 *
980 * @type function
981 * @date 1/03/2011
982 *
983 * @param N/A
984 * @return N/A
985 */
986
987 $(document).ready(function(){
988
989
990 // update post_id
991 acf.screen.post_id = acf.o.post_id;
992 acf.screen.nonce = acf.o.nonce;
993
994
995 // MPML
996 if( $('#icl-als-first').length > 0 )
997 {
998 var href = $('#icl-als-first').children('a').attr('href'),
999 regex = new RegExp( "lang=([^&#]*)" ),
1000 results = regex.exec( href );
1001
1002 // lang
1003 acf.screen.lang = results[1];
1004
1005 }
1006
1007 });
1008
1009
1010 /*
1011 * acf/update_field_groups
1012 *
1013 * finds the new id's for metaboxes and show's hides metaboxes
1014 *
1015 * @type event
1016 * @date 1/03/2011
1017 *
1018 * @param N/A
1019 * @return N/A
1020 */
1021
1022 $(document).on('acf/update_field_groups', function(){
1023
1024 // Only for a post.
1025 // This is an attempt to stop the action running on the options page add-on.
1026 if( ! acf.screen.post_id || ! $.isNumeric(acf.screen.post_id) )
1027 {
1028 return false;
1029 }
1030
1031
1032 $.ajax({
1033 url: ajaxurl,
1034 data: acf.screen,
1035 type: 'post',
1036 dataType: 'json',
1037 success: function(result){
1038
1039 // validate
1040 if( !result )
1041 {
1042 return false;
1043 }
1044
1045
1046 // hide all metaboxes
1047 $('.acf_postbox').addClass('acf-hidden');
1048 $('.acf_postbox-toggle').addClass('acf-hidden');
1049
1050
1051 // dont bother loading style or html for inputs
1052 if( result.length == 0 )
1053 {
1054 return false;
1055 }
1056
1057
1058 // show the new postboxes
1059 $.each(result, function(k, v) {
1060
1061
1062 // vars
1063 var $el = $('#acf_' + v),
1064 $toggle = $('#adv-settings .acf_postbox-toggle[for="acf_' + v + '-hide"]');
1065
1066
1067 // classes
1068 $el.removeClass('acf-hidden hide-if-js');
1069 $toggle.removeClass('acf-hidden');
1070 $toggle.find('input[type="checkbox"]').attr('checked', 'checked');
1071
1072
1073 // load fields if needed
1074 $el.find('.acf-replace-with-fields').each(function(){
1075
1076 var $replace = $(this);
1077
1078 $.ajax({
1079 url : ajaxurl,
1080 data : {
1081 action : 'acf/post/render_fields',
1082 acf_id : v,
1083 post_id : acf.o.post_id,
1084 nonce : acf.o.nonce
1085 },
1086 type : 'post',
1087 dataType : 'html',
1088 success : function( html ){
1089
1090 $replace.replaceWith( html );
1091
1092 $(document).trigger('acf/setup_fields', $el);
1093
1094 }
1095 });
1096
1097 });
1098 });
1099
1100
1101 // load style
1102 $.ajax({
1103 url : ajaxurl,
1104 data : {
1105 action : 'acf/post/get_style',
1106 acf_id : result[0],
1107 nonce : acf.o.nonce
1108 },
1109 type : 'post',
1110 dataType : 'html',
1111 success : function( result ){
1112
1113 $('#acf_style').html( result );
1114
1115 }
1116 });
1117
1118
1119
1120 }
1121 });
1122 });
1123
1124
1125 /*
1126 * Events
1127 *
1128 * Updates acf.screen with more data and triggers the update event
1129 *
1130 * @type function
1131 * @date 1/03/2011
1132 *
1133 * @param N/A
1134 * @return N/A
1135 */
1136
1137 $(document).on('change', '#page_template', function(){
1138
1139 acf.screen.page_template = $(this).val();
1140
1141 $(document).trigger('acf/update_field_groups');
1142
1143 });
1144
1145
1146 $(document).on('change', '#parent_id', function(){
1147
1148 var val = $(this).val();
1149
1150
1151 // set page_type / page_parent
1152 if( val != "" )
1153 {
1154 acf.screen.page_type = 'child';
1155 acf.screen.page_parent = val;
1156 }
1157 else
1158 {
1159 acf.screen.page_type = 'parent';
1160 acf.screen.page_parent = 0;
1161 }
1162
1163
1164 $(document).trigger('acf/update_field_groups');
1165
1166 });
1167
1168
1169 $(document).on('change', '#post-formats-select input[type="radio"]', function(){
1170
1171 var val = $(this).val();
1172
1173 if( val == '0' )
1174 {
1175 val = 'standard';
1176 }
1177
1178 acf.screen.post_format = val;
1179
1180 $(document).trigger('acf/update_field_groups');
1181
1182 });
1183
1184
1185 function _sync_taxonomy_terms() {
1186
1187 // vars
1188 var values = [];
1189
1190
1191 $('.categorychecklist input:checked, .acf-taxonomy-field input:checked, .acf-taxonomy-field option:selected').each(function(){
1192
1193 // validate
1194 if( $(this).is(':hidden') || $(this).is(':disabled') )
1195 {
1196 return;
1197 }
1198
1199
1200 // validate media popup
1201 if( $(this).closest('.media-frame').exists() )
1202 {
1203 return;
1204 }
1205
1206
1207 // validate acf
1208 if( $(this).closest('.acf-taxonomy-field').exists() )
1209 {
1210 if( $(this).closest('.acf-taxonomy-field').attr('data-load_save') == '0' )
1211 {
1212 return;
1213 }
1214 }
1215
1216
1217 // append
1218 if( values.indexOf( $(this).val() ) === -1 )
1219 {
1220 values.push( $(this).val() );
1221 }
1222
1223 });
1224
1225
1226 // update screen
1227 acf.screen.post_category = values;
1228 acf.screen.taxonomy = values;
1229
1230
1231 // trigger change
1232 $(document).trigger('acf/update_field_groups');
1233
1234 }
1235
1236
1237 $(document).on('change', '.categorychecklist input, .acf-taxonomy-field input, .acf-taxonomy-field select', function(){
1238
1239 // a taxonomy field may trigger this change event, however, the value selected is not
1240 // actually a term relatinoship, it is meta data
1241 if( $(this).closest('.acf-taxonomy-field').exists() )
1242 {
1243 if( $(this).closest('.acf-taxonomy-field').attr('data-save') == '0' )
1244 {
1245 return;
1246 }
1247 }
1248
1249
1250 // this may be triggered from editing an imgae in a popup. Popup does not support correct metaboxes so ignore this
1251 if( $(this).closest('.media-frame').exists() )
1252 {
1253 return;
1254 }
1255
1256
1257 // set timeout to fix issue with chrome which does not register the change has yet happened
1258 setTimeout(function(){
1259
1260 _sync_taxonomy_terms();
1261
1262 }, 1);
1263
1264
1265 });
1266
1267
1268
1269
1270 })(jQuery);
1271
1272 (function($){
1273
1274 /*
1275 * Color Picker
1276 *
1277 * jQuery functionality for this field type
1278 *
1279 * @type object
1280 * @date 20/07/13
1281 *
1282 * @param N/A
1283 * @return N/A
1284 */
1285
1286 var _cp = acf.fields.color_picker = {
1287
1288 $el : null,
1289 $input : null,
1290
1291 set : function( o ){
1292
1293 // merge in new option
1294 $.extend( this, o );
1295
1296
1297 // find input
1298 this.$input = this.$el.find('input[type="text"]');
1299
1300
1301 // return this for chaining
1302 return this;
1303
1304 },
1305 init : function(){
1306
1307 // vars (reference)
1308 var $input = this.$input;
1309
1310
1311 // is clone field?
1312 if( acf.helpers.is_clone_field($input) )
1313 {
1314 return;
1315 }
1316
1317
1318 this.$input.wpColorPicker();
1319
1320
1321
1322 }
1323 };
1324
1325
1326 /*
1327 * acf/setup_fields
1328 *
1329 * run init function on all elements for this field
1330 *
1331 * @type event
1332 * @date 20/07/13
1333 *
1334 * @param {object} e event object
1335 * @param {object} el DOM object which may contain new ACF elements
1336 * @return N/A
1337 */
1338
1339 $(document).on('acf/setup_fields', function(e, el){
1340
1341 $(el).find('.acf-color_picker').each(function(){
1342
1343 _cp.set({ $el : $(this) }).init();
1344
1345 });
1346
1347 });
1348
1349
1350 })(jQuery);
1351
1352 (function($){
1353
1354 /*
1355 * Date Picker
1356 *
1357 * static model for this field
1358 *
1359 * @type event
1360 * @date 1/06/13
1361 *
1362 */
1363
1364 acf.fields.date_picker = {
1365
1366 $el : null,
1367 $input : null,
1368 $hidden : null,
1369
1370 o : {},
1371
1372 set : function( o ){
1373
1374 // merge in new option
1375 $.extend( this, o );
1376
1377
1378 // find input
1379 this.$input = this.$el.find('input[type="text"]');
1380 this.$hidden = this.$el.find('input[type="hidden"]');
1381
1382
1383 // get options
1384 this.o = acf.helpers.get_atts( this.$el );
1385
1386
1387 // return this for chaining
1388 return this;
1389
1390 },
1391 init : function(){
1392
1393 // is clone field?
1394 if( acf.helpers.is_clone_field(this.$hidden) )
1395 {
1396 return;
1397 }
1398
1399
1400 // get and set value from alt field
1401 this.$input.val( this.$hidden.val() );
1402
1403
1404 // create options
1405 var options = $.extend( {}, acf.l10n.date_picker, {
1406 dateFormat : this.o.save_format,
1407 altField : this.$hidden,
1408 altFormat : this.o.save_format,
1409 changeYear : true,
1410 yearRange : "-100:+100",
1411 changeMonth : true,
1412 showButtonPanel : true,
1413 firstDay : this.o.first_day
1414 });
1415
1416
1417 // add date picker
1418 this.$input.addClass('active').datepicker( options );
1419
1420
1421 // now change the format back to how it should be.
1422 this.$input.datepicker( "option", "dateFormat", this.o.display_format );
1423
1424
1425 // wrap the datepicker (only if it hasn't already been wrapped)
1426 if( $('body > #ui-datepicker-div').length > 0 )
1427 {
1428 $('#ui-datepicker-div').wrap('<div class="ui-acf" />');
1429 }
1430
1431 },
1432 blur : function(){
1433
1434 if( !this.$input.val() )
1435 {
1436 this.$hidden.val('');
1437 }
1438
1439 }
1440
1441 };
1442
1443
1444 /*
1445 * acf/setup_fields
1446 *
1447 * run init function on all elements for this field
1448 *
1449 * @type event
1450 * @date 20/07/13
1451 *
1452 * @param {object} e event object
1453 * @param {object} el DOM object which may contain new ACF elements
1454 * @return N/A
1455 */
1456
1457 $(document).on('acf/setup_fields', function(e, el){
1458
1459 $(el).find('.acf-date_picker').each(function(){
1460
1461 acf.fields.date_picker.set({ $el : $(this) }).init();
1462
1463 });
1464
1465 });
1466
1467
1468 /*
1469 * Events
1470 *
1471 * jQuery events for this field
1472 *
1473 * @type event
1474 * @date 1/06/13
1475 *
1476 */
1477
1478 $(document).on('blur', '.acf-date_picker input[type="text"]', function( e ){
1479
1480 acf.fields.date_picker.set({ $el : $(this).parent() }).blur();
1481
1482 });
1483
1484
1485 })(jQuery);
1486
1487 (function($){
1488
1489 /*
1490 * File
1491 *
1492 * static model for this field
1493 *
1494 * @type event
1495 * @date 1/06/13
1496 *
1497 */
1498
1499
1500 // reference
1501 var _media = acf.media;
1502
1503
1504 acf.fields.file = {
1505
1506 $el : null,
1507 $input : null,
1508
1509 o : {},
1510
1511 set : function( o ){
1512
1513 // merge in new option
1514 $.extend( this, o );
1515
1516
1517 // find input
1518 this.$input = this.$el.find('input[type="hidden"]');
1519
1520
1521 // get options
1522 this.o = acf.helpers.get_atts( this.$el );
1523
1524
1525 // multiple?
1526 this.o.multiple = this.$el.closest('.repeater').exists() ? true : false;
1527
1528
1529 // wp library query
1530 this.o.query = {};
1531
1532
1533 // library
1534 if( this.o.library == 'uploadedTo' )
1535 {
1536 this.o.query.uploadedTo = acf.o.post_id;
1537 }
1538
1539
1540 // return this for chaining
1541 return this;
1542
1543 },
1544 init : function(){
1545
1546 // is clone field?
1547 if( acf.helpers.is_clone_field(this.$input) )
1548 {
1549 return;
1550 }
1551
1552 },
1553 add : function( file ){
1554
1555 // this function must reference a global div variable due to the pre WP 3.5 uploader
1556 // vars
1557 var div = _media.div;
1558
1559
1560 // set atts
1561 div.find('.acf-file-icon').attr( 'src', file.icon );
1562 div.find('.acf-file-title').text( file.title );
1563 div.find('.acf-file-name').text( file.name ).attr( 'href', file.url );
1564 div.find('.acf-file-size').text( file.size );
1565 div.find('.acf-file-value').val( file.id ).trigger('change');
1566
1567
1568 // set div class
1569 div.addClass('active');
1570
1571
1572 // validation
1573 div.closest('.field').removeClass('error');
1574
1575 },
1576
1577 new_frame: function( attributes ){
1578
1579 // set global var
1580 _media.div = this.$el;
1581
1582
1583 // clear the frame
1584 _media.clear_frame();
1585
1586
1587 // vars
1588 attributes.states = [];
1589
1590 // append states
1591 attributes.states.push(
1592 new wp.media.controller.Library({
1593 library : wp.media.query( this.o.query ),
1594 multiple : attributes.multiple,
1595 title : attributes.title,
1596 priority : 20,
1597 filterable : 'all'
1598 })
1599 );
1600
1601
1602 // edit image functionality (added in WP 3.9)
1603 if( acf.helpers.isset(wp, 'media', 'controller', 'EditImage') ) {
1604
1605 attributes.states.push( new wp.media.controller.EditImage() );
1606
1607 }
1608
1609
1610 // Create the media frame
1611 _media.frame = wp.media( attributes );
1612
1613
1614 // edit image view
1615 // source: media-views.js:2410 editImageContent()
1616 _media.frame.on('content:render:edit-image', function(){
1617
1618 var image = this.state().get('image'),
1619 view = new wp.media.view.EditImage( { model: image, controller: this } ).render();
1620
1621 this.content.set( view );
1622
1623 // after creating the wrapper view, load the actual editor via an ajax call
1624 view.loadEditor();
1625
1626 }, _media.frame);
1627
1628
1629 // update toolbar button
1630 _media.frame.on( 'toolbar:create:select', function( toolbar ) {
1631
1632 toolbar.view = new wp.media.view.Toolbar.Select({
1633 text: attributes.button.text,
1634 controller: this
1635 });
1636
1637 }, _media.frame );
1638
1639
1640 // return
1641 return _media.frame;
1642
1643 },
1644
1645 edit : function(){
1646
1647 // vars
1648 var id = this.$input.val();
1649
1650
1651 // create frame
1652 this.new_frame({
1653 title : acf.l10n.file.edit,
1654 multiple : false,
1655 button : { text : acf.l10n.file.update }
1656 });
1657
1658
1659 // open
1660 _media.frame.on('open',function() {
1661
1662 // set to browse
1663 if( _media.frame.content._mode != 'browse' )
1664 {
1665 _media.frame.content.mode('browse');
1666 }
1667
1668
1669 // add class
1670 _media.frame.$el.closest('.media-modal').addClass('acf-media-modal acf-expanded');
1671
1672
1673 // set selection
1674 var selection = _media.frame.state().get('selection'),
1675 attachment = wp.media.attachment( id );
1676
1677
1678 // to fetch or not to fetch
1679 if( $.isEmptyObject(attachment.changed) )
1680 {
1681 attachment.fetch();
1682 }
1683
1684
1685 selection.add( attachment );
1686
1687 });
1688
1689
1690 // close
1691 _media.frame.on('close',function(){
1692
1693 // remove class
1694 _media.frame.$el.closest('.media-modal').removeClass('acf-media-modal');
1695
1696 });
1697
1698
1699 // Finally, open the modal
1700 acf.media.frame.open();
1701
1702 },
1703 remove : function()
1704 {
1705
1706 // set atts
1707 this.$el.find('.acf-file-icon').attr( 'src', '' );
1708 this.$el.find('.acf-file-title').text( '' );
1709 this.$el.find('.acf-file-name').text( '' ).attr( 'href', '' );
1710 this.$el.find('.acf-file-size').text( '' );
1711 this.$el.find('.acf-file-value').val( '' ).trigger('change');
1712
1713
1714 // remove class
1715 this.$el.removeClass('active');
1716
1717 },
1718 popup : function()
1719 {
1720 // reference
1721 var t = this;
1722
1723
1724 // create frame
1725 this.new_frame({
1726 title : acf.l10n.file.select,
1727 multiple : t.o.multiple,
1728 button : { text : acf.l10n.file.select }
1729 });
1730
1731
1732 // customize model / view
1733 acf.media.frame.on('content:activate', function(){
1734
1735 // vars
1736 var toolbar = null,
1737 filters = null;
1738
1739
1740 // populate above vars making sure to allow for failure
1741 try
1742 {
1743 toolbar = acf.media.frame.content.get().toolbar;
1744 filters = toolbar.get('filters');
1745 }
1746 catch(e)
1747 {
1748 // one of the objects was 'undefined'... perhaps the frame open is Upload Files
1749 //console.log( e );
1750 }
1751
1752
1753 // validate
1754 if( !filters )
1755 {
1756 return false;
1757 }
1758
1759
1760 // no need for 'uploaded' filter
1761 if( t.o.library == 'uploadedTo' )
1762 {
1763 filters.$el.find('option[value="uploaded"]').remove();
1764 filters.$el.after('<span>' + acf.l10n.file.uploadedTo + '</span>')
1765
1766 $.each( filters.filters, function( k, v ){
1767
1768 v.props.uploadedTo = acf.o.post_id;
1769
1770 });
1771 }
1772
1773 });
1774
1775
1776 // When an image is selected, run a callback.
1777 acf.media.frame.on( 'select', function() {
1778
1779 // get selected images
1780 selection = _media.frame.state().get('selection');
1781
1782 if( selection )
1783 {
1784 var i = 0;
1785
1786 selection.each(function(attachment){
1787
1788 // counter
1789 i++;
1790
1791
1792 // select / add another file field?
1793 if( i > 1 )
1794 {
1795 // vars
1796 var $td = _media.div.closest('td'),
1797 $tr = $td.closest('.row'),
1798 $repeater = $tr.closest('.repeater'),
1799 key = $td.attr('data-field_key'),
1800 selector = 'td .acf-file-uploader:first';
1801
1802
1803 // key only exists for repeater v1.0.1 +
1804 if( key )
1805 {
1806 selector = 'td[data-field_key="' + key + '"] .acf-file-uploader';
1807 }
1808
1809
1810 // add row?
1811 if( ! $tr.next('.row').exists() )
1812 {
1813 $repeater.find('.add-row-end').trigger('click');
1814
1815 }
1816
1817
1818 // update current div
1819 _media.div = $tr.next('.row').find( selector );
1820
1821 }
1822
1823
1824 // vars
1825 var file = {
1826 id : attachment.id,
1827 title : attachment.attributes.title,
1828 name : attachment.attributes.filename,
1829 url : attachment.attributes.url,
1830 icon : attachment.attributes.icon,
1831 size : attachment.attributes.filesize
1832 };
1833
1834
1835 // add file to field
1836 acf.fields.file.add( file );
1837
1838
1839 });
1840 // selection.each(function(attachment){
1841 }
1842 // if( selection )
1843
1844 });
1845 // acf.media.frame.on( 'select', function() {
1846
1847
1848 // Finally, open the modal
1849 acf.media.frame.open();
1850
1851
1852 return false;
1853 }
1854
1855 };
1856
1857
1858 /*
1859 * Events
1860 *
1861 * jQuery events for this field
1862 *
1863 * @type function
1864 * @date 1/03/2011
1865 *
1866 * @param N/A
1867 * @return N/A
1868 */
1869
1870 $(document).on('click', '.acf-file-uploader .acf-button-edit', function( e ){
1871
1872 e.preventDefault();
1873
1874 acf.fields.file.set({ $el : $(this).closest('.acf-file-uploader') }).edit();
1875
1876 });
1877
1878 $(document).on('click', '.acf-file-uploader .acf-button-delete', function( e ){
1879
1880 e.preventDefault();
1881
1882 acf.fields.file.set({ $el : $(this).closest('.acf-file-uploader') }).remove();
1883
1884 });
1885
1886
1887 $(document).on('click', '.acf-file-uploader .add-file', function( e ){
1888
1889 e.preventDefault();
1890
1891 acf.fields.file.set({ $el : $(this).closest('.acf-file-uploader') }).popup();
1892
1893 });
1894
1895
1896 })(jQuery);
1897
1898 (function($){
1899
1900 /*
1901 * Location
1902 *
1903 * static model for this field
1904 *
1905 * @type event
1906 * @date 1/06/13
1907 *
1908 */
1909
1910 acf.fields.google_map = {
1911
1912 $el : null,
1913 $input : null,
1914
1915 o : {},
1916 api: {
1917 sensor: false,
1918 libraries: 'places'
1919 },
1920
1921 ready : false,
1922 geocoder : false,
1923 map : false,
1924 maps : {},
1925
1926 set : function( o ){
1927
1928 // merge in new option
1929 $.extend( this, o );
1930
1931
1932 // find input
1933 this.$input = this.$el.find('.input-address');
1934
1935
1936 // get options
1937 this.o = acf.helpers.get_atts( this.$el );
1938
1939
1940 // get map
1941 if( this.maps[ this.o.id ] )
1942 {
1943 this.map = this.maps[ this.o.id ];
1944 }
1945
1946
1947 // return this for chaining
1948 return this;
1949
1950 },
1951 init : function(){
1952
1953 // geocode
1954 if( !this.geocoder )
1955 {
1956 this.geocoder = new google.maps.Geocoder();
1957 }
1958
1959
1960 // google maps is loaded and ready
1961 this.ready = true;
1962
1963
1964 // is clone field?
1965 if( acf.helpers.is_clone_field(this.$input) )
1966 {
1967 return;
1968 }
1969
1970 this.render();
1971
1972 },
1973 render : function(){
1974
1975 // reference
1976 var _this = this,
1977 _$el = this.$el;
1978
1979
1980 // vars
1981 var args = {
1982 zoom : parseInt(this.o.zoom),
1983 center : new google.maps.LatLng(this.o.lat, this.o.lng),
1984 mapTypeId : google.maps.MapTypeId.ROADMAP
1985 };
1986
1987 // create map
1988 this.map = new google.maps.Map( this.$el.find('.canvas')[0], args);
1989
1990
1991 // add search
1992 var autocomplete = new google.maps.places.Autocomplete( this.$el.find('.search')[0] );
1993 autocomplete.map = this.map;
1994 autocomplete.bindTo('bounds', this.map);
1995
1996
1997 // add dummy marker
1998 this.map.marker = new google.maps.Marker({
1999 draggable : true,
2000 raiseOnDrag : true,
2001 map : this.map,
2002 });
2003
2004
2005 // add references
2006 this.map.$el = this.$el;
2007
2008
2009 // value exists?
2010 var lat = this.$el.find('.input-lat').val(),
2011 lng = this.$el.find('.input-lng').val();
2012
2013 if( lat && lng )
2014 {
2015 this.update( lat, lng ).center();
2016 }
2017
2018
2019 // events
2020 google.maps.event.addListener(autocomplete, 'place_changed', function( e ) {
2021
2022 // reference
2023 var $el = this.map.$el;
2024
2025
2026 // manually update address
2027 var address = $el.find('.search').val();
2028 $el.find('.input-address').val( address );
2029 $el.find('.title h4').text( address );
2030
2031
2032 // vars
2033 var place = this.getPlace();
2034
2035
2036 // validate
2037 if( place.geometry )
2038 {
2039 var lat = place.geometry.location.lat(),
2040 lng = place.geometry.location.lng();
2041
2042
2043 _this.set({ $el : $el }).update( lat, lng ).center();
2044 }
2045 else
2046 {
2047 // client hit enter, manulaly get the place
2048 _this.geocoder.geocode({ 'address' : address }, function( results, status ){
2049
2050 // validate
2051 if( status != google.maps.GeocoderStatus.OK )
2052 {
2053 console.log('Geocoder failed due to: ' + status);
2054 return;
2055 }
2056
2057 if( !results[0] )
2058 {
2059 console.log('No results found');
2060 return;
2061 }
2062
2063
2064 // get place
2065 place = results[0];
2066
2067 var lat = place.geometry.location.lat(),
2068 lng = place.geometry.location.lng();
2069
2070
2071 _this.set({ $el : $el }).update( lat, lng ).center();
2072
2073 });
2074 }
2075
2076 });
2077
2078
2079 google.maps.event.addListener( this.map.marker, 'dragend', function(){
2080
2081 // reference
2082 var $el = this.map.$el;
2083
2084
2085 // vars
2086 var position = this.map.marker.getPosition(),
2087 lat = position.lat(),
2088 lng = position.lng();
2089
2090 _this.set({ $el : $el }).update( lat, lng ).sync();
2091
2092 });
2093
2094
2095 google.maps.event.addListener( this.map, 'click', function( e ) {
2096
2097 // reference
2098 var $el = this.$el;
2099
2100
2101 // vars
2102 var lat = e.latLng.lat(),
2103 lng = e.latLng.lng();
2104
2105
2106 _this.set({ $el : $el }).update( lat, lng ).sync();
2107
2108 });
2109
2110
2111
2112 // add to maps
2113 this.maps[ this.o.id ] = this.map;
2114
2115
2116 },
2117
2118 update : function( lat, lng ){
2119
2120 // vars
2121 var latlng = new google.maps.LatLng( lat, lng );
2122
2123
2124 // update inputs
2125 this.$el.find('.input-lat').val( lat );
2126 this.$el.find('.input-lng').val( lng ).trigger('change');
2127
2128
2129 // update marker
2130 this.map.marker.setPosition( latlng );
2131
2132
2133 // show marker
2134 this.map.marker.setVisible( true );
2135
2136
2137 // update class
2138 this.$el.addClass('active');
2139
2140
2141 // validation
2142 this.$el.closest('.field').removeClass('error');
2143
2144
2145 // return for chaining
2146 return this;
2147 },
2148
2149 center : function(){
2150
2151 // vars
2152 var position = this.map.marker.getPosition(),
2153 lat = this.o.lat,
2154 lng = this.o.lng;
2155
2156
2157 // if marker exists, center on the marker
2158 if( position )
2159 {
2160 lat = position.lat();
2161 lng = position.lng();
2162 }
2163
2164
2165 var latlng = new google.maps.LatLng( lat, lng );
2166
2167
2168 // set center of map
2169 this.map.setCenter( latlng );
2170 },
2171
2172 sync : function(){
2173
2174 // reference
2175 var $el = this.$el;
2176
2177
2178 // vars
2179 var position = this.map.marker.getPosition(),
2180 latlng = new google.maps.LatLng( position.lat(), position.lng() );
2181
2182
2183 this.geocoder.geocode({ 'latLng' : latlng }, function( results, status ){
2184
2185 // validate
2186 if( status != google.maps.GeocoderStatus.OK )
2187 {
2188 console.log('Geocoder failed due to: ' + status);
2189 return;
2190 }
2191
2192 if( !results[0] )
2193 {
2194 console.log('No results found');
2195 return;
2196 }
2197
2198
2199 // get location
2200 var location = results[0];
2201
2202
2203 // update h4
2204 $el.find('.title h4').text( location.formatted_address );
2205
2206
2207 // update input
2208 $el.find('.input-address').val( location.formatted_address ).trigger('change');
2209
2210 });
2211
2212
2213 // return for chaining
2214 return this;
2215 },
2216
2217 locate : function(){
2218
2219 // reference
2220 var _this = this,
2221 _$el = this.$el;
2222
2223
2224 // Try HTML5 geolocation
2225 if( ! navigator.geolocation )
2226 {
2227 alert( acf.l10n.google_map.browser_support );
2228 return this;
2229 }
2230
2231
2232 // show loading text
2233 _$el.find('.title h4').text(acf.l10n.google_map.locating + '...');
2234 _$el.addClass('active');
2235
2236 navigator.geolocation.getCurrentPosition(function(position){
2237
2238 // vars
2239 var lat = position.coords.latitude,
2240 lng = position.coords.longitude;
2241
2242 _this.set({ $el : _$el }).update( lat, lng ).sync().center();
2243
2244 });
2245
2246
2247 },
2248
2249 clear : function(){
2250
2251 // update class
2252 this.$el.removeClass('active');
2253
2254
2255 // clear search
2256 this.$el.find('.search').val('');
2257
2258
2259 // clear inputs
2260 this.$el.find('.input-address').val('');
2261 this.$el.find('.input-lat').val('');
2262 this.$el.find('.input-lng').val('');
2263
2264
2265 // hide marker
2266 this.map.marker.setVisible( false );
2267 },
2268
2269 edit : function(){
2270
2271 // update class
2272 this.$el.removeClass('active');
2273
2274
2275 // clear search
2276 var val = this.$el.find('.title h4').text();
2277
2278
2279 this.$el.find('.search').val( val ).focus();
2280
2281 },
2282
2283 refresh : function(){
2284
2285 // trigger resize on div
2286 google.maps.event.trigger(this.map, 'resize');
2287
2288 // center map
2289 this.center();
2290
2291 }
2292
2293 };
2294
2295
2296 /*
2297 * acf/setup_fields
2298 *
2299 * run init function on all elements for this field
2300 *
2301 * @type event
2302 * @date 20/07/13
2303 *
2304 * @param {object} e event object
2305 * @param {object} el DOM object which may contain new ACF elements
2306 * @return N/A
2307 */
2308
2309 $(document).on('acf/setup_fields', function(e, el){
2310
2311 // reference
2312 var self = acf.fields.google_map;
2313
2314
2315 // vars
2316 var $fields = $(el).find('.acf-google-map');
2317
2318
2319 // validate
2320 if( ! $fields.exists() ) return false;
2321
2322
2323 // no google
2324 if( !acf.helpers.isset(window, 'google', 'load') ) {
2325
2326 // load API
2327 $.getScript('https://www.google.com/jsapi', function(){
2328
2329 // load maps
2330 google.load('maps', '3', { other_params: $.param(self.api), callback: function(){
2331
2332 $fields.each(function(){
2333
2334 acf.fields.google_map.set({ $el : $(this) }).init();
2335
2336 });
2337
2338 }});
2339
2340 });
2341
2342 return false;
2343
2344 }
2345
2346
2347 // no maps or places
2348 if( !acf.helpers.isset(window, 'google', 'maps', 'places') ) {
2349
2350 google.load('maps', '3', { other_params: $.param(self.api), callback: function(){
2351
2352 $fields.each(function(){
2353
2354 acf.fields.google_map.set({ $el : $(this) }).init();
2355
2356 });
2357
2358 }});
2359
2360 return false;
2361
2362 }
2363
2364
2365 // google exists
2366 $fields.each(function(){
2367
2368 acf.fields.google_map.set({ $el : $(this) }).init();
2369
2370 });
2371
2372
2373 // return
2374 return true;
2375
2376 });
2377
2378
2379 /*
2380 * Events
2381 *
2382 * jQuery events for this field
2383 *
2384 * @type function
2385 * @date 1/03/2011
2386 *
2387 * @param N/A
2388 * @return N/A
2389 */
2390
2391 $(document).on('click', '.acf-google-map .acf-sprite-remove', function( e ){
2392
2393 e.preventDefault();
2394
2395 acf.fields.google_map.set({ $el : $(this).closest('.acf-google-map') }).clear();
2396
2397 $(this).blur();
2398
2399 });
2400
2401
2402 $(document).on('click', '.acf-google-map .acf-sprite-locate', function( e ){
2403
2404 e.preventDefault();
2405
2406 acf.fields.google_map.set({ $el : $(this).closest('.acf-google-map') }).locate();
2407
2408 $(this).blur();
2409
2410 });
2411
2412 $(document).on('click', '.acf-google-map .title h4', function( e ){
2413
2414 e.preventDefault();
2415
2416 acf.fields.google_map.set({ $el : $(this).closest('.acf-google-map') }).edit();
2417
2418 });
2419
2420 $(document).on('keydown', '.acf-google-map .search', function( e ){
2421
2422 // prevent form from submitting
2423 if( e.which == 13 )
2424 {
2425 return false;
2426 }
2427
2428 });
2429
2430 $(document).on('blur', '.acf-google-map .search', function( e ){
2431
2432 // vars
2433 var $el = $(this).closest('.acf-google-map');
2434
2435
2436 // has a value?
2437 if( $el.find('.input-lat').val() )
2438 {
2439 $el.addClass('active');
2440 }
2441
2442 });
2443
2444 $(document).on('acf/fields/tab/show acf/conditional_logic/show', function( e, $field ){
2445
2446 // validate
2447 if( ! acf.fields.google_map.ready )
2448 {
2449 return;
2450 }
2451
2452
2453 // validate
2454 if( $field.attr('data-field_type') == 'google_map' )
2455 {
2456 acf.fields.google_map.set({ $el : $field.find('.acf-google-map') }).refresh();
2457 }
2458
2459 });
2460
2461
2462
2463 })(jQuery);
2464
2465 (function($){
2466
2467 /*
2468 * Image
2469 *
2470 * static model for this field
2471 *
2472 * @type event
2473 * @date 1/06/13
2474 *
2475 */
2476
2477
2478 // reference
2479 var _media = acf.media;
2480
2481
2482 acf.fields.image = {
2483
2484 $el : null,
2485 $input : null,
2486
2487 o : {},
2488
2489 set : function( o ){
2490
2491 // merge in new option
2492 $.extend( this, o );
2493
2494
2495 // find input
2496 this.$input = this.$el.find('input[type="hidden"]');
2497
2498
2499 // get options
2500 this.o = acf.helpers.get_atts( this.$el );
2501
2502
2503 // multiple?
2504 this.o.multiple = this.$el.closest('.repeater').exists() ? true : false;
2505
2506
2507 // wp library query
2508 this.o.query = {
2509 type : 'image'
2510 };
2511
2512
2513 // library
2514 if( this.o.library == 'uploadedTo' )
2515 {
2516 this.o.query.uploadedTo = acf.o.post_id;
2517 }
2518
2519
2520 // return this for chaining
2521 return this;
2522
2523 },
2524 init : function(){
2525
2526 // is clone field?
2527 if( acf.helpers.is_clone_field(this.$input) )
2528 {
2529 return;
2530 }
2531
2532 },
2533 add : function( image ){
2534
2535 // this function must reference a global div variable due to the pre WP 3.5 uploader
2536 // vars
2537 var div = _media.div;
2538
2539
2540 // set atts
2541 div.find('.acf-image-image').attr( 'src', image.url );
2542 div.find('.acf-image-value').val( image.id ).trigger('change');
2543
2544
2545 // set div class
2546 div.addClass('active');
2547
2548
2549 // validation
2550 div.closest('.field').removeClass('error');
2551
2552 },
2553
2554 new_frame: function( attributes ){
2555
2556 // set global var
2557 _media.div = this.$el;
2558
2559
2560 // clear the frame
2561 _media.clear_frame();
2562
2563
2564 // vars
2565 attributes.states = [];
2566
2567
2568 // append states
2569 attributes.states.push(
2570 new wp.media.controller.Library({
2571 library : wp.media.query( this.o.query ),
2572 multiple : attributes.multiple,
2573 title : attributes.title,
2574 priority : 20,
2575 filterable : 'all'
2576 })
2577 );
2578
2579
2580 // edit image functionality (added in WP 3.9)
2581 if( acf.helpers.isset(wp, 'media', 'controller', 'EditImage') ) {
2582
2583 attributes.states.push( new wp.media.controller.EditImage() );
2584
2585 }
2586
2587
2588 // Create the media frame
2589 _media.frame = wp.media( attributes );
2590
2591
2592 // edit image view
2593 // source: media-views.js:2410 editImageContent()
2594 _media.frame.on('content:render:edit-image', function(){
2595
2596 var image = this.state().get('image'),
2597 view = new wp.media.view.EditImage( { model: image, controller: this } ).render();
2598
2599 this.content.set( view );
2600
2601 // after creating the wrapper view, load the actual editor via an ajax call
2602 view.loadEditor();
2603
2604 }, _media.frame);
2605
2606
2607 // update toolbar button
2608 _media.frame.on( 'toolbar:create:select', function( toolbar ) {
2609
2610 toolbar.view = new wp.media.view.Toolbar.Select({
2611 text: attributes.button.text,
2612 controller: this
2613 });
2614
2615 }, _media.frame );
2616
2617
2618 // return
2619 return _media.frame;
2620
2621 },
2622
2623 edit : function(){
2624
2625 // vars
2626 var id = this.$input.val();
2627
2628
2629 // create frame
2630 this.new_frame({
2631 title : acf.l10n.image.edit,
2632 multiple : false,
2633 button : { text : acf.l10n.image.update }
2634 });
2635
2636
2637 // open
2638 _media.frame.on('open',function() {
2639
2640 // set to browse
2641 if( _media.frame.content._mode != 'browse' )
2642 {
2643 _media.frame.content.mode('browse');
2644 }
2645
2646
2647 // add class
2648 _media.frame.$el.closest('.media-modal').addClass('acf-media-modal acf-expanded');
2649
2650
2651 // set selection
2652 var selection = _media.frame.state().get('selection'),
2653 attachment = wp.media.attachment( id );
2654
2655
2656 // to fetch or not to fetch
2657 if( $.isEmptyObject(attachment.changed) )
2658 {
2659 attachment.fetch();
2660 }
2661
2662
2663 selection.add( attachment );
2664
2665 });
2666
2667
2668 // close
2669 _media.frame.on('close',function(){
2670
2671 // remove class
2672 _media.frame.$el.closest('.media-modal').removeClass('acf-media-modal');
2673
2674 });
2675
2676
2677 // Finally, open the modal
2678 _media.frame.open();
2679
2680 },
2681 remove : function()
2682 {
2683
2684 // set atts
2685 this.$el.find('.acf-image-image').attr( 'src', '' );
2686 this.$el.find('.acf-image-value').val( '' ).trigger('change');
2687
2688
2689 // remove class
2690 this.$el.removeClass('active');
2691
2692 },
2693 popup : function()
2694 {
2695 // reference
2696 var t = this;
2697
2698
2699 // create frame
2700 this.new_frame({
2701 title : acf.l10n.image.select,
2702 multiple : t.o.multiple,
2703 button : { text : acf.l10n.image.select }
2704 });
2705
2706
2707 // customize model / view
2708 _media.frame.on('content:activate', function(){
2709
2710 // vars
2711 var toolbar = null,
2712 filters = null;
2713
2714
2715 // populate above vars making sure to allow for failure
2716 try
2717 {
2718 toolbar = acf.media.frame.content.get().toolbar;
2719 filters = toolbar.get('filters');
2720 }
2721 catch(e)
2722 {
2723 // one of the objects was 'undefined'... perhaps the frame open is Upload Files
2724 //console.log( e );
2725 }
2726
2727
2728 // validate
2729 if( !filters )
2730 {
2731 return false;
2732 }
2733
2734
2735 // filter only images
2736 $.each( filters.filters, function( k, v ){
2737
2738 v.props.type = 'image';
2739
2740 });
2741
2742
2743 // no need for 'uploaded' filter
2744 if( t.o.library == 'uploadedTo' )
2745 {
2746 filters.$el.find('option[value="uploaded"]').remove();
2747 filters.$el.after('<span>' + acf.l10n.image.uploadedTo + '</span>')
2748
2749 $.each( filters.filters, function( k, v ){
2750
2751 v.props.uploadedTo = acf.o.post_id;
2752
2753 });
2754 }
2755
2756
2757 // remove non image options from filter list
2758 filters.$el.find('option').each(function(){
2759
2760 // vars
2761 var v = $(this).attr('value');
2762
2763
2764 // don't remove the 'uploadedTo' if the library option is 'all'
2765 if( v == 'uploaded' && t.o.library == 'all' )
2766 {
2767 return;
2768 }
2769
2770 if( v.indexOf('image') === -1 )
2771 {
2772 $(this).remove();
2773 }
2774
2775 });
2776
2777
2778 // set default filter
2779 filters.$el.val('image').trigger('change');
2780
2781 });
2782
2783
2784 // When an image is selected, run a callback.
2785 acf.media.frame.on( 'select', function() {
2786
2787 // get selected images
2788 selection = _media.frame.state().get('selection');
2789
2790 if( selection )
2791 {
2792 var i = 0;
2793
2794 selection.each(function(attachment){
2795
2796 // counter
2797 i++;
2798
2799
2800 // select / add another image field?
2801 if( i > 1 )
2802 {
2803 // vars
2804 var $td = _media.div.closest('td'),
2805 $tr = $td.closest('.row'),
2806 $repeater = $tr.closest('.repeater'),
2807 key = $td.attr('data-field_key'),
2808 selector = 'td .acf-image-uploader:first';
2809
2810
2811 // key only exists for repeater v1.0.1 +
2812 if( key )
2813 {
2814 selector = 'td[data-field_key="' + key + '"] .acf-image-uploader';
2815 }
2816
2817
2818 // add row?
2819 if( ! $tr.next('.row').exists() )
2820 {
2821 $repeater.find('.add-row-end').trigger('click');
2822
2823 }
2824
2825
2826 // update current div
2827 _media.div = $tr.next('.row').find( selector );
2828
2829 }
2830
2831
2832 // vars
2833 var image = {
2834 id : attachment.id,
2835 url : attachment.attributes.url
2836 };
2837
2838 // is preview size available?
2839 if( attachment.attributes.sizes && attachment.attributes.sizes[ t.o.preview_size ] )
2840 {
2841 image.url = attachment.attributes.sizes[ t.o.preview_size ].url;
2842 }
2843
2844 // add image to field
2845 acf.fields.image.add( image );
2846
2847
2848 });
2849 // selection.each(function(attachment){
2850 }
2851 // if( selection )
2852
2853 });
2854 // acf.media.frame.on( 'select', function() {
2855
2856
2857 // Finally, open the modal
2858 acf.media.frame.open();
2859
2860
2861 return false;
2862 },
2863
2864 // temporary gallery fix
2865 text : {
2866 title_add : "Select Image",
2867 title_edit : "Edit Image"
2868 }
2869
2870 };
2871
2872
2873 /*
2874 * Events
2875 *
2876 * jQuery events for this field
2877 *
2878 * @type function
2879 * @date 1/03/2011
2880 *
2881 * @param N/A
2882 * @return N/A
2883 */
2884
2885 $(document).on('click', '.acf-image-uploader .acf-button-edit', function( e ){
2886
2887 e.preventDefault();
2888
2889 acf.fields.image.set({ $el : $(this).closest('.acf-image-uploader') }).edit();
2890
2891 });
2892
2893 $(document).on('click', '.acf-image-uploader .acf-button-delete', function( e ){
2894
2895 e.preventDefault();
2896
2897 acf.fields.image.set({ $el : $(this).closest('.acf-image-uploader') }).remove();
2898
2899 });
2900
2901
2902 $(document).on('click', '.acf-image-uploader .add-image', function( e ){
2903
2904 e.preventDefault();
2905
2906 acf.fields.image.set({ $el : $(this).closest('.acf-image-uploader') }).popup();
2907
2908 });
2909
2910
2911 })(jQuery);
2912
2913 (function($){
2914
2915 /*
2916 * Radio
2917 *
2918 * static model and events for this field
2919 *
2920 * @type event
2921 * @date 1/06/13
2922 *
2923 */
2924
2925 acf.fields.radio = {
2926
2927 $el : null,
2928 $input : null,
2929 $other : null,
2930 farbtastic : null,
2931
2932 set : function( o ){
2933
2934 // merge in new option
2935 $.extend( this, o );
2936
2937
2938 // find input
2939 this.$input = this.$el.find('input[type="radio"]:checked');
2940 this.$other = this.$el.find('input[type="text"]');
2941
2942
2943 // return this for chaining
2944 return this;
2945
2946 },
2947 change : function(){
2948
2949 if( this.$input.val() == 'other' )
2950 {
2951 this.$other.attr('name', this.$input.attr('name'));
2952 this.$other.show();
2953 }
2954 else
2955 {
2956 this.$other.attr('name', '');
2957 this.$other.hide();
2958 }
2959 }
2960 };
2961
2962
2963 /*
2964 * Events
2965 *
2966 * jQuery events for this field
2967 *
2968 * @type function
2969 * @date 1/03/2011
2970 *
2971 * @param N/A
2972 * @return N/A
2973 */
2974
2975 $(document).on('change', '.acf-radio-list input[type="radio"]', function( e ){
2976
2977 acf.fields.radio.set({ $el : $(this).closest('.acf-radio-list') }).change();
2978
2979 });
2980
2981
2982 })(jQuery);
2983
2984 (function($){
2985
2986 /*
2987 * Relationship
2988 *
2989 * static model for this field
2990 *
2991 * @type event
2992 * @date 1/06/13
2993 *
2994 */
2995
2996 acf.fields.relationship = {
2997
2998 $el : null,
2999 $input : null,
3000 $left : null,
3001 $right : null,
3002
3003 o : {},
3004
3005 timeout : null,
3006
3007 set : function( o ){
3008
3009 // merge in new option
3010 $.extend( this, o );
3011
3012
3013 // find elements
3014 this.$input = this.$el.children('input[type="hidden"]');
3015 this.$left = this.$el.find('.relationship_left'),
3016 this.$right = this.$el.find('.relationship_right');
3017
3018
3019 // get options
3020 this.o = acf.helpers.get_atts( this.$el );
3021
3022
3023 // return this for chaining
3024 return this;
3025
3026 },
3027 init : function(){
3028
3029 // reference
3030 var _this = this;
3031
3032
3033 // is clone field?
3034 if( acf.helpers.is_clone_field(this.$input) )
3035 {
3036 return;
3037 }
3038
3039
3040 // set height of right column
3041 this.$right.find('.relationship_list').height( this.$left.height() -2 );
3042
3043
3044 // right sortable
3045 this.$right.find('.relationship_list').sortable({
3046 axis : 'y',
3047 items : '> li',
3048 forceHelperSize : true,
3049 forcePlaceholderSize : true,
3050 scroll : true,
3051 update : function(){
3052
3053 _this.$input.trigger('change');
3054
3055 }
3056 });
3057
3058
3059 // load more
3060 var $el = this.$el;
3061
3062 this.$left.find('.relationship_list').scrollTop( 0 ).on('scroll', function(e){
3063
3064 // validate
3065 if( $el.hasClass('loading') || $el.hasClass('no-results') )
3066 {
3067 return;
3068 }
3069
3070
3071 // Scrolled to bottom
3072 if( $(this).scrollTop() + $(this).innerHeight() >= $(this).get(0).scrollHeight )
3073 {
3074 var paged = parseInt( $el.attr('data-paged') );
3075
3076 // update paged
3077 $el.attr('data-paged', (paged + 1) );
3078
3079 // fetch
3080 _this.set({ $el : $el }).fetch();
3081 }
3082
3083 });
3084
3085
3086 // ajax fetch values for left side
3087 this.fetch();
3088
3089 },
3090 fetch : function(){
3091
3092 // reference
3093 var _this = this,
3094 $el = this.$el;
3095
3096
3097 // add loading class, stops scroll loading
3098 $el.addClass('loading');
3099
3100
3101 // get results
3102 $.ajax({
3103 url : acf.o.ajaxurl,
3104 type : 'post',
3105 dataType : 'json',
3106 data : $.extend({
3107 action : 'acf/fields/relationship/query_posts',
3108 post_id : acf.o.post_id,
3109 nonce : acf.o.nonce
3110 }, this.o ),
3111 success : function( json ){
3112
3113
3114 // render
3115 _this.set({ $el : $el }).render( json );
3116
3117 }
3118 });
3119
3120 },
3121 render : function( json ){
3122
3123 // reference
3124 var _this = this;
3125
3126
3127 // update classes
3128 this.$el.removeClass('no-results').removeClass('loading');
3129
3130
3131 // new search?
3132 if( this.o.paged == 1 )
3133 {
3134 this.$el.find('.relationship_left li:not(.load-more)').remove();
3135 }
3136
3137
3138 // no results?
3139 if( ! json || ! json.html )
3140 {
3141 this.$el.addClass('no-results');
3142 return;
3143 }
3144
3145
3146 // append new results
3147 this.$el.find('.relationship_left .load-more').before( json.html );
3148
3149
3150 // next page?
3151 if( ! json.next_page_exists )
3152 {
3153 this.$el.addClass('no-results');
3154 }
3155
3156
3157 // apply .hide to left li's
3158 this.$left.find('a').each(function(){
3159
3160 var id = $(this).attr('data-post_id');
3161
3162 if( _this.$right.find('a[data-post_id="' + id + '"]').exists() )
3163 {
3164 $(this).parent().addClass('hide');
3165 }
3166
3167 });
3168
3169 },
3170 add : function( $a ){
3171
3172 // vars
3173 var id = $a.attr('data-post_id'),
3174 title = $a.html();
3175
3176
3177 // max posts
3178 if( this.$right.find('a').length >= this.o.max )
3179 {
3180 alert( acf.l10n.relationship.max.replace('{max}', this.o.max) );
3181 return false;
3182 }
3183
3184
3185 // can be added?
3186 if( $a.parent().hasClass('hide') )
3187 {
3188 return false;
3189 }
3190
3191
3192 // hide
3193 $a.parent().addClass('hide');
3194
3195
3196 // template
3197 var html = [
3198 '<li>',
3199 '<a href="#" data-post_id="' + $a.attr('data-post_id') + '">',
3200 $a.html() + '<span class="acf-button-remove"></span>',
3201 '</a>',
3202 '<input type="hidden" name="' + this.$input.attr('name') + '[]" value="' + $a.attr('data-post_id') + '" />',
3203 '</li>'].join('');
3204
3205
3206 // add new li
3207 this.$right.find('.relationship_list').append( html )
3208
3209
3210 // trigger change on new_li
3211 this.$input.trigger('change');
3212
3213
3214 // validation
3215 this.$el.closest('.field').removeClass('error');
3216
3217
3218 },
3219 remove : function( $a ){
3220
3221 // remove
3222 $a.parent().remove();
3223
3224
3225 // show
3226 this.$left.find('a[data-post_id="' + $a.attr('data-post_id') + '"]').parent('li').removeClass('hide');
3227
3228
3229 // trigger change on new_li
3230 this.$input.trigger('change');
3231
3232 }
3233
3234 };
3235
3236
3237 /*
3238 * acf/setup_fields
3239 *
3240 * run init function on all elements for this field
3241 *
3242 * @type event
3243 * @date 20/07/13
3244 *
3245 * @param {object} e event object
3246 * @param {object} el DOM object which may contain new ACF elements
3247 * @return N/A
3248 */
3249
3250 $(document).on('acf/setup_fields', function(e, el){
3251
3252 $(el).find('.acf_relationship').each(function(){
3253
3254 acf.fields.relationship.set({ $el : $(this) }).init();
3255
3256 });
3257
3258 });
3259
3260
3261 /*
3262 * Events
3263 *
3264 * jQuery events for this field
3265 *
3266 * @type function
3267 * @date 1/03/2011
3268 *
3269 * @param N/A
3270 * @return N/A
3271 */
3272
3273 $(document).on('change', '.acf_relationship .select-post_type', function(e){
3274
3275 // vars
3276 var val = $(this).val(),
3277 $el = $(this).closest('.acf_relationship');
3278
3279
3280 // update attr
3281 $el.attr('data-post_type', val);
3282 $el.attr('data-paged', 1);
3283
3284
3285 // fetch
3286 acf.fields.relationship.set({ $el : $el }).fetch();
3287
3288 });
3289
3290
3291 $(document).on('click', '.acf_relationship .relationship_left .relationship_list a', function( e ){
3292
3293 e.preventDefault();
3294
3295 acf.fields.relationship.set({ $el : $(this).closest('.acf_relationship') }).add( $(this) );
3296
3297 $(this).blur();
3298
3299 });
3300
3301 $(document).on('click', '.acf_relationship .relationship_right .relationship_list a', function( e ){
3302
3303 e.preventDefault();
3304
3305 acf.fields.relationship.set({ $el : $(this).closest('.acf_relationship') }).remove( $(this) );
3306
3307 $(this).blur();
3308
3309 });
3310
3311 $(document).on('keyup', '.acf_relationship input.relationship_search', function( e ){
3312
3313 // vars
3314 var val = $(this).val(),
3315 $el = $(this).closest('.acf_relationship');
3316
3317
3318 // update attr
3319 $el.attr('data-s', val);
3320 $el.attr('data-paged', 1);
3321
3322
3323 // fetch
3324 clearTimeout( acf.fields.relationship.timeout );
3325 acf.fields.relationship.timeout = setTimeout(function(){
3326
3327 acf.fields.relationship.set({ $el : $el }).fetch();
3328
3329 }, 500);
3330
3331 });
3332
3333 $(document).on('keypress', '.acf_relationship input.relationship_search', function( e ){
3334
3335 // don't submit form
3336 if( e.which == 13 )
3337 {
3338 e.preventDefault();
3339 }
3340
3341 });
3342
3343
3344 })(jQuery);
3345
3346 (function($){
3347
3348 acf.fields.tab = {
3349
3350 add_group : function( $wrap ){
3351
3352 // vars
3353 var html = '';
3354
3355
3356 // generate html
3357 if( $wrap.is('tbody') )
3358 {
3359 html = '<tr class="acf-tab-wrap"><td colspan="2"><ul class="hl clearfix acf-tab-group"></ul></td></tr>';
3360 }
3361 else
3362 {
3363 html = '<div class="acf-tab-wrap"><ul class="hl clearfix acf-tab-group"></ul></div>';
3364 }
3365
3366
3367 // append html
3368 $wrap.children('.field_type-tab:first').before( html );
3369
3370 },
3371
3372 add_tab : function( $tab ){
3373
3374 // vars
3375 var $field = $tab.closest('.field'),
3376 $wrap = $field.parent(),
3377
3378 key = $field.attr('data-field_key'),
3379 label = $tab.text();
3380
3381
3382 // create tab group if it doesnt exist
3383 if( ! $wrap.children('.acf-tab-wrap').exists() )
3384 {
3385 this.add_group( $wrap );
3386 }
3387
3388 // add tab
3389 $wrap.children('.acf-tab-wrap').find('.acf-tab-group').append('<li><a class="acf-tab-button" href="#" data-key="' + key + '">' + label + '</a></li>');
3390
3391 },
3392
3393 toggle : function( $a ){
3394
3395 // reference
3396 var _this = this;
3397
3398
3399 //console.log( 'toggle %o ', $a);
3400 // vars
3401 var $wrap = $a.closest('.acf-tab-wrap').parent(),
3402 key = $a.attr('data-key');
3403
3404
3405 // classes
3406 $a.parent('li').addClass('active').siblings('li').removeClass('active');
3407
3408
3409 // hide / show
3410 $wrap.children('.field_type-tab').each(function(){
3411
3412
3413 // vars
3414 var $tab = $(this);
3415
3416
3417 if( $tab.attr('data-field_key') == key )
3418 {
3419 _this.show_tab_fields( $(this) );
3420 }
3421 else
3422 {
3423 _this.hide_tab_fields( $(this) );
3424 }
3425
3426
3427 });
3428
3429 },
3430
3431 show_tab_fields : function( $field ) {
3432
3433 //console.log('show tab fields %o', $field);
3434 $field.nextUntil('.field_type-tab').each(function(){
3435
3436 $(this).removeClass('acf-tab_group-hide').addClass('acf-tab_group-show');
3437 $(document).trigger('acf/fields/tab/show', [ $(this) ]);
3438
3439 });
3440 },
3441
3442 hide_tab_fields : function( $field ) {
3443
3444 $field.nextUntil('.field_type-tab').each(function(){
3445
3446 $(this).removeClass('acf-tab_group-show').addClass('acf-tab_group-hide');
3447 $(document).trigger('acf/fields/tab/hide', [ $(this) ]);
3448
3449 });
3450 },
3451
3452 refresh : function( $el ){
3453
3454 // reference
3455 var _this = this;
3456
3457
3458 // trigger
3459 $el.find('.acf-tab-group').each(function(){
3460
3461 $(this).find('.acf-tab-button:first').each(function(){
3462
3463 _this.toggle( $(this) );
3464
3465 });
3466
3467 });
3468
3469 }
3470
3471 };
3472
3473
3474 /*
3475 * acf/setup_fields
3476 *
3477 * run init function on all elements for this field
3478 *
3479 * @type event
3480 * @date 20/07/13
3481 *
3482 * @param {object} e event object
3483 * @param {object} el DOM object which may contain new ACF elements
3484 * @return N/A
3485 */
3486
3487 $(document).on('acf/setup_fields', function(e, el){
3488
3489 // add tabs
3490 $(el).find('.acf-tab').each(function(){
3491
3492 acf.fields.tab.add_tab( $(this) );
3493
3494 });
3495
3496
3497 // activate first tab
3498 acf.fields.tab.refresh( $(el) );
3499
3500
3501 // NOTE: this code is defined BEFORE the acf.conditional_logic action. This is becuase the 'acf/setup_fields' listener is defined INSIDE the conditional_logic.init() function which is run on doc.ready
3502
3503 // trigger conditional logic
3504 // this code ( acf/setup_fields ) is run after the main acf.conditional_logic.init();
3505 //console.log('acf/setup_fields (after tab refresh) calling acf.conditional_logic.refresh()');
3506 //acf.conditional_logic.refresh();
3507
3508 });
3509
3510
3511
3512
3513 /*
3514 * Events
3515 *
3516 * jQuery events for this field
3517 *
3518 * @type function
3519 * @date 1/03/2011
3520 *
3521 * @param N/A
3522 * @return N/A
3523 */
3524
3525 $(document).on('click', '.acf-tab-button', function( e ){
3526
3527 e.preventDefault();
3528
3529 acf.fields.tab.toggle( $(this) );
3530
3531 $(this).trigger('blur');
3532
3533 });
3534
3535
3536 $(document).on('acf/conditional_logic/hide', function( e, $target, item ){
3537
3538 // validate
3539 if( $target.attr('data-field_type') != 'tab' )
3540 {
3541 return;
3542 }
3543
3544 //console.log('conditional_logic/hide tab %o', $target);
3545
3546
3547 // vars
3548 var $tab = $target.siblings('.acf-tab-wrap').find('a[data-key="' + $target.attr('data-field_key') + '"]');
3549
3550
3551 // if tab is already hidden, then ignore the following functiolnality
3552 if( $tab.is(':hidden') )
3553 {
3554 return;
3555 }
3556
3557
3558 // visibility
3559 $tab.parent().hide();
3560
3561
3562 // if
3563 if( $tab.parent().siblings(':visible').exists() )
3564 {
3565 // if the $target to be hidden is a tab button, lets toggle a sibling tab button
3566 $tab.parent().siblings(':visible').first().children('a').trigger('click');
3567 }
3568 else
3569 {
3570 // no onther tabs
3571 acf.fields.tab.hide_tab_fields( $target );
3572 }
3573
3574 });
3575
3576
3577 $(document).on('acf/conditional_logic/show', function( e, $target, item ){
3578
3579 // validate
3580 if( $target.attr('data-field_type') != 'tab' )
3581 {
3582 return;
3583 }
3584
3585
3586 //console.log('conditional_logic/show tab %o', $target);
3587
3588
3589 // vars
3590 var $tab = $target.siblings('.acf-tab-wrap').find('a[data-key="' + $target.attr('data-field_key') + '"]');
3591
3592
3593 // if tab is already visible, then ignore the following functiolnality
3594 if( $tab.is(':visible') )
3595 {
3596 return;
3597 }
3598
3599
3600 // visibility
3601 $tab.parent().show();
3602
3603
3604 // if this is the active tab
3605 if( $tab.parent().hasClass('active') )
3606 {
3607 $tab.trigger('click');
3608 return;
3609 }
3610
3611
3612 // if the sibling active tab is actually hidden by conditional logic, take ownership of tabs
3613 if( $tab.parent().siblings('.active').is(':hidden') )
3614 {
3615 // show this tab group
3616 $tab.trigger('click');
3617 return;
3618 }
3619
3620
3621 });
3622
3623
3624
3625 })(jQuery);
3626
3627 (function($){
3628
3629
3630 /*
3631 * Validation
3632 *
3633 * JS model
3634 *
3635 * @type object
3636 * @date 1/06/13
3637 *
3638 */
3639
3640 acf.validation = {
3641
3642 status : true,
3643 disabled : false,
3644
3645 run : function(){
3646
3647 // reference
3648 var _this = this;
3649
3650
3651 // reset
3652 _this.status = true;
3653
3654
3655 // loop through all fields
3656 $('.field.required, .form-field.required').each(function(){
3657
3658 // run validation
3659 _this.validate( $(this) );
3660
3661
3662 });
3663 // end loop through all fields
3664 },
3665
3666 /*
3667 * show_spinner
3668 *
3669 * This function will show a spinner element. Logic changed in WP 4.2
3670 *
3671 * @type function
3672 * @date 3/05/2015
3673 * @since 5.2.3
3674 *
3675 * @param $spinner (jQuery)
3676 * @return n/a
3677 */
3678
3679 show_spinner: function( $spinner ){
3680
3681 // bail early if no spinner
3682 if( !$spinner.exists() ) {
3683
3684 return;
3685
3686 }
3687
3688
3689 // vars
3690 var wp_version = acf.o.wp_version;
3691
3692
3693 // show
3694 if( parseFloat(wp_version) >= 4.2 ) {
3695
3696 $spinner.addClass('is-active');
3697
3698 } else {
3699
3700 $spinner.css('display', 'inline-block');
3701
3702 }
3703
3704 },
3705
3706
3707 /*
3708 * hide_spinner
3709 *
3710 * This function will hide a spinner element. Logic changed in WP 4.2
3711 *
3712 * @type function
3713 * @date 3/05/2015
3714 * @since 5.2.3
3715 *
3716 * @param $spinner (jQuery)
3717 * @return n/a
3718 */
3719
3720 hide_spinner: function( $spinner ){
3721
3722 // bail early if no spinner
3723 if( !$spinner.exists() ) {
3724
3725 return;
3726
3727 }
3728
3729
3730 // vars
3731 var wp_version = acf.o.wp_version;
3732
3733
3734 // hide
3735 if( parseFloat(wp_version) >= 4.2 ) {
3736
3737 $spinner.removeClass('is-active');
3738
3739 } else {
3740
3741 $spinner.css('display', 'none');
3742
3743 }
3744
3745 },
3746
3747 validate : function( div ){
3748
3749 // var
3750 var ignore = false,
3751 $tab = null;
3752
3753
3754 // set validation data
3755 div.data('validation', true);
3756
3757
3758 // not visible
3759 if( div.is(':hidden') )
3760 {
3761 // ignore validation
3762 ignore = true;
3763
3764
3765 // if this field is hidden by a tab group, allow validation
3766 if( div.hasClass('acf-tab_group-hide') )
3767 {
3768 ignore = false;
3769
3770
3771 // vars
3772 var $tab_field = div.prevAll('.field_type-tab:first'),
3773 $tab_group = div.prevAll('.acf-tab-wrap:first');
3774
3775
3776 // if the tab itself is hidden, bypass validation
3777 if( $tab_field.hasClass('acf-conditional_logic-hide') )
3778 {
3779 ignore = true;
3780 }
3781 else
3782 {
3783 // activate this tab as it holds hidden required field!
3784 $tab = $tab_group.find('.acf-tab-button[data-key="' + $tab_field.attr('data-field_key') + '"]');
3785 }
3786 }
3787 }
3788
3789
3790 // if is hidden by conditional logic, ignore
3791 if( div.hasClass('acf-conditional_logic-hide') )
3792 {
3793 ignore = true;
3794 }
3795
3796
3797 // if field group is hidden, igrnoe
3798 if( div.closest('.postbox.acf-hidden').exists() ) {
3799
3800 ignore = true;
3801
3802 }
3803
3804
3805 if( ignore )
3806 {
3807 return;
3808 }
3809
3810
3811
3812 // text / textarea
3813 if( div.find('input[type="text"], input[type="email"], input[type="number"], input[type="hidden"], textarea').val() == "" )
3814 {
3815 div.data('validation', false);
3816 }
3817
3818
3819 // wysiwyg
3820 if( div.find('.acf_wysiwyg').exists() && typeof(tinyMCE) == "object")
3821 {
3822 div.data('validation', true);
3823
3824 var id = div.find('.wp-editor-area').attr('id'),
3825 editor = tinyMCE.get( id );
3826
3827
3828 if( editor && !editor.getContent() )
3829 {
3830 div.data('validation', false);
3831 }
3832 }
3833
3834
3835 // select
3836 if( div.find('select').exists() )
3837 {
3838 div.data('validation', true);
3839
3840 if( div.find('select').val() == "null" || ! div.find('select').val() )
3841 {
3842 div.data('validation', false);
3843 }
3844 }
3845
3846
3847 // radio
3848 if( div.find('input[type="radio"]').exists() )
3849 {
3850 div.data('validation', false);
3851
3852 if( div.find('input[type="radio"]:checked').exists() )
3853 {
3854 div.data('validation', true);
3855 }
3856 }
3857
3858
3859 // checkbox
3860 if( div.find('input[type="checkbox"]').exists() )
3861 {
3862 div.data('validation', false);
3863
3864 if( div.find('input[type="checkbox"]:checked').exists() )
3865 {
3866 div.data('validation', true);
3867 }
3868 }
3869
3870
3871 // relationship
3872 if( div.find('.acf_relationship').exists() )
3873 {
3874 div.data('validation', false);
3875
3876 if( div.find('.acf_relationship .relationship_right input').exists() )
3877 {
3878 div.data('validation', true);
3879 }
3880 }
3881
3882
3883 // repeater
3884 if( div.find('.repeater').exists() )
3885 {
3886 div.data('validation', false);
3887
3888 if( div.find('.repeater tr.row').exists() )
3889 {
3890 div.data('validation', true);
3891 }
3892 }
3893
3894
3895 // gallery
3896 if( div.find('.acf-gallery').exists() )
3897 {
3898 div.data('validation', false);
3899
3900 if( div.find('.acf-gallery .thumbnail').exists())
3901 {
3902 div.data('validation', true);
3903 }
3904 }
3905
3906
3907 // hook for custom validation
3908 $(document).trigger('acf/validate_field', [ div ] );
3909
3910
3911 // set validation
3912 if( ! div.data('validation') )
3913 {
3914 // show error
3915 this.status = false;
3916 div.closest('.field').addClass('error');
3917
3918
3919 // custom validation message
3920 if( div.data('validation_message') )
3921 {
3922 var $label = div.find('p.label:first'),
3923 $message = null;
3924
3925
3926 // remove old message
3927 $label.children('.acf-error-message').remove();
3928
3929
3930 $label.append( '<span class="acf-error-message"><i class="bit"></i>' + div.data('validation_message') + '</span>' );
3931 }
3932
3933
3934 // display field (curently hidden due to another tab being active)
3935 if( $tab )
3936 {
3937 $tab.trigger('click');
3938 }
3939
3940 }
3941 }
3942
3943 };
3944
3945
3946 /*
3947 * Events
3948 *
3949 * Remove error class on focus
3950 *
3951 * @type function
3952 * @date 1/03/2011
3953 *
3954 * @param N/A
3955 * @return N/A
3956 */
3957
3958 $(document).on('focus click', '.field.required input, .field.required textarea, .field.required select', function( e ){
3959
3960 $(this).closest('.field').removeClass('error');
3961
3962 });
3963
3964
3965 /*
3966 $(document).on('blur change', '.field.required input, .field.required textarea, .field.required select', function( e ){
3967
3968 acf.validation.validate( $(this).closest('.field') );
3969
3970 });
3971 */
3972
3973
3974 /*
3975 * Save Post
3976 *
3977 * If user is saving a draft, allow them to bypass the validation
3978 *
3979 * @type function
3980 * @date 1/03/2011
3981 *
3982 * @param N/A
3983 * @return N/A
3984 */
3985
3986 $(document).on('click', '#save-post', function(){
3987
3988 acf.validation.disabled = true;
3989
3990 });
3991
3992
3993 /*
3994 * Submit Post
3995 *
3996 * Run validation and return true|false accordingly
3997 *
3998 * @type function
3999 * @date 1/03/2011
4000 *
4001 * @param N/A
4002 * @return N/A
4003 */
4004
4005 $(document).on('submit', '#post', function(){
4006
4007 // If disabled, bail early on the validation check
4008 if( acf.validation.disabled )
4009 {
4010 return true;
4011 }
4012
4013
4014 // do validation
4015 acf.validation.run();
4016
4017
4018 if( ! acf.validation.status ) {
4019
4020 // vars
4021 var $form = $(this);
4022
4023
4024 // show message
4025 $form.siblings('#message').remove();
4026 $form.before('<div id="message" class="error"><p>' + acf.l10n.validation.error + '</p></div>');
4027
4028
4029 // hide ajax stuff on submit button
4030 if( $('#submitdiv').exists() ) {
4031
4032 // remove disabled classes
4033 $('#submitdiv').find('.disabled').removeClass('disabled');
4034 $('#submitdiv').find('.button-disabled').removeClass('button-disabled');
4035 $('#submitdiv').find('.button-primary-disabled').removeClass('button-primary-disabled');
4036
4037
4038 // remove spinner
4039 acf.validation.hide_spinner( $('#submitdiv .spinner') );
4040
4041 }
4042
4043 return false;
4044 }
4045
4046
4047 // remove hidden postboxes
4048 // + this will stop them from being posted to save
4049 $('.acf_postbox.acf-hidden').remove();
4050
4051
4052 // submit the form
4053 return true;
4054
4055 });
4056
4057
4058 })(jQuery);
4059
4060 (function($){
4061
4062 /*
4063 * WYSIWYG
4064 *
4065 * jQuery functionality for this field type
4066 *
4067 * @type object
4068 * @date 20/07/13
4069 *
4070 * @param N/A
4071 * @return N/A
4072 */
4073
4074 var _wysiwyg = acf.fields.wysiwyg = {
4075
4076 $el: null,
4077 $textarea: null,
4078
4079 o: {},
4080
4081 set: function( o ){
4082
4083 // merge in new option
4084 $.extend( this, o );
4085
4086
4087 // find textarea
4088 this.$textarea = this.$el.find('textarea');
4089
4090
4091 // get options
4092 this.o = acf.helpers.get_atts( this.$el );
4093 this.o.id = this.$textarea.attr('id');
4094
4095
4096 // return this for chaining
4097 return this;
4098
4099 },
4100
4101 has_tinymce : function(){
4102
4103 var r = false;
4104
4105 if( typeof(tinyMCE) == "object" )
4106 {
4107 r = true;
4108 }
4109
4110 return r;
4111
4112 },
4113
4114 get_toolbar : function(){
4115
4116 // safely get toolbar
4117 if( acf.helpers.isset( this, 'toolbars', this.o.toolbar ) ) {
4118
4119 return this.toolbars[ this.o.toolbar ];
4120
4121 }
4122
4123
4124 // return
4125 return false;
4126
4127 },
4128
4129 init : function(){
4130
4131 // is clone field?
4132 if( acf.helpers.is_clone_field( this.$textarea ) )
4133 {
4134 return;
4135 }
4136
4137
4138 // vars
4139 var id = this.o.id,
4140 toolbar = this.get_toolbar(),
4141 command = 'mceAddControl',
4142 setting = 'theme_advanced_buttons{i}';
4143
4144
4145 // backup
4146 var _settings = $.extend( {}, tinyMCE.settings );
4147
4148
4149 // v4 settings
4150 if( tinymce.majorVersion == 4 ) {
4151
4152 command = 'mceAddEditor';
4153 setting = 'toolbar{i}';
4154
4155 }
4156
4157
4158 // add toolbars
4159 if( toolbar ) {
4160
4161 for( var i = 1; i < 5; i++ ) {
4162
4163 // vars
4164 var v = '';
4165
4166
4167 // load toolbar
4168 if( acf.helpers.isset( toolbar, 'theme_advanced_buttons' + i ) ) {
4169
4170 v = toolbar['theme_advanced_buttons' + i];
4171
4172 }
4173
4174
4175 // update setting
4176 tinyMCE.settings[ setting.replace('{i}', i) ] = v;
4177
4178 }
4179
4180 }
4181
4182
4183 // add editor
4184 tinyMCE.execCommand( command, false, id);
4185
4186
4187 // events - load
4188 $(document).trigger('acf/wysiwyg/load', id);
4189
4190
4191 // add events (click, focus, blur) for inserting image into correct editor
4192 setTimeout(function(){
4193
4194 _wysiwyg.add_events( id );
4195
4196 }, 100);
4197
4198
4199 // restore tinyMCE.settings
4200 tinyMCE.settings = _settings;
4201
4202
4203 // set active editor to null
4204 wpActiveEditor = null;
4205
4206 },
4207
4208 add_events: function( id ){
4209
4210 // vars
4211 var editor = tinyMCE.get( id );
4212
4213
4214 // validate
4215 if( !editor ) return;
4216
4217
4218 // vars
4219 var $container = $('#wp-' + id + '-wrap'),
4220 $body = $( editor.getBody() );
4221
4222
4223 // events
4224 $container.on('click', function(){
4225
4226 $(document).trigger('acf/wysiwyg/click', id);
4227
4228 });
4229
4230 $body.on('focus', function(){
4231
4232 $(document).trigger('acf/wysiwyg/focus', id);
4233
4234 });
4235
4236 $body.on('blur', function(){
4237
4238 $(document).trigger('acf/wysiwyg/blur', id);
4239
4240 });
4241
4242
4243 },
4244 destroy : function(){
4245
4246 // vars
4247 var id = this.o.id,
4248 command = 'mceRemoveControl';
4249
4250
4251 // Remove tinymcy functionality.
4252 // Due to the media popup destroying and creating the field within such a short amount of time,
4253 // a JS error will be thrown when launching the edit window twice in a row.
4254 try {
4255
4256 // vars
4257 var editor = tinyMCE.get( id );
4258
4259
4260 // validate
4261 if( !editor ) {
4262
4263 return;
4264
4265 }
4266
4267
4268 // v4 settings
4269 if( tinymce.majorVersion == 4 ) {
4270
4271 command = 'mceRemoveEditor';
4272
4273 }
4274
4275
4276 // store value
4277 var val = editor.getContent();
4278
4279
4280 // remove editor
4281 tinyMCE.execCommand(command, false, id);
4282
4283
4284 // set value
4285 this.$textarea.val( val );
4286
4287
4288 } catch(e) {
4289
4290 //console.log( e );
4291
4292 }
4293
4294
4295 // set active editor to null
4296 wpActiveEditor = null;
4297
4298 }
4299
4300 };
4301
4302
4303 /*
4304 * acf/setup_fields
4305 *
4306 * run init function on all elements for this field
4307 *
4308 * @type event
4309 * @date 20/07/13
4310 *
4311 * @param {object} e event object
4312 * @param {object} el DOM object which may contain new ACF elements
4313 * @return N/A
4314 */
4315
4316 $(document).on('acf/setup_fields', function(e, el){
4317
4318 // validate
4319 if( ! _wysiwyg.has_tinymce() )
4320 {
4321 return;
4322 }
4323
4324
4325 // Destory all WYSIWYG fields
4326 // This hack will fix a problem when the WP popup is created and hidden, then the ACF popup (image/file field) is opened
4327 $(el).find('.acf_wysiwyg').each(function(){
4328
4329 _wysiwyg.set({ $el : $(this) }).destroy();
4330
4331 });
4332
4333
4334 // Add WYSIWYG fields
4335 setTimeout(function(){
4336
4337 $(el).find('.acf_wysiwyg').each(function(){
4338
4339 _wysiwyg.set({ $el : $(this) }).init();
4340
4341 });
4342
4343 }, 0);
4344
4345 });
4346
4347
4348 /*
4349 * acf/remove_fields
4350 *
4351 * This action is called when the $el is being removed from the DOM
4352 *
4353 * @type event
4354 * @date 20/07/13
4355 *
4356 * @param {object} e event object
4357 * @param {object} $el jQuery element being removed
4358 * @return N/A
4359 */
4360
4361 $(document).on('acf/remove_fields', function(e, $el){
4362
4363 // validate
4364 if( ! _wysiwyg.has_tinymce() )
4365 {
4366 return;
4367 }
4368
4369
4370 $el.find('.acf_wysiwyg').each(function(){
4371
4372 _wysiwyg.set({ $el : $(this) }).destroy();
4373
4374 });
4375
4376 });
4377
4378
4379 /*
4380 * acf/wysiwyg/click
4381 *
4382 * this event is run when a user clicks on a WYSIWYG field
4383 *
4384 * @type event
4385 * @date 17/01/13
4386 *
4387 * @param {object} e event object
4388 * @param {int} id WYSIWYG ID
4389 * @return N/A
4390 */
4391
4392 $(document).on('acf/wysiwyg/click', function(e, id){
4393
4394 wpActiveEditor = id;
4395
4396 container = $('#wp-' + id + '-wrap').closest('.field').removeClass('error');
4397
4398 });
4399
4400
4401 /*
4402 * acf/wysiwyg/focus
4403 *
4404 * this event is run when a user focuses on a WYSIWYG field body
4405 *
4406 * @type event
4407 * @date 17/01/13
4408 *
4409 * @param {object} e event object
4410 * @param {int} id WYSIWYG ID
4411 * @return N/A
4412 */
4413
4414 $(document).on('acf/wysiwyg/focus', function(e, id){
4415
4416 wpActiveEditor = id;
4417
4418 container = $('#wp-' + id + '-wrap').closest('.field').removeClass('error');
4419
4420 });
4421
4422
4423 /*
4424 * acf/wysiwyg/blur
4425 *
4426 * this event is run when a user loses focus on a WYSIWYG field body
4427 *
4428 * @type event
4429 * @date 17/01/13
4430 *
4431 * @param {object} e event object
4432 * @param {int} id WYSIWYG ID
4433 * @return N/A
4434 */
4435
4436 $(document).on('acf/wysiwyg/blur', function(e, id){
4437
4438 wpActiveEditor = null;
4439
4440 // update the hidden textarea
4441 // - This fixes a but when adding a taxonomy term as the form is not posted and the hidden tetarea is never populated!
4442 var editor = tinyMCE.get( id );
4443
4444
4445 // validate
4446 if( !editor )
4447 {
4448 return;
4449 }
4450
4451
4452 var el = editor.getElement();
4453
4454
4455 // save to textarea
4456 editor.save();
4457
4458
4459 // trigger change on textarea
4460 $( el ).trigger('change');
4461
4462 });
4463
4464
4465 /*
4466 * acf/sortable_start
4467 *
4468 * this event is run when a element is being drag / dropped
4469 *
4470 * @type event
4471 * @date 10/11/12
4472 *
4473 * @param {object} e event object
4474 * @param {object} el DOM object which may contain new ACF elements
4475 * @return N/A
4476 */
4477
4478 $(document).on('acf/sortable_start', function(e, el) {
4479
4480 // validate
4481 if( ! _wysiwyg.has_tinymce() )
4482 {
4483 return;
4484 }
4485
4486
4487 $(el).find('.acf_wysiwyg').each(function(){
4488
4489 _wysiwyg.set({ $el : $(this) }).destroy();
4490
4491 });
4492
4493 });
4494
4495
4496 /*
4497 * acf/sortable_stop
4498 *
4499 * this event is run when a element has finnished being drag / dropped
4500 *
4501 * @type event
4502 * @date 10/11/12
4503 *
4504 * @param {object} e event object
4505 * @param {object} el DOM object which may contain new ACF elements
4506 * @return N/A
4507 */
4508
4509 $(document).on('acf/sortable_stop', function(e, el) {
4510
4511 // validate
4512 if( ! _wysiwyg.has_tinymce() )
4513 {
4514 return;
4515 }
4516
4517
4518 $(el).find('.acf_wysiwyg').each(function(){
4519
4520 _wysiwyg.set({ $el : $(this) }).init();
4521
4522 });
4523
4524 });
4525
4526
4527 /*
4528 * window load
4529 *
4530 * @description:
4531 * @since: 3.5.5
4532 * @created: 22/12/12
4533 */
4534
4535 $(window).on('load', function(){
4536
4537 // validate
4538 if( ! _wysiwyg.has_tinymce() )
4539 {
4540 return;
4541 }
4542
4543
4544 // vars
4545 var wp_content = $('#wp-content-wrap').exists(),
4546 wp_acf_settings = $('#wp-acf_settings-wrap').exists()
4547 mode = 'tmce';
4548
4549
4550 // has_editor
4551 if( wp_acf_settings )
4552 {
4553 // html_mode
4554 if( $('#wp-acf_settings-wrap').hasClass('html-active') )
4555 {
4556 mode = 'html';
4557 }
4558 }
4559
4560
4561 setTimeout(function(){
4562
4563 // trigger click on hidden wysiwyg (to get in HTML mode)
4564 if( wp_acf_settings && mode == 'html' )
4565 {
4566 $('#acf_settings-tmce').trigger('click');
4567 }
4568
4569 }, 1);
4570
4571
4572 setTimeout(function(){
4573
4574 // trigger html mode for people who want to stay in HTML mode
4575 if( wp_acf_settings && mode == 'html' )
4576 {
4577 $('#acf_settings-html').trigger('click');
4578 }
4579
4580 // Add events to content editor
4581 if( wp_content )
4582 {
4583 _wysiwyg.add_events('content');
4584 }
4585
4586
4587 }, 11);
4588
4589
4590 });
4591
4592
4593 /*
4594 * Full screen
4595 *
4596 * @description: this hack will hide the 'image upload' button in the wysiwyg full screen mode if the field has disabled image uploads!
4597 * @since: 3.6
4598 * @created: 26/02/13
4599 */
4600
4601 $(document).on('click', '.acf_wysiwyg a.mce_fullscreen', function(){
4602
4603 // vars
4604 var wysiwyg = $(this).closest('.acf_wysiwyg'),
4605 upload = wysiwyg.attr('data-upload');
4606
4607 if( upload == 'no' )
4608 {
4609 $('#mce_fullscreen_container td.mceToolbar .mce_add_media').remove();
4610 }
4611
4612 });
4613
4614
4615 })(jQuery);
4616
4617