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