PluginProbe
Cooked – Recipe Management / 1.16.1
Cooked – Recipe Management v1.16.1
1.16.1 1.16.0 1.15.0 trunk 1.10.0 1.11.0 1.11.1 1.11.2 1.11.3 1.11.4 1.12.0 1.13.0 1.14.0 1.7.10 1.7.11 1.7.12 1.7.13 1.7.15.1 1.7.15.3 1.7.15.4 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 All 37 releases
cooked / assets / admin / js / cooked-functions.js

cooked-functions.js in Cooked – Recipe Management 1.16.1, at assets/admin/js/cooked-functions.js

1,529 lines 67.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 var $_CookedConditionalTimeout = false;
2
3 /** Maps touch events on sortable drag handles to mouse events so sortable works on touch devices. */
4 var cookedSortableTouchHandler = function(event) {
5 var target = event.target;
6 var types = {
7 touchstart: "mousedown",
8 touchmove: "mousemove",
9 touchend: "mouseup"
10 };
11
12 // Only handle touches on drag handles within cooked sortable containers
13 var dragHandle = target.closest('.cooked-icon-drag');
14 if (!dragHandle || !dragHandle.closest('.cooked-sortable')) {
15 return; // Let the event proceed normally (allows scrolling)
16 }
17
18 if (!event.changedTouches || !event.changedTouches.length || !types[event.type]) {
19 return;
20 }
21
22 var touch = event.changedTouches[0];
23
24 // Prevent default to stop page scrolling when dragging
25 event.preventDefault();
26
27 var simulatedEvent = new MouseEvent(types[event.type], {
28 bubbles: true,
29 cancelable: true,
30 view: window,
31 detail: 1,
32 screenX: touch.screenX,
33 screenY: touch.screenY,
34 clientX: touch.clientX,
35 clientY: touch.clientY,
36 ctrlKey: false,
37 altKey: false,
38 shiftKey: false,
39 metaKey: false,
40 button: 0,
41 relatedTarget: null
42 });
43 touch.target.dispatchEvent(simulatedEvent);
44 };
45
46 (function( $ ) {
47
48 $(document).ready(function() {
49 var $_CookedColorPickers = $('.cooked-color-field'),
50 $_CookedSelectFields = $('#cooked_recipe_settings').find('select'),
51 $_CookedRecipeTabs = $('#cooked-recipe-tabs'),
52 $_CookedRecipeSettingsWrap = $('#cooked-settings-wrap'),
53 $_CookedRecipeSettings = $('#cooked_recipe_settings'),
54 $_CookedRecipeSettingsSubmit = $_CookedRecipeSettings.find( 'input#submit' )
55 $_CookedTooltips = $('.cooked-tooltip'),
56 $_CookedConditionals = $('.cooked-conditional-hidden'),
57 $_CookedSortable = $('.cooked-sortable'),
58 $_CookedRecipeSaveDefault = $('.cooked-layout-save-default'),
59 $_CookedShortcodeField = $('.cooked-shortcode-field'),
60 $_CookedIngredientBuilder = $('#cooked-ingredients-builder'),
61 $_CookedAutoNutritionButton = $('.cooked-auto-nutrition-button'),
62 $_CookedDirectionBuilder = $('#cooked-directions-builder'),
63 $_CookedRecipeGallery = $('#cooked-recipe-image-gallery'),
64 $_CookedNutritionFactsTab = $('#cooked-recipe-tab-content-nutrition'),
65 $_CookedSettingsPanel = $('#cooked-settings-panel'),
66 $_CookedSettingsTabs = $('#cooked-settings-tabs');
67
68 // Cooked Color Pickers
69 if ($_CookedColorPickers.length) {
70 $_CookedColorPickers.wpColorPicker();
71 }
72
73 // Cooked Sortables
74 if ($_CookedSortable.length) {
75 document.addEventListener("touchstart", cookedSortableTouchHandler, { passive: false });
76 document.addEventListener("touchmove", cookedSortableTouchHandler, { passive: false });
77 document.addEventListener("touchend", cookedSortableTouchHandler, { passive: false });
78
79 if ($_CookedSortable.find('.cooked-icon-drag').length) {
80 $_CookedSortable.sortable({
81 handle: '.cooked-icon-drag',
82 // scroll: true,
83 // scrollSensitivity: 80,
84 // scrollSpeed: 30,
85 stop: function(event, ui) {
86 // Update direction step numbers when reordering directions
87 if (ui.item.closest('#cooked-directions-builder').length) {
88 cooked_reset_direction_builder();
89 }
90
91 let textarea = ui.item.find('textarea');
92 var canUseWpEditor = !!(
93 cooked_admin_functions_js_vars.wp_editor_roles_allowed &&
94 typeof wp !== 'undefined' &&
95 wp.editor &&
96 typeof wp.editor.remove === 'function' &&
97 typeof wp.editor.initialize === 'function'
98 );
99
100 if (textarea.length && canUseWpEditor) {
101 let textareaName = textarea.attr('name');
102 let fieldID = textarea.attr('id');
103
104 wp.editor.remove(fieldID);
105 wp.editor.initialize(fieldID, {
106 tinymce: {
107 wpautop: false,
108 toolbar1: 'bold,italic,underline,blockquote,strikethrough,bullist,numlist,alignleft,aligncenter,alignright,undo,redo,wp_link_advanced,unlink,fullscreen',
109 toolbar2: '',
110 toolbar3: '',
111 toolbar4: '',
112 height: 100,
113 textarea_name: textareaName,
114 plugins: 'link lists fullscreen wordpress wplink',
115 setup: function(editor) {
116 // @TODO: Inline Link editor does not work.
117 // editor.on('init', function() {
118 // if (typeof wpLink !== 'undefined') {
119 // editor.addCommand('WP_Link', function() {
120 // window.wpActiveEditor = editor.id;
121 // wpLink.open(editor.id);
122 // return false;
123 // });
124 // }
125 // });
126 }
127 },
128 quicktags: true,
129 mediaButtons: false
130 });
131 }
132 }
133 });
134 } else {
135 $_CookedSortable.sortable({
136 // scroll: true,
137 // scrollSensitivity: 80,
138 // scrollSpeed: 30,
139 stop: function(event, ui) {
140 // Update direction step numbers when reordering directions
141 if (ui.item.closest('#cooked-directions-builder').length) {
142 cooked_reset_direction_builder();
143 }
144 }
145 });
146 }
147 }
148
149 // Save as Default
150 if ($_CookedRecipeSaveDefault.length) {
151 var saveDefaultTooltip = $_CookedRecipeSaveDefault.tooltipster({
152 theme : ['tooltipster-light', 'tooltipster-sideTip-cooked'],
153 trigger : 'click',
154 animation : 'grow',
155 delay : 0,
156 speed : 200,
157 maxWidth : 275,
158 contentAsHTML : true,
159 interactive : true,
160 functionReady: function(instance, helper) {
161 $('.cooked-save-default-all').on('click', function(e) {
162 e.preventDefault();
163
164 var thisButton = $(this),
165 cooked_save_default_nonce = thisButton.data('nonce'),
166 cooked_save_default_bulk_nonce = thisButton.data('bulk-nonce'),
167 thisContainer = thisButton.parent(),
168 confirm_save = confirm(cooked_admin_functions_js_vars.i18n_confirm_save_default_all),
169 recipe_editor_textarea = $( "#_recipe_settings_content" ),
170 recipe_editor = tinymce.get('_recipe_settings_content');
171
172 if (recipe_editor === null) {
173 var recipe_editor_content = recipe_editor_textarea.val();
174 } else {
175 var recipe_editor_content = recipe_editor.getContent();
176 }
177
178 if (confirm_save && !thisButton.hasClass('disabled')) {
179 thisContainer.find('.button, .button-primary').addClass('disabled');
180
181 $.post(
182 cooked_admin_functions_js_vars.ajax_url,
183 {
184 action: 'cooked_save_default',
185 'default_content': recipe_editor_content,
186 nonce: cooked_save_default_nonce
187 },
188 function() {
189 $.post(
190 cooked_admin_functions_js_vars.ajax_url,
191 {
192 action: 'cooked_get_recipe_count',
193 nonce: cooked_save_default_bulk_nonce
194 },
195 function(response) {
196 thisButton.removeClass("button-primary").addClass("button");
197
198 if (response && response.success && response.data.total > 0) {
199 cooked_recipe_update_counter = 0;
200 cooked_set_default_template(0, response.data.total, recipe_editor_content, cooked_save_default_bulk_nonce, instance);
201 }
202 },
203 'json'
204 );
205 }
206 );
207 }
208 });
209
210 $('.cooked-save-default-new').on('click', function(e) {
211 e.preventDefault();
212 var thisButton = $(this),
213 nonce = thisButton.data('nonce'),
214 thisContainer = thisButton.parent(),
215 recipe_editor_textarea = $("#_recipe_settings_content"),
216 recipe_editor = tinymce.get('_recipe_settings_content');
217
218 if (recipe_editor === null) {
219 var recipe_editor_content = recipe_editor_textarea.val();
220 } else {
221 var recipe_editor_content = recipe_editor.getContent();
222 }
223
224 if (!thisButton.hasClass('disabled')) {
225 thisContainer.find('.button, .button-primary').addClass('disabled');
226 var ajax__save_default_new = $.post(
227 cooked_admin_functions_js_vars.ajax_url,
228 {
229 action: 'cooked_save_default',
230 'default_content': recipe_editor_content,
231 nonce: nonce
232 },
233 function(result) {
234 thisButton.text( cooked_admin_functions_js_vars.i18n_saved );
235 thisContainer.find('.button-primary').removeClass('disabled');
236 }
237 ).fail(function(result) {
238 //console.log( 'Error: ' + result );
239 });
240 }
241 });
242 }
243 });
244
245 $('.cooked-layout-load-default').on('click', function(e) {
246 e.preventDefault();
247
248 var thisButton = $(this),
249 thisContainer = thisButton.parent(),
250 confirm_load = confirm( cooked_admin_functions_js_vars.i18n_confirm_load_default ),
251 recipe_editor_textarea = $( "#_recipe_settings_content" ),
252 recipe_editor = tinymce.get('_recipe_settings_content');
253
254 if (confirm_load && !thisButton.hasClass('disabled')) {
255 thisContainer.find('.button, .button-primary').addClass('disabled');
256 var ajax__save_default_all = $.post(
257 cooked_admin_functions_js_vars.ajax_url,
258 {
259 action: 'cooked_load_default'
260 },
261 function (result) {
262 if ( recipe_editor === null ) {
263 recipe_editor_textarea.val( result );
264 } else {
265 recipe_editor_textarea.val( result );
266 recipe_editor.setContent( result );
267 }
268
269 thisContainer.find('.button, .button-primary').removeClass('disabled');
270 }).fail(function(result) {
271 thisContainer.find('.button, .button-primary').removeClass('disabled');
272 });
273 }
274 });
275 }
276
277 // Cooked Select Wrappers
278 if ($_CookedSelectFields.length) {
279 $_CookedSelectFields.each(function() {
280 $(this).wrap('<div class="cooked-select-wrapper" />');
281 });
282 }
283
284 // Cooked Tooltips
285 if ($_CookedTooltips.length){
286 $_CookedTooltips.tooltipster({
287 theme : ['tooltipster-light', 'tooltipster-sideTip-cooked'],
288 animation : 'grow',
289 delay : 100,
290 speed : 200,
291 maxWidth : 275,
292 contentAsHTML : true,
293 interactive : true
294 });
295 }
296
297 // Cooked Shortcode Fields
298 if ($_CookedShortcodeField.length) {
299 $_CookedShortcodeField.on('click',function(e) {
300 $(this).select();
301 });
302 }
303
304 // Conditional Fields (Recipes and Settings Pages)
305 if ($_CookedConditionals.length) {
306 var conditionalFields = [];
307 $_CookedConditionals.each(function() {
308 var thisBlock = $(this),
309 thisBlockType,
310 thisID = $(this).data('condition'),
311 thisVal = $(this).data('value'),
312 thisField = $('#'+thisID);
313
314 if (thisVal){
315 thisVal = thisVal.split(' ');
316 } else {
317 thisVal = false;
318 }
319
320 if ( thisBlock.is('li') || thisBlock.is('span') ) {
321 thisBlockType = 'inline-block';
322 } else {
323 thisBlockType = 'block';
324 }
325
326 if (thisField.is(":checkbox") && thisField.is(":checked") || thisField.is(":radio") && thisField.is(":checked")) {
327 thisBlock.css({'display':thisBlockType});
328 } else if (thisField.is(":checkbox") && !thisField.is(":checked") || thisField.is(":radio") && !thisField.is(":checked")) {
329 thisBlock.hide();
330 } else if (!thisField.is(":checkbox") && !thisVal && thisField.val() || !thisField.is(":checkbox") && thisVal && $.inArray(thisField.val(),thisVal) > -1){
331 thisBlock.css({'display':thisBlockType});
332 } else if (!thisField.is(":radio") && !thisVal && thisField.val() || !thisField.is(":radio") && thisVal && $.inArray(thisField.val(),thisVal) > -1){
333 thisBlock.css({'display':thisBlockType});
334 } else {
335 thisBlock.hide();
336 }
337
338 if ($.inArray(thisID,conditionalFields) == -1){
339 conditionalFields.push(thisID);
340 }
341
342 var conditionalsLength = conditionalFields.length;
343 for (var i = 0; i < conditionalsLength; i++) {
344 cooked_init_conditional_field(thisID);
345 }
346
347 });
348 }
349
350 // Recipe Tabs
351 if ($_CookedRecipeTabs.length) {
352 var $_CookedRecipeTab = $_CookedRecipeTabs.find('li'),
353 $_CookedRecipeTabsOffset = $_CookedRecipeTabs.offset().top - 32; // 32px for the admin bar
354
355 $(window).on('load scroll',function() {
356 var scroll = $(window).scrollTop();
357 if (scroll >= $_CookedRecipeTabsOffset) {
358 $_CookedRecipeSettings.addClass("stuck");
359 } else {
360 $_CookedRecipeSettings.removeClass("stuck");
361 }
362 });
363
364 $_CookedRecipeTab.on('click', function(e) {
365 e.preventDefault();
366 $_CookedRecipeTab.removeClass('active');
367
368 var thisTab = $(this),
369 thisTabID = thisTab.attr('id').split('cooked-recipe-tab-')[1];
370
371 $('.cooked-recipe-tab-content').hide();
372 var $newTab = $('#cooked-recipe-tab-content-' + thisTabID);
373 $newTab.show();
374
375 thisTab.addClass('active');
376 });
377 }
378
379 // Checkboxes
380 if ( $_CookedSettingsTabs.length || $_CookedRecipeTabs.length ) {
381 var greenSwitches = Array.prototype.slice.call(document.querySelectorAll('.cooked-switch'));
382 var redSwitches = Array.prototype.slice.call(document.querySelectorAll('.cooked-switch-red'));
383 var yellowSwitches = Array.prototype.slice.call(document.querySelectorAll('.cooked-switch-yellow'));
384 var graySwitches = Array.prototype.slice.call(document.querySelectorAll('.cooked-switch-gray'));
385
386 greenSwitches.forEach(function( html ) {
387 var greenSwitchery = new Switchery( html, { color: '#00c086', size: 'small' } );
388 });
389
390 redSwitches.forEach(function( html ) {
391 var redSwitchery = new Switchery( html, { color: '#d44d1f', size: 'small' } );
392 });
393
394 yellowSwitches.forEach(function( html ) {
395 var yellowSwitchery = new Switchery( html, { color: '#ffad18', size: 'small' } );
396 });
397
398 graySwitches.forEach(function( html ) {
399 var graySwitchery = new Switchery( html, { color: '#aaaaaa', size: 'small' } );
400 });
401 }
402
403 // Settings Tabs
404 if ($_CookedSettingsTabs.length) {
405 var CookedSettingsTabHash = window.location.hash;
406 var $_CookedSettingsTab = $_CookedSettingsTabs.find('li'),
407 $_CookedSettingsTabOffset = $_CookedSettingsTabs.offset().top - 32; // 32px for the admin bar
408
409 $(window).on('load scroll',function() {
410 var scroll = $(window).scrollTop();
411 if (scroll >= $_CookedSettingsTabOffset) {
412 $_CookedSettingsPanel.addClass("stuck");
413 $("#cooked-settings-wrap").addClass("is-stuck");
414 } else {
415 $_CookedSettingsPanel.removeClass("stuck");
416 $("#cooked-settings-wrap").removeClass("is-stuck");
417 }
418 });
419
420 if ( CookedSettingsTabHash ) {
421 var activeTab = CookedSettingsTabHash;
422 activeTab = activeTab.split('#');
423 activeTab = activeTab[1];
424 $_CookedSettingsTabs.find('li').removeClass('active');
425 $_CookedSettingsTabs.find('a[href="'+CookedSettingsTabHash+'"]').parent().addClass('active');
426 if ( activeTab == 'migration' ) {
427 $_CookedRecipeSettingsSubmit.hide();
428 } else {
429 $_CookedRecipeSettingsSubmit.show();
430 }
431 $('.cooked-settings-tab-content').hide();
432 $('#cooked-settings-tab-content-'+activeTab).show();
433 } else {
434 var activeTab = $_CookedSettingsTabs.find('.active > a').attr('href');
435 activeTab = activeTab.split('#');
436 activeTab = activeTab[1];
437 $('.cooked-settings-tab-content').hide();
438 $('#cooked-settings-tab-content-'+activeTab).show();
439 }
440
441 $_CookedSettingsTab.on('click',function(e) {
442 $('.tab-content').hide();
443 var thisTab = $(this).find('a');
444 $_CookedSettingsTabs.find('li').removeClass('active');
445
446 $(this).addClass('active');
447 var activeTab = thisTab.attr('href');
448 activeTab = activeTab.split('#');
449 activeTab = activeTab[1];
450
451 if ( activeTab == 'migration' ) {
452 $_CookedRecipeSettingsSubmit.hide();
453 } else {
454 $_CookedRecipeSettingsSubmit.show();
455 }
456
457 $('.cooked-settings-tab-content').hide();
458 $('#cooked-settings-tab-content-'+activeTab).show();
459
460 if ( $('#cooked-settings-panel').hasClass('stuck') ) {
461 window.scrollTo(0,130);
462 } else {
463 window.scrollTo(0,0);
464 }
465 });
466
467 var settings_image_frame;
468
469 $_CookedSettingsPanel.on('click', '.cooked-settings-image-remove', function(e) {
470 e.preventDefault();
471
472 var $field = $(this).closest('.cooked-settings-image-field');
473
474 $field.removeClass('cooked-has-image');
475 $field.find('.cooked-settings-image-preview-img').attr('src', '').removeAttr('srcset').removeAttr('sizes');
476 $field.find('.cooked-settings-image-input').val('0');
477 $field.find('.cooked-settings-image-button').val(cooked_admin_functions_js_vars.i18n_image_title);
478 });
479
480 $_CookedSettingsPanel.on('click', '.cooked-settings-image-button', function(e) {
481 e.preventDefault();
482
483 var $button = $(this);
484 var $field = $button.closest('.cooked-settings-image-field');
485
486 if (settings_image_frame) {
487 settings_image_frame.off('select');
488 }
489
490 settings_image_frame = wp.media({
491 title: cooked_admin_functions_js_vars.i18n_image_title,
492 button: { text: cooked_admin_functions_js_vars.i18n_image_button },
493 library: { type: 'image' }
494 });
495
496 settings_image_frame.on('select', function() {
497 var media_attachment = settings_image_frame.state().get('selection').first().toJSON();
498 var thumbUrl = media_attachment.sizes && media_attachment.sizes.thumbnail
499 ? media_attachment.sizes.thumbnail.url
500 : media_attachment.url;
501
502 $field.addClass('cooked-has-image');
503 $field.find('.cooked-settings-image-preview-img')
504 .attr('src', thumbUrl)
505 .removeAttr('srcset')
506 .removeAttr('sizes');
507 $field.find('.cooked-settings-image-input').val(media_attachment.id);
508 $button.val(cooked_admin_functions_js_vars.i18n_image_change);
509 });
510
511 settings_image_frame.open();
512 });
513 }
514
515 if ($_CookedIngredientBuilder.length) {
516 cooked_reset_ingredient_builder();
517
518 $_CookedIngredientBuilder.on('keydown', 'input[data-ingredient-part="name"]', function(e) {
519 if ( e.keyCode === 9 || e.keyCode === 13 ) {
520 if ( $(this).parents('.cooked-ingredient-block').is(':last-child') ) {
521 e.preventDefault();
522 $('#cooked-recipe-tab-content-ingredients').find('.cooked-add-ingredient-button').trigger('click');
523 $_CookedIngredientBuilder.find('.cooked-ingredient-block:last-child input[data-ingredient-part="amount"]').focus();
524 }
525 } else {
526 return;
527 }
528 });
529
530 $_CookedIngredientBuilder.parent().on('click', '.cooked-show-substitution', function(e) {
531 e.preventDefault();
532 var thisParent = $(this).parent();
533 if ( thisParent.hasClass('cooked-expanded') ) {
534 thisParent.removeClass('cooked-expanded');
535 } else {
536 thisParent.addClass('cooked-expanded');
537 thisParent.find('input[data-ingredient-part="sub_amount"]').focus();
538 }
539 });
540
541 $_CookedIngredientBuilder.on('keyup change', 'input[data-ingredient-part="sub_amount"], input[data-ingredient-part="sub_name"], select[data-ingredient-part="sub_measurement"]', function(e) {
542 var parentBlock = $(this).parents('.recipe-setting-block'),
543 subName = parentBlock.find('input[data-ingredient-part="sub_name"]').val();
544
545 if (subName && subName.trim()){
546 parentBlock.addClass('cooked-has-substitution');
547 } else {
548 parentBlock.removeClass('cooked-has-substitution');
549 }
550 });
551
552 if ($_CookedAutoNutritionButton.length) {
553 $_CookedIngredientBuilder.on('change', 'input[data-ingredient-part="name"]', function(e) {
554 var ingredient_name_value = false;
555
556 $_CookedIngredientBuilder.find('input[data-ingredient-part="name"]').each(function() {
557 if ($(this).val() != '') {
558 ingredient_name_value = true;
559 return false; // Break the loop
560 }
561 });
562
563 if (ingredient_name_value) {
564 $_CookedAutoNutritionButton.prop('disabled', false);
565 } else {
566 $_CookedAutoNutritionButton.prop('disabled', true);
567 }
568 });
569 }
570
571 $_CookedIngredientBuilder.on('keyup', 'input[data-ingredient-part="url"]', function(e) {
572 var thisVal = $(this).val(),
573 parentBlock = $(this).parents('.recipe-setting-block');
574 if (thisVal){
575 parentBlock.addClass('cooked-has-url');
576 } else {
577 parentBlock.removeClass('cooked-has-url');
578 }
579 });
580
581 $_CookedIngredientBuilder.parent().on('click', '.cooked-add-ingredient-button', function(e) {
582 e.preventDefault();
583 var clonedIngredientTemplate = $_CookedIngredientBuilder.parent().find('.cooked-ingredient-template').clone().removeClass('cooked-template cooked-ingredient-template').addClass('cooked-ingredient-block');
584 $_CookedIngredientBuilder.append(clonedIngredientTemplate);
585 cooked_reset_ingredient_builder();
586 });
587
588 $_CookedIngredientBuilder.parent().on('click','.cooked-add-heading-button',function(e) {
589 e.preventDefault();
590 var clonedHeadingTemplate = $_CookedIngredientBuilder.parent().find('.cooked-heading-template').clone().removeClass('cooked-template cooked-heading-template').addClass('cooked-ingredient-block cooked-ingredient-heading');
591 $_CookedIngredientBuilder.append(clonedHeadingTemplate);
592 cooked_reset_ingredient_builder();
593 });
594
595 $_CookedIngredientBuilder.parent().on('click','.cooked-delete-ingredient',function(e) {
596 e.preventDefault();
597 $(this).parent().remove();
598 cooked_reset_ingredient_builder();
599 });
600 }
601
602 if ($_CookedDirectionBuilder.length) {
603 cooked_reset_direction_builder();
604
605 $_CookedDirectionBuilder.parent().on('click', '.cooked-show-heading-element', function(e) {
606 e.preventDefault();
607 var thisParent = $(this).parent();
608 if ( thisParent.hasClass('cooked-expanded') ) {
609 thisParent.removeClass('cooked-expanded');
610 } else {
611 thisParent.addClass('cooked-expanded');
612 thisParent.find('input[data-direction-part="section_heading_element"]').focus();
613 }
614 });
615
616 $_CookedDirectionBuilder.parent().on('click', '.cooked-add-direction-button', function(e) {
617 e.preventDefault();
618 var clonedDirectionTemplate = $_CookedDirectionBuilder.parent().find('.cooked-direction-template').clone().removeClass('cooked-template cooked-direction-template').addClass('cooked-direction-block');
619 $_CookedDirectionBuilder.append(clonedDirectionTemplate);
620 cooked_reset_direction_builder();
621 });
622
623 $_CookedDirectionBuilder.parent().on('click', '.cooked-add-heading-button', function(e) {
624 e.preventDefault();
625 var clonedHeadingTemplate = $_CookedDirectionBuilder.parent().find('.cooked-heading-template').clone().removeClass('cooked-template cooked-heading-template').addClass('cooked-direction-block cooked-direction-heading');
626 $_CookedDirectionBuilder.append(clonedHeadingTemplate);
627 cooked_reset_direction_builder();
628 });
629
630 $_CookedDirectionBuilder.parent().on('click', '.cooked-delete-direction', function(e) {
631 e.preventDefault();
632 var directionBlock = $(this).parent();
633 var canRemoveWpEditor = !!(
634 cooked_admin_functions_js_vars.wp_editor_roles_allowed &&
635 typeof wp !== 'undefined' &&
636 wp.editor &&
637 typeof wp.editor.remove === 'function'
638 );
639
640 if (canRemoveWpEditor) {
641 var directionTextarea = directionBlock.find('textarea[data-direction-part="content"]');
642 var fieldID = directionTextarea.attr('id');
643
644 if (fieldID) {
645 wp.editor.remove(fieldID);
646 }
647 }
648
649 directionBlock.remove();
650 cooked_reset_direction_builder();
651 });
652
653 $_CookedDirectionBuilder.parent().on('click', '.remove-image-button', function(e) {
654 e.preventDefault();
655 var $parent = $(this).parent();
656 $parent.removeClass('cooked-has-image');
657 $parent.find('img').attr('src', '').removeAttr('srcset').removeAttr('sizes');
658 $parent.find('input[data-direction-part="image"]').val('');
659 cooked_reset_direction_builder();
660 });
661
662 $_CookedDirectionBuilder.parent().on('click', '.remove-video-button', function(e) {
663 e.preventDefault();
664 var $parent = $(this).parent();
665 $parent.removeClass('cooked-has-video');
666 $parent.find('.cooked-direction-video-preview').attr('src', '');
667 $parent.find('input[data-direction-part="video"]').val('');
668 $parent.find('.direction-video-button').prop('value', cooked_admin_functions_js_vars.i18n_video_title);
669 cooked_reset_direction_builder();
670 });
671
672 // Instantiates the variable that holds the media library frame.
673 var direction_image_frame, direction_video_frame, directionID;
674
675 $('body').on('click', '.cooked-direction-img-placeholder, .cooked-direction-img', function(e) {
676 e.preventDefault();
677 var thisButton = $(this).parent().find('.direction-image-button');
678 thisButton.trigger('click');
679 });
680
681 // Runs when the image button is clicked.
682 $('body').on('click', '.direction-image-button', function(e) {
683 var thisButton = $(this);
684 directionID = thisButton.data('id');
685
686 e.preventDefault();
687
688 // If the frame already exists, re-open it.
689 if ( direction_image_frame ) {
690 direction_image_frame.open();
691 return;
692 }
693
694 // Sets up the media library frame
695 direction_image_frame = wp.media.frames.direction_image_frame = wp.media({
696 title: cooked_admin_functions_js_vars.i18n_image_title,
697 button: { text: cooked_admin_functions_js_vars.i18n_image_button },
698 library: { type: 'image' }
699 });
700
701 // Runs when an image is selected.
702 direction_image_frame.on('select', function() {
703 // Grabs the attachment selection and creates a JSON representation of the model.
704 var media_attachment = direction_image_frame.state().get('selection').first().toJSON();
705
706 // Sends the attachment URL to our custom image input field.
707 // Remove srcset/sizes so the UI updates when an image was previously loaded (WP outputs srcset on edit).
708 var $directionImg = $('#direction-' + directionID + '-image-src');
709 $directionImg.attr('src', media_attachment.sizes.thumbnail.url)
710 .removeAttr('srcset')
711 .removeAttr('sizes')
712 .parent().addClass('cooked-has-image');
713 $('input[name="_recipe_settings[directions][' + directionID + '][image]"]').val( media_attachment.id );
714 $('.direction-image-button[data-id="' + directionID + '"]').prop( 'value', cooked_admin_functions_js_vars.i18n_image_change );
715 });
716
717 // Opens the media library frame.
718 direction_image_frame.open();
719 });
720
721 $('body').on('click', '.cooked-direction-video-placeholder, .cooked-direction-video-preview', function(e) {
722 e.preventDefault();
723 var thisButton = $(this).parent().find('.direction-video-button');
724 thisButton.trigger('click');
725 });
726
727 // Runs when the video button is clicked.
728 $('body').on('click', '.direction-video-button', function(e) {
729 var thisButton = $(this);
730 directionID = thisButton.data('id');
731
732 e.preventDefault();
733
734 // If the frame already exists, re-open it.
735 if ( direction_video_frame ) {
736 direction_video_frame.open();
737 return;
738 }
739
740 // Sets up the media library frame
741 direction_video_frame = wp.media.frames.direction_video_frame = wp.media({
742 title: cooked_admin_functions_js_vars.i18n_video_title,
743 button: { text: cooked_admin_functions_js_vars.i18n_video_button },
744 library: { type: 'video' }
745 });
746
747 // Runs when a video is selected.
748 direction_video_frame.on('select', function() {
749 var media_attachment = direction_video_frame.state().get('selection').first().toJSON();
750
751 var $videoBlock = $('.direction-video-button[data-id="' + directionID + '"]').closest('.cooked-direction-video');
752 $videoBlock.find('.cooked-direction-video-preview').attr('src', media_attachment.url);
753 $videoBlock.find('input[data-direction-part="video"]').val( media_attachment.id );
754 $videoBlock.addClass('cooked-has-video');
755 $('.direction-video-button[data-id="' + directionID + '"]').prop( 'value', cooked_admin_functions_js_vars.i18n_video_change );
756 });
757
758 // Opens the media library frame.
759 direction_video_frame.open();
760 });
761 }
762
763 // Bulk Add Modal
764 cooked_init_bulk_add($);
765
766 if ( $_CookedRecipeGallery.length ) {
767
768 var gallery_images_frame;
769 cooked_init_gallery_sorting();
770
771 // Runs when the Add Images button is clicked in the Gallery tab.
772 $('body').on('click','.cooked-gallery-add-button', function(e) {
773 var thisButton = $(this);
774 e.preventDefault();
775
776 // If the frame already exists, re-open it.
777 if ( gallery_images_frame ) {
778 gallery_images_frame.open();
779 return;
780 }
781
782 // Sets up the media library frame
783 gallery_images_frame = wp.media.frames.gallery_images_frame = wp.media({
784 title: cooked_admin_functions_js_vars.i18n_gallery_image_title,
785 button: { text: cooked_admin_functions_js_vars.i18n_gallery_image_title },
786 library: { type: 'image' },
787 multiple: true
788 });
789
790 // Runs when an image is selected.
791 gallery_images_frame.on('select', function() {
792 // Grabs the attachment selection and creates a JSON representation of the model.
793 var media_attachments = gallery_images_frame.state().get('selection').toJSON();
794 var thisThumbnail;
795
796 for (var media_key in media_attachments) {
797 if (!media_attachments.hasOwnProperty(media_key)) continue;
798 var media_attachment = media_attachments[media_key];
799 thisThumbnail = media_attachment.sizes.thumbnail.url;
800 $('#cooked-recipe-image-gallery').append( '<div data-attachment-id="' + media_attachment.id + '" class="cooked-recipe-gallery-item"><img src="' + thisThumbnail + '" /><span class="cooked-gallery-item-title">' + media_attachment.title + '</span><input type="hidden" name="_recipe_settings[gallery][items][]" value="' + media_attachment.id + '" /><a href="#" data-attachment-id="' + media_attachment.id + '" class="cooked-gallery-edit-button"><i class="cooked-icon cooked-icon-pencil"></i></a><a href="#" class="remove-image-button"><i class="cooked-icon cooked-icon-times"></i></a></div>' );
801 }
802
803 cooked_init_gallery_sorting();
804 });
805
806 // Opens the media library frame.
807 gallery_images_frame.open();
808 });
809
810 $('body').on('click','.cooked-recipe-gallery-item img', function(e) {
811 e.preventDefault();
812 var thisButton = $(this).parent().find('.cooked-gallery-edit-button');
813 thisButton.trigger('click');
814 });
815
816 $('body').on('click','.cooked-gallery-edit-button', function(e) {
817 var thisButton = $(this),
818 attachment_id = thisButton.data('attachment-id');
819
820 e.preventDefault();
821
822 // Sets up the media library frame
823 var image_edit_frame = wp.media.frames.gallery_images_frame = wp.media({
824 title: cooked_admin_functions_js_vars.i18n_edit_image_title,
825 button: { text: cooked_admin_functions_js_vars.i18n_edit_image_button },
826 library: { type: 'image' },
827 multiple: false
828 });
829
830 image_edit_frame.on('open',function() {
831 var selection = image_edit_frame.state().get('selection');
832 attachment = wp.media.attachment(attachment_id);
833 attachment.fetch();
834 selection.add( attachment ? [ attachment ] : [] );
835 });
836
837 // Opens the media library frame.
838 image_edit_frame.open();
839
840 // Runs when an image is selected.
841 image_edit_frame.on('select', function() {
842 // Grabs the attachment selection and creates a JSON representation of the model.
843 var media_attachments = image_edit_frame.state().get('selection').toJSON();
844 var thisThumbnail;
845
846 for (var media_key in media_attachments) {
847 if (!media_attachments.hasOwnProperty(media_key)) continue;
848 var media_attachment = media_attachments[media_key];
849 thisThumbnail = media_attachment.sizes.thumbnail.url;
850 thisButton.parent().replaceWith( '<div data-attachment-id="' + media_attachment.id + '" class="cooked-recipe-gallery-item"><img src="' + thisThumbnail + '" /><span class="cooked-gallery-item-title">' + media_attachment.title + '</span><input type="hidden" name="_recipe_settings[gallery][items][]" value="' + media_attachment.id + '" /><a href="#" data-attachment-id="' + media_attachment.id + '" class="cooked-gallery-edit-button"><i class="cooked-icon cooked-icon-pencil"></i></a><a href="#" class="remove-image-button"><i class="cooked-icon cooked-icon-times"></i></a></div>' );
851 }
852
853 cooked_init_gallery_sorting();
854 });
855 });
856
857 $_CookedRecipeGallery.on('click', '.remove-image-button', function(e) {
858 var thisButton = $(this);
859 directionID = thisButton.data('id');
860 e.preventDefault();
861
862 if ( directionID ) {
863 $('#direction-'+directionID+'-image-src').parent().removeClass('cooked-has-image').prop('src',false);
864 $('input[name="_recipe_settings[directions]['+directionID+'][image]"]').val('');
865 $('.direction-image-button[data-id="'+directionID+'"]').prop( 'value',cooked_admin_functions_js_vars.i18n_image_title );
866 } else {
867 thisButton.parent().remove();
868 }
869 });
870 }
871
872 if ( $_CookedNutritionFactsTab.length ) {
873 init_nutrition_facts( $_CookedNutritionFactsTab );
874
875 $_CookedNutritionFactsTab.on('keyup', 'input', function(e) {
876 init_nutrition_facts( $_CookedNutritionFactsTab );
877 });
878 }
879
880 /**** Time Pickers ****/
881 if ( $('#cooked-prep-time').length ) {
882 $( '#cooked-prep-time,#cooked-cook-time' ).on('change', function() {
883 var prepTimeValue = parseInt( $( '#cooked-prep-time' ).val() ),
884 cookTimeValue = parseInt( $( '#cooked-cook-time' ).val() );
885 cooked_updateTotalTimeValue( prepTimeValue, cookTimeValue );
886 });
887 }
888
889 });
890
891 })( jQuery );
892
893 var cooked_recipe_update_counter = 0;
894 var cooked_bulk_per_page = 20;
895
896 /** Applies default recipe content in paginated AJAX batches and updates the progress UI. */
897 function cooked_set_default_template(page, total_recipes, content, nonce, instance) {
898 if (total_recipes <= 0) {
899 return;
900 }
901
902 var progress = jQuery('#cooked-template-progress');
903 var progress_bar = progress.find('.cooked-progress-bar');
904 var progress_text = jQuery('#cooked-template-progress-text');
905
906 if (!progress.hasClass('cooked-active')) {
907 progress.addClass('cooked-active');
908 progress_text.addClass('cooked-active');
909 progress_bar.css({ "width" : "0%" });
910 instance.reposition();
911 }
912
913 jQuery.post(
914 cooked_admin_functions_js_vars.ajax_url,
915 {
916 action: 'cooked_save_default_bulk',
917 page: page,
918 default_content: content,
919 nonce: nonce
920 },
921 function(response) {
922 if (response && response.success) {
923 cooked_recipe_update_counter = Math.min((page + 1) * cooked_bulk_per_page, total_recipes);
924
925 var progress_percent = Math.round((cooked_recipe_update_counter / total_recipes) * 100);
926 progress_bar.css({ "width" : progress_percent + "%" });
927 progress_text.text(cooked_recipe_update_counter + " / " + total_recipes);
928
929 if (response.data.has_more) {
930 cooked_set_default_template(page + 1, total_recipes, content, nonce, instance);
931 } else {
932 jQuery('.cooked-save-default-all').text(cooked_admin_functions_js_vars.i18n_applied);
933 progress_bar.css({ "width" : "100%" });
934 progress_text.text(total_recipes + " / " + total_recipes);
935 progress.removeClass('cooked-active');
936 progress_text.removeClass('cooked-active').text("");
937
938 instance.reposition();
939 }
940 }
941 },
942 'json'
943 );
944 }
945
946 /** Sets the total time field to prep time plus cook time. */
947 function cooked_updateTotalTimeValue( prepTime, cookTime ) {
948 var totalTimeInput = jQuery( '#cooked-total-time' ),
949 totalTime = prepTime + cookTime;
950
951 totalTimeInput.val( totalTime );
952 }
953
954 // Reset the Gallery Builder
955 // Assign a value to the hidden input field.
956 // Init the jQuery UI Sort
957 function cooked_init_gallery_sorting() {
958 jQuery('#cooked-recipe-image-gallery').sortable();
959 }
960
961 /** Syncs nutrition tab inputs with the live nutrition label preview (values and daily-value percents). */
962 function init_nutrition_facts( nutritionTab ) {
963 nutritionTab.find('input').each(function() {
964 var thisInput = jQuery(this),
965 thisID = thisInput.attr('id'),
966 thisVal = thisInput.val(),
967 hasPercent = jQuery('.cooked-nut-percent[data-labeltype="' + thisID + '"]').length;
968
969 if ( jQuery('.cooked-nut-label[data-labeltype="' + thisID + '"]').length ) {
970 if ( thisVal ) {
971 jQuery('.cooked-nut-label[data-labeltype="' + thisID + '"]').text( thisVal );
972 jQuery('.cooked-nut-label[data-labeltype="' + thisID + '"]').parents('li').eq(0).show();
973 jQuery('.cooked-nut-label[data-labeltype="' + thisID + '"]').parents('p').eq(0).show();
974 if ( hasPercent ) {
975 var thisPercentElem = jQuery('.cooked-nut-percent[data-labeltype="' + thisID + '"]'),
976 pvd = thisPercentElem.data('pdv');
977 if ( pvd ) {
978 thisPercent = Math.ceil( ( thisVal / pvd ) * 100 );
979 thisPercentElem.text( thisPercent );
980 }
981 }
982 } else {
983 jQuery('.cooked-nut-label[data-labeltype="' + thisID + '"]').text( '___' );
984 jQuery('.cooked-nut-label[data-labeltype="' + thisID + '"]').parents('li').eq(0).hide();
985 jQuery('.cooked-nut-label[data-labeltype="' + thisID + '"]').parents('p').eq(0).hide();
986 }
987 }
988 });
989 }
990
991 // Reset Time Picker Settings & Names
992 function cooked_reset_ingredient_builder() {
993 var ingredientBlocks = jQuery('.cooked-ingredient-block'),
994 total_ingredients_blocks = 0,
995 total_blocks = 0,
996 ingredientNameValue = false;
997
998 if (ingredientBlocks.length > 0) {
999 ingredientBlocks.each(function() {
1000 var randomKeyForInterval = cooked_get_random_int(10000000, 99999999);
1001 total_blocks++;
1002
1003 // Set the input "name" values.
1004 var $_this = jQuery(this);
1005
1006 if (!$_this.hasClass('cooked-ingredient-heading')) {
1007 total_ingredients_blocks++;
1008 }
1009
1010 $_this.find("[data-ingredient-part]").each(function() {
1011 var thisField = jQuery(this);
1012 if (thisField.attr('name') == '') {
1013 var ingredientPartName = thisField.data('ingredient-part');
1014 thisField.attr('name', '_recipe_settings[ingredients][' + randomKeyForInterval + '][' + ingredientPartName + ']');
1015 }
1016 });
1017 });
1018
1019 if ($_CookedAutoNutritionButton.length) {
1020 jQuery('input[data-ingredient-part="name"]').each(function() {
1021 if (jQuery(this).val() != '') {
1022 ingredientNameValue = true;
1023 return false; // Break the loop
1024 }
1025 });
1026 }
1027 }
1028
1029 if (total_ingredients_blocks) {
1030 jQuery('.cooked-ingredient-headers').show();
1031 if ($_CookedAutoNutritionButton.length) {
1032 if (ingredientNameValue) {
1033 $_CookedAutoNutritionButton.prop('disabled', false);
1034 } else {
1035 $_CookedAutoNutritionButton.prop('disabled', true);
1036 }
1037 }
1038 } else {
1039 jQuery('.cooked-ingredient-headers').hide();
1040 if ($_CookedAutoNutritionButton.length) $_CookedAutoNutritionButton.prop('disabled', true);
1041 }
1042
1043 if (total_blocks) {
1044 jQuery('#cooked-ingredients-builder').css( { 'margin-bottom':'20px' } );
1045 } else {
1046 jQuery('#cooked-ingredients-builder').css( { 'margin-bottom':'12px' } );
1047 }
1048 }
1049
1050 // Reset Time Picker Settings & Names
1051 function cooked_reset_direction_builder() {
1052 var directionBlocks = jQuery('.cooked-direction-block'),
1053 total_blocks = 0;
1054
1055 directionBlocks.each(function() {
1056 var randomKeyForInterval = cooked_get_random_int(10000000, 99999999);
1057 total_blocks++;
1058
1059 // Set the input "name" values.
1060 var $_this = jQuery(this);
1061
1062 $_this.find("[data-direction-part]").each(function() {
1063 var thisField = jQuery(this);
1064 var directionPartName = thisField.data('direction-part');
1065
1066 if ( thisField.attr('name') == '') {
1067 thisField.attr( 'name', '_recipe_settings[directions][' + randomKeyForInterval + '][' + directionPartName + ']' );
1068 }
1069
1070 if ( thisField.attr('data-id') == '') {
1071 thisField.attr( 'data-id', randomKeyForInterval );
1072 }
1073
1074 let theId = thisField.attr('id');
1075
1076 if ( theId == '' || theId == undefined ) {
1077 directionPartName = directionPartName.replace( '_', '-' );
1078 var fieldID = 'direction-' + randomKeyForInterval + '-' + directionPartName;
1079 thisField.attr('id', fieldID);
1080
1081 var canInitializeWpEditor = !!(
1082 cooked_admin_functions_js_vars.wp_editor_roles_allowed &&
1083 typeof wp !== 'undefined' &&
1084 wp.editor &&
1085 typeof wp.editor.initialize === 'function'
1086 );
1087
1088 if (directionPartName === 'content' && thisField.is('textarea') && canInitializeWpEditor) {
1089 // Init the WordPress Editor.
1090 wp.editor.initialize(fieldID, {
1091 tinymce: {
1092 wpautop: false,
1093 toolbar1: 'bold,italic,underline,blockquote,strikethrough,bullist,numlist,alignleft,aligncenter,alignright,undo,redo,wp_link_advanced,unlink,fullscreen',
1094 toolbar2: '',
1095 toolbar3: '',
1096 toolbar4: '',
1097 height: 100,
1098 textarea_name: '_recipe_settings[directions][' + randomKeyForInterval + '][' + directionPartName + ']',
1099 plugins: 'link lists fullscreen wordpress wplink',
1100 setup: function(editor) {
1101 // @TODO: Inline Link editor does not work.
1102 // editor.on('init', function() {
1103 // if (typeof wpLink !== 'undefined') {
1104 // editor.addCommand('WP_Link', function() {
1105 // window.wpActiveEditor = editor.id;
1106 // wpLink.open(editor.id);
1107 // return false;
1108 // });
1109 // }
1110 // });
1111 }
1112 },
1113 quicktags: true,
1114 mediaButtons: false
1115 });
1116 }
1117 }
1118 });
1119 });
1120
1121 // Update step numbers for direction blocks (skip section headings).
1122 var stepNum = 0;
1123 jQuery('#cooked-directions-builder').find('.cooked-direction-block').each(function() {
1124 var $_block = jQuery(this);
1125 if ( !$_block.hasClass('cooked-direction-heading') ) {
1126 stepNum++;
1127 $_block.find('.cooked-direction-number').text(stepNum);
1128 $_block.toggleClass('cooked-direction-has-number-wide', stepNum > 9);
1129 $_block.addClass('cooked-direction-has-number');
1130 } else {
1131 $_block.removeClass('cooked-direction-has-number cooked-direction-has-number-wide');
1132 }
1133 });
1134
1135 if ( total_blocks ) {
1136 jQuery('#cooked-directions-builder').css( { 'margin-bottom': '20px' } );
1137 } else {
1138 jQuery('#cooked-directions-builder').css( { 'margin-bottom': '12px' } );
1139 }
1140 }
1141
1142 // Get random integer for sortable lists (ingredients and directions)
1143 function cooked_get_random_int(min, max) {
1144 return Math.floor(Math.random() * (max - min)) + min;
1145 }
1146
1147 // Check if value is an integer (for amount field in Ingredients Builder)
1148 function cooked_is_int(val) {
1149 if (Math.floor(val) == val && $.isNumeric(val)) {
1150 return true;
1151 } else {
1152 return false;
1153 }
1154 }
1155
1156 // Cooked Conditional Fields Function
1157 function cooked_init_conditional_field(thisID) {
1158 var thisField = jQuery('#' + thisID);
1159
1160 if (thisField.is(':radio')) {
1161 jQuery('body').find('input:radio').on('change', function() {
1162 if (!thisField.is(':checked')) {
1163 jQuery('body').find("[data-condition='" + thisID + "']").each(function() {
1164 jQuery(this).hide();
1165 });
1166 }
1167 });
1168 }
1169
1170 thisField.on('change', function() {
1171 jQuery('#cooked_recipe_settings').addClass('cooked-loading');
1172
1173 window.clearTimeout($_CookedConditionalTimeout);
1174
1175 $_CookedConditionalTimeout = window.setTimeout(function() {
1176 jQuery('body').find("[data-condition='" + thisID + "']").each(function() {
1177 var thisBlock = jQuery(this),
1178 thisBlockType;
1179
1180 if ( thisBlock.is('li') || thisBlock.is('span') ) {
1181 thisBlockType = 'inline-block';
1182 } else {
1183 thisBlockType = 'block';
1184 }
1185
1186 var thisVal = thisBlock.data('value');
1187
1188 if (thisVal) {
1189 thisVal = thisVal.split(' ');
1190 } else {
1191 thisVal = false;
1192 }
1193
1194 if (thisField.is(":checkbox") && thisField.is(":checked") || thisField.is(":radio") && thisField.is(":checked")) {
1195 thisBlock.css({'display':thisBlockType});
1196 } else if (thisField.is(":checkbox") && !thisField.is(":checked")) {
1197 thisBlock.hide();
1198 } else if (!thisField.is(":checkbox") && !thisVal && thisField.val() || !thisField.is(":checkbox") && thisVal && jQuery.inArray(thisField.val(),thisVal) > -1) {
1199 thisBlock.css({'display':thisBlockType});
1200 } else if (!thisField.is(":radio") && !thisVal && thisField.val() || !thisField.is(":radio") && thisVal && jQuery.inArray(thisField.val(),thisVal) > -1) {
1201 thisBlock.css({'display':thisBlockType});
1202 } else {
1203 thisBlock.hide();
1204 }
1205 });
1206
1207 jQuery('#cooked_recipe_settings').removeClass('cooked-loading');
1208
1209 }, 25);
1210 });
1211 }
1212
1213 /** Wires the bulk-add modal (preview, parse, submit) for ingredients and directions. */
1214 function cooked_init_bulk_add($) {
1215 var $overlay = $('#cooked-bulk-add-overlay');
1216 if (!$overlay.length) return;
1217
1218 var $textarea = $('#cooked-bulk-add-textarea'),
1219 $preview = $('#cooked-bulk-add-preview'),
1220 $previewList = $('#cooked-bulk-add-preview-list'),
1221 $title = $('#cooked-bulk-add-title'),
1222 $typeField = $('#cooked-bulk-add-type'),
1223 $submitBtn = $overlay.find('.cooked-bulk-add-submit'),
1224 $spinner = $overlay.find('.cooked-bulk-add-spinner'),
1225 jsVars = cooked_admin_functions_js_vars,
1226 parseTimer = null,
1227 lastParsedText = '';
1228
1229 /** Opens the bulk-add overlay for the given type and sets copy/placeholders. */
1230 function openModal(type) {
1231 $typeField.val(type);
1232 $textarea.val('');
1233 $previewList.empty();
1234 $preview.attr('data-bulk-type', '');
1235 $preview.hide();
1236 $submitBtn.attr('disabled', 'disabled');
1237 $spinner.hide();
1238 lastParsedText = '';
1239
1240 if (type === 'ingredients') {
1241 $title.text(jsVars.i18n_bulk_add_ingredients);
1242 $textarea.attr('placeholder', jsVars.i18n_bulk_add_placeholder_ingredients);
1243 $submitBtn.text(jsVars.i18n_bulk_add_submit_ingredients);
1244 } else {
1245 $title.text(jsVars.i18n_bulk_add_directions);
1246 $textarea.attr('placeholder', jsVars.i18n_bulk_add_placeholder_directions);
1247 $submitBtn.text(jsVars.i18n_bulk_add_submit_directions);
1248 }
1249
1250 $overlay.show();
1251 $textarea.focus();
1252 }
1253
1254 /** Hides the bulk-add overlay and clears its state. */
1255 function closeModal() {
1256 $overlay.hide();
1257 $textarea.val('');
1258 $previewList.empty();
1259 $preview.attr('data-bulk-type', '');
1260 $preview.hide();
1261 lastParsedText = '';
1262 if (parseTimer) clearTimeout(parseTimer);
1263 }
1264
1265 /** Strips leading list markers from a single line of bulk text. */
1266 function cleanLine(line) {
1267 line = line.trim();
1268 line = line.replace(/^(\d+\)\s+|\d+\.\s+|[a-z]+\)\s+|[•·\-\*]\s+|[A-Z]+\.\s+|[IVX]+\.\s+)/, '');
1269 return line.trim();
1270 }
1271
1272 /** Splits bulk textarea content into non-empty cleaned lines. */
1273 function parseTextToLines(text) {
1274 var lines = text.split(/[\r\n]+/);
1275 var result = [];
1276 for (var i = 0; i < lines.length; i++) {
1277 var cleaned = cleanLine(lines[i]);
1278 if (cleaned) {
1279 result.push(cleaned);
1280 }
1281 }
1282 return result;
1283 }
1284
1285 /** Escapes a string for safe insertion into HTML attribute strings. */
1286 function escHtml(str) {
1287 return $('<span>').text(str).html();
1288 }
1289
1290 /** Renders bulk directions preview rows from parsed lines. */
1291 function renderDirectionsPreview(lines) {
1292 $previewList.empty();
1293 if (!lines.length) {
1294 $preview.attr('data-bulk-type', '');
1295 $preview.hide();
1296 $submitBtn.attr('disabled', 'disabled');
1297 return;
1298 }
1299 $preview.attr('data-bulk-type', 'directions');
1300 $preview.show();
1301 $submitBtn.removeAttr('disabled');
1302
1303 for (var i = 0; i < lines.length; i++) {
1304 var $row = $('<div class="cooked-bulk-add-preview-row"></div>');
1305 var $cb = $('<label class="cooked-bulk-add-heading-toggle"><input type="checkbox" data-index="' + i + '" /><span>' + jsVars.i18n_bulk_add_section_heading + '</span></label>');
1306 var $text = $('<input type="text" class="cooked-bulk-add-preview-text" data-index="' + i + '" />').val(lines[i]);
1307 $row.append($cb).append($text);
1308 $previewList.append($row);
1309 }
1310 }
1311
1312 /** Renders bulk ingredients preview rows (optionally with server-parsed amount/unit/name). */
1313 function renderIngredientsPreview(lines, parsed) {
1314 $previewList.empty();
1315 if (!lines.length) {
1316 $preview.attr('data-bulk-type', '');
1317 $preview.hide();
1318 $submitBtn.attr('disabled', 'disabled');
1319 return;
1320 }
1321 $preview.attr('data-bulk-type', 'ingredients');
1322 $preview.show();
1323 $submitBtn.removeAttr('disabled');
1324
1325 if (!$previewList.find('.cooked-bulk-add-preview-header').length) {
1326 $previewList.prepend(
1327 '<div class="cooked-bulk-add-preview-header">' +
1328 '<span class="cooked-bulk-add-col-heading"></span>' +
1329 '<span class="cooked-bulk-add-col-amount">' + escHtml(jsVars.i18n_bulk_add_amount) + '</span>' +
1330 '<span class="cooked-bulk-add-col-unit">' + escHtml(jsVars.i18n_bulk_add_unit) + '</span>' +
1331 '<span class="cooked-bulk-add-col-name">' + escHtml(jsVars.i18n_bulk_add_name) + '</span>' +
1332 '</div>'
1333 );
1334 }
1335
1336 for (var i = 0; i < lines.length; i++) {
1337 var p = parsed && parsed[i] ? parsed[i] : { amount: '', measurement: '', name: lines[i] };
1338 var $row = $('<div class="cooked-bulk-add-preview-row cooked-bulk-add-preview-row-ingredient" data-index="' + i + '"></div>');
1339 var $cb = $('<label class="cooked-bulk-add-heading-toggle"><input type="checkbox" data-index="' + i + '" /><span>' + jsVars.i18n_bulk_add_section_heading + '</span></label>');
1340 var $headingWrap = $('<div class="cooked-bulk-add-heading-line-wrap"></div>');
1341 var $headingLbl = $('<span class="cooked-bulk-add-heading-line-label"></span>').text(jsVars.i18n_bulk_add_heading_line_label);
1342 var $lineInput = $('<input type="text" class="cooked-bulk-add-preview-text" data-index="' + i + '" />').val(lines[i]);
1343 $headingWrap.append($headingLbl).append($lineInput);
1344 var $amt = $('<input type="text" class="cooked-bulk-add-parsed-amount" data-index="' + i + '" />').val(p.amount);
1345 var $unit = $('<input type="text" class="cooked-bulk-add-parsed-unit" data-index="' + i + '" />').val(p.measurement);
1346 var $name = $('<input type="text" class="cooked-bulk-add-parsed-name" data-index="' + i + '" />').val(p.name);
1347 $row.append($cb).append($headingWrap).append($amt).append($unit).append($name);
1348 $previewList.append($row);
1349 }
1350 }
1351
1352 /** Fetches AJAX-parsed ingredient lines and refreshes the preview. */
1353 function fetchIngredientParse(lines) {
1354 if (!lines.length) {
1355 renderIngredientsPreview([], null);
1356 return;
1357 }
1358
1359 $spinner.show().css('visibility', 'visible');
1360
1361 $.post(jsVars.ajax_url, {
1362 action: 'cooked_parse_bulk_ingredients',
1363 nonce: jsVars.cooked_bulk_add_nonce,
1364 lines: lines
1365 }, function(response) {
1366 $spinner.hide();
1367 var parsed = (response.success && response.data && response.data.parsed) ? response.data.parsed : null;
1368 renderIngredientsPreview(lines, parsed);
1369 }).fail(function() {
1370 $spinner.hide();
1371 renderIngredientsPreview(lines, null);
1372 });
1373 }
1374
1375 $textarea.on('input', function() {
1376 var type = $typeField.val();
1377 var lines = parseTextToLines($(this).val());
1378 var textKey = lines.join('\n');
1379
1380 if (type === 'directions') {
1381 renderDirectionsPreview(lines);
1382 } else {
1383 if (textKey === lastParsedText) return;
1384 lastParsedText = textKey;
1385 renderIngredientsPreview(lines, null);
1386 if (parseTimer) clearTimeout(parseTimer);
1387 parseTimer = setTimeout(function() {
1388 fetchIngredientParse(lines);
1389 }, 400);
1390 }
1391 });
1392
1393 $(document).on('click', '.cooked-bulk-add-button', function(e) {
1394 e.preventDefault();
1395 var type = $(this).data('type');
1396 openModal(type);
1397 });
1398
1399 $overlay.on('click', '.cooked-bulk-add-close, .cooked-bulk-add-cancel', function(e) {
1400 e.preventDefault();
1401 closeModal();
1402 });
1403
1404 $overlay.on('click', function(e) {
1405 if ($(e.target).is($overlay)) {
1406 closeModal();
1407 }
1408 });
1409
1410 $(document).on('keydown', function(e) {
1411 if (e.key === 'Escape' && $overlay.is(':visible')) {
1412 closeModal();
1413 }
1414 });
1415
1416 $previewList.on('change', 'input[type="checkbox"]', function() {
1417 var $row = $(this).closest('.cooked-bulk-add-preview-row');
1418 var isHeading = $(this).is(':checked');
1419 $row.toggleClass('cooked-bulk-add-is-heading', isHeading);
1420 });
1421
1422 $submitBtn.on('click', function(e) {
1423 e.preventDefault();
1424 if ($(this).attr('disabled')) return;
1425
1426 var type = $typeField.val();
1427 var items = [];
1428
1429 if (type === 'ingredients') {
1430 $previewList.find('.cooked-bulk-add-preview-row').each(function() {
1431 var $row = $(this);
1432 var isHeading = $row.find('input[type="checkbox"]').is(':checked');
1433 var text = $row.find('.cooked-bulk-add-preview-text').val().trim();
1434 if (!text) return;
1435
1436 if (isHeading) {
1437 items.push({ text: text, heading: true });
1438 } else {
1439 items.push({
1440 heading: false,
1441 amount: $row.find('.cooked-bulk-add-parsed-amount').val().trim(),
1442 measurement: $row.find('.cooked-bulk-add-parsed-unit').val().trim(),
1443 name: $row.find('.cooked-bulk-add-parsed-name').val().trim() || text
1444 });
1445 }
1446 });
1447 } else {
1448 $previewList.find('.cooked-bulk-add-preview-row').each(function() {
1449 var $row = $(this);
1450 var text = $row.find('.cooked-bulk-add-preview-text').val().trim();
1451 var isHeading = $row.find('input[type="checkbox"]').is(':checked');
1452 if (text) {
1453 items.push({ text: text, heading: isHeading });
1454 }
1455 });
1456 }
1457
1458 if (!items.length) return;
1459
1460 if (type === 'directions') {
1461 cooked_bulk_add_directions(items);
1462 closeModal();
1463 } else {
1464 cooked_bulk_add_ingredients_parsed(items);
1465 closeModal();
1466 }
1467 });
1468 }
1469
1470 /** Appends direction rows from bulk-add items (headings or plain steps) and resets the builder. */
1471 function cooked_bulk_add_directions(items) {
1472 var $_builder = jQuery('#cooked-directions-builder');
1473 var $_parent = $_builder.parent();
1474
1475 for (var i = 0; i < items.length; i++) {
1476 var item = items[i];
1477
1478 if (item.heading) {
1479 var $heading = $_parent.find('.cooked-heading-template').clone()
1480 .removeClass('cooked-template cooked-heading-template')
1481 .addClass('cooked-direction-block cooked-direction-heading');
1482 $heading.find('[data-direction-part="section_heading_name"]').val(item.text);
1483 $_builder.append($heading);
1484 } else {
1485 var $direction = $_parent.find('.cooked-direction-template').clone()
1486 .removeClass('cooked-template cooked-direction-template')
1487 .addClass('cooked-direction-block');
1488 $direction.find('[data-direction-part="content"]').val(item.text);
1489 $_builder.append($direction);
1490 }
1491 }
1492
1493 cooked_reset_direction_builder();
1494 }
1495
1496 /** Appends ingredient rows from bulk-add items (headings or amount/unit/name) and resets the builder. */
1497 function cooked_bulk_add_ingredients_parsed(items) {
1498 var $_builder = jQuery('#cooked-ingredients-builder');
1499 var $_parent = $_builder.parent();
1500
1501 for (var i = 0; i < items.length; i++) {
1502 var item = items[i];
1503
1504 if (item.heading) {
1505 var $heading = $_parent.find('.cooked-heading-template').clone()
1506 .removeClass('cooked-template cooked-heading-template')
1507 .addClass('cooked-ingredient-block cooked-ingredient-heading');
1508 $heading.find('[data-ingredient-part="section_heading_name"]').val(item.text);
1509 $_builder.append($heading);
1510 } else {
1511 var $ingredient = $_parent.find('.cooked-ingredient-template').clone()
1512 .removeClass('cooked-template cooked-ingredient-template')
1513 .addClass('cooked-ingredient-block');
1514
1515 $ingredient.find('[data-ingredient-part="amount"]').val(item.amount || '');
1516
1517 if (item.measurement) {
1518 $ingredient.find('[data-ingredient-part="measurement"]').val(item.measurement);
1519 }
1520
1521 $ingredient.find('[data-ingredient-part="name"]').val(item.name || '');
1522
1523 $_builder.append($ingredient);
1524 }
1525 }
1526
1527 cooked_reset_ingredient_builder();
1528 }
1529