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