|
| 1 | +/** |
| 2 | + * FFC — Appointment-receipt templates tab (Scheduling Settings → Receipt, #945). |
| 3 | + * |
| 4 | + * Per scheduling mode (Regular / Custom): a dropdown selects which pool template |
| 5 | + * the comprovante PDF uses, and an inline CodeMirror editor edits that template's |
| 6 | + * HTML. Shipped/default templates are read-only; "Duplicate to edit" creates an |
| 7 | + * editable copy via AJAX and selects it. Changing the dropdown live-loads the |
| 8 | + * chosen template's HTML. |
| 9 | + * |
| 10 | + * @since 6.20.0 |
| 11 | + */ |
| 12 | +( function ( $ ) { |
| 13 | + 'use strict'; |
| 14 | + |
| 15 | + var cfg = window.ffcReceiptTemplates || {}; |
| 16 | + |
| 17 | + $( function () { |
| 18 | + var modes = cfg.modes || [ 'regular', 'custom' ]; |
| 19 | + var editors = {}; |
| 20 | + |
| 21 | + modes.forEach( function ( mode ) { |
| 22 | + var editorId = 'ffc_receipt_' + mode + '_editor'; |
| 23 | + var $ta = $( '#' + editorId ); |
| 24 | + if ( ! $ta.length ) { |
| 25 | + return; |
| 26 | + } |
| 27 | + |
| 28 | + var cm = initEditor( editorId ); |
| 29 | + if ( cm ) { |
| 30 | + editors[ mode ] = cm; |
| 31 | + // The server rendered `readonly` for non-editable selections. |
| 32 | + setReadOnly( cm, $ta.prop( 'readonly' ) ); |
| 33 | + } |
| 34 | + |
| 35 | + // Dropdown change → live-load the chosen template's HTML. |
| 36 | + $( '#ffc_receipt_' + mode ).on( 'change', function () { |
| 37 | + loadTemplate( mode, parseInt( $( this ).val(), 10 ) || 0, editors ); |
| 38 | + } ); |
| 39 | + |
| 40 | + // Duplicate the currently-selected template into an editable copy. |
| 41 | + $( '.ffc-receipt-duplicate[data-mode="' + mode + '"]' ).on( 'click', function () { |
| 42 | + duplicateTemplate( mode, editors ); |
| 43 | + } ); |
| 44 | + } ); |
| 45 | + |
| 46 | + // Sync every editor back to its textarea on submit. |
| 47 | + $( '.ffc-receipt-form' ).on( 'submit', function () { |
| 48 | + Object.keys( editors ).forEach( function ( mode ) { |
| 49 | + editors[ mode ].save(); |
| 50 | + } ); |
| 51 | + } ); |
| 52 | + } ); |
| 53 | + |
| 54 | + /** |
| 55 | + * Initialize CodeMirror on a textarea; returns the CodeMirror instance or null. |
| 56 | + * |
| 57 | + * @param {string} textareaId Textarea element id. |
| 58 | + * @return {Object|null} CodeMirror instance. |
| 59 | + */ |
| 60 | + function initEditor( textareaId ) { |
| 61 | + if ( ! cfg.editorSettings || 'undefined' === typeof window.wp || ! window.wp.codeEditor ) { |
| 62 | + return null; |
| 63 | + } |
| 64 | + var editor; |
| 65 | + try { |
| 66 | + editor = window.wp.codeEditor.initialize( textareaId, cfg.editorSettings ); |
| 67 | + } catch ( err ) { |
| 68 | + return null; |
| 69 | + } |
| 70 | + if ( ! editor || ! editor.codemirror ) { |
| 71 | + return null; |
| 72 | + } |
| 73 | + var cm = editor.codemirror; |
| 74 | + cm.on( 'change', function () { |
| 75 | + cm.save(); |
| 76 | + } ); |
| 77 | + return cm; |
| 78 | + } |
| 79 | + |
| 80 | + /** |
| 81 | + * Toggle a CodeMirror instance's read-only state. |
| 82 | + * |
| 83 | + * @param {Object} cm CodeMirror instance. |
| 84 | + * @param {boolean} readonly Whether the editor is read-only. |
| 85 | + * @return {void} |
| 86 | + */ |
| 87 | + function setReadOnly( cm, readonly ) { |
| 88 | + cm.setOption( 'readOnly', readonly ? 'nocursor' : false ); |
| 89 | + $( cm.getWrapperElement() ).toggleClass( 'ffc-editor-readonly', !! readonly ); |
| 90 | + } |
| 91 | + |
| 92 | + /** |
| 93 | + * Reflect an editable/read-only state across the editor, note and duplicate |
| 94 | + * button for a mode. |
| 95 | + * |
| 96 | + * @param {string} mode Scheduling mode. |
| 97 | + * @param {Object} cm CodeMirror instance. |
| 98 | + * @param {boolean} editable Whether the selected template is editable. |
| 99 | + * @return {void} |
| 100 | + */ |
| 101 | + function applyEditable( mode, cm, editable ) { |
| 102 | + setReadOnly( cm, ! editable ); |
| 103 | + var strings = cfg.strings || {}; |
| 104 | + $( '.ffc-receipt-note[data-mode="' + mode + '"]' ).text( |
| 105 | + editable ? ( strings.editableNote || '' ) : ( strings.readonlyNote || '' ) |
| 106 | + ); |
| 107 | + // The duplicate button only makes sense for a read-only selection. |
| 108 | + $( '.ffc-receipt-duplicate[data-mode="' + mode + '"]' ).toggle( ! editable ); |
| 109 | + } |
| 110 | + |
| 111 | + /** |
| 112 | + * Load a template's HTML into the mode's editor via AJAX. |
| 113 | + * |
| 114 | + * @param {string} mode Scheduling mode. |
| 115 | + * @param {number} id Template id (0 = shipped default). |
| 116 | + * @param {Object} editors Map of mode → CodeMirror. |
| 117 | + * @return {void} |
| 118 | + */ |
| 119 | + function loadTemplate( mode, id, editors ) { |
| 120 | + var cm = editors[ mode ]; |
| 121 | + if ( ! cm ) { |
| 122 | + return; |
| 123 | + } |
| 124 | + $.post( cfg.ajaxUrl, { |
| 125 | + action: cfg.loadAction, |
| 126 | + nonce: cfg.nonce, |
| 127 | + id: id, |
| 128 | + } ) |
| 129 | + .done( function ( res ) { |
| 130 | + if ( res && res.success && res.data ) { |
| 131 | + cm.setValue( res.data.html || '' ); |
| 132 | + cm.save(); |
| 133 | + applyEditable( mode, cm, !! res.data.editable ); |
| 134 | + } |
| 135 | + } ); |
| 136 | + } |
| 137 | + |
| 138 | + /** |
| 139 | + * Duplicate the mode's currently-selected template into an editable copy. |
| 140 | + * |
| 141 | + * @param {string} mode Scheduling mode. |
| 142 | + * @param {Object} editors Map of mode → CodeMirror. |
| 143 | + * @return {void} |
| 144 | + */ |
| 145 | + function duplicateTemplate( mode, editors ) { |
| 146 | + var $select = $( '#ffc_receipt_' + mode ); |
| 147 | + var id = parseInt( $select.val(), 10 ) || 0; |
| 148 | + |
| 149 | + $.post( cfg.ajaxUrl, { |
| 150 | + action: cfg.dupAction, |
| 151 | + nonce: cfg.nonce, |
| 152 | + id: id, |
| 153 | + } ) |
| 154 | + .done( function ( res ) { |
| 155 | + if ( ! res || ! res.success || ! res.data ) { |
| 156 | + window.alert( ( cfg.strings && cfg.strings.dupFailed ) || 'Error' ); |
| 157 | + return; |
| 158 | + } |
| 159 | + // Add + select the new editable copy. |
| 160 | + $( '<option>' ) |
| 161 | + .attr( 'value', res.data.id ) |
| 162 | + .attr( 'data-default', '0' ) |
| 163 | + .text( res.data.label ) |
| 164 | + .appendTo( $select ); |
| 165 | + $select.val( String( res.data.id ) ); |
| 166 | + |
| 167 | + var cm = editors[ mode ]; |
| 168 | + if ( cm ) { |
| 169 | + cm.setValue( res.data.html || '' ); |
| 170 | + cm.save(); |
| 171 | + applyEditable( mode, cm, true ); |
| 172 | + } |
| 173 | + } ) |
| 174 | + .fail( function () { |
| 175 | + window.alert( ( cfg.strings && cfg.strings.dupFailed ) || 'Error' ); |
| 176 | + } ); |
| 177 | + } |
| 178 | +} )( jQuery ); |
0 commit comments