| Lines 64-69
          window.onbeforeunload = function () {
      
      
        Link Here | 
        
          | 64 |     } | 64 |     } | 
        
          | 65 | }; | 65 | }; | 
        
          | 66 |  | 66 |  | 
            
              |  |  | 67 | // Add event handlers to any elements with .expand-textarea classes | 
            
              | 68 | // this allows the CodeMirror widget to be displayed | 
            
              | 69 | function addExpandHandler() { | 
            
              | 70 |     // Don't duplicate click event handlers | 
            
              | 71 |     const ev = $._data($('.expand-textarea'), 'events'); | 
            
              | 72 |     if (ev && ev.click) { | 
            
              | 73 |         return; | 
            
              | 74 |     } | 
            
              | 75 |     $(".expand-textarea").on("click", function (e) { | 
            
              | 76 |         e.preventDefault(); | 
            
              | 77 |         $(this).hide(); | 
            
              | 78 |         var target = $(this).data("target"); | 
            
              | 79 |         var syntax = $(this).data("syntax"); | 
            
              | 80 |         $("#collapse_" + target).show(); | 
            
              | 81 |         if (syntax) { | 
            
              | 82 |             var editor = CodeMirror.fromTextArea(document.getElementById("pref_" + target), { | 
            
              | 83 |                 lineNumbers: true, | 
            
              | 84 |                 mode: syntax, | 
            
              | 85 |                 lineWrapping: true, | 
            
              | 86 |                 viewportMargin: Infinity, | 
            
              | 87 |                 gutters: ["CodeMirror-lint-markers"], | 
            
              | 88 |                 lint: true | 
            
              | 89 |             }); | 
            
              | 90 |             editor.on("change", function () { | 
            
              | 91 |                 mark_modified.call($("#pref_" + target)[0]); | 
            
              | 92 |             }); | 
            
              | 93 |             editor.on("blur", function () { | 
            
              | 94 |                 editor.save(); | 
            
              | 95 |             }); | 
            
              | 96 |         } else { | 
            
              | 97 |             $("#pref_" + target).show(); | 
            
              | 98 |         } | 
            
              | 99 |     }); | 
            
              | 100 | } | 
            
              | 101 |  | 
            
              | 102 | // Add event handlers to any elements with .collapse-textarea classes | 
            
              | 103 | // this allows the hiding of the CodeMirror widget | 
            
              | 104 | function addCollapseHandler() { | 
            
              | 105 |     // Don't duplicate click event handlers | 
            
              | 106 |     const ev = $._data($('.collapse-textarea'), 'events'); | 
            
              | 107 |     if (ev && ev.click) { | 
            
              | 108 |         return; | 
            
              | 109 |     } | 
            
              | 110 |     $(".collapse-textarea").on("click", function (e) { | 
            
              | 111 |         e.preventDefault(); | 
            
              | 112 |         $(this).hide(); | 
            
              | 113 |         var target = $(this).data("target"); | 
            
              | 114 |         var syntax = $(this).data("syntax"); | 
            
              | 115 |         $("#expand_" + target).show(); | 
            
              | 116 |         if (syntax) { | 
            
              | 117 |             var editor = $("#pref_" + target).next(".CodeMirror")[0].CodeMirror; | 
            
              | 118 |             editor.toTextArea(); | 
            
              | 119 |         } | 
            
              | 120 |         $("#pref_" + target).hide(); | 
            
              | 121 |     }); | 
            
              | 122 | } | 
            
              | 123 |  | 
            
              | 124 | // Add a handler for any consent delete links | 
            
              | 125 | function addConsentDeleteHandler() { | 
            
              | 126 |     // Don't duplicate click event handlers | 
            
              | 127 |     const ev = $._data($('.consentDelete'), 'events'); | 
            
              | 128 |     if (ev && ev.click) { | 
            
              | 129 |         return; | 
            
              | 130 |     } | 
            
              | 131 |     $('.consentDelete').on('click', function (e) { | 
            
              | 132 |         e.preventDefault(); | 
            
              | 133 |         const target = $(this).data('target'); | 
            
              | 134 |         const proceed = confirm(__('Are you sure you want to delete this consent item?')); | 
            
              | 135 |         if (proceed) { | 
            
              | 136 |             $('#' + target).remove(); | 
            
              | 137 |         } | 
            
              | 138 |     }); | 
            
              | 139 | } | 
            
              | 140 |  | 
        
          | 67 | $( document ).ready( function () { | 141 | $( document ).ready( function () { | 
        
          | 68 |  | 142 |  | 
        
          | 69 |     $("table.preferences").dataTable($.extend(true, {}, dataTablesDefaults, { | 143 |     $("table.preferences").dataTable($.extend(true, {}, dataTablesDefaults, { | 
  
    | Lines 107-150
          $( document ).ready( function () {
      
      
        Link Here | 
        
          | 107 |         return false; | 181 |         return false; | 
        
          | 108 |     }); | 182 |     }); | 
        
          | 109 |  | 183 |  | 
          
            
              | 110 |     $( ".expand-textarea" ).on("click", function(e){ | 184 |     addExpandHandler(); | 
            
              | 111 |         e.preventDefault(); |  |  | 
            
              | 112 |         $(this).hide(); | 
            
              | 113 |         var target = $(this).data("target"); | 
            
              | 114 |         var syntax = $(this).data("syntax"); | 
            
              | 115 |         $("#collapse_" + target ).show(); | 
            
              | 116 |         if( syntax ){ | 
            
              | 117 |             var editor = CodeMirror.fromTextArea( document.getElementById( "pref_" + target ), { | 
            
              | 118 |                 lineNumbers: true, | 
            
              | 119 |                 mode: syntax, | 
            
              | 120 |                 lineWrapping: true, | 
            
              | 121 |                 viewportMargin: Infinity, | 
            
              | 122 |                 gutters: ["CodeMirror-lint-markers"], | 
            
              | 123 |                 lint: true | 
            
              | 124 |             }); | 
            
              | 125 |             editor.on("change", function(){ | 
            
              | 126 |                 mark_modified.call( $("#pref_" + target )[0]); | 
            
              | 127 |             }); | 
            
              | 128 |             editor.on("blur", function(){ | 
            
              | 129 |                 editor.save(); | 
            
              | 130 |             }); | 
            
              | 131 |         } else { | 
            
              | 132 |             $("#pref_" + target ).show(); | 
            
              | 133 |         } | 
            
              | 134 |     }); | 
        
          | 135 |  | 185 |  | 
          
            
              | 136 |     $( ".collapse-textarea" ).on("click", function(e){ | 186 |     addCollapseHandler(); | 
            
              | 137 |         e.preventDefault(); |  |  | 
            
              | 138 |         $(this).hide(); | 
            
              | 139 |         var target = $(this).data("target"); | 
            
              | 140 |         var syntax = $(this).data("syntax"); | 
            
              | 141 |         $("#expand_" + target ).show(); | 
            
              | 142 |         if( syntax ){ | 
            
              | 143 |             var editor = $("#pref_" + target ).next(".CodeMirror")[0].CodeMirror; | 
            
              | 144 |             editor.toTextArea(); | 
            
              | 145 |         } | 
            
              | 146 |         $("#pref_" + target ).hide(); | 
            
              | 147 |     }); | 
        
          | 148 |  | 187 |  | 
        
          | 149 |     $("h3").attr("class", "expanded").attr("title", __("Click to collapse this section")); | 188 |     $("h3").attr("class", "expanded").attr("title", __("Click to collapse this section")); | 
        
          | 150 |     var collapsible = $(".collapsed,.expanded"); | 189 |     var collapsible = $(".collapsed,.expanded"); | 
  
    | Lines 262-289
          $( document ).ready( function () {
      
      
        Link Here | 
        
          | 262 |         }).appendTo("#prefModalForm"); | 301 |         }).appendTo("#prefModalForm"); | 
        
          | 263 |  | 302 |  | 
        
          | 264 |         $("#saveModalPrefs").data("target", this.id ); | 303 |         $("#saveModalPrefs").data("target", this.id ); | 
            
              |  |  | 304 |         $("#saveModalPrefs").data("type", "modalselect" ); | 
        
          | 265 |         $("#prefModalLabel").text( pref_name ); | 305 |         $("#prefModalLabel").text( pref_name ); | 
        
          | 266 |         $("#prefModal").modal("show"); | 306 |         $("#prefModal").modal("show"); | 
        
          | 267 |     }); | 307 |     }); | 
        
          | 268 |  | 308 |  | 
          
            
              | 269 |     $(".modaljs").on("click", function () { | 309 |     // Initialise the content of our modal, using the function | 
            
              | 270 |         console.log('modaljs clicked'); | 310 |     // specified in the data-initiator attribute | 
            
              |  |  | 311 |     $('.modaljs').on('click', function () { | 
            
              | 312 |         const init = $(this).data('initiator'); | 
            
              | 313 |         if (init) { | 
            
              | 314 |             window[init](this); | 
            
              | 315 |             $("#prefModal").modal("show"); | 
            
              | 316 |         } | 
        
          | 271 |     }); | 317 |     }); | 
        
          | 272 |  | 318 |  | 
          
            
              | 273 |     $("#saveModalPrefs").on("click", function(){ | 319 |     // Initialise the content of our modal, if we are dealing | 
            
              | 274 |         var formfieldid = $("#" + $(this).data("target") ); | 320 |     // with a modalselect modal | 
            
              |  |  | 321 |     function prepareModalSelect(formfieldid) { | 
        
          | 275 |         var prefs = []; | 322 |         var prefs = []; | 
        
          | 276 |         $("#prefModal input[type='checkbox']").each(function(){ | 323 |         $("#prefModal input[type='checkbox']").each(function(){ | 
        
          | 277 |             if( $(this).prop("checked") ){ | 324 |             if( $(this).prop("checked") ){ | 
        
          | 278 |                 prefs.push( this.value ); | 325 |                 prefs.push( this.value ); | 
        
          | 279 |             } | 326 |             } | 
        
          | 280 |         }); | 327 |         }); | 
            
              |  |  | 328 |         return prefs.join("|"); | 
            
              | 329 |     } | 
            
              | 330 |  | 
            
              | 331 |     // Return a checkbox with an appropriate checked state | 
            
              | 332 |     function checkBox(id, className, state) { | 
            
              | 333 |         return state ? | 
            
              | 334 |             '<input id="' + id + '" type="checkbox" class="' + className + '" checked>' : | 
            
              | 335 |             '<input id="' + id + '" type="checkbox" class="' + className + '">'; | 
            
              | 336 |     } | 
            
              | 337 |  | 
            
              | 338 |     // Create a consentJS item, correctly populated | 
            
              | 339 |     function createConsentJSItem(item, idx) { | 
            
              | 340 |         const id = 'ConsentJS_' + idx; | 
            
              | 341 |         const code = item.code && item.code.length > 0 ? atob(item.code) : ''; | 
            
              | 342 |         const itemId = item.id && item.id.length > 0 ? item.id : ''; | 
            
              | 343 |         return '<div id="' + id + '" class="consentJSItem" data-id="' + itemId + '"><div class="consentRow"><div class="consentItem"><label class="required" for="name_' + id + '">' + __('Name') + ':</label><input id="name_' + id + '" class="metaName" type="text" value="' + item.name + '"><span class="required">' + __('Required')  + '</span></div><div class="consentItem"><label class="required" for="description_' + id + '">' + __('Description') + ':</label><input id="description_' + id + '" class="metaDescription" type="text" value="' + item.description + '"><span class="required">' + __('Required') + '</span></div><div class="consentItem"><label for="opacConsent_' + id + '">' + __('Requires consent in OPAC') + ':</label>' + checkBox('opacConsent_' + id, 'opacConsent', item.opacConsent) + '</div><div class="consentItem"><label for="staffConsent_' + id + '">' + __('Requires consent in staff interface') + ':</label>' + checkBox('staffConsent_' + id, 'staffConsent', item.staffConsent) + '</div></div><div class="consentRow codeRow"><textarea style="display:none;" id="pref_' + id + '" class="preference preference-code codemirror" rows="10" cols="40">' + code + '</textarea><div><a id="expand_' + id + '" class="expand-textarea" data-target="' + id + '" data-syntax="javascript" href="#">' + __('Click to expand') + '</a><a id="collapse_' + id + '" class="collapse-textarea" data-target="' + id + '" data-syntax="javascript" href="#" style="display:none">' + __('Click to collapse') + '</a></div></div><a class="consentDelete" data-target="' + id + '" href="#">Delete</a></div>'; | 
            
              | 344 |     } | 
            
              | 345 |  | 
            
              | 346 |     // Return the markup for all consentJS items concatenated | 
            
              | 347 |     function populateConsentMarkup(items) { | 
            
              | 348 |         return items.reduce(function (acc, current, idx) { | 
            
              | 349 |             return acc + createConsentJSItem(current, idx); | 
            
              | 350 |         }, ''); | 
            
              | 351 |     } | 
            
              | 352 |  | 
            
              | 353 |     // Return the markup for a validation warning | 
            
              | 354 |     function populateValidationWarning() { | 
            
              | 355 |         return '<div id="consentJSWarning" class="error" style="display:none;">' + __('You must complete all fields') + '</div>'; | 
            
              | 356 |     } | 
        
          | 281 |  | 357 |  | 
          
            
              | 282 |         formfieldid.val( prefs.join("|") ) | 358 |     // Return a new, empty consent item | 
            
              | 283 |             .addClass("modified"); | 359 |     function emptyConsentItem() { | 
            
              | 284 |         mark_modified.call( formfieldid ); | 360 |         return { | 
            
              | 285 |         KOHA.Preferences.Save( formfieldid.closest("form") ); | 361 |             name: '', | 
            
              | 286 |         $("#prefModal").modal("hide"); | 362 |             description: '', | 
            
              |  |  | 363 |             code: '', | 
            
              | 364 |             consentInOpac: false, | 
            
              | 365 |             consentInStaff: false | 
            
              | 366 |         }; | 
            
              | 367 |     } | 
            
              | 368 |  | 
            
              | 369 |     // Add the handler for a new empty consent item | 
            
              | 370 |     function addNewHandler() { | 
            
              | 371 |         $("#consentJSAddNew").on("click", function (e) { | 
            
              | 372 |             e.preventDefault(); | 
            
              | 373 |             const currentLen = $('.consentJSItem').length; | 
            
              | 374 |             const newItem = emptyConsentItem(); | 
            
              | 375 |             const markup = createConsentJSItem(newItem, currentLen); | 
            
              | 376 |             $('#prefModal .modal-body #consentJSItems').append($(markup)); | 
            
              | 377 |             addExpandHandler(); | 
            
              | 378 |             addCollapseHandler(); | 
            
              | 379 |             addConsentDeleteHandler(); | 
            
              | 380 |         }); | 
            
              | 381 |     } | 
            
              | 382 |  | 
            
              | 383 |     // Populate the consentJS modal, we also initialise any | 
            
              | 384 |     // event handlers that are required. This function is added | 
            
              | 385 |     // to the window object so we can call it if we are passed it's name | 
            
              | 386 |     // as a data-initiator attribute | 
            
              | 387 |     // (e.g.) | 
            
              | 388 |     // const f = 'populateConsentJS'; | 
            
              | 389 |     // window[f](); | 
            
              | 390 |     window.populateConsentJS = function(el) { | 
            
              | 391 |         let items = []; | 
            
              | 392 |         let decoded = ''; | 
            
              | 393 |         if (el.value && el.value.length > 0) { | 
            
              | 394 |             try { | 
            
              | 395 |                 decoded = atob(el.value); | 
            
              | 396 |             } catch (err) { | 
            
              | 397 |                 throw (__( | 
            
              | 398 |                     'Unable to Base64 decode value stored in ConsentJS syspref: ' + | 
            
              | 399 |                     err.message | 
            
              | 400 |                 )); | 
            
              | 401 |             } | 
            
              | 402 |             try { | 
            
              | 403 |                 items = JSON.parse(decoded); | 
            
              | 404 |             } catch (err) { | 
            
              | 405 |                 throw (__( | 
            
              | 406 |                     'Unable to JSON parse decoded value stored in ConsentJS syspref: ' + | 
            
              | 407 |                     err.message | 
            
              | 408 |                 )); | 
            
              | 409 |             } | 
            
              | 410 |         } | 
            
              | 411 |         const markup = populateConsentMarkup(items); | 
            
              | 412 |         const validationWarning = populateValidationWarning(); | 
            
              | 413 |         const pref_name = el.id.replace(/pref_/, ''); | 
            
              | 414 |         $('#saveModalPrefs').data('target', el.id); | 
            
              | 415 |         $('#prefModalLabel').text( pref_name ); | 
            
              | 416 |         $('#prefModal .modal-body').html($('<div id="consentJSItems">' + validationWarning + markup + '</div><div><a href="#" id="consentJSAddNew">' + __('Add new code') + '</a></div>')); | 
            
              | 417 |         addExpandHandler(); | 
            
              | 418 |         addCollapseHandler(); | 
            
              | 419 |         addNewHandler(); | 
            
              | 420 |         addConsentDeleteHandler(); | 
            
              | 421 |     } | 
            
              | 422 |  | 
            
              | 423 |     // Prepare the data in the UI for sending back as a syspref. | 
            
              | 424 |     // We validate that everything is what we expect. This function is added | 
            
              | 425 |     // to the window object so we can call it if we are passed it's name | 
            
              | 426 |     // as a data-initiator attribute | 
            
              | 427 |     // e.g. | 
            
              | 428 |     // const f = 'prepareConsentJS'; | 
            
              | 429 |     // window[f](); | 
            
              | 430 |     window.prepareConsentJS = function () { | 
            
              | 431 |         const items = $('.consentJSItem'); | 
            
              | 432 |         const invalid = []; | 
            
              | 433 |         const valid = []; | 
            
              | 434 |         items.each(function () { | 
            
              | 435 |             const id = $(this).data('id').length > 0 ? | 
            
              | 436 |                 $(this).data('id') : | 
            
              | 437 |                 '_' + Math.random().toString(36).substr(2, 9); | 
            
              | 438 |             const name = $(this).find('.metaName').val(); | 
            
              | 439 |             const desc = $(this).find('.metaDescription').val(); | 
            
              | 440 |             const opacConsent = $(this).find('.opacConsent').is(':checked') | 
            
              | 441 |             const staffConsent = $(this).find('.staffConsent').is(':checked'); | 
            
              | 442 |             const code = $(this).find('.preference-code').val(); | 
            
              | 443 |             // If the name, description and code are empty, then they've | 
            
              | 444 |             // added a new entry, but not filled it in, we can skip it | 
            
              | 445 |             if (name.length === 0 && desc.length === 0 && code.length === 0) { | 
            
              | 446 |                 return; | 
            
              | 447 |             } | 
            
              | 448 |             // They've filled in at least some info | 
            
              | 449 |             if ( | 
            
              | 450 |                 (name.length === 0) || | 
            
              | 451 |                 (desc.length === 0) || | 
            
              | 452 |                 (code.length === 0) | 
            
              | 453 |             ) { | 
            
              | 454 |                 invalid.push(this); | 
            
              | 455 |             } else { | 
            
              | 456 |                 const obj = { | 
            
              | 457 |                     id: id, | 
            
              | 458 |                     name: name, | 
            
              | 459 |                     description: desc, | 
            
              | 460 |                     opacConsent: opacConsent, | 
            
              | 461 |                     staffConsent: staffConsent, | 
            
              | 462 |                     code: btoa(code) | 
            
              | 463 |                 } | 
            
              | 464 |                 valid.push(obj); | 
            
              | 465 |             } | 
            
              | 466 |         }); | 
            
              | 467 |         // We failed validation | 
            
              | 468 |         if (invalid.length > 0) { | 
            
              | 469 |             $('#consentJSWarning').show(); | 
            
              | 470 |             return false; | 
            
              | 471 |         } | 
            
              | 472 |         $('#consentJSWarning').hide(); | 
            
              | 473 |         if (valid.length === 0) { | 
            
              | 474 |             return ''; | 
            
              | 475 |         } | 
            
              | 476 |         const json = JSON.stringify(valid); | 
            
              | 477 |         const base64 = btoa(json); | 
            
              | 478 |         return base64; | 
            
              | 479 |     } | 
            
              | 480 |  | 
            
              | 481 |     $("#saveModalPrefs").on("click", function(){ | 
            
              | 482 |         var formfieldid = $("#" + $(this).data("target")); | 
            
              | 483 |         let finalString = ""; | 
            
              | 484 |         if ($(this).data("type") == "modalselect") { | 
            
              | 485 |             finalString = prepareModalSelect(formfieldid); | 
            
              | 486 |         } else { | 
            
              | 487 |             const processor = $(".modaljs").data("processor"); | 
            
              | 488 |             finalString = window[processor](); | 
            
              | 489 |         } | 
            
              | 490 |         // A processor can return false if any of the submitted | 
            
              | 491 |         // data has failed validation | 
            
              | 492 |         if (finalString !== false) { | 
            
              | 493 |             formfieldid.val(finalString).addClass("modified"); | 
            
              | 494 |             mark_modified.call( formfieldid ); | 
            
              | 495 |             KOHA.Preferences.Save( formfieldid.closest("form") ); | 
            
              | 496 |             $("#prefModal").modal("hide"); | 
            
              | 497 |         } | 
        
          | 287 |     }); | 498 |     }); | 
        
          | 288 |  | 499 |  | 
        
          | 289 |     $("#prefModal").on("hide.bs.modal", function(){ | 500 |     $("#prefModal").on("hide.bs.modal", function(){ | 
            
              | 290 | -  |  |  |