Bug 29137: Add new syspref to disable AV creation within the cataloguing module
[koha.git] / koha-tmpl / intranet-tmpl / prog / js / cataloging.js
1 /* global __ */
2 /* exported openAuth ExpandField CloneField CloneSubfield UnCloneField CloneItemSubfield CheckMandatorySubfields */
3
4 /*
5  * Unified file for catalogue edition
6  */
7
8 /* Functions developed for addbiblio.tt and authorities.tt */
9
10 // returns the fieldcode based upon tag div id
11 function getFieldCode(tagDivId){
12     // format : tag_<tagnumber>_...
13     return tagDivId.substr(3+1,3);
14 }
15
16 //returns the field and subfieldcode based upon subfield div id
17 function getFieldAndSubfieldCode(subfieldDivId){
18     // format : subfield<tagnumber><subfieldnumber>...
19     return subfieldDivId.substr(8,3+1);
20 }
21
22 //returns the subfieldcode based upon subfieldid writing
23 function getSubfieldCode(tagsubfieldid){
24     // 3 : tag +3 : tagnumber +4 : number of _ +8 subfield -1 begins at 0
25     return tagsubfieldid.substr(3+3+4+8-1,1);
26 }
27
28 // Take the base of tagsubfield information (removing the subfieldcodes and subfieldindexes)
29 // returns the filter
30 function getTagInputnameFilter(tagsubfieldid){
31     var tagsubfield=tagsubfieldid.substr(0,tagsubfieldid.lastIndexOf("_"));
32     var tagcode=tagsubfield.substr(tagsubfield.lastIndexOf("_"));
33     tagsubfield=tagsubfield.substr(0,tagsubfield.lastIndexOf("_"));
34     tagsubfield=tagsubfield.substr(0,tagsubfield.lastIndexOf("_"));
35     tagsubfield=tagsubfield+"_."+tagcode;
36     return tagsubfield;
37 }
38
39 // if source is "auth", we are editing an authority otherwise it is a biblio
40 function openAuth(tagsubfieldid,authtype,source) {
41     // let's take the base of tagsubfield information (removing the indexes and the codes
42     var element=document.getElementById(tagsubfieldid);
43     var tagsubfield=getTagInputnameFilter(tagsubfieldid);
44     var elementsubfcode=getSubfieldCode(element.name);
45     var mainmainstring=element.value;
46     var mainstring = new Array();
47
48     var inputs = element.closest('ul').getElementsByTagName('input');
49     for (var myindex =0; myindex<inputs.length;myindex++){
50         if (inputs[myindex].name && inputs[myindex].name.match(tagsubfield)){
51             var subfieldcode=getSubfieldCode(inputs[myindex].name);
52             if (isNaN(parseInt(subfieldcode)) && inputs[myindex].value != "" && subfieldcode!=elementsubfcode){
53                 mainstring.push(inputs[myindex].value);
54             }
55         }
56     }
57     mainstring = mainstring.join(' ');
58     window.open("../authorities/auth_finder.pl?source="+source+"&authtypecode="+authtype+"&index="+tagsubfieldid+"&value_mainstr="+encodeURIComponent(mainmainstring)+"&value_main="+encodeURIComponent(mainstring), "_blank",'width=700,height=550,toolbar=false,scrollbars=yes');
59 }
60
61 function ExpandField(index) {
62     var original = document.getElementById(index); //original <li>
63     var lis = original.getElementsByTagName('li');
64     for(var i=0,lislen = lis.length ; i<lislen ; i++){   // foreach li
65         if(lis[i].hasAttribute('id') == 0 ) {continue; } // li element is specific to Select2
66         if(lis[i].getAttribute('id').match(/^subfield/)){  // if it s a subfield
67             if (!lis[i].style.display) {
68                 // first time => show all subfields
69                 lis[i].style.display = 'flex';
70             } else if (lis[i].style.display == 'none') {
71                 // show
72                 lis[i].style.display = 'flex';
73             } else {
74                 // hide
75                 lis[i].style.display = 'none';
76             }
77         }
78     }
79 }
80
81 var current_select2;
82 var Select2Utils = {
83     removeSelect2: function(selects) {
84         if ($.fn.select2) {
85             $(selects).each(function(){
86                 $(this).select2('destroy');
87             });
88         }
89     },
90
91     initSelect2: function(selects) {
92         if ($.fn.select2) {
93             if ( window.auth_values_creation === undefined || ! auth_values_creation ) {
94                 $(selects).select2().on("select2:clear", function () {
95                     $(this).on("select2:opening.cancelOpen", function (evt) {
96                         evt.preventDefault();
97                         $(this).off("select2:opening.cancelOpen");
98                     });
99                 });
100             } else {
101                 $(selects).each(function(){
102                     if ( !$(this).data("category") ) {
103                         $(this).select2().on("select2:clear", function () {
104                             $(this).on("select2:opening.cancelOpen", function (evt) {
105                                 evt.preventDefault();
106                                 $(this).off("select2:opening.cancelOpen");
107                             });
108                         });
109                     } else {
110                         $(this).select2({
111                             tags: true,
112                             createTag: function (tag) {
113                                 return {
114                                     id: tag.term,
115                                     text: tag.term,
116                                     newTag: true
117                                 };
118                             },
119                             templateResult: function(state) {
120                                 if (state.newTag) {
121                                     return state.text + " " + __("(select to create)");
122                                 }
123                                 return state.text;
124                             }
125                         }).on("select2:select", function(e) {
126                             if(e.params.data.newTag){
127                                 current_select2 = this;
128                                 var category = $(this).data("category");
129                                 $("#avCreate #new_av_category").html(category);
130                                 $("#avCreate input[name='category']").val(category);
131                                 $("#avCreate input[name='value']").val('');
132                                 $("#avCreate input[name='description']").val(e.params.data.text);
133
134                                 $(this).val($(this).find("option:first").val()).trigger('change');
135                                 $('#avCreate').modal({show:true});
136                             }
137                         }).on("select2:clear", function () {
138                             $(this).on("select2:opening.cancelOpen", function (evt) {
139                                 evt.preventDefault();
140
141                                 $(this).off("select2:opening.cancelOpen");
142                             });
143                         });
144                     }
145                 });
146             }
147         }
148     }
149 };
150
151 /**
152  * To clone a field
153  * @param hideMarc '0' for false, '1' for true
154  * @param advancedMARCEditor '0' for false, '1' for true
155  */
156 function CloneField(index, hideMarc, advancedMARCEditor) {
157     var original = document.getElementById(index); //original <li>
158     Select2Utils.removeSelect2($(original).find('select'));
159
160     var clone = original.cloneNode(true);
161     var new_key = CreateKey();
162     var new_id  = original.getAttribute('id')+new_key;
163
164     clone.setAttribute('id',new_id); // setting a new id for the parent li
165
166     var divs = Array.from(clone.getElementsByTagName('li')).concat(Array.from(clone.getElementsByTagName('div')));
167
168     // if hide_marc, indicators are hidden fields
169     // setting a new name for the new indicator
170     for(var i=0; i < 2; i++) {
171         var indicator = clone.getElementsByTagName('input')[i];
172         indicator.setAttribute('name',indicator.getAttribute('name')+new_key);
173     }
174
175     // settings all subfields
176     var divslen = divs.length;
177     for( i=0; i < divslen ; i++ ){      // foreach div/li
178         if( divs[i].getAttribute("id") && divs[i].getAttribute("id").match(/^subfield/)){  // if it s a subfield
179
180             // set the attribute for the new 'li' subfields
181             divs[i].setAttribute('id',divs[i].getAttribute('id')+new_key);
182
183             var inputs   = divs[i].getElementsByTagName('input');
184             var id_input = "";
185             var olddiv;
186             var oldcontrol;
187
188             for( j = 0 ; j < inputs.length ; j++ ) {
189                 if(inputs[j].getAttribute("id") && inputs[j].getAttribute("id").match(/^tag_/) ){
190                     inputs[j].value = "";
191
192                     //Remove the color added by the automatic linker
193                     $(inputs[j]).removeClass("matching_authority_field no_matching_authority_field");
194                 }
195             }
196             var textareas = divs[i].getElementsByTagName('textarea');
197             for( j = 0 ; j < textareas.length ; j++ ) {
198                 if(textareas[j].getAttribute("id") && textareas[j].getAttribute("id").match(/^tag_/) ){
199                     textareas[j].value = "";
200                 }
201             }
202             // Remove the status icons added by the automatic linker
203             $(divs[i]).find('.subfield_status').remove();
204             if( inputs.length > 0 ){
205                 inputs[0].setAttribute('id',inputs[0].getAttribute('id')+new_key);
206                 inputs[0].setAttribute('name',inputs[0].getAttribute('name')+new_key);
207
208                 try {
209                     id_input = inputs[1].getAttribute('id')+new_key;
210                     inputs[1].setAttribute('id',id_input);
211                     inputs[1].setAttribute('name',inputs[1].getAttribute('name')+new_key);
212                 } catch(e) {
213                     try{ // it s a select if it is not an input
214                         var selects = divs[i].getElementsByTagName('select');
215                         id_input = selects[0].getAttribute('id')+new_key;
216                         selects[0].setAttribute('id',id_input);
217                         selects[0].setAttribute('name',selects[0].getAttribute('name')+new_key);
218                     }catch(e2){ // it is a textarea if it s not a select or an input
219                         var textareas = divs[i].getElementsByTagName('textarea');
220                         if( textareas.length > 0 ){
221                             id_input = textareas[0].getAttribute('id')+new_key;
222                             textareas[0].setAttribute('id',id_input);
223                             textareas[0].setAttribute('name',textareas[0].getAttribute('name')+new_key);
224                         }
225                     }
226                 }
227                 if( $(inputs[1]).hasClass('framework_plugin') ) {
228                     olddiv= original.getElementsByTagName('li')[i];
229                     oldcontrol= olddiv.getElementsByTagName('input')[1];
230                     AddEventHandlers( oldcontrol,inputs[1],id_input );
231                 }
232             }
233             // when cloning a subfield, re set its label too.
234             try {
235                 var labels = divs[i].getElementsByTagName('label');
236                 labels[0].setAttribute('for', id_input);
237             }
238             catch(e) {
239                 // do nothing if label does not exist.
240             }
241
242             // setting its '+' and '-' buttons
243             try {
244                 var anchors = divs[i].getElementsByTagName('a');
245                 for (var j = 0; j < anchors.length; j++) {
246                     if(anchors[j].getAttribute('class') == 'buttonPlus'){
247                         anchors[j].setAttribute('onclick',"CloneSubfield('" + divs[i].getAttribute('id') + "','" + advancedMARCEditor + "'); return false;");
248                     } else if (anchors[j].getAttribute('class') == 'buttonMinus') {
249                         anchors[j].setAttribute('onclick',"UnCloneField('" + divs[i].getAttribute('id') + "'); return false;");
250                     }
251                 }
252             }
253             catch(e){
254                 // do nothig if ButtonPlus & CloneButtonPlus don t exist.
255             }
256
257             // button ...
258             var spans=0;
259             try {
260                 spans = divs[i].getElementsByTagName('a');
261             } catch(e) {
262                 // no spans
263             }
264             if(spans){
265                 var buttonDot;
266                 if(!CloneButtonPlus){ // it s impossible to have  + ... (buttonDot AND buttonPlus)
267                     buttonDot = spans[0];
268                     if(buttonDot){
269                         // 2 possibilities :
270                         try{
271                             if( $(buttonDot).hasClass('framework_plugin') ) {
272                                 olddiv= original.getElementsByTagName('li')[i];
273                                 oldcontrol= olddiv.getElementsByTagName('a')[0];
274                                 AddEventHandlers(oldcontrol,buttonDot,id_input);
275                             }
276                             try {
277                                 // do not copy the script section.
278                                 var script = spans[0].getElementsByTagName('script')[0];
279                                 spans[0].removeChild(script);
280                             } catch(e) {
281                                 // do nothing if there is no script
282                             }
283                         } catch(e){
284                             //
285                         }
286                     }
287                 }
288             }
289
290         } else { // it's a indicator div
291             if ( divs[i].getAttribute("id") && divs[i].getAttribute('id').match(/^div_indicator/)) {
292
293                 // setting a new id for the indicator div
294                 divs[i].setAttribute('id',divs[i].getAttribute('id')+new_key);
295
296                 inputs = divs[i].getElementsByTagName('input');
297                 inputs[0].setAttribute('id',inputs[0].getAttribute('id')+new_key);
298                 inputs[1].setAttribute('id',inputs[1].getAttribute('id')+new_key);
299
300                 var CloneButtonPlus;
301                 try {
302                     anchors = divs[i].getElementsByTagName('a');
303                     for ( j = 0; j < anchors.length; j++) {
304                         if (anchors[j].getAttribute('class') == 'buttonPlus') {
305                             anchors[j].setAttribute('onclick',"CloneField('" + new_id + "','" + hideMarc + "','" + advancedMARCEditor + "'); return false;");
306                         } else if (anchors[j].getAttribute('class') == 'buttonMinus') {
307                             anchors[j].setAttribute('onclick',"UnCloneField('" + new_id + "'); return false;");
308                         } else if (anchors[j].getAttribute('class') == 'expandfield') {
309                             anchors[j].setAttribute('onclick',"ExpandField('" + new_id + "'); return false;");
310                         }
311                     }
312                 }
313                 catch(e){
314                     // do nothig CloneButtonPlus doesn't exist.
315                 }
316
317             }
318         }
319     }
320
321     // insert this line on the page
322     original.parentNode.insertBefore(clone,original.nextSibling);
323
324     $("ul.sortable_subfield", clone).sortable();
325
326     Select2Utils.initSelect2($(original).find('select'));
327     Select2Utils.initSelect2($(clone).find('select'));
328 }
329
330
331 /**
332  * To clone a subfield
333  * @param index
334  * @param advancedMARCEditor '0' for false, '1' for true
335  */
336 function CloneSubfield(index, advancedMARCEditor){
337     var original = document.getElementById(index); //original <div>
338     Select2Utils.removeSelect2($(original).find('select'));
339     var clone = original.cloneNode(true);
340     var new_key = CreateKey();
341     // set the attribute for the new 'li' subfields
342     var inputs     = clone.getElementsByTagName('input');
343     var selects    = clone.getElementsByTagName('select');
344     var textareas  = clone.getElementsByTagName('textarea');
345     var linkid;
346     var oldcontrol;
347
348     // input
349     var id_input = "";
350     for(var i=0,len=inputs.length; i<len ; i++ ){
351         id_input = inputs[i].getAttribute('id')+new_key;
352         inputs[i].setAttribute('id',id_input);
353         inputs[i].setAttribute('name',inputs[i].getAttribute('name')+new_key);
354         if(inputs[i].getAttribute("id") && inputs[i].getAttribute("id").match(/^tag_/) ){
355             inputs[i].value = "";
356         }
357         linkid = id_input;
358     }
359
360     // Plugin input
361     if( $(inputs[1]).hasClass('framework_plugin') ) {
362         oldcontrol= original.getElementsByTagName('input')[1];
363         AddEventHandlers( oldcontrol, inputs[1], linkid );
364     }
365
366     // select
367     for(i=0,len=selects.length; i<len ; i++ ){
368         id_input = selects[i].getAttribute('id')+new_key;
369         selects[i].setAttribute('id',selects[i].getAttribute('id')+new_key);
370         selects[i].setAttribute('name',selects[i].getAttribute('name')+new_key);
371         linkid = id_input;
372     }
373
374     // textarea
375     for( i=0,len=textareas.length; i<len ; i++ ){
376         id_input = textareas[i].getAttribute('id')+new_key;
377         textareas[i].setAttribute('id',textareas[i].getAttribute('id')+new_key);
378         textareas[i].setAttribute('name',textareas[i].getAttribute('name')+new_key);
379         if(textareas[i].getAttribute("id") && textareas[i].getAttribute("id").match(/^tag_/) ){
380             textareas[i].value = "";
381         }
382         linkid = id_input;
383     }
384
385     // Handle click event on buttonDot for plugin
386     var links  = clone.getElementsByTagName('a');
387     if( $(links[0]).hasClass('framework_plugin') ) {
388         oldcontrol= original.getElementsByTagName('a')[0];
389         AddEventHandlers( oldcontrol, links[0], linkid );
390     }
391
392     if(advancedMARCEditor == '0') {
393         // when cloning a subfield, reset its label too.
394         var label = clone.getElementsByTagName('label')[0];
395         if( label ){
396             label.setAttribute('for',id_input);
397         }
398     }
399
400     // setting a new id for the parent div
401     var new_id  = original.getAttribute('id')+new_key;
402     clone.setAttribute('id',new_id);
403
404     try {
405         var anchors = clone.getElementsByTagName('a');
406         if(anchors.length){
407             for( i = 0 ,len = anchors.length ; i < len ; i++){
408                 if(anchors[i].getAttribute('class') == 'buttonPlus'){
409                     anchors[i].setAttribute('onclick',"CloneSubfield('" + new_id + "','" + advancedMARCEditor + "'); return false;");
410                 } else if (anchors[i].getAttribute('class') == 'buttonMinus') {
411                     anchors[i].setAttribute('onclick',"UnCloneField('" + new_id + "'); return false;");
412                 }
413             }
414         }
415     }
416     catch(e){
417         // do nothig if ButtonPlus & CloneButtonPlus don't exist.
418     }
419     // insert this line on the page
420     original.parentNode.insertBefore(clone,original.nextSibling);
421
422     //Restablish select2 for the cloned elements.
423     Select2Utils.initSelect2($(original).find('select'));
424     Select2Utils.initSelect2($(clone).find('select'));
425
426     // delete data of cloned subfield
427     clone.querySelectorAll('input.input_marceditor').value = "";
428 }
429
430 function AddEventHandlers (oldcontrol, newcontrol, newinputid ) {
431 // This function is a helper for CloneField and CloneSubfield.
432 // It adds the event handlers from oldcontrol to newcontrol.
433 // newinputid is the id attribute of the cloned controlling input field
434 // Note: This code depends on the jQuery data for events; this structure
435 // is moved to _data as of jQuery 1.8.
436     var ev= $(oldcontrol).data('events');
437     if(typeof ev != 'undefined') {
438         $.each(ev, function(prop,val) {
439             $.each(val, function(prop2,val2) {
440                 $(newcontrol).off( val2.type );
441                 $(newcontrol).on( val2.type, {id: newinputid}, val2.handler );
442             });
443         });
444     }
445 }
446
447 /**
448  * This function removes or clears unwanted subfields
449  */
450 function UnCloneField(index) {
451     var original = document.getElementById(index);
452     var canUnclone = false;
453     if ($(original).hasClass("tag")) {
454         // unclone a field, check if there will remain one field
455         var fieldCode = getFieldCode(index);
456         // tag divs with id begining with original field code
457         var cloneFields = $('.tag[id^="tag_'+fieldCode+'"]');
458         if (cloneFields.length > 1) {
459             canUnclone = true;
460         }
461     } else {
462         // unclone a subfield, check if there will remain one subfield
463         var subfieldCode = getFieldAndSubfieldCode(index);
464         // subfield divs of same field with id begining with original field and subfield field code
465         var cloneSubfields = $(original).parent().children('.subfield_line[id^="subfield'+subfieldCode+'"]');
466         if (cloneSubfields.length > 1) {
467             canUnclone = true;
468         }
469     }
470     if (canUnclone) {
471         // remove clone
472         original.parentNode.removeChild(original);
473     } else {
474         // clear inputs, but don't delete
475         $(":input.input_marceditor", original).each(function(){
476             // thanks to http://www.learningjquery.com/2007/08/clearing-form-data for
477             // hint about clearing selects correctly
478             var type = this.type;
479             var tag = this.tagName.toLowerCase();
480             if (type == 'text' || type == 'password' || tag == 'textarea') {
481                 this.value = "";
482             } else if (type == 'checkbox' || type == 'radio') {
483                 this.checked = false;
484             } else if (tag == 'select') {
485                 this.selectedIndex = -1;
486                 // required for Select2 to be able to update its control
487                 $(this).trigger('change');
488             }
489         });
490         $(":input.indicator", original).val("");
491     }
492 }
493
494 /**
495  * This function create a random number
496  */
497 function CreateKey(){
498     return parseInt(Math.random() * 100000);
499 }
500
501 /* Functions developed for additem.tt */
502
503 /**
504  * To clone a subfield.<br>
505  * @param original subfield div to clone
506  */
507 function CloneItemSubfield(original){
508     Select2Utils.removeSelect2($(original).find('select'));
509     var clone = original.cloneNode(true);
510     var new_key = CreateKey();
511
512     // set the attribute for the new 'li' subfields
513     var inputs     = clone.getElementsByTagName('input');
514     var selects    = clone.getElementsByTagName('select');
515     var textareas  = clone.getElementsByTagName('textarea');
516
517     // input (except hidden type)
518     var id_input = "";
519     for(var i=0,len=inputs.length; i<len ; i++ ){
520         if (inputs[i].getAttribute('type') != 'hidden') {
521             id_input = inputs[i].getAttribute('id')+new_key;
522             inputs[i].setAttribute('id',id_input);
523         }
524     }
525
526     // select
527     for( i=0,len=selects.length; i<len ; i++ ){
528         id_input = selects[i].getAttribute('id')+new_key;
529         selects[i].setAttribute('id',selects[i].getAttribute('id')+new_key);
530     }
531
532     // textarea
533     for( i=0,len=textareas.length; i<len ; i++ ){
534         id_input = textareas[i].getAttribute('id')+new_key;
535         textareas[i].setAttribute('id',textareas[i].getAttribute('id')+new_key);
536     }
537
538     // when cloning a subfield, reset its label too.
539     var label = clone.getElementsByTagName('label')[0];
540     label.setAttribute('for',id_input);
541
542     // setting a new if for the parent div
543     var new_id = original.getAttribute('id')+new_key;
544     clone.setAttribute('id',new_id);
545
546     // insert this line on the page
547     original.parentNode.insertBefore(clone,original.nextSibling);
548     Select2Utils.initSelect2($(original).find('select'));
549     Select2Utils.initSelect2($(clone).find('select'));
550 }
551
552 /**
553  * Check mandatory subfields of a cataloging form and adds <code>missing</code> class to those who are empty.<br>
554  * @param p the parent object of subfields to check
555  * @return the number of empty mandatory subfields
556  */
557 function CheckMandatorySubfields(p){
558     var total = 0;
559     $(p).find(".subfield_line input[name='mandatory'][value='1']").each(function(){
560         var editor = $(this).siblings("[name='field_value']");
561         if (!editor.val()) {
562             editor.addClass("missing");
563             total++;
564         }
565     });
566     return total;
567 }
568
569 function CheckImportantSubfields(p){
570     var total = 0;
571     $(p).find(".subfield_line input[name='important'][value='1']").each(function(i){
572         var editor = $(this).siblings("[name='field_value']");
573         if (!editor.val()) {
574             editor.addClass("missing");
575             total++;
576         }
577     });
578     return total;
579 }
580
581 $(document).ready(function() {
582     $("input.input_marceditor, input.indicator").addClass('noEnterSubmit');
583     $(document).ajaxSuccess(function() {
584         $("input.input_marceditor, input.indicator").addClass('noEnterSubmit');
585     });
586
587     if ( window.editor === undefined ) { // TODO This does not work with the advanced editor
588         Select2Utils.initSelect2($('.subfield_line select[data-category=""]')); // branches, itemtypes and cn_source
589         Select2Utils.initSelect2($('.subfield_line select[data-category!=""]'));
590     }
591
592     $("#avCreate").on("hidden.bs.modal", function(){
593         add_new_av.resetForm(); /* resets form state for jQuery Validate plugin */
594         $("#add_new_av")[0].reset();
595         $(".avCreate_error").hide();
596     });
597
598     var add_new_av = $("#add_new_av").validate({
599         submitHandler: function(form) {
600             var category         = form.category.value;
601             var value            = form.value.value;
602             var description      = form.description.value;
603             var opac_description = form.opac_description.value;
604
605             var data = "category="+encodeURIComponent(category)
606                 +"&value="+encodeURIComponent(value)
607                 +"&description="+encodeURIComponent(description)
608                 +"&opac_description="+encodeURIComponent(opac_description);
609             $.ajax({
610                 type: "POST",
611                 url: "/cgi-bin/koha/svc/authorised_values",
612                 data: data,
613                 success: function(response) {
614                     $('#avCreate').modal('hide');
615
616                     $(current_select2).append('<option selected value="'+response.value+'">'+response.description+'</option>');
617                     $("#avCreate").modal("hide");
618                 },
619                 error: function() {
620                     $(".avCreate_error").html(__("Something went wrong. Maybe the value already exists?")).show();
621                 }
622             });
623             return false;
624         }
625     });
626
627 });