diff --git a/modules/bibedit/lib/bibedit_engine.js b/modules/bibedit/lib/bibedit_engine.js
index 29e44452a..e2f94e49e 100644
--- a/modules/bibedit/lib/bibedit_engine.js
+++ b/modules/bibedit/lib/bibedit_engine.js
@@ -1,4964 +1,4989 @@
 /*
  * This file is part of Invenio.
  * Copyright (C) 2009, 2010, 2011 CERN.
  *
  * Invenio is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
  * published by the Free Software Foundation; either version 2 of the
  * License, or (at your option) any later version.
  *
  * Invenio is distributed in the hope that it will be useful, but
  * WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with Invenio; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  */
 
 /*
  * This is the main BibEdit Javascript.
  */
 
 /* ************************* Table of contents ********************************
  *
  * 1. Global variables
  *
  * 2. Initialization
  *   - $()
  *   - initJeditable
  *   - initMisc
  *
  * 3. Ajax
  *   - initAjax
  *   - createReq
  *   - onAjaxError
  *   - onAjaxSuccess
  *
  * 4. Hash management
  *   - initStateFromHash
  *   - deserializeHash
  *   - changeAndSerializeHash
  *
  * 5. Data logic
  *   - getTagsSorted
  *   - getFieldPositionInTag
  *   - getPreviousTag
  *   - deleteFieldFromTag
  *   - cmpFields
  *   - fieldIsProtected
  *   - containsProtected
  *   - getMARC
  *   - getFieldTag
  *   - getSubfieldTag
  *   - validMARC
  *
  * 6. Record UI
  *   - onNewRecordClick
  *   - getRecord
  *   - onGetRecordSuccess
  *   - onSubmitClick
  *   - onPreviewClick
  *   - onCancelClick
  *   - onCloneRecordClick
  *   - onDeleteRecordClick
  *   - onMergeClick
  *   - bindNewRecordHandlers
  *   - cleanUp
  *   - addHandler_autocompleteAffiliations
  *
  * 7. Editor UI
  *   - colorFields
  *   - reColorFields
  *   - onMARCTagsClick
  *   - onHumanTagsClick
  *   - updateTags
  *   - onFieldBoxClick
  *   - onSubfieldBoxClick
  *   - onAddFieldClick
  *   - onAddFieldControlfieldClick
  *   - onAddFieldChange
  *   - onAddFieldSave
  *   - onAddSubfieldsClick
  *   - onAddSubfieldsChange
  *   - onAddSubfieldsSave
  *   - onDoubleClick
  *   - onContentChange
  *   - onMoveSubfieldClick
  *   - onDeleteClick
  */
 
 
 
 /*
  * **************************** 1. Global variables ****************************
  */
 
 // Record data
 var gRecID = null;
 var gRecIDLoading = null;
 var gRecRev = null;
 var gRecRevAuthor = null;
 var gRecLatestRev = null;
 var gRecord = null;
 // Search results (record IDs)
 var gResultSet = null;
 // Current index in the result set
 var gResultSetIndex = null;
 // Tag format.
 var gTagFormat = null;
 // Has the record been modified?
 var gRecordDirty = false;
 // Last recorded cache modification time
 var gCacheMTime = null;
 
 // Are we navigating a set of records?
 var gNavigatingRecordSet = false;
 
 // The current hash (fragment part of the URL).
 var gHash;
 // The current hash deserialized to an object.
 var gHashParsed;
 // Hash check timer ID.
 var gHashCheckTimerID;
 // The previous and current state (this is not exactly the same as the state
 // parameter, but an internal state control mechanism).
 var gPrevState;
 var gState;
 // A current status
 var gCurrentStatus;
 
 // a global array of visible changes associated with a currently viewed record
 // This array is cleared always when a new changes set is applied... then it is used
 // for redrawing the change fields
 // The index in this array is used when referring to a particular change [ like finding an appropriate box]
 
 var gHoldingPenChanges = [];
 
 // A global variable used to avoid multiple retrieving of the same changes stored in the Holding Pen
 // this is the dictionary indexed by the HoldingPen entry identifiers and containing the javascript objects
 // representing the records
 // due to this mechanism, applying previously previewed changes, as well as previewing the change for the
 // second time, can be made much faster
 var gHoldingPenLoadedChanges = {};
 
 // The changes that have been somehow processed and should not be displayed as already processed
 
 var gDisabledHpEntries = {};
 
 // is the read-only mode enabled ?
 var gReadOnlyMode = false;
 
 // revisions history
 var gRecRevisionHistory = [];
 
 var gUndoList = []; // list of possible undo operations
 var gRedoList = []; // list of possible redo operations
 
 // number of bibcirculation copies from the retrieval time
 var gPhysCopiesNum = 0;
 var gBibCircUrl = null;
 
 var gDisplayBibCircPanel = false;
 
 //KB related variables
 var gKBSubject = null;
 var gKBInstitution = null;
 
 
 /*
  * **************************** 2. Initialization ******************************
  */
 
 window.onload = function(){
   if (typeof(jQuery) == 'undefined'){
     alert('ERROR: jQuery not found!\n\n' +
     'The Record Editor requires jQuery, which does not appear to be ' +
     'installed on this server. Please alert your system ' +
     'administrator.\n\nInstructions on how to install jQuery and other ' +
     "required plug-ins can be found in Invenio's INSTALL file.");
     var imgError = document.createElement('img');
     imgError.setAttribute('src', '/img/circle_red.png');
     var txtError = document.createTextNode('jQuery missing');
     var cellIndicator = document.getElementById('cellIndicator');
     cellIndicator.replaceChild(imgError, cellIndicator.firstChild);
     var cellStatus = document.getElementById('cellStatus');
     cellStatus.replaceChild(txtError, cellStatus.firstChild);
   }
 };
 
 $(function(){
   /*
    * Initialize all components.
    */
   initMenu();
   initJeditable();
   initAjax();
   initMisc();
   createTopToolbar();
   initStateFromHash();
   gHashCheckTimerID = setInterval(initStateFromHash, gHASH_CHECK_INTERVAL);
   initHotkeys();
   initClipboardLibrary();
   initClipboard()
 });
 
 
 function failInReadOnly(){
   /** Function checking if the current BibEdit mode is read-only. In sucha a case, a warning
     dialog is displayed and true returned.
     If bibEdit is in read/write mode, false is returned
    */
   if (gReadOnlyMode === true){
     alert("It is impossible to perform this operation in the Read/Only mode. Please switch to Read-write mode before trying again");
     return true;
   }
   else{
     return false;
   }
 }
 
 function initClipboard(){
   // attaching the events -> handlers are stored in bibedit_engine.js file
   $(document).bind("copy", onPerformCopy);
   $(document).bind("paste", onPerformPaste);
 }
 
 function initMisc(){
   /*
    * Miscellaneous initialization operations.
    */
   // CERN allows for capital MARC indicators.
   if (gCERN_SITE){
     validMARC.reIndicator1 = /[\dA-Za-z]{1}/;
     validMARC.reIndicator2 = /[\dA-Za-z]{1}/;
   }
 
   // Warn user if BibEdit is being closed while a record is open.
   window.onbeforeunload = function(){
     if (gRecID && gRecordDirty){
       return '******************** WARNING ********************\n' +
              '                  You have unsubmitted changes.\n\n' +
              'You should go back to the page and click either:\n' +
              ' * Submit (to save your changes permanently)\n      or\n' +
              ' * Cancel (to discard your changes)';
     }
   };
 
   //Initialising the BibCircualtion integration plugin
   $("#bibEditBibCirculationBtn").bind("click", onBibCirculationBtnClicked);
 }
 
 function initJeditable(){
   /*
    * Overwrite Jeditable plugin function to add the autocomplete handler
    * to textboxes corresponding to fields in gTagsToAutocomplete
    */
   $.editable.types['textarea'].element = function(settings, original) {
     var form = this;
     var textarea = $('<textarea />');
     if (settings.rows) {
         textarea.attr('rows', settings.rows);
     } else if (settings.height != "none") {
         textarea.height(settings.height);
     }
     if (settings.cols) {
         textarea.attr('cols', settings.cols);
     } else if (settings.width != "none") {
         textarea.width(settings.width);
     }
     $(this).append(textarea);
 
     /* original variable is the cell that contains the textbox */
     var cell_id_split = $(original).attr('id').split('_');
     /* create subfield id corresponding to original cell */
     cell_id_split[0] = 'subfieldTag';
     var subfield_id = cell_id_split.join('_');
     /* Add autocomplete handler to fields in gTagsToAutocomplete */
     var fieldInfo = $(original).parents("tr").siblings().eq(0).children().eq(1).html()
     if ($.inArray(fieldInfo + $(original).siblings('#' + subfield_id).text(), gTagsToAutocomplete) != -1) {
         addHandler_autocompleteAffiliations(textarea);
     }
     textarea.bind('keydown', 'return', function(event){ form.submit(); return false;});
     initInputHotkeys(textarea);
     return(textarea);
   }
 }
 
 /*
  * **************************** 3. Ajax ****************************************
  */
 
 function initAjax(){
   /*
    * Initialize Ajax.
    */
   $.ajaxSetup(
     {cache: false,
       dataType: 'json',
       error: onAjaxError,
       type: 'POST',
       url: '/'+ gSITE_RECORD +'/edit/'
     }
   );
 }
 
 function createReq(data, onSuccess, asynchronous){
   /*
    * Create Ajax request.
    */
   if (asynchronous == undefined){
     asynchronous = true;
   }
   // Include and increment transaction ID.
   var tID = createReq.transactionID++;
   createReq.transactions[tID] = data['requestType'];
   data.ID = tID;
   // Include cache modification time if we have it.
   if (gCacheMTime){
     data.cacheMTime = gCacheMTime;
   }
   // Send the request.
   $.ajax({data: {jsondata: JSON.stringify(data)},
            success: function(json){
                       onAjaxSuccess(json, onSuccess);
                     },
            async: asynchronous
   });
 }
 // Transactions data.
 createReq.transactionID = 0;
 createReq.transactions = [];
 
 function createBulkReq(reqsData, onSuccess, optArgs){
   /* optArgs is a disctionary containning the optional arguments
      possible keys include:
        asynchronous : if the request should be asynchronous
        undoRedo : handler for the undo operation
   */
     // creating a bulk request ... the cache timestamp is not saved
 
     var data = {'requestType' : 'applyBulkUpdates',
                  'requestsData' : reqsData,
                  'recID' : gRecID};
     if (optArgs.undoRedo != undefined){
         data.undoRedo = optArgs.undoRedo;
     }
 
     createReq(data, onSuccess, optArgs.asynchronous);
 }
 
 function onAjaxError(XHR, textStatus, errorThrown){
   /*
    * Handle Ajax request errors.
    */
   alert('Request completed with status ' + textStatus +
     '\nResult: ' + XHR.responseText +
     '\nError: ' + errorThrown);
 }
 
 function onAjaxSuccess(json, onSuccess){
   /*
    * Handle server response to Ajax requests, in particular error situations.
    * See BibEdit config for result codes.
    * If a function onSuccess is specified this will be called in the end,
    * if no error was encountered.
    */
   var resCode = json['resultCode'];
   var recID = json['recID'];
   if (resCode == 100){
     // User's session has timed out.
     gRecID = null;
     gRecIDLoading = null;
     window.location = recID ? gSITE_URL + '/'+ gSITE_RECORD +'/' + recID + '/edit/'
       : gSITE_URL + '/'+ gSITE_RECORD +'/edit/';
     return;
   }
   else if ($.inArray(resCode, [101, 102, 103, 104, 105, 106, 107, 108, 109])
      != -1){
     cleanUp(!gNavigatingRecordSet, null, null, true, true);
     if ($.inArray(resCode, [108, 109]) == -1)
       $('.headline').text('Record Editor: Record #' + recID);
     displayMessage(resCode);
     if (resCode == 107)
       return;
       $('#lnkGetRecord').bind('click', function(event){
         getRecord(recID);
         event.preventDefault();
       });
     updateStatus('error', gRESULT_CODES[resCode]);
   }
   else if (resCode == 110){
     displayMessage(resCode, true, [json['errors'].toString()]);
     updateStatus('error', gRESULT_CODES[resCode]);
   }
   else{
     var cacheOutdated = json['cacheOutdated'];
     var requestType = createReq.transactions[json['ID']];
     if (cacheOutdated && requestType == 'submit'){
       // User wants to submit, but cache is outdated. Outdated means that the
       // DB version of the record has changed after the cache was created.
       displayCacheOutdatedScreen(requestType);
       $('#lnkMergeCache').bind('click', onMergeClick);
       $('#lnkForceSubmit').bind('click', function(event){
   onSubmitClick.force = true;
   onSubmitClick();
   event.preventDefault();
       });
       $('#lnkDiscardChanges').bind('click', function(event){
   onCancelClick();
   event.preventDefault();
       });
       updateStatus('error', 'Error: Record cache is outdated');
     }
     else{
       if (requestType != 'getRecord'){
   // On getRecord requests the below actions will be performed in
   // onGetRecordSuccess (after cleanup).
   var cacheMTime = json['cacheMTime'];
   if (cacheMTime)
     // Store new cache modification time.
     gCacheMTime = cacheMTime;
   var cacheDirty = json['cacheDirty'];
   if (cacheDirty){
     // Cache is dirty. Enable submit button.
     gRecordDirty = cacheDirty;
     $('#btnSubmit').removeAttr('disabled');
     $('#btnSubmit').css('background-color', 'lightgreen');
   }
       }
       if (onSuccess) {
           // No critical errors; call onSuccess function.
           onSuccess(json);
       }
     }
   }
 }
 
 function resetBibeditState(){
   /* A function clearing the state of the bibEdit (all the panels content)
   */
   gHoldingPenLoadedChanges = {};
   gHoldingPenChanges = [];
   gDisabledHpEntries = {};
   gReadOnlyMode = false;
   gRecRevisionHistory = [];
   gUndoList = [];
   gRedoList = [];
   gPhysCopiesNum = 0;
   gBibCircUrl = null;
 
   updateInterfaceAccordingToMode();
   updateRevisionsHistory();
   updateUrView();
   updateBibCirculationPanel();
   holdingPenPanelRemoveEntries();
 }
 
 /*
  * **************************** 4. Hash management *****************************
  */
 
 function initStateFromHash(){
   /*
    * Initialize or update page state from hash.
    * Any program functions changing the hash should use changeAndSerializeHash()
    * which circumvents this function, meaning this function should only run on
    * page load and when browser navigation buttons (ie. Back and Forward) are
    * clicked. Any invalid hashes entered by the user will be ignored.
    */
   if (window.location.hash == gHash)
     // Hash is the same as last time we checked, do nothing.
     return;
 
   gHash = window.location.hash;
   gHashParsed = deserializeHash(gHash);
   gPrevState = gState;
   var tmpState = gHashParsed.state;
   var tmpRecID = gHashParsed.recid;
   var tmpRecRev = gHashParsed.recrev;
   var tmpReadOnlyMode = gHashParsed.romode;
 
   // Find out which internal state the new hash leaves us with
   if (tmpState && tmpRecID){
     // We have both state and record ID.
     if ($.inArray(tmpState, ['edit', 'submit', 'cancel', 'deleteRecord']) != -1)
   gState = tmpState;
     else
       // Invalid state, fail...
       return;
   }
   else if (tmpState){
     // We only have state.
     if (tmpState == 'edit')
       gState = 'startPage';
     else if (tmpState == 'newRecord')
       gState = 'newRecord';
     else
       // Invalid state, fail... (all states but 'edit' and 'newRecord' are
       // illegal without record ID).
       return;
   }
   else
     // Invalid hash, fail...
     return;
 
   if (gState != gPrevState
     || (gState == 'edit' && parseInt(tmpRecID) != gRecID) || // different record number
     (tmpRecRev != undefined && tmpRecRev != gRecRev) // different revision
     || (tmpRecRev == undefined && gRecRev != gRecLatestRev) // latest revision requested but another open
     || (tmpReadOnlyMode != gReadOnlyMode)){ // switched between read-only and read-write modes
 
     // We have an actual and legal change of state. Clean up and update the
     // page.
     updateStatus('updating');
     if (gRecID && !gRecordDirty && !tmpReadOnlyMode)
       // If the record is unchanged, delete the cache.
       createReq({recID: gRecID, requestType: 'deleteRecordCache'});
     switch (gState){
       case 'startPage':
         cleanUp(true, '', 'recID', true, true);
         updateStatus('ready');
         break;
       case 'edit':
         var recID = parseInt(tmpRecID);
         if (isNaN(recID)){
           // Invalid record ID.
           cleanUp(true, tmpRecID, 'recID', true);
           $('.headline').text('Record Editor: Record #' + tmpRecID);
           displayMessage(102);
           updateStatus('error', gRESULT_CODES[102]);
         }
         else{
           cleanUp(true, recID, 'recID');
           gReadOnlyMode = tmpReadOnlyMode;
             if (tmpRecRev != undefined && tmpRecRev != 0){
               getRecord(recID, tmpRecRev);
             } else {
               getRecord(recID);
             }
         }
       break;
     case 'newRecord':
       cleanUp(true, '', null, null, true);
       $('.headline').text('Record Editor: Create new record');
       displayNewRecordScreen();
       bindNewRecordHandlers();
       updateStatus('ready');
       break;
     case 'submit':
       cleanUp(true, '', null, true);
       $('.headline').text('Record Editor: Record #' + tmpRecID);
       displayMessage(4);
       updateStatus('ready');
       break;
     case 'cancel':
       cleanUp(true, '', null, true, true);
       updateStatus('ready');
       break;
     case 'deleteRecord':
       cleanUp(true, '', null, true);
       $('.headline').text('Record Editor: Record #' + tmpRecID);
       displayMessage(10);
       updateStatus('ready');
         break;
     }
   }
   else
   // What changed was not of interest, continue as if nothing happened.
     return;
 }
 
 function deserializeHash(aHash){
   /*
    * Deserializes a string (given as parameter or taken from the window object)
    * into the hash object.
    */
   if (aHash == undefined){
     aHash = window.location.hash;
   }
   var hash = {};
   var args = aHash.slice(1).split('&');
   var tmpArray;
   for (var i=0, n=args.length; i<n; i++){
     tmpArray = args[i].split('=');
     if (tmpArray.length == 2)
       hash[tmpArray[0]] = tmpArray[1];
   }
   return hash;
 }
 
 function changeAndSerializeHash(updateData){
   /*
    * Change the hash object to use the data from the object given as parameter.
    * Then update the hash accordingly, WITHOUT invoking initStateFromHash().
    */
   clearTimeout(gHashCheckTimerID);
   gHashParsed = {};
   for (var key in updateData){
     gHashParsed[key.toString()] = updateData[key].toString();
   }
   gHash = '#';
   for (key in gHashParsed){
     gHash += key + '=' + gHashParsed[key] + '&';
   }
   gHash = gHash.slice(0, -1);
   gState = gHashParsed.state;
   window.location.hash = gHash;
   gHashCheckTimerID = setInterval(initStateFromHash, gHASH_CHECK_INTERVAL);
 }
 
 
 /*
  * **************************** 5. Data logic **********************************
 */
 
 function getTagsSorted(){
   /*
    * Return field tags in sorted order.
    */
   var tags = [];
   for (var tag in gRecord){
     tags.push(tag);
   }
   return tags.sort();
 }
 
 function getFieldPositionInTag(tag, field){
   /*
    * Determine the local (in tag) position of a new field.
    */
   var fields = gRecord[tag];
   if (fields){
     var fieldLength = fields.length, i = 0;
     while (i < fieldLength && cmpFields(field, fields[i]) != -1)
       i++;
     return i;
   }
   else
     return 0;
 }
 
 function getPreviousTag(tag){
   /*
    * Determine the previous tag in the record (if the given tag is the first
    * tag, 0 will be returned).
    */
   var tags = getTagsSorted();
   var tagPos = $.inArray(tag, tags);
   if (tagPos == -1){
     tags.push(tag);
     tags.sort();
     tagPos = $.inArray(tag, tags);
   }
   if (tagPos > 0)
     return tags[tagPos-1];
   return 0;
 }
 
 function deleteFieldFromTag(tag, fieldPosition){
   /*
    * Delete a specified field.
    */
   var field = gRecord[tag][fieldPosition];
   var fields = gRecord[tag];
   fields.splice($.inArray(field, fields), 1);
   // If last field, delete tag.
   if (fields.length == 0){
     delete gRecord[tag];
   }
 }
 
 function cmpFields(field1, field2){
   /*
    * Compare fields by indicators (tag assumed equal).
    */
   if (field1[1].toLowerCase() > field2[1].toLowerCase())
     return 1;
   else if (field1[1].toLowerCase() < field2[1].toLowerCase())
     return -1;
   else if (field1[2].toLowerCase() > field2[2].toLowerCase())
     return 1;
   else if (field1[2].toLowerCase() < field2[2].toLowerCase())
     return -1;
   return 0;
 }
 
 function insertFieldToRecord(record, fieldId, ind1, ind2, subFields){
   /**Inserting a new field on the client side and returning the position of the newly created field*/
   newField = [subFields, ind1, ind2, '', 0];
   if (record[fieldId] == undefined){
     record[fieldId] = [newField]
     return 0;
   } else {
     record[fieldId].push(newField);
     return (record[fieldId].length-1);
   }
 }
 
 function transformRecord(record){
   /**Transforming a bibrecord to a form that is easier to compare that is a dictionary
    * field identifier -> field indices -> fields list -> [subfields list, position in the record]
    *
    * The data is enriched with the positions inside the record in a following manner:
    * each field consists of:
    * */
   result = {};
   for (fieldId in record){
     result[fieldId] = {}
     indicesList = []; // a list of all the indices ... utilised later when determining the positions
     for (fieldIndex in record[fieldId]){
 
       indices =  "";
       if (record[fieldId][fieldIndex][1] == ' '){
         indices += "_";
       }else{
         indices += record[fieldId][fieldIndex][1]
       }
 
       if (record[fieldId][fieldIndex][2] == ' '){
         indices += "_";
       }else{
         indices += record[fieldId][fieldIndex][2]
       }
 
       if (result[fieldId][indices] == undefined){
         result[fieldId][indices] = []; // a future list of fields sharing the same indice
         indicesList.push(indices);
       }
       result[fieldId][indices].push([record[fieldId][fieldIndex][0], 0]);
     }
 
     // now calculating the positions within a field identifier ( utilised on the website )
 
     position = 0;
 
     indices = indicesList.sort();
     for (i in indices){
       for (fieldInd in result[fieldId][indices[i]]){
         result[fieldId][indices[i]][fieldInd][1] = position;
         position ++;
       }
     }
   }
 
     return result;
 }
 
 function filterChanges(changeset){
   /*Filtering the changes list -> removing the changes related to the fields
    * that should never be changed */
   unchangableTags = {"001" : true}; // a dictionary of the fields that should not be modified
   result = [];
   for (changeInd in changeset){
     change = changeset[changeInd];
     if ((change.tag == undefined) || (!(change.tag in unchangableTags))){
       result.push(change);
     }
   }
   return result;
 }
 
 ///// Functions generating easy to display changes list
 
 function compareFields(fieldId, indicators, fieldPos, field1, field2){
   result = [];
   for (sfPos in field2){
     if (field1[sfPos] == undefined){
       //  adding the subfield at the end of the record can be treated in a more graceful manner
       result.push(
           {"change_type" : "subfield_added",
            "tag" : fieldId,
            "indicators" : indicators,
            "field_position" : fieldPos,
            "subfield_code" : field2[sfPos][0],
            "subfield_content" : field2[sfPos][1]});
     }
     else
     {
       // the subfield exists in both the records
       if (field1[sfPos][0] != field2[sfPos][0]){
       //  a structural change ... we replace the entire field
         return [{"change_type" : "field_changed",
            "tag" : fieldId,
            "indicators" : indicators,
            "field_position" : fieldPos,
            "field_content" : field2}];
       } else
       {
         if (field1[sfPos][1] != field2[sfPos][1]){
           result.push({"change_type" : "subfield_changed",
             "tag" : fieldId,
             "indicators" : indicators,
             "field_position" : fieldPos,
             "subfield_position" : sfPos,
             "subfield_code" : field2[sfPos][0],
             "subfield_content" : field2[sfPos][1]});
 
         }
       }
     }
   }
 
   for (sfPos in field1){
     if (field2[sfPos] == undefined){
       result.push({"change_type" : "subfield_removed",
                 "tag" : fieldId,
                 "indicators" : indicators,
                 "field_position" : fieldPos,
                 "subfield_position" : sfPos});
     }
   }
 
   return result;
 }
 
 function compareIndicators(fieldId, indicators, fields1, fields2){
    /*a helper function allowing to compare inside one indicator
     * excluded from compareRecords for the code clarity reason*/
   result = []
   for (fieldPos in fields2){
     if (fields1[fieldPos] == undefined){
       result.push({"change_type" : "field_added",
                   "tag" : fieldId,
                   "indicators" : indicators,
                   "field_content" : fields2[fieldPos][0]});
     } else { // comparing the content of the subfields
       result = result.concat(compareFields(fieldId, indicators, fields1[fieldPos][1], fields1[fieldPos][0], fields2[fieldPos][0]));
     }
   }
 
   for (fieldPos in fields1){
     if (fields2[fieldPos] == undefined){
       fieldPosition = fields1[fieldPos][1];
       result.push({"change_type" : "field_removed",
              "tag" : fieldId,
              "indicators" : indicators,
              "field_position" : fieldPosition});
     }
   }
   return result;
 }
 
 function compareRecords(record1, record2){
   /*Compares two bibrecords, producing a list of atom changes that can be displayed
    * to the user if for example applying the Holding Pen change*/
    // 1) This is more convenient to have a different structure of the storage
   r1 = transformRecord(record1);
   r2 = transformRecord(record2);
   result = [];
 
   for (fieldId in r2){
     if (r1[fieldId] == undefined){
       for (indicators in r2[fieldId]){
         for (field in r2[fieldId][indicators]){
           result.push({"change_type" : "field_added",
                         "tag" : fieldId,
                         "indicators" : indicators,
                         "field_content" : r2[fieldId][indicators][field][0]});
 
 
         }
       }
     }
     else
     {
       for (indicators in r2[fieldId]){
         if (r1[fieldId][indicators] == undefined){
           for (field in r2[fieldId][indicators]){
             result.push({"change_type" : "field_added",
                          "tag" : fieldId,
                          "indicators" : indicators,
                          "field_content" : r2[fieldId][indicators][field][0]});
 
 
           }
         }
         else{
           result = result.concat(compareIndicators(fieldId, indicators,
               r1[fieldId][indicators], r2[fieldId][indicators]));
         }
       }
 
       for (indicators in r1[fieldId]){
         if (r2[fieldId][indicators] == undefined){
           for (fieldInd in r1[fieldId][indicators]){
             fieldPosition = r1[fieldId][indicators][fieldInd][1];
             result.push({"change_type" : "field_removed",
                  "tag" : fieldId,
                  "field_position" : fieldPosition});
           }
 
         }
       }
 
     }
   }
 
   for (fieldId in r1){
     if (r2[fieldId] == undefined){
       for (indicators in r1[fieldId]){
         for (field in r1[fieldId][indicators])
         {
           // field position has to be calculated here !!!
           fieldPosition = r1[fieldId][indicators][field][1]; // field position inside the mark
           result.push({"change_type" : "field_removed",
                        "tag" : fieldId,
                        "field_position" : fieldPosition});
 
         }
       }
     }
   }
   return result;
 }
 
 function fieldIsProtected(MARC){
   /*
    * Determine if a MARC field is protected or part of a protected group of
    * fields.
    */
   do{
     var i = MARC.length - 1;
     if ($.inArray(MARC, gPROTECTED_FIELDS) != -1)
       return true;
     MARC = MARC.substr(0, i);
     i--;
   }
   while (i >= 1)
   return false;
 }
 
 function containsProtectedField(fieldData){
   /*
    * Determine if a field data structure contains protected elements (useful
    * when checking if a deletion command is valid).
    * The data structure must be an object with the following levels
    * - Tag
    *   - Field position
    *     - Subfield index
    */
   var fieldPositions, subfieldIndexes, MARC;
   for (var tag in fieldData){
     fieldPositions = fieldData[tag];
     for (var fieldPosition in fieldPositions){
       subfieldIndexes = fieldPositions[fieldPosition];
       if (subfieldIndexes.length == 0){
   MARC = getMARC(tag, fieldPosition);
   if (fieldIsProtected(MARC))
     return MARC;
   }
       else{
   for (var i=0, n=subfieldIndexes.length; i<n; i++){
     MARC = getMARC(tag, fieldPosition, subfieldIndexes[i]);
     if (fieldIsProtected(MARC))
       return MARC;
   }
       }
     }
   }
   return false;
 }
 
 function getMARC(tag, fieldPosition, subfieldIndex){
   /*
    * Return the MARC representation of a field or a subfield.
    */
   var field = gRecord[tag][fieldPosition];
   var ind1, ind2;
   if (validMARC.reControlTag.test(tag))
     ind1 = '', ind2 = '';
   else{
     ind1 = (field[1] == ' ' || !field[1]) ? '_' : field[1];
     ind2 = (field[2] == ' ' || !field[2]) ? '_' : field[2];
   }
   if (subfieldIndex == undefined)
     return tag + ind1 + ind2;
   else
     return tag + ind1 + ind2 + field[0][subfieldIndex][0];
 }
 
 function getFieldTag(MARC){
   /*
    * Get the tag name of a field in format as specified by gTagFormat.
    */
   MARC = MARC.substr(0, 5);
   if (gTagFormat == 'human'){
     var tagName = gTAG_NAMES[MARC];
     if (tagName != undefined)
       // Direct hit. Return it.
       return tagName;
     else{
       // Start looking for wildcard hits.
       if (MARC.length == 3){
   // Controlfield
   tagName = gTAG_NAMES[MARC.substr(0, 2) + '%'];
   if (tagName != undefined && tagName != MARC + 'x')
     return tagName;
       }
       else{
   // Regular field, try finding wildcard hit by shortening expression
   // gradually. Ignores wildcards which gives values like '27x'.
   var term = MARC + '%', i = 5;
   do{
     tagName = gTAG_NAMES[term];
     if (tagName != undefined){
       if (tagName != MARC.substr(0, i) + 'x')
         return tagName;
       break;
     }
     i--;
     term = MARC.substr(0, i) + '%';
   }
   while (i >= 3)
       }
     }
   }
   return MARC;
 }
 
 function getSubfieldTag(MARC){
   /*
    * Get the tag name of a subfield in format as specified by gTagFormat.
    */
   if (gTagFormat == 'human'){
     var subfieldName = gTAG_NAMES[MARC];
       if (subfieldName != undefined)
   return subfieldName;
   }
   return MARC.charAt(5);
 }
 
 function validMARC(datatype, value){
   /*
    * Validate a value of given datatype according to the MARC standard. The
    * value should be restricted/extended to it's expected size before being
    * passed to this function.
    * Datatype can be 'ControlTag', 'Tag', 'Indicator' or 'SubfieldCode'.
    * Returns a boolean.
    */
   return eval('validMARC.re' + datatype + '.test(value)');
 }
 // MARC validation REs
 validMARC.reControlTag = /00[1-9A-Za-z]{1}/;
 validMARC.reTag = /(0([1-9A-Z][0-9A-Z])|0([1-9a-z][0-9a-z]))|(([1-9A-Z][0-9A-Z]{2})|([1-9a-z][0-9a-z]{2}))/;
 validMARC.reIndicator1 = /[\da-zA-Z]{1}/;
 validMARC.reIndicator2 = /[\da-zA-Z]{1}/;
 //validMARC.reSubfieldCode = /[\da-z!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?{}_^`~\[\]\\]{1}/;
 validMARC.reSubfieldCode = /[\da-z!&quot;#$%&amp;'()*+,-.\/:;&lt;=&gt;?{}_^`~\[\]\\]{1}/;
 
 /*
  * **************************** 6. Record UI ***********************************
  */
 
 function onNewRecordClick(event){
   /*
    * Handle 'New' button (new record).
    */
   updateStatus('updating');
   if (gRecordDirty){
     if (!displayAlert('confirmLeavingChangedRecord')){
       updateStatus('ready');
       event.preventDefault();
       return;
     }
   }
   else
     // If the record is unchanged, erase the cache.
     if (gReadOnlyMode == false){
       createReq({recID: gRecID, requestType: 'deleteRecordCache'});
   }
   changeAndSerializeHash({state: 'newRecord'});
   cleanUp(true, '');
   $('.headline').text('Record Editor: Create new record');
   displayNewRecordScreen();
   bindNewRecordHandlers();
   updateStatus('ready');
   updateToolbar(false);
   event.preventDefault();
 }
 
 function onTemplateRecordClick(event){
     /* Handle 'Template management' button */
     var template_window = window.open('/record/edit/templates', '', 'resizeable,scrollbars');
     template_window.document.close(); // needed for chrome and safari
 }
 
 function getRecord(recID, recRev, onSuccess){
   /* A function retrieving the bibliographic record, using an AJAX request.
    *
    * recID : the identifier of a record to be retrieved from the server
    * recRev : the revision of the record to be retrieved (0 or undefined
    *          means retrieving the newest version )
    * onSuccess : The callback to be executed upon retrieval. The default
    *             callback loads the retrieved record into the bibEdit user
    *             interface
    */
 
   // Temporary store the record ID by attaching it to the onGetRecordSuccess
   // function.
   if (onSuccess == undefined)
     onSuccess = onGetRecordSuccess;
   if (recRev != undefined && recRev != 0){
     changeAndSerializeHash({state: 'edit', recid: recID, recrev: recRev});
   }
   else{
     changeAndSerializeHash({state: 'edit', recid: recID});
   }
 
   gRecIDLoading = recID;
 
   reqData = {recID: recID,
              requestType: 'getRecord',
              deleteRecordCache:
              getRecord.deleteRecordCache,
              clonedRecord: getRecord.clonedRecord,
              inReadOnlyMode: gReadOnlyMode};
 
   if (recRev != undefined && recRev != 0){
     reqData.recordRevision = recRev;
     reqData.inReadOnlyMode = true;
   }
 
   resetBibeditState();
   createReq(reqData, onSuccess);
 
   onHoldingPenPanelRecordIdChanged(recID); // reloading the Holding Pen toolbar
   getRecord.deleteRecordCache = false;
   getRecord.clonedRecord = false;
 }
 // Enable this flag to delete any existing cache before fetching next record.
 getRecord.deleteRecordCache = false;
 // Enable this flag to tell that we are fetching a record that has just been
 // cloned (enables proper feedback, highlighting).
 getRecord.clonedRecord = false;
 
 
 function onGetRecordSuccess(json){
   /*
    * Handle successfull 'getRecord' requests.
    */
   cleanUp(!gNavigatingRecordSet);
   // Store record data.
   gRecID = json['recID'];
   gRecIDLoading = null;
   gRecRev = json['recordRevision'];
   gRecRevAuthor = json['revisionAuthor'];
   gPhysCopiesNum = json['numberOfCopies'];
   gBibCircUrl = json['bibCirculationUrl'];
   gDisplayBibCircPanel = json['canRecordHavePhysicalCopies'];
 
   // Get KB information
   gKBSubject = json['KBSubject'];
   gKBInstitution = json['KBInstitution'];
 
   var revDt = formatDateTime(getRevisionDate(gRecRev));
   var recordRevInfo = "record revision: " + revDt;
   var revAuthorString = gRecRevAuthor;
   $('.revisionLine').html(recordRevInfo + ' ' + revAuthorString)
   gRecord = json['record'];
   gTagFormat = json['tagFormat'];
   gRecordDirty = json['cacheDirty'];
   gCacheMTime = json['cacheMTime'];
 
   if (json['cacheOutdated']){
     // User had an existing outdated cache.
     displayCacheOutdatedScreen('getRecord');
     $('#lnkMergeCache').bind('click', onMergeClick);
     $('#lnkDiscardChanges').bind('click', function(event){
       getRecord.deleteRecordCache = true;
       getRecord(gRecID);
       event.preventDefault();
     });
     $('#lnkRemoveMsg').bind('click', function(event){
       $('#bibEditMessage').remove();
       event.preventDefault();
     });
   }
 
   gHoldingPenChanges = json['pendingHpChanges'];
   gDisabledHpEntries = json['disabledHpChanges'];
   gHoldingPenLoadedChanges = {};
 
   adjustHPChangesetsActivity();
   updateBibCirculationPanel();
 
   // updating the undo/redo lists
   gUndoList = json['undoList'];
   gRedoList = json['redoList'];
   updateUrView();
 
   // Display record.
   displayRecord();
   // Activate menu record controls.
   activateRecordMenu();
   // the current mode should is indicated by the result from the server
   gReadOnlyMode = (json['inReadOnlyMode'] != undefined) ? json['inReadOnlyMode'] : false;
   gRecLatestRev = (json['latestRevision'] != undefined) ? json['latestRevision'] : null;
   gRecRevisionHistory = (json['revisionsHistory'] != undefined) ? json['revisionsHistory'] : null;
 
   updateInterfaceAccordingToMode();
 
   if (gRecordDirty){
     $('#btnSubmit').removeAttr('disabled');
     $('#btnSubmit').css('background-color', 'lightgreen');
   }
   if (gTagFormat == 'MARC')
     $('#btnHumanTags').bind('click', onHumanTagsClick).removeAttr('disabled');
   else
     $('#btnMARCTags').bind('click', onMARCTagsClick).removeAttr('disabled');
   // Unfocus record selection field (to facilitate hotkeys).
   $('#txtSearchPattern').blur();
   if (json['resultCode'] == 9)
     $('#spnRecID').effect('highlight', {color: gCLONED_RECORD_COLOR},
       gCLONED_RECORD_COLOR_FADE_DURATION);
   updateStatus('report', gRESULT_CODES[json['resultCode']]);
   updateRevisionsHistory();
   adjustGeneralHPControlsVisibility();
 
   createReq({recID: gRecID, requestType: 'getTickets'}, onGetTicketsSuccess);
 
   updateToolbar(true);
 }
 
 function onGetTemplateSuccess(json) {
   onGetRecordSuccess(json);
 }
 
 function onSubmitClick(){
   /*
    * Handle 'Submit' button (submit record).
    */
   updateStatus('updating');
   if (displayAlert('confirmSubmit')){
+    var preview = getPreview();
     createReq({recID: gRecID, requestType: 'submit',
          force: onSubmitClick.force}, function(json){
        // Submission was successful.
       changeAndSerializeHash({state: 'submit', recid: gRecID});
       var resCode = json['resultCode'];
       cleanUp(!gNavigatingRecordSet, '', null, true);
       updateStatus('report', gRESULT_CODES[resCode]);
       displayMessage(resCode);
+      $('#bibEditMessage').after(preview);
       updateToolbar(false);
       resetBibeditState()
     }, false);
     onSubmitClick.force = false;
     resetBibeditState();
   }
   else
     updateStatus('ready');
 }
 
 // Enable this flag to force the next submission even if cache is outdated.
 onSubmitClick.force = false;
 
 function onPreviewClick(){
   /*
    * Handle 'Preview' button (preview record).
    */
-    createReq({recID: gRecID, requestType: 'preview'
+    createReq({data: {'new_window': false}, recID: gRecID, requestType: 'preview'
        }, function(json){
        // Preview was successful.
         var html_preview = json['html_preview'];
         var preview_window = window.open('', '', 'width=768,height=768,resizeable,scrollbars');
         preview_window.document.write(html_preview);
         preview_window.document.close(); // needed for chrome and safari
        });
 }
 
+function getPreview() {
+    var html_preview;
+    createReq({'new_window': false, recID: gRecID, requestType: 'preview'
+       }, function(json){
+       // Preview was successful.
+        html_preview = json['html_preview'];
+       }, false);
+    return html_preview;
+}
+
 function onCancelClick(){
   /*
    * Handle 'Cancel' button (cancel editing).
    */
   updateStatus('updating');
   if (!gRecordDirty || displayAlert('confirmCancel')) {
   createReq({
     recID: gRecID,
     requestType: 'cancel'
   }, function(json){
     // Cancellation was successful.
       changeAndSerializeHash({
           state: 'cancel',
           recid: gRecID
         });
         cleanUp(!gNavigatingRecordSet, '', null, true, true);
         updateStatus('report', gRESULT_CODES[json['resultCode']]);
       }, false);
       holdingPenPanelRemoveEntries();
       gUndoList = [];
       gRedoList = [];
       gReadOnlyMode = false;
       gRecRevisionHistory = [];
       gHoldingPenLoadedChanges = [];
       gHoldingPenChanges = [];
       gPhysCopiesNum = 0;
       gBibCircUrl = null;
       // making the changes visible
       updateBibCirculationPanel();
       updateInterfaceAccordingToMode();
       updateRevisionsHistory();
       updateUrView();
       updateToolbar(false);
 
     }
     else {
       updateStatus('ready');
     }
 }
 
 function onCloneRecordClick(){
   /*
    * Handle 'Clone' button (clone record).
    */
   updateStatus('updating');
   if (!displayAlert('confirmClone')){
     updateStatus('ready');
     return;
   }
   else if (!gRecordDirty)
     // If the record is unchanged, erase the cache.
     createReq({recID: gRecID, requestType: 'deleteRecordCache'});
   createReq({requestType: 'newRecord', newType: 'clone', recID: gRecID},
     function(json){
       var newRecID = json['newRecID'];
       $('#txtSearchPattern').val(newRecID);
       getRecord.clonedRecord = true;
       getRecord(newRecID);
   }, false);
 }
 
 function onDeleteRecordClick(){
   /*
    * Handle 'Delete record' button.
    */
   if (gPhysCopiesNum > 0){
     displayAlert('errorPhysicalCopiesExist');
     return;
   }
   if (displayAlert('confirmDeleteRecord')){
     updateStatus('updating');
     createReq({recID: gRecID, requestType: 'deleteRecord'}, function(json){
       // Record deletion was successful.
       changeAndSerializeHash({state: 'deleteRecord', recid: gRecID});
       cleanUp(!gNavigatingRecordSet, '', null, true);
       var resCode = json['resultCode'];
       // now cleaning the interface - removing holding pen entries and record history
       resetBibeditState();
       updateStatus('report', gRESULT_CODES[resCode]);
       displayMessage(resCode);
       updateToolbar(false);
     }, false);
   }
 }
 
 function onMergeClick(event){
   /*
    * Handle click on 'Merge' link (to merge outdated cache with current DB
    * version of record).
    */
   notImplemented(event);
 
   updateStatus('updating');
   createReq({recID: gRecID, requestType: 'prepareRecordMerge'}, function(json){
     // Null gRecID to avoid warning when leaving page.
     gRecID = null;
     var recID = json['recID'];
     window.location = gSITE_URL + '/'+ gSITE_RECORD +'/merge/#recid1=' + recID + '&recid2=' +
       'tmp';
   });
   event.preventDefault();
 }
 
 function bindNewRecordHandlers(){
   /*
    * Bind event handlers to links on 'Create new record' page.
    */
   $('#lnkNewEmptyRecord').bind('click', function(event){
     updateStatus('updating');
     createReq({requestType: 'newRecord', newType: 'empty'}, function(json){
       getRecord(json['newRecID']);
     }, false);
     event.preventDefault();
   });
   for (var i=0, n=gRECORD_TEMPLATES.length; i<n; i++)
     $('#lnkNewTemplateRecord_' + i).bind('click', function(event){
       updateStatus('updating');
       var templateNo = this.id.split('_')[1];
       createReq({requestType: 'newRecord', newType: 'template',
 	templateFilename: gRECORD_TEMPLATES[templateNo][0]}, function(json){
 	  getRecord(json['newRecID'], 0, onGetTemplateSuccess); // recRev = 0 -> current revision
       }, false);
       event.preventDefault();
     });
 }
 
 function cleanUp(disableRecBrowser, searchPattern, searchType,
      focusOnSearchBox, resetHeadline){
   /*
    * Clean up display and data.
    */
   // Deactivate controls.
   deactivateRecordMenu();
   if (disableRecBrowser){
     disableRecordBrowser();
     gResultSet = null;
     gResultSetIndex = null;
     gNavigatingRecordSet = false;
   }
   // Clear main content area.
   /*if (resetHeadline)
     $('.headline').text('Record Editor');*/
   $('#bibEditContent').empty();
   // Clear search area.
   if (typeof(searchPattern) == 'string' || typeof(searchPattern) == 'number')
     $('#txtSearchPattern').val(searchPattern);
   if ($.inArray(searchType, ['recID', 'reportnumber', 'anywhere']) != -1)
     $('#sctSearchType').val(searchPattern);
   if (focusOnSearchBox)
     $('#txtSearchPattern').focus();
   // Clear tickets.
   $('#tickets').empty();
   // Clear data.
   gRecID = null;
   gRecord = null;
   gTagFormat = null;
   gRecordDirty = false;
   gCacheMTime = null;
   gSelectionMode = false;
   gReadOnlyMode = false;
   gHoldingPenLoadedChanges = null;
   gHoldingPenChanges = null;
   gUndoList = [];
   gRedoList = [];
   gBibCircUrl = null;
   gPhysCopiesNum = 0;
 }
 
 function addHandler_autocompleteAffiliations(tg) {
     /*
      * Add autocomplete handler to a given cell
      */
     /* If gKBInstitution is not defined in the system, do nothing */
     if ($.inArray(gKBInstitution,gAVAILABLE_KBS) == -1)
         return
     $(tg).autocomplete({
     source: function( request, response ) {
                 $.getJSON("/kb/export",
                 { kbname: gKBInstitution, format: 'jquery', term: request.term},
                 response);
     },
     search: function() {
                 var term = this.value;
                 if (term.length < 3) {
                     return false;
                 }
     }
     });
 }
 
 /*
  * **************************** 7. Editor UI ***********************************
  */
 
 function colorFields(){
   /*
    * Color every other field (rowgroup) gray to increase readability.
    */
   $('#bibEditTable tbody:even').each(function(){
     $(this).addClass('bibEditFieldColored');
   });
 }
 
 function reColorFields(){
   /*
    * Update coloring by removing existing, then recolor.
    */
   $('#bibEditTable tbody').each(function(){
     $(this).removeClass('bibEditFieldColored');
   });
   colorFields();
 }
 
 function onMARCTagsClick(event){
   /*
    * Handle 'MARC' link (MARC tags).
    */
   $(this).unbind('click').attr('disabled', 'disabled');
   createReq({recID: gRecID, requestType: 'changeTagFormat', tagFormat: 'MARC'});
   gTagFormat = 'MARC';
   updateTags();
   $('#btnHumanTags').bind('click', onHumanTagsClick).removeAttr('disabled');
   event.preventDefault();
 }
 
 function onHumanTagsClick(event){
   /*
    * Handle 'Human' link (Human tags).
    */
   $(this).unbind('click').attr('disabled', 'disabled');
   createReq({recID: gRecID, requestType: 'changeTagFormat',
        tagFormat: 'human'});
   gTagFormat = 'human';
   updateTags();
   $('#btnMARCTags').bind('click', onMARCTagsClick).removeAttr('disabled');
   event.preventDefault();
 }
 
 function onLnkSpecialSymbolsClick(){
     var special_char_list = ['&#192;','&#193;','&#194;','&#195;','&#196;','&#197;',
                             '&#198;','&#199;','&#200;','&#201;','&#202;','&#203;',
                             '&#204;','&#205;','&#206;','&#207;','&#208;','&#209;',
                             '&#210;','&#211;','&#212;','&#213;','&#214;','&#215;',
                             '&#216;','&#217;','&#218;','&#219;','&#220;','&#221;',
                             '&#222;','&#223;','&#224;','&#225;','&#226;','&#227;',
                             '&#228;','&#229;','&#230;','&#231;','&#232;','&#233;',
                             '&#234;','&#235;','&#236;','&#237;','&#238;','&#239;',
                             '&#240;','&#241;','&#242;','&#243;','&#244;','&#245;',
                             '&#246;','&#247;','&#248;','&#249;','&#250;','&#251;',
                             '&#252;','&#253;','&#254;','&#255;'];
     var html_content;
     html_content = '<html><head><title>Special Symbols</title>';
     html_content += '<style type="text/css">';
     html_content += '#char_table_div { padding: 20px 0px 0px 20px; }';
     html_content += '#symbol_table { border: 1px solid black; border-collapse:collapse;}';
     html_content += 'td { border: 1px solid black; padding: 5px 5px 5px 5px;}';
     html_content += '</style>';
     html_content += '</head><body>';
     html_content += '<div id="char_table_div"><table id="symbol_table"><tr>';
     var char_list_length = special_char_list.length;
     for (var i=0; i<char_list_length; i++) {
         html_content += '<td>' + special_char_list[i] + '</td>';
         if ((i+1)%10 == 0) {
             html_content += '</tr><tr>';
         }
     }
     html_content += '</tr></table></div></body></html>';
     var special_char_window = window.open('', '', 'width=310,height=310,resizeable,scrollbars');
     special_char_window.document.write(html_content);
     special_char_window.document.close(); // needed for chrome and safari
 }
 
 function updateTags(){
   /*
    * Check and update all tags (also subfield codes) against the currently
    * selected tag format.
    */
   $('.bibEditCellFieldTag').each(function(){
     var currentTag = $(this).text();
     var tmpArray = this.id.split('_');
     var tag = tmpArray[1], fieldPosition = tmpArray[2];
     var newTag = getFieldTag(getMARC(tag, fieldPosition));
     if (newTag != currentTag)
       $(this).text(newTag);
   });
   $('.bibEditCellSubfieldTag').each(function(){
     var currentTag = $(this).text();
     var tmpArray = this.id.split('_');
     var tag = tmpArray[1], fieldPosition = tmpArray[2],
       subfieldIndex = tmpArray[3];
     var newTag = getSubfieldTag(getMARC(tag, fieldPosition, subfieldIndex));
     if (newTag != currentTag)
       $(this).text(newTag);
   });
 }
 
 function onFieldBoxClick(box){
   /*
    * Handle field select boxes.
    */
   // Check/uncheck all subfield boxes, add/remove selected class.
   var rowGroup = $('#rowGroup_' + box.id.slice(box.id.indexOf('_')+1));
   if (box.checked){
     $(rowGroup).find('td[id^=content]').andSelf().addClass('bibEditSelected');
     if (gReadOnlyMode == false){
       $('#btnDeleteSelected').removeAttr('disabled');
     }
   }
   else{
     $(rowGroup).find('td[id^=content]').andSelf().removeClass(
       'bibEditSelected');
     if (!$('.bibEditSelected').length)
       // Nothing is selected, disable "Delete selected"-button.
       $('#btnDeleteSelected').attr('disabled', 'disabled');
   }
   $(rowGroup).find('input[type="checkbox"]').attr('checked', box.checked);
 }
 
 function onSubfieldBoxClick(box){
   /*
    * Handle subfield select boxes.
    */
   var tmpArray = box.id.split('_');
   var tag = tmpArray[1], fieldPosition = tmpArray[2],
     subfieldIndex = tmpArray[3];
   var fieldID = tag + '_' + fieldPosition;
   var subfieldID = fieldID + '_' + subfieldIndex;
   // If uncheck, uncheck field box and remove selected class.
   if (!box.checked){
     $('#content_' + subfieldID).removeClass('bibEditSelected');
     $('#boxField_' + fieldID).attr('checked', false);
     $('#rowGroup_' + fieldID).removeClass('bibEditSelected');
     if (!$('.bibEditSelected').length)
       // Nothing is selected, disable "Delete selected"-button.
       $('#btnDeleteSelected').attr('disabled', 'disabled');
   }
   // If check and all other subfield boxes checked, check field box, add
   // selected class.
   else{
     $('#content_' + subfieldID).addClass('bibEditSelected');
     var field = gRecord[tag][fieldPosition];
     if (field[0].length == $(
       '#rowGroup_' + fieldID + ' input[type=checkbox]' +
       '[class=bibEditBoxSubfield]:checked').length){
       $('#boxField_' + fieldID).attr('checked', true);
       $('#rowGroup_' + fieldID).addClass('bibEditSelected');
     }
     $('#btnDeleteSelected').removeAttr('disabled');
   }
 }
 
 function addFieldGatherInformations(fieldTmpNo){
   /**
    * Purpose: Gather the information about a current form
    * Called when adding x similar fields
    *
    * Input(s): int:fieldTmpNo - temporary number to identify the field being
    * added
    *
    * Returns: [template_num, data]
    * where data is in the same format as the templates data.
    *
   */
   var templateNum = $('#selectAddFieldTemplate_' + fieldTmpNo).attr("value");
   var tag = $("#txtAddFieldTag_" + fieldTmpNo).attr("value");
 
   // now checking if this is a controlfield ... controlfield if ind1 box is invisible
   if ($("#txtAddFieldInd1_" + fieldTmpNo + ":visible").length == 1){
     var ind1 = $("#txtAddFieldInd1_" + fieldTmpNo).attr("value");
     var ind2 = $("#txtAddFieldInd2_" + fieldTmpNo).attr("value");
     var subfieldTmpNo = $('#rowGroupAddField_' + fieldTmpNo).data('freeSubfieldTmpNo');
     var subfields = [];
     for (i=0;i<subfieldTmpNo;i++){
       var subfieldCode = $('#txtAddFieldSubfieldCode_' + fieldTmpNo + '_' + i).attr("value");
       var subfieldValue = $('#txtAddFieldValue_' + fieldTmpNo + '_' + i).attr("value");
       subfields.push([subfieldCode, subfieldValue]);
     }
 
     data = {
       "name": "nonexisting template - values taken from the field",
       "description": "The description of a template",
       "tag" : tag,
       "ind1" : ind1,
       "ind2" : ind2,
       "subfields" : subfields,
       "isControlfield" : false
     };
   } else {
     cfValue = $("#txtAddFieldValue_" + fieldTmpNo + "_0").attr("value");
     data = {
       "name": "nonexisting template - values taken from the field",
       "description": "The description of a template",
       "tag" : tag,
       "value" : cfValue,
       "isControlfield" : true
     }
   }
 
   return [templateNum, data];
 }
 
 function addFieldAddSubfieldEditor(jQRowGroupID, fieldTmpNo, defaultCode, defaultValue){
   /**
      Adding a subfield input control into the editor
      optional parameters:
 
      defaultCode - the subfield code that will be displayed
      defaultValue - the value that will be displayed by default in the editor
   */
   var subfieldTmpNo = $(jQRowGroupID).data('freeSubfieldTmpNo');
   $(jQRowGroupID).data('freeSubfieldTmpNo', subfieldTmpNo+1);
 
   var addFieldRows = $(jQRowGroupID + ' tr');
 
   $(addFieldRows).eq(addFieldRows.length-1).before(createAddFieldRow(
     fieldTmpNo, subfieldTmpNo, defaultCode, defaultValue));
   $('#txtAddFieldSubfieldCode_' + fieldTmpNo + '_' + subfieldTmpNo).bind(
     'keyup', onAddFieldChange);
   $('#btnAddFieldRemove_' + fieldTmpNo + '_' + subfieldTmpNo).bind('click', function(){
     $('#rowAddField_' + this.id.slice(this.id.indexOf('_')+1)).remove();
   });
   $('#txtAddFieldValue_' + fieldTmpNo + '_' + subfieldTmpNo).on(
     'focus', function(e){
       if ($(this).hasClass('bibEditVolatileSubfield')){
         $(this).select();
         $(this).removeClass("bibEditVolatileSubfield");
       }
     }
   ).on("mouseup", function(e) { e.preventDefault(); });
   var contentEditorId = '#txtAddFieldValue_' + fieldTmpNo + '_' + subfieldTmpNo;
   $(contentEditorId).bind('keyup', function(e){
     onAddFieldValueKeyPressed(e, jQRowGroupID, fieldTmpNo, subfieldTmpNo);
   });
 
 }
 
 function onAddFieldJumpToNextSubfield(jQRowGroupID, fieldTmpNo, subfieldTmpNo){
   /* Gets all the open text boxes for the current field and submits the changes
    * if it is the last one.
    */
   var fieldOpenInputs = $('input[id^="txtAddFieldValue_' + fieldTmpNo + '"]');
 
   var currentInputSelector = "#txtAddFieldValue_" + fieldTmpNo + "_" + subfieldTmpNo;
   var currentInput = $(currentInputSelector);
   var currentInputIndex = fieldOpenInputs.index(currentInput);
 
   if (currentInputIndex === fieldOpenInputs.length-1) {
     addFieldSave(fieldTmpNo);
   }
   else {
     fieldOpenInputs[currentInputIndex+1].focus();
   }
 }
 
 function applyFieldTemplate(jQRowGroupID, formData, fieldTmpNo){
   /** A function that applies a template
       formNo is the number of addfield form that is treated at teh moment
       formData is the data of the field template
   */
 
   // first cleaning the existing fields
 
   $(jQRowGroupID).data('isControlfield', formData.isControlfield);
   if (formData.isControlfield){
     changeFieldToControlfield(fieldTmpNo);
     $("#txtAddFieldTag_" + fieldTmpNo).attr("value", formData.tag);
     $("#txtAddFieldInd1_" + fieldTmpNo).attr("value", '');
     $("#txtAddFieldInd2_" + fieldTmpNo).attr("value", '');
     $("#txtAddFieldValue_" + fieldTmpNo + "_0").attr("value", formData.value);
   }
   else
   {
     changeFieldToDatafield(fieldTmpNo);
     var subfieldTmpNo = $(jQRowGroupID).data('freeSubfieldTmpNo');
     $(jQRowGroupID).data('freeSubfieldTmpNo', 0);
 
     for (i=subfieldTmpNo-1; i>=0; i--){
       $('#rowAddField_' + fieldTmpNo + '_' + i).remove();
     }
 
     for (subfieldInd in formData.subfields){
       subfield = formData.subfields[subfieldInd];
       addFieldAddSubfieldEditor(jQRowGroupID, fieldTmpNo, subfield[0], subfield[1]);
     }
 
     // now changing the main field properties
     $("#txtAddFieldTag_" + fieldTmpNo).attr("value", formData.tag);
     $("#txtAddFieldInd1_" + fieldTmpNo).attr("value", formData.ind1);
     $("#txtAddFieldInd2_" + fieldTmpNo).attr("value", formData.ind2);
   }
 }
 
 function createAddFieldInterface(initialContent, initialTemplateNo){
   /* Create form to add a new field. If only one field is selected, the
    * new field will be inserted below it. Otherwise, the new field will
    * be inserted in the 3rd position
    */
 
   // Check if we are in the use case of adding in a specific position
   var selected_fields = getSelectedFields();
   var insert_below_selected = false;
   if (selected_fields != undefined) {
       var count_fields = 0;
       var selected_local_field_pos;
       var selected_tag, selected_ind1, selected_ind2;
       for (var tag in selected_fields.fields) {
           for (var localFieldPos in selected_fields.fields[tag]) {
               count_fields++;
               selected_local_field_pos = localFieldPos;
           }
           selected_tag = tag;
           selected_ind1 = selected_fields.fields[tag][localFieldPos][1];
           selected_ind2 = selected_fields.fields[tag][localFieldPos][2];
       }
       if (count_fields === 1)
           insert_below_selected = true;
   }
 
   var fieldTmpNo = onAddFieldClick.addFieldFreeTmpNo++;
   var jQRowGroupID = '#rowGroupAddField_' + fieldTmpNo;
   $('#bibEditColFieldTag').css('width', '90px');
   var tbodyElements = $('#bibEditTable tbody');
 
   // If only one field selected, add below the selected field
   if (insert_below_selected === true) {
       $('#rowGroup' + '_' + selected_tag + '_' + selected_local_field_pos).after(
       createAddFieldForm(fieldTmpNo, initialTemplateNo, selected_tag, selected_ind1, selected_ind2));
       $(jQRowGroupID).data('insertionPoint', parseInt(selected_local_field_pos) + 1);
       $(jQRowGroupID).data('selected_tag', selected_tag);
   }
   else {
       var insertionPoint = (tbodyElements.length >= 4) ? 3 : tbodyElements.length-1;
       $('#bibEditTable tbody').eq(insertionPoint).after(
       createAddFieldForm(fieldTmpNo, initialTemplateNo));
   }
   
   $(jQRowGroupID).data('freeSubfieldTmpNo', 1);
 
   // Bind event handlers.
   $('#btnAddFieldAddSubfield_' + fieldTmpNo).bind('click', function(){
     addFieldAddSubfieldEditor(jQRowGroupID, fieldTmpNo, "", "");
   });
   $('#txtAddFieldTag_' + fieldTmpNo).bind('keyup', onAddFieldChange);
   $('#txtAddFieldInd1_' + fieldTmpNo).bind('keyup', onAddFieldChange);
   $('#txtAddFieldInd2_' + fieldTmpNo).bind('keyup', onAddFieldChange);
   $('#txtAddFieldSubfieldCode_' + fieldTmpNo + '_0').bind('keyup',
 							  onAddFieldChange);
   $('#txtAddFieldValue_' + fieldTmpNo + '_0').bind('keyup', function (e){
     onAddFieldValueKeyPressed(e, jQRowGroupID, fieldTmpNo, 0);
   });
 
   $('#selectAddFieldTemplate_' + fieldTmpNo).bind('change', function(e){
       value = $('#selectAddFieldTemplate_' + fieldTmpNo).attr("value");
       applyFieldTemplate(jQRowGroupID, fieldTemplates[value], fieldTmpNo);
   });
   $('#selectAddSimilarFields_' + fieldTmpNo).bind('click', function(e){
     var data = addFieldGatherInformations(fieldTmpNo);
     var numRepetitions = parseInt($('#selectAddFieldTemplateTimes_' + fieldTmpNo).attr('value'));
     for (var i=0; i< numRepetitions; i++){
       createAddFieldInterface(data[1], data[0]);
     }
   });
 
   if (initialContent != undefined){
     applyFieldTemplate(jQRowGroupID, initialContent , fieldTmpNo);
   }else{
     $(jQRowGroupID).data('isControlfield', false);
   }
   reColorFields();
   if (insert_below_selected === true) {
       $('#txtAddFieldSubfieldCode_' + fieldTmpNo + '_0').focus();
   }
   else {
       $('#txtAddFieldTag_' + fieldTmpNo).focus();
   }
   // Color the new form for a short period.
   $(jQRowGroupID).effect('highlight', {color: gNEW_ADD_FIELD_FORM_COLOR},
     gNEW_ADD_FIELD_FORM_COLOR_FADE_DURATION);
 
 }
 
 function onAddSubfieldValueKeyPressed(e, tag, fieldPosition, subfieldPosition){
   if (e.which == 13){
     // enter key pressed.
     var subfieldsNum = $('#rowGroup_' + tag + '_' + fieldPosition + ' .bibEditTxtSubfieldCode').length;
     if (subfieldPosition < (subfieldsNum - 1)){
       //jump to the next field
       $('#txtAddSubfieldsCode_' + tag + '_' + fieldPosition + '_' + (subfieldPosition + 1))[0].focus();
     } else {
       onAddSubfieldsSave(e, tag, fieldPosition);
     }
   }
   if (e.which == 27){
     // escape key pressed
     $('#rowAddSubfields_' + tag + '_' + fieldPosition + '_' + 0).nextAll().andSelf().remove();
   }
 }
 
 function onAddFieldValueKeyPressed(e, jQRowGroupID, fieldTmpNo, subfieldInd){
   if (e.which == 13){
     // enter key pressed
     onAddFieldJumpToNextSubfield(jQRowGroupID, fieldTmpNo, subfieldInd);
   }
   if (e.which == 27){
     // escape key pressed
     $(jQRowGroupID).remove();
     if (!$('#bibEditTable > [id^=rowGroupAddField]').length)
       $('#bibEditColFieldTag').css('width', '48px');
     reColorFields();
   }
 }
 function onAddFieldClick(){
   /*
    * Handle 'Add field' button.
    */
   if (failInReadOnly())
     return;
   createAddFieldInterface();
 }
 
 // Incrementing temporary field numbers.
 onAddFieldClick.addFieldFreeTmpNo = 100000;
 
 function changeFieldToControlfield(fieldTmpNo){
   /**
      Switching the field to be a control field
    */
 
   // removing additional entries
   var addFieldRows = $('#rowGroupAddField_' + fieldTmpNo + ' tr');
   $(addFieldRows).slice(2, addFieldRows.length-1).remove();
 
   // Clear all fields.
   var addFieldTextInput = $('#rowGroupAddField_' + fieldTmpNo +
           ' input[type=text]');
   $(addFieldTextInput).val('').removeClass('bibEditInputError');
 
   // Toggle hidden fields.
   var elems = $('#txtAddFieldInd1_' + fieldTmpNo + ', #txtAddFieldInd2_' +
     fieldTmpNo + ', #txtAddFieldSubfieldCode_' + fieldTmpNo + '_0,' +
     '#btnAddFieldAddSubfield_' + fieldTmpNo).hide();
 
   $('#txtAddFieldTag_' + fieldTmpNo).focus();
 }
 
 function changeFieldToDatafield(fieldTmpNo){
   /**
      Switching the field to be a datafield
    */
   // making the elements visible
   var elems = $('#txtAddFieldInd1_' + fieldTmpNo + ', #txtAddFieldInd2_' +
     fieldTmpNo + ', #txtAddFieldSubfieldCode_' + fieldTmpNo + '_0,' +
     '#btnAddFieldAddSubfield_' + fieldTmpNo).show();
 
   $('#txtAddFieldTag_' + fieldTmpNo).focus();
 }
 
 function onAddFieldChange(event){
   /*
    * Validate MARC and add or remove error class.
    */
 
   // first handling the case of escape key, which is a little different that others
   var fieldTmpNo = this.id.split('_')[1];
 
   if (event.which == 27){
     // escape key pressed
     var jQRowGroupID = "#rowGroupAddField_" + fieldTmpNo;
     $(jQRowGroupID).remove();
     if (!$('#bibEditTable > [id^=rowGroupAddField]').length)
       $('#bibEditColFieldTag').css('width', '48px');
     reColorFields();
   }
   else if (this.value.length == this.maxLength){
     var fieldType;
     if (this.id.indexOf('Tag') != -1){
       var jQRowGroupID = "#rowGroupAddField_" + fieldTmpNo;
       fieldType = ($(jQRowGroupID).data('isControlfield')) ? 'ControlTag' : 'Tag';
     }
     else if (this.id.indexOf('Ind1') != -1)
       fieldType = 'Indicator1';
     else if (this.id.indexOf('Ind2') != -1)
       fieldType = 'Indicator2';
     else
       fieldType = 'SubfieldCode';
 
     var valid = (((fieldType == 'Indicator1' || fieldType == 'Indicator2')
       && (this.value == '_' || this.value == ' '))
      || validMARC(fieldType, this.value));
     if (!valid && !$(this).hasClass('bibEditInputError'))
       $(this).addClass('bibEditInputError');
     else if (valid){
       if ($(this).hasClass('bibEditInputError'))
   $(this).removeClass('bibEditInputError');
       if (event.keyCode != 9 && event.keyCode != 16){
 	switch(fieldType){
 	  case 'ControlTag':
 	    $(this).parent().nextAll().eq(3).children('input').focus();
 	    break;
 	  case 'Tag':
 	  case 'Indicator1':
 	    $(this).next().focus();
 	    break;
 	  case 'Indicator2':
           // in case the indicator is present, we can be sure this is not a control field... so we can safely jump to the subfield code input
           $('#txtAddFieldSubfieldCode_' + fieldTmpNo + '_0')[0].focus();
 	    break;
 	  case 'SubfieldCode':
             /* Generate ID of the field tag input */
             var fieldTagID = ('#' + $(this).attr('id').replace('SubfieldCode', 'Tag')).split('_');
             fieldTagID.pop();
             fieldTagID = fieldTagID.join('_');
             var fieldTag = $(this).parent().prev().prev().children().eq(0).val(),
                 fieldInd1 = $(this).parent().prev().prev().children().eq(1).val(),
                 fieldInd2 = $(this).parent().prev().prev().children().eq(2).val();
             if (fieldInd1 == '') {
                 fieldInd1 = '_';
             }
             if (fieldInd2 == '') {
                 fieldInd2 = '_';
             }
             if ($.inArray(fieldTag +  fieldInd1 + fieldInd2 + this.value, gTagsToAutocomplete) != -1) {
                 addHandler_autocompleteAffiliations($(this).parent().next().children('input'));
             }
 	    $(this).parent().next().children('input').focus();
 	    break;
 	  default:
 	    ;
 	}
       }
     }
   }
   else if ($(this).hasClass('bibEditInputError'))
     $(this).removeClass('bibEditInputError');
 }
 
 function onAddFieldSave(event){
   var fieldTmpNo = this.id.split('_')[1];
   addFieldSave(fieldTmpNo);
 }
 
 function addFieldSave(fieldTmpNo)
 {
   /*
    * Handle 'Save' button in add field form.
    */
   updateStatus('updating');
 
   var jQRowGroupID = "#rowGroupAddField_" + fieldTmpNo;
   var controlfield = $(jQRowGroupID).data('isControlfield');
   var tag = $('#txtAddFieldTag_' + fieldTmpNo).val();
   var value = $('#txtAddFieldValue_' + fieldTmpNo + '_0').val();
   var subfields = [], ind1 = ' ', ind2 = ' ';
 
   // variables used when we are adding a field in a specific position
   var insertPosition = $(jQRowGroupID).data('insertionPoint');
   var selected_tag = $(jQRowGroupID).data('selected_tag');
 
   if (controlfield){
     // Controlfield. Validate and prepare to update.
     if (fieldIsProtected(tag)){
       displayAlert('alertAddProtectedField', [tag]);
       updateStatus('ready');
       return;
     }
     if (!validMARC('ControlTag', tag) || value == ''){
       displayAlert('alertCriticalInput');
       updateStatus('ready');
       return;
     }
     var field = [[], ' ', ' ', value, 0];
     var fieldPosition = getFieldPositionInTag(tag, field);
   }
   else{
     // Regular field. Validate and prepare to update.
     ind1 = $('#txtAddFieldInd1_' + fieldTmpNo).val();
     ind1 = (ind1 == '' || ind1 == '_') ? ' ' : ind1;
     ind2 = $('#txtAddFieldInd2_' + fieldTmpNo).val();
     ind2 = (ind2 == '' || ind2 == '_') ? ' ' : ind2;
     var MARC = tag + ind1 + ind2;
     if (fieldIsProtected(MARC)){
       displayAlert('alertAddProtectedField', [MARC]);
       updateStatus('ready');
       return;
     }
     var validInd1 = (ind1 == ' ' || validMARC('Indicator1', ind1));
     var validInd2 = (ind2 == ' ' || validMARC('Indicator2', ind2));
     if (!validMARC('Tag', tag)
   || !validInd1
   || !validInd2){
       displayAlert('alertCriticalInput');
       updateStatus('ready');
       return;
     }
     // Collect valid subfields in an array.
     var invalidOrEmptySubfields = false;
      $('#rowGroupAddField_' + fieldTmpNo + ' .bibEditTxtSubfieldCode'
       ).each(function(){
         var subfieldTmpNo = this.id.slice(this.id.lastIndexOf('_')+1);
         var txtValue = $('#txtAddFieldValue_' + fieldTmpNo + '_' +
     subfieldTmpNo);
         var value = $(txtValue).val();
         value = value.replace(/^\s+|\s+$/g,""); // Remove whitespace from the ends of strings
         var isStillVolatile = txtValue.hasClass('bibEditVolatileSubfield');
 
         if (!$(this).hasClass('bibEditInputError')
           && this.value != ''
 	  && !$(txtValue).hasClass('bibEditInputError')
           && value != ''){
             if (!isStillVolatile){
               subfields.push([this.value, value]);
             }
         }
         else
           invalidOrEmptySubfields = true;
       });
 
     if (invalidOrEmptySubfields){
       if (!subfields.length){
   // No valid subfields.
   displayAlert('alertCriticalInput');
   updateStatus('ready');
   return;
       }
       else if (!displayAlert('confirmInvalidOrEmptyInput')){
   updateStatus('ready');
   return;
       }
     }
 
     if (subfields[0] == undefined){
       displayAlert('alertEmptySubfieldsList');
       return;
     }
     var field = [subfields, ind1, ind2, '', 0];
     var fieldPosition;
     if ((insertPosition != undefined) && (tag == selected_tag)) {
         fieldPosition = $(jQRowGroupID).data('insertionPoint');
     }
     else {
         fieldPosition = getFieldPositionInTag(tag, field);
     }
   }
 
   var subfieldsExtended = [];
   /* Loop through all subfields to look for new subfields in the format
    * $$aContent$$bMore content and split them accordingly */
   for (var i=0, n=subfields.length; i<n ;i++) {
       if (valueContainsSubfields(subfields[i][1])) {
           var subfieldsToAdd = new Array(), subfieldCode = subfields[i][0];
           splitContentSubfields(subfields[i][1], subfieldCode, subfieldsToAdd);
           subfieldsExtended.push.apply(subfieldsExtended,subfieldsToAdd);
       }
       else{
           subfieldsExtended.push(subfields[i]);
       }
   }
   if (typeof subfieldsExtended[0] != 'undefined') {
       /* We have split some subfields */
       for (var i=0, n=subfieldsExtended.length; i < n; i++) {
           subfields[i] = subfieldsExtended[i];
       }
   }
 
   /* If adding a reference, add $$9 CURATOR */
   if (tag == '999') {
       subfields[subfields.length] = new Array('9', 'CURATOR');
   }
 
   // adding an undo handler
   var undoHandler = prepareUndoHandlerAddField(tag,
                                                ind1,
                                                ind2,
                                                fieldPosition,
                                                subfields,
                                                controlfield,
                                                value);
   addUndoOperation(undoHandler);
 
   // Create Ajax request.
   var data = {
     recID: gRecID,
     requestType: 'addField',
     controlfield: controlfield,
     fieldPosition: fieldPosition,
     tag: tag,
     ind1: ind1,
     ind2: ind2,
     subfields: subfields,
     value: value,
     undoRedo: undoHandler
   };
   createReq(data, function(json){
     updateStatus('report', gRESULT_CODES[json['resultCode']]);
   }, false);
 
   // Continue local updating.
   var fields = gRecord[tag];
   // New field?
   if (!fields) {
     gRecord[tag] = [field];
   }
   else{
     fields.splice(fieldPosition, 0, field);
   }
   // Remove form.
   $('#rowGroupAddField_' + fieldTmpNo).remove();
   if (!$('#bibEditTable > [id^=rowGroupAddField]').length)
       $('#bibEditColFieldTag').css('width', '48px');
   // Redraw all fields with the same tag and recolor the full table.
   redrawFields(tag);
   reColorFields();
   // Scroll and color the new field for a short period.
   var rowGroup = $('#rowGroup_' + tag + '_' + fieldPosition);
   if (insertPosition === undefined) {
     $('#bibEditContent').scrollTop($(rowGroup).position().top);
   }
   $(rowGroup).effect('highlight', {color: gNEW_CONTENT_COLOR},
          gNEW_CONTENT_COLOR_FADE_DURATION);
 }
 
 
 function onAddSubfieldsClick(img){
   /*
    * Handle 'Add subfield' buttons.
    */
   var fieldID = img.id.slice(img.id.indexOf('_')+1);
   addSubfield(fieldID);
 }
 
 function addSubfield(fieldID, defSubCode, defValue) {
   /* add a subfield based on fieldID, where the first 3 digits are
    * the main tag, followed by _ and the position of the field.
    * defSubCode = the default value for subfield code
   */
   var jQRowGroupID = '#rowGroup_' + fieldID;
   var tmpArray = fieldID.split('_');
   var tag = tmpArray[0];var fieldPosition = tmpArray[1];
   if ($('#rowAddSubfieldsControls_' + fieldID).length == 0){
     // The 'Add subfields' form does not exist for this field.
     $(jQRowGroupID).append(createAddSubfieldsForm(fieldID, defSubCode, defValue));
     $(jQRowGroupID).data('freeSubfieldTmpNo', 1);
     $('#txtAddSubfieldsCode_' + fieldID + '_' + 0).bind('keyup',
       onAddSubfieldsChange);
     $('#txtAddSubfieldsValue_' + fieldID + '_0').bind('keyup', function (e){
       onAddSubfieldValueKeyPressed(e, tag, fieldPosition, 0);
     });
     $('#txtAddSubfieldsCode_' + fieldID + '_' + 0).focus();
   }
   else{
     // The 'Add subfields' form exist for this field. Just add another row.
     var subfieldTmpNo = $(jQRowGroupID).data('freeSubfieldTmpNo');
     $(jQRowGroupID).data('freeSubfieldTmpNo', subfieldTmpNo+1);
     var subfieldTmpID = fieldID + '_' + subfieldTmpNo;
     $('#rowAddSubfieldsControls_' + fieldID).before(
       createAddSubfieldsRow(fieldID, subfieldTmpNo));
     $('#txtAddSubfieldsCode_' + subfieldTmpID).bind('keyup',
       onAddSubfieldsChange);
     $('#btnAddSubfieldsRemove_' + subfieldTmpID).bind('click', function(){
       $('#rowAddSubfields_' + subfieldTmpID).remove();
     });
     $('#txtAddSubfieldsValue_' + subfieldTmpID).bind('keyup', function (e){
       onAddSubfieldValueKeyPressed(e, tag, fieldPosition, subfieldTmpNo);
     });
   }
 }
 
 function onAddSubfieldsChange(event){
   /*
    * Validate subfield code and add or remove error class.
    */
   if (this.value.length == 1){
     var valid = validMARC('SubfieldCode', this.value);
     if (!valid && !$(this).hasClass('bibEditInputError')){
       $(this).addClass('bibEditInputError');
     }
     else if (valid){
       if ($(this).hasClass('bibEditInputError')) {
         $(this).removeClass('bibEditInputError');
       }
       if (event.keyCode != 9 && event.keyCode != 16){
         /* If we are creating a new field present in gTagsToAutocomplete, add autocomplete handler */
         var fieldInfo = $(this).parents("tr").siblings().eq(0).children().eq(1).html();
         if ($.inArray(fieldInfo + this.value, gTagsToAutocomplete) != -1) {
           addHandler_autocompleteAffiliations($(this).parent().next().children('input'));
         }
         $(this).parent().next().children('input').focus();
       }
     }
   }
   else if ($(this).hasClass('bibEditInputError')){
     $(this).removeClass('bibEditInputError');
   }
 }
 
 function onAddSubfieldsSave(event, tag, fieldPosition){
   /*
    * Handle 'Save' button in add subfields form.
    */
   updateStatus('updating');
 
   var fieldID = tag + '_' + fieldPosition;
   var subfields = [];
   var protectedSubfield = false, invalidOrEmptySubfields = false;
   // Collect valid fields in an array.
   $('#rowGroup_' + fieldID + ' .bibEditTxtSubfieldCode'
    ).each(function(){
      var MARC = getMARC(tag, fieldPosition) + this.value;
      if ($.inArray(MARC, gPROTECTED_FIELDS) != -1){
        protectedSubfield = MARC;
        return false;
      }
      var subfieldTmpNo = this.id.slice(this.id.lastIndexOf('_')+1);
      var txtValue = $('#txtAddSubfieldsValue_' + fieldID + '_' +
        subfieldTmpNo);
      var value = $(txtValue).val();
      if (!$(this).hasClass('bibEditInputError')
    && this.value != ''
    && !$(txtValue).hasClass('bibEditInputError')
    && value != '')
        subfields.push([this.value, value]);
      else
        invalidOrEmptySubfields = true;
   });
 
   // Report problems, like protected, empty or invalid fields.
   if (protectedSubfield){
     displayAlert('alertAddProtectedSubfield');
     updateStatus('ready');
     return;
   }
   if (invalidOrEmptySubfields && !displayAlert('confirmInvalidOrEmptyInput')){
     updateStatus('ready');
     return;
   }
 
   if (!subfields.length == 0){
       /* Loop through all subfields to look for new subfields in the format
        * $$aContent$$bMore content and split them accordingly */
       var subfieldsExtended = [];
       for (var i=0; i<subfields.length;i++) {
           if (valueContainsSubfields(subfields[i][1])) {
               var subfieldsToAdd = new Array(), subfieldCode = subfields[i][0];
               splitContentSubfields(subfields[i][1], subfieldCode, subfieldsToAdd);
               subfieldsExtended.push.apply(subfieldsExtended,subfieldsToAdd);
           }
           else{
               subfieldsExtended.push(subfields[i]);
           }
       }
       if (typeof subfieldsExtended[0] != 'undefined') {
           /* We have split some subfields */
           for (var i=0;i<subfieldsExtended.length;i++) {
               subfields[i] = subfieldsExtended[i];
           }
       }
      // creating the undo/redo handler
     var urHandler = prepareUndoHandlerAddSubfields(tag, fieldPosition, subfields);
     addUndoOperation(urHandler);
     // Create Ajax request
     var data = {
       recID: gRecID,
       requestType: 'addSubfields',
       tag: tag,
       fieldPosition: fieldPosition,
       subfields: subfields,
       undoRedo: urHandler
     };
     createReq(data, function(json){
       updateStatus('report', gRESULT_CODES[json['resultCode']]);
     }, false);
 
     // Continue local updating
     var field = gRecord[tag][fieldPosition];
     field[0] = field[0].concat(subfields);
     var rowGroup  = $('#rowGroup_' + fieldID);
     var coloredRowGroup = $(rowGroup).hasClass('bibEditFieldColored');
     $(rowGroup).replaceWith(createField(tag, field, fieldPosition));
     if (coloredRowGroup)
       $('#rowGroup_' + fieldID).addClass('bibEditFieldColored');
 
     // Color the new fields for a short period.
     var rows = $('#rowGroup_' + fieldID + ' tr');
     $(rows).slice(rows.length - subfields.length).effect('highlight', {
       color: gNEW_CONTENT_COLOR}, gNEW_CONTENT_COLOR_FADE_DURATION);
   }
   else{
     // No valid fields were submitted.
     $('#rowAddSubfields_' + fieldID + '_' + 0).nextAll().andSelf().remove();
     updateStatus('ready');
   }
 }
 
 function convertFieldIntoEditable(cell, shouldSelect){
   // chacking if the clicked field is still present int the DOM structure ... if not, we have just removed the element
   if ($(cell).parent().parent().parent()[0] == undefined){
     return;
   }
   // first we have to detach all exisiting editables ... which means detaching the event
   editEvent = 'click';
   $(cell).unbind(editEvent);
 
   $(cell).editable(
     function(value){
       newVal = onContentChange(value, this);
       if (newVal.substring(0,9) == "VOLATILE:"){
         $(cell).addClass("bibEditVolatileSubfield");
         newVal = newVal.substring(9);
         $(cell).addClass("bibEditVolatileSubfield");
         if (!shouldSelect){
           // the field should start selecting all the content upon the click
           convertFieldIntoEditable(cell, true);
         }
       }
       else{
         $(cell).removeClass("bibEditVolatileSubfield");
         if (shouldSelect){
           // this is a volatile field any more - clicking should not
           // select all the content inside.
           convertFieldIntoEditable(cell, false);
         }
       }
 
       return newVal;
     }, {
       type: 'textarea',
       callback: function(data, settings){
         var tmpArray = this.id.split('_');
         var tag = tmpArray[1], fieldPosition = tmpArray[2],
         subfieldIndex = tmpArray[3];
 
         for (changeNum in gHoldingPenChanges){
           change =  gHoldingPenChanges[changeNum];
           if (change.tag == tag &&
               change.field_position == fieldPosition &&
               change.subfield_position != undefined &&
               change.subfield_position == subfieldIndex){
               addChangeControl(changeNum, true);
           }
         }
       },
       event: editEvent,
       data: function(){
         // Get the real content from the record structure (instead of
         // from the view, where HTML entities are escaped).
         var tmpArray = this.id.split('_');
         var tag = tmpArray[1], fieldPosition = tmpArray[2],
         subfieldIndex = tmpArray[3];
         var field = gRecord[tag][fieldPosition];
         var tmpResult = "";
         if (tmpArray[0] == 'fieldTag'){
             var ind1 = (field[1] == " ") ? "_" : field[1];
             var ind2 = (field[2] == " ") ? "_" : field[2];
             tmpResult = tag + ind1 + ind2;
         }
         else if (subfieldIndex == undefined){
           // Controlfield
           tmpResult = field[3];
         }
         else if (tmpArray[0] == 'subfieldTag'){
             tmpResult = field[0][subfieldIndex][0];
         }
         else {
             tmpResult = field[0][subfieldIndex][1];
         }
         if (tmpResult.substring(0,9) == "VOLATILE:"){
           tmpResult = tmpResult.substring(9);
         }
         return tmpResult;
       },
       placeholder: '',
       onblur: 'submit',
       select: shouldSelect
     });
 }
 
 function onContentClick(cell){
   /*
    * Handle click on editable content fields.
    */
   // Check if subfield is volatile subfield from a template
   var shouldSelect = false;
   if ( $(cell).hasClass('bibEditVolatileSubfield') ){
     shouldSelect = true;
   }
   if (!$(cell).hasClass('edit_area')){
     $(cell).addClass('edit_area').removeAttr('onclick');
     convertFieldIntoEditable(cell, shouldSelect);
     $(cell).trigger('click');
   }
 }
 
 function getUpdateSubfieldValueRequestData(tag, fieldPosition, subfieldIndex, 
         subfieldCode, value, changeNo, undoDescriptor, modifySubfieldCode){
   var requestType;
   if (modifySubfieldCode == true) {
       requestType = 'modifySubfieldTag';
   }
   else {
       requestType = 'modifyContent';
   }
   var data = {
     recID: gRecID,
     requestType: requestType,
     tag: tag,
     fieldPosition: fieldPosition,
     subfieldIndex: subfieldIndex,
     subfieldCode: subfieldCode,
     value: value
   };
   if (changeNo != undefined && changeNo != -1){
     data.hpChanges = {toDisable: [changeNo]};
   }
   if (undoDescriptor != undefined && undoDescriptor != null){
     data.undoRedo = undoDescriptor;
   }
   return data;
 }
 
 function updateSubfieldValue(tag, fieldPosition, subfieldIndex, subfieldCode, 
                             value, consumedChange, undoDescriptor,
                             modifySubfieldCode){
   updateStatus('updating');
   // Create Ajax request for simple updating the subfield value
   if (consumedChange == undefined || consumedChange == null){
     consumedChange = -1;
   }
   
   var data = getUpdateSubfieldValueRequestData(tag,
                                                fieldPosition,
                                                subfieldIndex,
                                                subfieldCode,
                                                value,
                                                consumedChange,
                                                undoDescriptor,
                                                modifySubfieldCode);
 
   createReq(data, function(json){
     updateStatus('report', gRESULT_CODES[json['resultCode']]);
   }, false);
 }
 
 function getBulkUpdateSubfieldContentRequestData(tag, fieldPosition,
                                                  subfieldIndex, subfieldCode,
                                                  value, consumedChange,
                                                  undoDescriptor, subfieldsToAdd, subfield_offset) {
     /*
      *Purpose: prepare data to be included in the request for a bulk update
      *         of the subfield content
      *
      *Return: object: Array of changes to be applied
      *
      */
     if (!subfield_offset){
         var subfield_offset = 1;
     }
 
     var changesAdd = [];
 
     var data = getUpdateSubfieldValueRequestData(tag,
                                                  fieldPosition,
                                                  subfieldIndex,
                                                  subfieldCode,
                                                  value,
                                                  consumedChange,
                                                  null,
                                                  false);
     changesAdd.push(data);
 
     data = {
       recID: gRecID,
       requestType: 'addSubfields',
       tag: tag,
       fieldPosition: fieldPosition,
       subfields: subfieldsToAdd.slice(subfield_offset)
     };
     changesAdd.push(data);
 
     return changesAdd
 }
 
 function bulkUpdateSubfieldContent(tag, fieldPosition, subfieldIndex, subfieldCode,
                             value, consumedChange, undoDescriptor, subfieldsToAdd, subfields_offset) {
     /*
      *Purpose: perform request for a bulk update as the user introduced in the content
      *         field multiple subfields to be added in the form $$aTest$$bAnother
      *
      *Input(s): string:tag - Field tag to be updated
      *          int:fieldPosition - position of the field with regard to the rest
      *                              of fields with the same tag
      *          int:subfieldIndex - position of the subfield with regard to the
      *                              other subfields in that field instance
      *          string:subfieldCode - Code of the subfield that is being modified
      *          string:value - old value present in the subfield
      *          consumedChange - undefined behaviour
      *          object:undoDescriptor - undo operations relative to the update
      *                                  action
      *          object:subfieldsToAdd - array containing subfields to add)
      *
      */
     updateStatus('updating');
     if (consumedChange == undefined || consumedChange == null){
         consumedChange = -1;
     }
 
     var data = getBulkUpdateSubfieldContentRequestData(tag,
                                                fieldPosition,
                                                subfieldIndex,
                                                subfieldCode,
                                                value,
                                                consumedChange,
                                                undoDescriptor,
                                                subfieldsToAdd,
                                                subfields_offset);
     var optArgs = {
       undoRedo: undoDescriptor
     };
     createBulkReq(data, function(json){
       updateStatus('report', gRESULT_CODES[json['resultCode']])}, optArgs);
 
     redrawFields(tag);
     reColorFields();
 
 
 }
 
 function updateFieldTag(oldTag, newTag, oldInd1, oldInd2, ind1, ind2, fieldPosition,
                         consumedChange, undoDescriptor){
   updateStatus('updating');
   // Create Ajax request for simple updating the subfield value
   if (consumedChange == undefined || consumedChange == null){
       consumedChange = -1;
   }
   var data = getUpdateFieldTagRequestData(oldTag,
                                           oldInd1,
                                           oldInd2,
                                           newTag,
                                           ind1,
                                           ind2,
                                           fieldPosition,
                                           consumedChange,
                                           undoDescriptor);
 
   createReq(data, function(json){
     updateStatus('report', gRESULT_CODES[json['resultCode']]);
   }, false);
 }
 
 function getUpdateFieldTagRequestData(oldTag, oldInd1, oldInd2, newTag, ind1, ind2,
                                       fieldPosition, changeNo, undoDescriptor){
   var data = {
     recID: gRecID,
     requestType: "modifyFieldTag",
     fieldPosition: fieldPosition,
     oldTag: oldTag,
     newTag: newTag,
     ind1: ind1,
     ind2: ind2,
     oldInd1: oldInd1,
     oldInd2: oldInd2
   };
   if (changeNo != undefined && changeNo != -1){
     data.hpChanges = {toDisable: [changeNo]};
   }
   if (undoDescriptor != undefined && undoDescriptor != null){
     data.undoRedo = undoDescriptor;
   }
 
   // updating the local model
   var currentField = gRecord[oldTag][fieldPosition];
   currentField[1] = ind1;
   currentField[2] = ind2;
   gRecord[oldTag].splice(fieldPosition,1);
   if (gRecord[oldTag].length == 0){
       delete gRecord[oldTag];
   }
   var fieldNewPos;
   if (gRecord[newTag] == undefined) {
       fieldNewPos = 0;
       gRecord[newTag] = [];
       gRecord[newTag][fieldNewPos] = currentField;
   }
   else {
       fieldNewPos = gRecord[newTag].length;
       gRecord[newTag].splice(fieldNewPos, 0, currentField);
   }
   // changing the display .... what if being edited right now ?
   redrawFields(oldTag);
   redrawFields(newTag);
   reColorFields();
 
   return data;
 }
 
 /*call autosuggest, get the values, suggest them to the user*/
 /*this is typically called when autosuggest key is pressed*/
 function onAutosuggest(event) {
   var mytarget = event.target;
   if (event.srcElement) mytarget = event.srcElement;/*fix for IE*/
   var myparent = mytarget.parentNode;
   var mygrandparent = myparent.parentNode;
   var parentid = myparent.id;
   var value = mytarget.value;
   var mylen = value.length;
   var replacement = ""; //used by autocomplete
   var tmpArray = mygrandparent.id.split('_');
   /*ids for autosuggest/autocomplete html elements*/
   var content_id = 'content_'+tmpArray[1]+'_'+tmpArray[2]+'_'+tmpArray[3];
   var autosuggest_id = 'autosuggest_'+tmpArray[1]+'_'+tmpArray[2]+'_'+tmpArray[3];
   var select_id = 'select_'+tmpArray[1]+'_'+tmpArray[2]+'_'+tmpArray[3];
   var maintag = tmpArray[1], fieldPosition = tmpArray[2],
 	  subfieldIndex = tmpArray[3];
   var field = gRecord[maintag][fieldPosition];
   var subfieldcode = field[0][subfieldIndex][0];
   var subtag1 = field[1];
   var subtag2 = field[2];
   //check if this an autosuggest or autocomplete field.
   var fullcode = getMARC(maintag, fieldPosition, subfieldIndex);
   var reqtype = ""; //autosuggest or autocomplete, according to tag..
   for (var i=0;i<gAUTOSUGGEST_TAGS.length;i++) {if (fullcode == gAUTOSUGGEST_TAGS[i]) {reqtype = "autosuggest"}}
   for (var i=0;i<gAUTOCOMPLETE_TAGS.length;i++) {if (fullcode == gAUTOCOMPLETE_TAGS[i]) {reqtype = "autocomplete"}}
   if (fullcode == gKEYWORD_TAG) {reqtype = "autokeyword"}
   if (reqtype == "") {
     return;
   }
 
   // Create Ajax request.
   var data = {
     recID: gRecID,
     maintag: maintag,
     subtag1: subtag1,
     subtag2: subtag2,
     subfieldcode: subfieldcode,
     requestType: reqtype,
     value: value
   }; //reqtype is autosuggest, autocomplete or autokeyword
   createReq(data, function(json){
     updateStatus('report', gRESULT_CODES[json['resultCode']]);
     suggestions = json[reqtype];
     if (reqtype == 'autocomplete') {
         if ((suggestions != null) && (suggestions.length > 0)) {
             //put the first one "here"
             replacement = suggestions[0];
             var myelement = document.getElementById(mygrandparent.id);
             if (myelement != null) {
                //put in the the gRecord
                gRecord[maintag][fieldPosition][0][subfieldIndex][1] = replacement;
                mytarget.value = replacement;
             }
             //for the rest, create new subfields
             for (var i=1, n=suggestions.length; i < n; i++) {
                 var valuein = suggestions[i];
                 var addhereID = maintag+"_"+fieldPosition; //an id to indicate where the new subfield goes
                 addSubfield(addhereID, subfieldcode, valuein);
             }
         } else { //autocomplete, nothing found
             alert("No suggestions for your search term "+value);
         }
     } //autocomplete
     if ((reqtype == 'autosuggest') || (reqtype == 'autokeyword')) {
         if ((suggestions != null) && (suggestions.length > 0)) {
             /*put the suggestions in the div autosuggest_xxxx*/
             //make a nice box..
             mysel = '<table width="400" border="0"><tr><td><span class="bibeditscrollArea"><ul>';
             //create the select items..
             for (var i=0, n=suggestions.length; i < n; i++) {
                tmpid = select_id+"-"+suggestions[i];
                mysel = mysel +'<li onClick="onAutosuggestSelect(\''+tmpid+'\');">'+suggestions[i]+"</li>";
             }
             mysel = mysel+"</ul></td>"
             //add a stylish close link in case the user does not find
             //the value among the suggestions
             mysel = mysel + "<td><form><input type='button' value='close' onClick='onAutosuggestSelect(\""+select_id+"-"+'\");></form></td>';
             mysel = mysel+"</tr></table>";
             //for (var i=0;i<suggestions.length;i++) { mysel = mysel + +suggestions[i]+ " "; }
             autosugg_in = document.getElementById(autosuggest_id);
             if (autosugg_in != null) {autosugg_in.innerHTML = mysel;}
          } else { //there were no suggestions
              alert("No suggestions for your search term "+value);
          }
     } //autosuggest
   }, false); /*NB! This function is called synchronously.*/
 } //onAutoSuggest
 
 
 /*put the content of the autosuggest select into the field where autoselect was lauched*/
 function onAutosuggestSelect(selectidandselval){
   /*first take the selectid. It is the string before the first hyphen*/
   var tmpArray = selectidandselval.split('-');
   var selectid = tmpArray[0];
   var selval =  tmpArray[1];
   /*generate the content element id and autosuggest element id from the selectid*/
   var tmpArray = selectid.split('_');
   var content_id = 'content_'+tmpArray[1]+'_'+tmpArray[2]+'_'+tmpArray[3];
   var autosuggest_id = 'autosuggest_'+tmpArray[1]+'_'+tmpArray[2]+'_'+tmpArray[3];
   var content_t = document.getElementById(content_id); //table
   var content = null; //the actual text
   //this is interesting, since if the user is browsing the list of selections by mouse,
   //the autogrown form has disapperaed and there is only the table left.. so check..
   if (content_t.innerHTML.indexOf("<form>") ==0) {
      var content_f = null; //form
      var content_ta = null; //textarea
      if (content_t) {
          content_f = content_t.firstChild; //form is the sub-elem of table
      }
      if (content_f) {
          content_ta = content_f.firstChild; //textarea is the sub-elem of form
      }
      if (!(content_ta)) {return;}
      content = content_ta;
   } else {
      content = content_t;
   }
   /*put value in place*/
   if (selval) {
       content.innerHTML = selval;
       content.value = selval;
   }
   /*remove autosuggest box*/
   var autosugg_in = document.getElementById(autosuggest_id);
   autosugg_in.innerHTML = "";
 }
 
 function check_subjects_KB(value) {
     /*
      * Query Subjects KB to look for a match
      */
     /* If KB is not defined in the system, just return value*/
     if ($.inArray(gKBSubject,gAVAILABLE_KBS) == -1)
         return value;
     var response='';
     $.ajaxSetup({async:false});
     $.getJSON("/kb/export",
              { kbname: gKBSubject, format: 'json', searchkey: value},
              function(data) {if (data[0]) {response = data[0].label;}}
              );
     $.ajaxSetup({async:true});
     if (response) {
         return response;
     }
     return value;
 }
 
 /* ---- Helper functions for adding subfields into the subfield content ---- */
 
 function valueContainsSubfields(value) {
     /*
      * Purpose: Check if value has subfields inside. E.g. test$$xAnother test
      *
      * Input(s): string:value - value introduced into the subfield
      *
      * Returns: boolean - true (subfields inside), false (no subfields)
      */
     var regExp = new RegExp(".*\\$\\$[0-9a-zA-Z].*");
     return regExp.test(value);
 }
 
 function splitContentSubfields(value, subfieldCode, subfieldsToAdd) {
     /*
      * Purpose: split content into pairs subfield index - subfield value
      *
      * Input(s): string:value - value introduced into the subfield
      *           Array:subfieldsToAdd - will contain all subfields extracted
      *
      */
     var splitValue = value.split('$$');
     subfieldsToAdd.push(new Array(subfieldCode, splitValue[0]));
     for (var i=1, n=splitValue.length; i<n; i++) {
         subfieldsToAdd.push(new Array(splitValue[i][0], splitValue[i].substring(1)));
     }
 }
 
 function is_reference_manually_curated(field){
     for (var i=0, n=field[0].length; i < n; i++) {
         if (field[0][i][0] == '9' && field[0][i][1] == "CURATOR")
             return true;
     }
     return false;
 }
 
 function onContentChange(value, th){
   /*
    * Purpose: jEditable callback when the user hits enter in the editable field.
    * Input(s): string:value - the new content value
    *           object:th - this (table cell)
    *
    * Returns: string - string to be introduced in the cell th
    */
    if (failInReadOnly()){
     return;
   }
   /* Extract information about the field to edit from cell id */
   var tmpArray = th.id.split('_');
   var tag = tmpArray[1], fieldPosition = tmpArray[2], subfieldIndex = tmpArray[3];
   var cellType = tmpArray[0];
 
   /* Get field instance to be updated from global variable gRecord */
   var field = gRecord[tag][fieldPosition];
   var tag_ind = tag + field[1] + field[2]; // tag + indicators. e.g 999C5
 
   /* Sanitize cell input value */
   value = value.replace(/\n/g, ' '); // Replace newlines with spaces.
   value = value.replace(/^\s+|\s+$/g,""); // Remove whitespace from the ends of strings
 
   var oldValue = ""; //variable that will contain old value from gRecord
   if (subfieldIndex == undefined){
     /* Edit field tag */
     if (cellType == 'fieldTag') {
         if (tag_ind == value.replace(/_/g, " "))
             return escapeHTML(value);
         else {
             oldValue = tag_ind;
         }
     }
     else{
         /* subfield index should not be undefined. Return the same value. */
         return escapeHTML(value);
     }
   }
   else {
     var oldSubfieldCode = field[0][subfieldIndex][0];
     if (cellType == 'subfieldTag') {
         /* Edit subfield code */
         if (field[0][subfieldIndex][0] == value)
             return escapeHTML(value);
         else {
             oldValue = field[0][subfieldIndex][0]; // get old subfield code from gRecord
             field[0][subfieldIndex][0] = value; // update gRecord
             oldSubfieldCode = field[0][subfieldIndex][0];
         }
     }
     else {
         var subfieldsToAdd = new Array(), bulkOperation = false, subfield_offset;
         /* Edit subfield value */
         /* If editing subject field, check KB */
         if (tag_ind == '65017' && field[0][subfieldIndex][0] == 'a') {
             value = check_subjects_KB(value);
         }
         /* Check if there are subfields inside of the content value
          * e.g 999C5 $$mThis a test$$hThis is a second subfield */
         else if (valueContainsSubfields(value)) {
             bulkOperation = true;
             splitContentSubfields(value, oldSubfieldCode, subfieldsToAdd);
+            if (tag_ind == '999C5' && !is_reference_manually_curated(field)){
+                subfieldsToAdd.push(new Array('9', 'CURATOR'));
+            }
             field[0].splice(subfieldIndex, 1); // update gRecord, remove old content
             field[0].push.apply(field[0], subfieldsToAdd); // update gRecord, add new subfields
             oldValue = field[0][subfieldIndex][1];
             subfield_offset = 1;
         }
+        /* If editing reference field, add $$9 subfield */
+        else if (tag_ind == '999C5' && !is_reference_manually_curated(field)){
+            bulkOperation = true;
+            subfieldsToAdd.push.apply(subfieldsToAdd, field[0]);
+            subfieldsToAdd.push(new Array("9", "CURATOR"));
+            var test = field[0].length;
+            field[0].splice(0, test);
+            field[0].push.apply(field[0], subfieldsToAdd); // update gRecord, add new
+            subfield_offset = subfieldsToAdd.length - 1;
+        }
         else if (field[0][subfieldIndex][1] == value)
             return escapeHTML(value);
         }
         /* If editing reference field, add $$9 subfield */
         else if (tag_ind == '999C5' && !is_reference_manually_curated(field)){
             bulkOperation = true;
             field[0][subfieldIndex][1] = value;
             subfieldsToAdd.push.apply(subfieldsToAdd, field[0]);
             subfieldsToAdd.push(new Array("9", "CURATOR"));
             field[0].splice(0, field[0].length);
             field[0].push.apply(field[0], subfieldsToAdd); // update gRecord, add new
             subfield_offset = subfieldsToAdd.length - 1;
         }
         else {
             /* If editing subject field, check KB */
             if (tag_ind == '65017' && field[0][subfieldIndex][0] == 'a') {
               value = check_subjects_KB(value);
               newValue = value;
             }
             oldValue = field[0][subfieldIndex][1]; // get old subfield value from gRecord
             field[0][subfieldIndex][1] = value; // update gRecord
         }
     }
   }
 
   var newValue = escapeHTML(value);
 
   var urHandler;
   var operation_type;
   switch (cellType) {
       case 'subfieldTag':
           value = field[0][subfieldIndex][1];
           operation_type = "change_subfield_code";
           urHandler = prepareUndoHandlerChangeSubfield(tag,
                                                        fieldPosition,
                                                        subfieldIndex,
                                                        value,
                                                        value,
                                                        oldValue,
                                                        oldSubfieldCode,
                                                        operation_type);
           break;
       case 'fieldTag':
           var oldTag = oldValue.substring(0,3);
           var oldInd1 = oldValue.substring(3,4);
           var oldInd2 = oldValue.substring(4,5);
           var newTag = value.substring(0,3);
           var newInd1 = value.substring(3,4);
           var newInd2 = value.substring(4,5);
           operation_type = "change_field_code";
           urHandler = prepareUndoHandlerChangeFieldCode(oldTag,
                                                         oldInd1,
                                                         oldInd2,
                                                         newTag,
                                                         newInd1,
                                                         newInd2,
                                                         fieldPosition,
                                                         operation_type);
           break;
       default:
           if (bulkOperation) {
               var undoHandlers = [];
               /* Prepare  undo handlers to modify subfield content and to
                * add new subfields */
 
               /* 1) Modify main subfield content */
               newValue = subfieldsToAdd[0][1];
               undoHandlers.push(prepareUndoHandlerChangeSubfield(tag,
                                                        fieldPosition,
                                                        subfieldIndex,
                                                        oldValue,
                                                        newValue,
                                                        oldSubfieldCode,
                                                        oldSubfieldCode,
                                                        "change_content"));
 
               /* 2) Add new subfields */
               undoHandlers.push(prepareUndoHandlerAddSubfields(tag,
                                                                fieldPosition,
                                                                subfieldsToAdd.slice(1)));
               urHandler = prepareUndoHandlerBulkOperation(undoHandlers, "addSufields");
           }
           else {
               operation_type = "change_content";
               urHandler = prepareUndoHandlerChangeSubfield(tag,
                                                            fieldPosition,
                                                            subfieldIndex,
                                                            oldValue,
                                                            newValue,
                                                            oldSubfieldCode,
                                                            oldSubfieldCode,
                                                            operation_type);
           }
 
   }
   addUndoOperation(urHandler);
 
   // Generate AJAX request
   switch (cellType) {
       case 'subfieldTag':
           value = field[0][subfieldIndex][1];
           updateSubfieldValue(tag, fieldPosition, subfieldIndex, oldSubfieldCode, value,
                               null, urHandler, modifySubfieldCode=true);
           break;
       case 'fieldTag':
           updateFieldTag(oldTag, newTag, oldInd1, oldInd2, newInd1, newInd2, fieldPosition, null, urHandler);
           break;
       default:
           if (bulkOperation) {
               bulkUpdateSubfieldContent(tag, fieldPosition, subfieldIndex, oldSubfieldCode, newValue, null, urHandler, subfieldsToAdd, subfield_offset);
           }
           else {
               updateSubfieldValue(tag, fieldPosition, subfieldIndex, oldSubfieldCode, value, null, urHandler);
           }
   }
 
   var idPrefix;
   if (cellType == 'subfieldTag') {
       idPrefix = '"#subfieldTag_';
   }
   else{
       idPrefix = '"#content_';
   }
   /* Create fading effect to show the cell modified */
   setTimeout('$(' + idPrefix + tag + '_' + fieldPosition + '_' + subfieldIndex +
       '").effect("highlight", {color: gNEW_CONTENT_COLOR}, ' +
       'gNEW_CONTENT_COLOR_FADE_DURATION)', gNEW_CONTENT_HIGHLIGHT_DELAY);
 
   return newValue;
 }
 
 function onMoveSubfieldClick(type, tag, fieldPosition, subfieldIndex){
   /*
    * Handle subfield moving arrows.
    */
   if (failInReadOnly()){
     return;
   }
   updateStatus('updating');
 
   // Check if moving is possible
   if (type == 'up') {
     if ( (parseInt(subfieldIndex) - 1 )< 0) {
       updateStatus('ready', '');
       return;
     }
   }
   else {
     if ((parseInt(subfieldIndex) + 1) >= gRecord[tag][fieldPosition][0].length) {
       updateStatus('ready', '');
       return;
     }
   }
   // creating the undoRedo Hanglers
   var undoHandler = prepareUndoHandlerMoveSubfields(tag, parseInt(fieldPosition), parseInt(subfieldIndex), type);
   addUndoOperation(undoHandler);
 
   var ajaxData = performMoveSubfield(tag, fieldPosition, subfieldIndex, type, undoHandler);
   createReq(ajaxData, function(json){
     updateStatus('report', gRESULT_CODES[json['resultCode']]);
   }, false);
 
 }
 
 function onDeleteClick(event){
   /*
    * Handle 'Delete selected' button or delete hotkeys.
    */
   if (failInReadOnly()){
     return;
   }
   updateStatus('updating');
 
   var toDelete = getSelectedFields();
   // Assert that no protected fields are scheduled for deletion.
   var protectedField = containsProtectedField(toDelete);
   if (protectedField){
     displayAlert('alertDeleteProtectedField', [protectedField]);
     updateStatus('ready');
     return;
   }
     // register the undo Handler
   var urHandler = prepareUndoHandlerDeleteFields(toDelete);
   addUndoOperation(urHandler);
   var ajaxData = deleteFields(toDelete, urHandler);
 
   createReq(ajaxData, function(json){
     updateStatus('report', gRESULT_CODES[json['resultCode']]);
   }, false);
 }
 
 function onMoveFieldUp(tag, fieldPosition) {
   if (failInReadOnly()){
     return;
   }
   fieldPosition = parseInt(fieldPosition);
   var thisField = gRecord[tag][fieldPosition];
   if (fieldPosition > 0) {
     var prevField = gRecord[tag][fieldPosition-1];
     // check if the previous field has the same indicators
     if ( cmpFields(thisField, prevField) == 0 ) {
       var undoHandler = prepareUndoHandlerMoveField(tag, fieldPosition, "up");
       addUndoOperation(undoHandler);
       var ajaxData = performMoveField(tag, fieldPosition, "up", undoHandler);
       createReq(ajaxData, function(json){
         updateStatus('report', gRESULT_CODES[json['resultCode']]);
       }, false);
     }
   }
 }
 
 function onMoveFieldDown(tag, fieldPosition) {
   if (failInReadOnly()){
     return;
   }
   fieldPosition = parseInt(fieldPosition);
   var thisField = gRecord[tag][fieldPosition];
   if (fieldPosition < gRecord[tag].length-1) {
     var nextField = gRecord[tag][fieldPosition+1];
     // check if the next field has the same indicators
     if ( cmpFields(thisField, nextField) == 0 ) {
       var undoHandler = prepareUndoHandlerMoveField(tag, fieldPosition, "down");
       addUndoOperation(undoHandler);
       var ajaxData = performMoveField(tag, fieldPosition, "down", undoHandler);
       createReq(ajaxData, function(json){
         updateStatus('report', gRESULT_CODES[json['resultCode']]);
       }, false);
     }
   }
 }
 
 
 
 function updateInterfaceAccordingToMode(){
   /* updates the user interface (in particular the activity of menu buttons)
      accordingly to the surrent operation mode of BibEdit.
    */
   // updating the switch button caption
   if (gReadOnlyMode){
     deactivateRecordMenu();
     $('#btnSwitchReadOnly').html("R/W");
   } else {
     activateRecordMenu();
     $('#btnSwitchReadOnly').html("Read-only");
   }
 }
 
 function switchToReadOnlyMode(){
   // Moving to the read only mode with BibEdit
 
   if (gRecordDirty == true){
     alert("Please submit the record or cancel your changes before going to the read-only mode ");
     return false;
   }
   gReadOnlyMode = true;
   createReq({recID: gRecID, requestType: 'deleteRecordCache'});
   gCacheMTime = 0;
 
   updateInterfaceAccordingToMode();
 }
 
 function canSwitchToReadWriteMode(){
   /*A function determining if at current moment, it is possible to switch to the read/write mode*/
   // If the revision is not the newest -> return false
   return true;
 }
 
 function switchToReadWriteMode(){
   // swtching to a normal editing mode of BibEdit
   if (!canSwitchToReadWriteMode()){
     alert("It is not possible to switch to the editing mode at the moment");
     return false;
   }
 
   gReadOnlyMode = false;
   // reading the record as if it was just opened
   getRecord(gRecID);
   updateInterfaceAccordingToMode();
 }
 
 
 function onSwitchReadOnlyMode(){
   // an event habdler being executed when user clicks on the switch to read only mode button
   if (gReadOnlyMode){
     switchToReadWriteMode();
   } else {
     switchToReadOnlyMode();
   }
 }
 
 
 // functions handling the revisions history
 
 function getCompareClickedHandler(revisionId){
   return function(e){
     //document.location = "/"+ gSITE_RECORD +"/merge/#recid1=" + gRecID + "&recid2=" + gRecID + "." + revisionId;
     var comparisonUrl = "/"+ gSITE_RECORD +"/edit/compare_revisions?recid=" +
       gRecID + "&rev1=" + gRecRev + "&rev2=" + revisionId;
     var newWindow = window.open(comparisonUrl);
     newWindow.focus();
     return false;
   };
 }
 
 function onRevertClick(revisionId){
   /*
    * Handle 'Revert' button (submit record).
    */
   updateStatus('updating');
   if (displayAlert('confirmRevert')){
     createReq({recID: gRecID, revId: revisionId, requestType: 'revert',
          force: onSubmitClick.force}, function(json){
     // Submission was successful.
       changeAndSerializeHash({state: 'submit', recid: gRecID});
       var resCode = json['resultCode'];
       cleanUp(!gNavigatingRecordSet, '', null, true);
       updateStatus('report', gRESULT_CODES[resCode]);
       displayMessage(resCode);
       // clear the list of record revisions
       resetBibeditState()
     });
     onSubmitClick.force = false;
   }
   else
     updateStatus('ready');
   holdingPenPanelRemoveEntries(); // clearing the holding pen entries list
 }
 
 function getRevertClickedHandler(revisionId){
   return function(e){
       onRevertClick(revisionId);
       return false;
   };
 }
 
 function updateRevisionsHistory(){
   if (gRecRevisionHistory == null){
       return;
   }
 
   var result = "";
   var results = [];
   for (revInd in  gRecRevisionHistory){
     tmpResult = displayRevisionHistoryEntry(gRecID, gRecRevisionHistory[revInd]);
     tmpResult["revisionID"] = gRecRevisionHistory[revInd];
     results.push(tmpResult);
     result += tmpResult["HTML"];
   }
 
   $("#bibEditRevisionsHistory").html(result);
   $(".bibEditRevHistoryEntryContent").bind("click", function(evt){
     var revision = $(this)[0].id.split("_")[1];
     updateStatus('updating');
     getRecord(gRecID, revision);
   });
 
   /*Attaching the actions on user interface*/
   for (resultInd in results){
     result = results[resultInd];
     $('#' + result['compareImgId']).bind("click", getCompareClickedHandler(result["revisionID"]));
     $('#' + result['revertImgId']).bind("click", getRevertClickedHandler(result["revisionID"]));
   }
 }
 
 function encodeXml(str){
     var resultString = "";
     for (var i=0, n=str.length; i<n; i++){
         var c = str.charAt(i);
         switch (c){
         case '<':
             resultString += "&lt;";
             break;
         case '>':
             resultString += "&gt;";
             break;
         case '&':
             resultString += "&amp;";
             break;
         case '"':
             resultString += "&quot;";
             break;
         case "'":
             resultString += "&apos;";
             break;
         default:
             resultString += c;
         }
     }
     return resultString;
 }
 
 function getSelectionMarcXml(){
   /*Gets the MARC XML of the current editor selection*/
 
   var checkedFieldBoxes = $('input[class="bibEditBoxField"]:checked'); // interesting only for the controlfields
                                                                        //  where no subfields are
   var checkedSubfieldBoxes = $('input[class="bibEditBoxSubfield"]:checked');
 
   // now constructing the interesting data
 
   var selectionNormal = {}; // a dictionary of identifiers taht have appeared already
 
   var selectionControlFields = [];
 
   var selectedFields = []; // a list of fields already selected
   var currentField = null; // a curently edited field
 
   // Collect subfields to be deleted in toDelete.
   var normalFieldsXml = "";
   var controlFieldsXml = "";
 
   $(checkedSubfieldBoxes).each(function(){
     var tmpArray = this.id.split('_');
     var tag = tmpArray[1], fieldPosition = tmpArray[2], subfieldIndex = tmpArray[3];
     if (currentField == null || currentField.tag != tag || currentField.position != fieldPosition){
       if (currentField != null){
         var newPos = selectedFields.length;
         selectedFields[newPos] = currentField;
         normalFieldsXml += "</datafield>"
       }
       // creating an empty field
       currentField={};
       currentField.subfields = [];
       currentField.tag = tag;
       currentField.position = fieldPosition;
       currentField.ind1 = gRecord[tag][fieldPosition][1];
       currentField.ind2 = gRecord[tag][fieldPosition][2];
       currentField.isControlField = false;
       selectionNormal[tag] = true;
       normalFieldsXml += "<datafield tag=\"" + currentField.tag + "\" ind1=\"" +
           currentField.ind1 + "\" ind2=\"" + currentField.ind2 + "\">";
     }
 
     // appending a current subfield
     var newPos = currentField.subfields.length;
     subfield = gRecord[tag][fieldPosition][0][subfieldIndex];
     currentField.subfields[newPos] = subfield;
       normalFieldsXml += "<subfield code=\"" + subfield[0] + "\">" + encodeXml(subfield[1]) + "</subfield>";
   });
 
   if (currentField != null){
     var newPos = selectedFields.length;
     selectedFields[newPos] = currentField;
     normalFieldsXml += "</datafield>";
   }
 
   // now extending by the control fields (they did not appear earlier)
   $(checkedFieldBoxes).each(function(){
     var tmpArray = this.id.split('_');
     var tag = tmpArray[1], fieldPosition = tmpArray[2];
     if (selectionNormal[tag] == undefined){
        // we have a control field ! otherwise, the field has been already utilised
       currentField = {};
       currentField.tag = tag;
       currentField.value = gRecord[tag][fieldPosition][3]
       var newPos = selectionControlFields.length;
       selectionControlFields[newPos] = currentField;
       controlFieldsXml += "<controlfield tag=\"" + currentField.tag + "\">" + currentField.value+ "</controlfield>";
     }
   });
 
   return "<record>" + controlFieldsXml + normalFieldsXml + "</record>";
 
 }
 
 function onPerformCopy(){
   /** The handler performing the copy operation
    */
   if (document.activeElement.type == "textarea" || document.activeElement.type == "text"){
     /*we do not want to perform this in case we are in an ordinary text area*/
     return;
   }
   var valueToCopy = getSelectionMarcXml();
   clipboardCopyValue(valueToCopy);
 }
 
 function onPerformPaste(){
   /* Performing the paste operation -> retriexing the MARC XML from the clipboard,
      decoding and applying the code to the
 
      According to the default behaviour, the fields are appended as last of the same kind
    */
 
   if (document.activeElement.type == "textarea" || document.activeElement.type == "text"){
     /*we do not want to perform this in case we are in an ordinary text area*/
     return;
   }
 
   var clipboardContent = clipboardPasteValue();
 
   var record = null;
   try{
     record = decodeMarcXMLRecord(clipboardContent);
   } catch (err){
     alert("Error when parsing XML occured ... " + err.mesage);
   }
 
   var changesAdd = []; // the ajax requests for all the fields
   var undoHandlers = [];
 
   for (tag in record){
     if (gRecord[tag] == undefined){
       gRecord[tag] = [];
     }
     // now appending the fields
     for (fieldInd in record[tag]){
       newPos = gRecord[tag].length;
       gRecord[tag][newPos] = record[tag][fieldInd];
       // enqueue ajax add field request
 
       isControlfield = record[tag][fieldInd][0].length == 0;
       ind1 = record[tag][fieldInd][1];
       ind2 = record[tag][fieldInd][2];
       subfields = record[tag][fieldInd][0];
       value: record[tag][fieldInd][3]; // in case of a control field
 
       changesAdd.push({
         recID: gRecID,
         requestType: "addField",
         controlfield : isControlfield,
         fieldPosition : newPos,
         tag: tag,
         ind1: record[tag][fieldInd][1],
         ind2: record[tag][fieldInd][2],
         subfields: record[tag][fieldInd][0],
         value: record[tag][fieldInd][3]
       });
 
       undoHandler = prepareUndoHandlerAddField(
           tag, ind1, ind2, newPos, subfields, isControlfield, value);
       undoHandlers.push(undoHandler);
     }
   }
 
   undoHandlers.reverse();
   var undoHandler = prepareUndoHandlerBulkOperation(undoHandlers, "paste");
   addUndoOperation(undoHandler);
   // now sending the Ajax Request
   var optArgs = {
       undoRedo: undoHandler
   };
 
   createBulkReq(changesAdd, function(json){
       updateStatus('report', gRESULT_CODES[json['resultCode']])}, optArgs);
 
   // tags have to be redrawn in the increasing order
 
   tags = [];
   for (tag in record){
     tags.push(tag);
   }
   tags.sort();
   for (tagInd in tags){
       redrawFields(tags[tagInd]);
   }
   reColorFields();
 }
 function addUndoOperation(operation){
   gUndoList.push(operation);
   invalidateRedo();
   updateUrView();
 }
 
 function invalidateRedo(){
   /** Invalidates the redo list - after some modification*/
   gRedoList = [];
 }
 
 function adjustUndoRedoBtnsActivity(){
   /** Making the undo/redo buttons active/inactive according to the needs
    */
   if (gUndoList.length > 0){
     $("#btnUndo").addAttribute("disabled", "");
   }
   else{
     $("#btnUndo").removeAttr("disabled");
   }
 
   if (gRedoList.length > 0){
     $("#btnRedo").addAttribute("disabled", "");
   }
   else{
     $("#btnRedo").removeAttr("disabled");
   }
 }
 
 
 function undoMany(number){
   /** A function undoing many operations from the undo list
 
       Arguments:
         number: number of operations to undo
    */
 
   var undoOperations = []
   for (i=0;i<number;i++){
     undoOperations.push(getUndoOperation());
   }
   performUndoOperations(undoOperations);
   updateUrView();
 }
 
 function prepareUndoHandlerEmpty(){
   /** Creating an empty undo/redo handler - might be useful in some cases
       when undo operation is required but should not be registered
   */
   return {
     operation_type: "no_operation"
   };
 }
 
 function prepareUndoHandlerAddField(tag, ind1, ind2, fieldPosition, subfields,
                                     isControlField, value ){
   /** A function creating an undo handler for the operation of affing a new
       field
 
     Arguments:
       tag:            tag of the field
       ind1:           first indicator (a single character string)
       ind2:           second indicator (a single character string)
       fieldPosition:  a position of the field among other fields with the same
                       tag and possibly different indicators)
       subFields:      a list of fields subfields. each subfield is decribed by
                       a pair: [code, value]
       isControlField: a boolean value indicating if the field is a control field
       value:          a value of a control field. (important in case of passing
                       iscontrolField equal true)
   */
 
   var result = {};
   result.operation_type = "add_field";
   result.newSubfields = subfields;
   result.tag = tag;
   result.ind1 = ind1;
   result.ind2 = ind2;
   result.fieldPosition = fieldPosition;
   result.isControlField = isControlField;
   if (isControlField){
     // value == false means that we are dealing with a control field
     result.value = value;
   } else{
     result.subfields = subfields;
   }
 
   return result;
 }
 
 function prepareUndoHandlerVisualizeChangeset(changesetNumber, changesListBefore, changesListAfter){
   var result = {};
   result.operation_type = "visualize_hp_changeset";
   result.changesetNumber = changesetNumber;
   result.oldChangesList = changesListBefore;
   result.newChangesList = changesListAfter;
   return result;
 }
 
 function prepareUndoHandlerApplyHPChange(changeHandler, changeNo){
   /** changeHandler - handler to the original undo/redo handler associated with the action
    */
   var result = {};
   result.operation_type = "apply_hp_change";
   result.handler = changeHandler;
   result.changeNo = changeNo;
   result.changeType = gHoldingPenChanges[changeNo].change_type;
   return result;
 }
 
 function prepareUndoHandlerApplyHPChanges(changeHandlers, changesBefore){
   /** Producing the undo/redo handler associated with application of
       more than one HoldingPen change
 
       Arguments:
         changeHandlers - a list od undo/redo handlers associated with subsequent changes.
         changesBefore = a list of Holding Pen changes before the operation
    */
 
   var result = {};
   result.operation_type = "apply_hp_changes";
   result.handlers = changeHandlers;
   result.changesBefore = changesBefore;
   return result;
 }
 
 function prepareUndoHandlerRemoveAllHPChanges(hpChanges){
   /** A function preparing the undo handler associated with the
       removal of all the Holding Pen changes present in teh interface */
   var result = {};
   result.operation_type = "remove_all_hp_changes";
   result.old_changes_list = hpChanges;
   return result;
 }
 
 function prepareUndoHandlerBulkOperation(undoHandlers, handlerTitle){
   /*
     Preparing an und/redo handler allowing to treat the bulk operations
     ( like for example in case of pasting fields )
     arguments:
       undoHandlers : handlers of separate operations from the bulk
       handlerTitle : a message to be displayed in the undo menu
   */
   var result = {};
 
   result.operation_type = "bulk_operation";
   result.handlers = undoHandlers;
   result.title = handlerTitle;
 
   return result;
 }
 
 function urPerformAddSubfields(tag, fieldPosition, subfields, isUndo){
     var ajaxData = {
       recID: gRecID,
       requestType: 'addSubfields',
       tag: tag,
       fieldPosition: fieldPosition,
       subfields: subfields,
       undoRedo: (isUndo ? "undo": "redo")
     };
 
     gRecord[tag][fieldPosition][0] = gRecord[tag][fieldPosition][0].concat(subfields);
     redrawFields(tag);
     reColorFields();
 
     return ajaxData;
 }
 
 function performModifyHPChanges(changesList, isUndo){
   /** Undoing or redoing the operation of modifying the changeset
    */
   // first local updates
   gHoldingPenChanges = changesList;
   refreshChangesControls();
   var result = prepareOtherUpdateRequest(isUndo);
   result.undoRedo = isUndo ? "undo" : "redo";
   result.hpChanges = {toOverride: changesList};
   return result;
 }
 
 function hideUndoPreview(){
   $("#undoOperationVisualisationField").addClass("bibEditHiddenElement");
   // clearing the selection !
   $(".bibEditURDescEntrySelected").removeClass("bibEditURDescEntrySelected");
 }
 
 function getRedoOperation(){
   // getting the operation to be redoed
   currentElement = gRedoList[0];
   gRedoList.splice(0, 1);
   gUndoList.push(currentElement);
   return currentElement;
 }
 
 function getUndoOperation(){
   // getting the operation to be undoe
   currentElement = gUndoList[gUndoList.length - 1];
   gUndoList.splice(gUndoList.length - 1, 1);
   gRedoList.splice(0, 0, currentElement);
   return currentElement;
 }
 
 function setAllUnselected(){
   // make all the fields and subfields deselected
   setSelectionStatusAll(false);
 }
 
 function setSelectionStatusAll(status){
   // Changing the selection status for all the fields
   subfieldBoxes = $('.bibEditBoxSubfield');
   subfieldBoxes.each(function(e){
     if (subfieldBoxes[e].checked != status){
       subfieldBoxes[e].click();
     }
   });
 }
 
 function prepareApplyAllHPChangesHandler(){
     // a container for many undo/redo operations in the same time
     throw 'To implement';
 }
 
 
 /*** Handlers for specific operations*/
 
 function renderURList(list, idPrefix, isInverted){
   // rendering the view of undo/redo list into a human-readible HTML
   // list -> an undo or redo list
   // idPrefix -> te prefix of the DOM identifier
 
   var result = "";
   var isPair = false;
   var helperCnt = 0;
 
   var iterationBeginning = list.length - 1;
   var iterationJump = -1;
   var iterationEnd = -1;
 
   if (isInverted === true){
     iterationBeginning = 0;
     iterationJump = 1;
     iterationEnd = list.length;
   }
 
   for (entryInd = iterationBeginning ; entryInd != iterationEnd ; entryInd += iterationJump){
       result += "<div class=\"" + (isPair ? "bibEditURPairRow" : "bibEditUROddRow" )+ " bibEditURDescEntry\" id=\"" + idPrefix + "_" + helperCnt + "\">";
       result += getHumanReadableUREntry(list[entryInd]);
       result += "</div>";
       isPair = ! isPair;
       helperCnt += 1;
   }
   result += "";
   return result;
 }
 
 function prepareApplyHPChangeHandler(){
     // A handler for HoldingPen change application/rejection
     throw 'to implement';
 }
 
 function processURUntil(entry){
   // Executing the bulk undo/redo
   var idParts = $(entry).attr("id").split("_");
   var index = parseInt(idParts[1]);
 
   if (idParts[0] == "undo"){
     undoMany(index+1);
   }
   else{
     redoMany(index+1);
   }
 }
 
 function prepareUndoHandlerChangeSubfield(tag, fieldPos, subfieldPos, oldVal, 
          newVal, oldCode, newCode, operation_type){
   var result = {};
   result.operation_type = operation_type;
   result.tag = tag;
   result.oldVal = oldVal;
   result.newVal = newVal;
   result.oldCode = oldCode;
   result.newCode = newCode;
   result.fieldPos = fieldPos;
   result.subfieldPos = subfieldPos;
   return result;
 }
 
 function prepareUndoHandlerChangeFieldCode(oldTag, oldInd1, oldInd2, newTag, newInd1,
                                            newInd2, fieldPos, operation_type){
   var result = {};
   result.operation_type = operation_type;
   result.oldTag = oldTag;
   result.oldInd1 = oldInd1;
   result.oldInd2 = oldInd2;
   result.newTag = newTag;
   result.ind1 = newInd1;
   result.ind2 = newInd2;
   result.fieldPos = fieldPos;
   
   if (gRecord[newTag] == undefined) {
       result.newFieldPos = 0;
   }
   else {
       result.newFieldPos = gRecord[newTag].length - 1;
   }
 
   return result;
 }
 
 function setAllSelected(){
   // make all the fields and subfields selected
   setSelectionStatusAll(true);
 }
 
 function showUndoPreview(){
   $("#undoOperationVisualisationField").removeClass("bibEditHiddenElement");
 }
 
 function prepareUndoHandlerMoveSubfields(tag, fieldPosition, subfieldPosition, direction){
   var result = {};
   result.operation_type = "move_subfield";
   result.tag = tag;
   result.field_position = fieldPosition;
   result.subfield_position = subfieldPosition;
   result.direction = direction;
   return result;
 }
 // Handlers to implement:
 
 function setFieldUnselected(tag, fieldPos){
   // unselect a given field
   setSelectionStatusField(tag, fieldPos, false);
 }
 
 function urPerformRemoveField(tag, position, isUndo){
   var toDeleteData = {};
   var toDeleteTmp = {};
   toDeleteTmp[position] = [];
   toDeleteData[tag] =  toDeleteTmp;
 
   // first preparing the data of Ajax request
 
   var ajaxData = {
     recID: gRecID,
     requestType: 'deleteFields',
     toDelete: toDeleteData,
     undoRedo: (isUndo ? "undo": "redo")
   };
 
   // updating the local model
   gRecord[tag].splice(position,1);
   if (gRecord[tag] == []){
     gRecord[tag] = undefined;
   }
   redrawFields(tag);
   reColorFields();
 
   return ajaxData;
 }
 
 function prepareOtherUpdateRequest(isUndo){
   return {
     requestType : 'otherUpdateRequest',
     recID : gRecID,
       undoRedo: ((isUndo === true) ? "undo" : "redo"),
     hpChanges: {}
   };
 }
 
 function performUndoApplyHpChanges(subRequests, oldChanges){
   /**
    Arguemnts:
      subRequests - subrequests performing the appropriate undo operations
    */
 
   // removing all teh undo/redo informations as they should be passed globally
   for (ind in subRequests){
       subRequests[ind].undoRedo = undefined;
   }
 //  var gHoldingPenChanges
   return {
     requestType: 'applyBulkUpdates',
     undoRedo: "undo",
     requestsData: subRequests,
     hpChanges: {toOverride: oldChanges}
   };
 }
 
 function performBulkOperation(subHandlers, isUndo){
   /**
    return the bulk operation
    Arguments:
      subReqs : requests performing the sub-operations
      isUndo - is current request undo or redo ?
    */
   var subReqs = [];
   if (isUndo === true){
     subReqs = preparePerformUndoOperations(subHandlers);
   } else {
     // We can not simply assign and revers as the original would be modified
     var handlers = [];
     for (handlerInd = subHandlers.length -1; handlerInd >= 0; handlerInd--){
       handlers.push(subHandlers[handlerInd]);
     }
     subReqs = preparePerformRedoOperations(handlers);
   }
 
   for (ind in subReqs){
     subReqs[ind].undoRedo = undefined;
   }
 
   return {
     requestType: 'applyBulkUpdates',
     undoRedo: (isUndo === true ? "undo" : "redo"),
     requestsData: subReqs,
     hpChanges: {}
   };
 }
 
 function preparePerformRedoOperations(operations){
   /** Redos an operation passed as an argument */
   var ajaxRequestsData = [];
   for (operationInd in operations){
     var operation = operations[operationInd];
     var ajaxData = {};
     var isMultiple = false; // is the current decription a list of descriptors ?
     switch (operation.operation_type){
     case "no_operation":
       ajaxData = prepareOtherUpdateRequest(false);
       break;
     case "change_content":
       ajaxData = urPerformChangeSubfieldContent(operation.tag,
                                      operation.fieldPos,
                                      operation.subfieldPos,
                                      operation.newCode,
                                      operation.newVal,
                                      false);
       break;
     case "change_subfield_code":
       ajaxData = urPerformChangeSubfieldCode(operation.tag,
                                      operation.fieldPos,
                                      operation.subfieldPos,
                                      operation.newCode,
                                      operation.newVal,
                                      false);
       break;
     case "change_field_code":
         ajaxData = urPerformChangeFieldCode(operation.newTag,
                                             operation.ind1,
                                             operation.ind2,
                                             operation.oldTag,
                                             operation.oldInd1,
                                             operation.oldInd2,
                                             operation.fieldPos,
                                             false);
       break;
     case "add_field":
       ajaxData = urPerformAddField(operation.isControlField,
                         operation.fieldPosition,
                         operation.tag,
                         operation.ind1,
                         operation.ind2,
                         operation.subfields,
                         operation.value,
                         false);
       break;
      case "add_subfields":
        ajaxData = urPerformAddSubfields(operation.tag,
                              operation.fieldPosition,
                              operation.newSubfields,
                              false);
        break;
 
     case "delete_fields":
       ajaxData = urPerformDeletePositionedFieldsSubfields(operation.toDelete, false);
       break;
 
     case "move_field":
       ajaxData = performMoveField(operation.tag, operation.field_position, operation.direction , false);
       break;
     case "move_subfield":
       ajaxData = performMoveSubfield(operation.tag, operation.field_position, operation.subfield_position, operation.direction, false);
       break;
     case "bulk_operation":
       ajaxData = performBulkOperation(operation.handlers, false);
       break;
     case "apply_hp_change":
       removeViewedChange(operation.changeNo); // we redo the change application so the change itself gets removed
       ajaxData = preparePerformRedoOperations([operation.handler]);
       ajaxData[0].hpChange = {};
       ajaxData[0].hpChange.toDisable = [operation.changeNo]; // reactivate this change
       isMultiple = true;
       break;
 
     case "apply_hp_changes":
       // in this case many changes are applied at once and the list of changes is completely overriden
       ajaxData = performUndoApplyHpChanges();
     case "change_field":
       ajaxData = urPerformChangeField(operation.tag, operation.fieldPos,
                                       operation.newInd1, operation.newInd2,
                                       operation.newSubfields,
                                       operation.newIsControlField,
                                       operation.oldValue , false);
       break;
     case "visualize_hp_changeset":
       ajaxData = prepareVisualizeChangeset(operation.changesetNumber,
         operation.newChangesList, "redo");
       break;
     case "remove_all_hp_changes":
       ajaxData = performModifyHPChanges([], false);
       break;
 
     default:
       alert("Error: wrong operation to redo");
       break;
     }
     // now dealing with the results
     if (isMultiple){
       // in this case we have to merge lists rather than include inside
       for (elInd in ajaxData){
         ajaxRequestsData.push(ajaxData[elInd]);
       }
     }
     else{
       ajaxRequestsData.push(ajaxData);
     }
   }
   return ajaxRequestsData;
 }
 
 function performRedoOperations(operations){
   ajaxRequestsData = preparePerformRedoOperations(operations);
   // now submitting the bulk request
   var optArgs = {
 //      undoRedo: "redo"
   };
 
   createBulkReq(ajaxRequestsData, function(json){
     updateStatus('report', gRESULT_CODES[json['resultCode']]);
   }, optArgs);
 }
 
 function prepareUndoHandlerDeleteFields(toDelete){
   /*Creating Undo/Redo handler for the operation of removal of fields and/or subfields
     Arguments: toDelete - indicates fields and subfields scheduled to be deleted.
       this argument should have a following structure:
       {
         "fields" : { tag: {fieldsPosition: field_structure_similar_to_on_from_gRecord}}
         "subfields" : {tag: { fieldPosition: { subfieldPosition: [code, value]}}}
       }
   */
   var result = {};
   result.operation_type = "delete_fields";
   result.toDelete = toDelete;
   return result;
 }
 
 function setSubfieldUnselected(tag, fieldPos, subfieldPos){
  // unseelcting a subfield
   setSelectionStatusSubfield(tag, fieldPos, subfieldPos, false);
 }
 
 
 function prepareUndoHandlerAddSubfields(tag, fieldPosition, subfields){
   /**
     tag : tag of the field inside which the fields should be added
     fieldPosition: position of the field
     subfields: new subfields to be added. This argument should be a list
       of lists representing a single subfield. Each subfield is represented
       by a list, containing 2 elements. [subfield_code, subfield_value]
   */
   var result = {};
   result.operation_type = "add_subfields";
   result.tag = tag;
   result.fieldPosition = fieldPosition;
   result.newSubfields = subfields;
   return result;
 }
 
 function setFieldSelected(tag, fieldPos){
   // select a given field
   setSelectionStatusField(tag, fieldPos, true);
 }
 
 function redoMany(number){
   // redoing an indicated number of operations
   var redoOperations = [];
   for (i=0;i<number;i++){
     redoOperations.push(getRedoOperation());
   }
   performRedoOperations(redoOperations);
   updateUrView();
 }
 function urPerformAddField(controlfield, fieldPosition, tag, ind1, ind2, subfields, value, isUndo){
   var ajaxData = {
     recID: gRecID,
     requestType: 'addField',
     controlfield: controlfield,
     fieldPosition: fieldPosition,
     tag: tag,
     ind1: ind1,
     ind2: ind2,
     subfields: subfields,
     value: value,
     undoRedo: (isUndo? "undo": "redo")
   };
 
   // updating the local situation
   if (gRecord[tag] == undefined){
     gRecord[tag] = [];
   }
   var newField = [(controlfield ? [] : subfields), ind1, ind2,
                   (controlfield ? value: ""), 0];
   gRecord[tag].splice(fieldPosition, 0, newField);
   redrawFields(tag);
   reColorFields();
 
   return ajaxData;
 }
 
 function urPerformRemoveSubfields(tag, fieldPosition, subfields, isUndo){
   var toDelete = {};
   toDelete[tag] = {};
   toDelete[tag][fieldPosition] = []
   var startingPosition = gRecord[tag][fieldPosition][0].length - subfields.length;
   for (var i=startingPosition, n=gRecord[tag][fieldPosition][0].length; i<n ; i++){
     toDelete[tag][fieldPosition].push(i);
   }
 
   var ajaxData = {
     recID: gRecID,
     requestType: 'deleteFields',
     toDelete: toDelete,
     undoRedo: (isUndo ? "undo": "redo")
   };
 
   // modifying the client-side interface
   gRecord[tag][fieldPosition][0].splice( gRecord[tag][fieldPosition][0].length - subfields.length, subfields.length);
   redrawFields(tag);
   reColorFields();
 
   return ajaxData;
 }
 
 function updateUrView(){
   /*Updating the information box in the bibEdit menu
     (What are the current undo/redo handlers*/
   $('#undoOperationVisualisationFieldContent')[0].innerHTML = (gUndoList.length == 0) ? "(empty)" :
         renderURList(gUndoList, "undo");
 //        gUndoList[gUndoList.length - 1].operation_type;
   $('#redoOperationVisualisationFieldContent')[0].innerHTML = (gRedoList.length == 0) ? "(empty)" :
         renderURList(gRedoList, "redo", true);
 
   // now attaching the events ... the function is uniform for all the elements present inside the document
 
     var urEntries = $('.bibEditURDescEntry');
     urEntries.each(function(index){
         $(urEntries[index]).bind("mouseover", function (e){
           $(urEntries[index]).find(".bibEditURDescEntryDetails").removeClass("bibEditHiddenElement");
             urMarkSelectedUntil(urEntries[index]);
         });
         $(urEntries[index]).bind("mouseout", function(e){
           $(urEntries[index]).find(".bibEditURDescEntryDetails").addClass("bibEditHiddenElement");
         });
         $(urEntries[index]).bind("click", function(e){
             processURUntil(urEntries[index]);
         });
     });
 }
 
 function performMoveSubfield(tag, fieldPosition, subfieldIndex, direction, undoRedo){
   var newSubfieldIndex = parseInt(subfieldIndex) + (direction == "up" ? -1 : 1);
   var fieldID = tag + '_' + fieldPosition;
   var field = gRecord[tag][fieldPosition];
   var subfields = field[0];
 
   // Create Ajax request.
   var ajaxData = {
     recID: gRecID,
     requestType: 'moveSubfield',
     tag: tag,
     fieldPosition: fieldPosition,
     subfieldIndex: subfieldIndex,
     newSubfieldIndex: newSubfieldIndex,
     undoRedo: (undoRedo == true) ?  "undo" : ((undoRedo == false) ?  "redo" : undoRedo)
   };
 
   // Continue local updating.
   var subfieldToSwap = subfields[newSubfieldIndex];
   subfields[newSubfieldIndex] = subfields[subfieldIndex];
   subfields[subfieldIndex] = subfieldToSwap;
   var rowGroup = $('#rowGroup_' + fieldID);
   var coloredRowGroup = $(rowGroup).hasClass('bibEditFieldColored');
   $(rowGroup).replaceWith(createField(tag, field, fieldPosition));
   if (coloredRowGroup)
     $('#rowGroup_' + fieldID).addClass('bibEditFieldColored');
 
   // taking care of having only the new subfield position selected
   setAllUnselected();
   setSubfieldSelected(tag, fieldPosition, newSubfieldIndex);
 
   return ajaxData;
 }
 
 function onRedo(evt){
   if (gRedoList.length <= 0){
     alert("No Redo operations to process");
     return;
   }
   redoMany(1);
 }
 
 // functions related to the automatic field selection/unseletion
 
 function hideRedoPreview(){
   $("#redoOperationVisualisationField").addClass("bibEditHiddenElement");
   // clearing the selection !
   $(".bibEditURDescEntrySelected").removeClass("bibEditURDescEntrySelected");
 }
 
 function urPerformAddPositionedFieldsSubfields(toAdd, isUndo){
   return createFields(toAdd, isUndo);
 }
 
 function setSubfieldSelected(tag, fieldPos, subfieldPos){
   // selecting a subfield
   setSelectionStatusSubfield(tag, fieldPos, subfieldPos, true);
 }
 
 function getHumanReadableUREntry(handler){
   // rendering a human readable description of an undo/redo operation
   // handler : the u/r handler to render
   var operationDescription;
 
   switch (handler.operation_type){
     case "move_field":
       operationDescription = "move field";
       break;
     case "move_field":
       operationDescription = "change field";
       break;
     case "move_subfield":
       operationDescription = "move subfield";
       break;
     case "change_content":
       operationDescription = "edit subfield";
       break;
     case "change_subfield_code":
       operationDescription = "edit subfield code";
       break;
     case "change_field_code":
       operationDescription = "edit field code";
       break;
     case "add_field":
       operationDescription = "add field";
       break;
     case "add_subfields":
       operationDescription = "add field";
       break;
     case "delete_fields":
       operationDescription = "delete";
       break;
     case "bulk_operation":
       operationDescription = handler.title;
       break;
     case "apply_hp_change":
       operationDescription = "holding pen";
       break;
     case "visualize_hp_changeset":
       operationDescription = "show changes";
       break;
     case "remove_all_hp_changes":
       operationDescription = "remove changes";
       break;
     default:
       operationDescription = "unknown operation";
       break;
   }
 
   // now rendering parameters of the handler
   var readableDescriptors = {
     'tag' : 'tag',
     'operation_type' : false,
     'field_position' : false,
     'subfield_position' : false,
     'subfieldPos' : false,
     'newVal' : 'new value',
     'oldVal' : 'old value',
     'fieldPos' : false,
     'toDelete' : false,
     'handlers' : false,
     'newFieldPos' : false
   };
 
   var handlerDetails = '<table>';
 
   for (characteristic in handler){
     if (readableDescriptors[characteristic] != false){
       var characteristicString = characteristic;
       if (readableDescriptors[characteristic] != undefined){
           characteristicString = readableDescriptors[characteristic];
       }
       handlerDetails += '<tr><td class="bibEditURDescChar">'
         + characteristicString + ':</td><td>' + handler[characteristic]  + '</td></tr>';
     }
   }
 
   handlerDetails += '</table>';
   // now generating the final result
   return '<div class="bibEditURDescHeader">'
     + operationDescription + '</div><div class="bibEditURDescEntryDetails bibEditHiddenElement">'
     + handlerDetails + '</div>';
 }
 
 function urMarkSelectedUntil(entry){
     // marking all the detailed entries, until a given one as selected
     //  these entries have the same prefix but a smaller number
     var identifierParts = $(entry).attr("id").split("_");
     var position = parseInt(identifierParts[1]);
     var potentialElements = $(".bibEditURDescEntry");
     potentialElements.each(function(index){
         var curIdentifierParts = $(potentialElements[index]).attr("id").split("_");
         if ((curIdentifierParts[0] == identifierParts[0]) && (parseInt(curIdentifierParts[1]) <= position)){
            $(potentialElements[index]).addClass("bibEditURDescEntrySelected");
         }
         else {
            $(potentialElements[index]).removeClass("bibEditURDescEntrySelected");
         }
     });
 }
 
 function onUndo(evt){
   if (gUndoList.length <= 0){
     alert("No Undo operations to process");
     return;
   }
   undoMany(1);
 }
 
 function preparePerformUndoOperations(operations){
   /** Undos an operation passed as an argument */
   var ajaxRequestsData = [];
   for (operationInd in operations){
     var operation = operations[operationInd];
     var action = null;
     var actionData = null;
     var ajaxData = {};
     var isMultiple = false; // is the current oepration handler a list
       // of operations rather than a single op ?
 
     switch (operation.operation_type){
     case "no_operation":
       ajaxData = prepareOtherUpdateRequest(true);
       break;
     case "change_content":
       ajaxData = urPerformChangeSubfieldContent(operation.tag,
                                                 operation.fieldPos,
                                                 operation.subfieldPos,
                                                 operation.oldCode,
                                                 operation.oldVal,
                                                 true);
       break;
     case "change_subfield_code":
       ajaxData = urPerformChangeSubfieldCode(operation.tag,
                                              operation.fieldPos,
                                              operation.subfieldPos,
                                              operation.oldCode,
                                              operation.oldVal,
                                              true);
       break;
     case "change_field_code":
       ajaxData = urPerformChangeFieldCode(operation.oldTag,
                                           operation.oldInd1,
                                           operation.oldInd2,
                                           operation.newTag,
                                           operation.ind1,
                                           operation.ind2,
                                           operation.newFieldPos,
                                           true);
       break;
     case "add_field":
       ajaxData = urPerformRemoveField(operation.tag,
                                       operation.fieldPosition,
                                       true);
       break;
     case "add_subfields":
       ajaxData = urPerformRemoveSubfields(operation.tag,
                                           operation.fieldPosition,
                                           operation.newSubfields,
                                           true);
       break;
 
     case "delete_fields":
       ajaxData = urPerformAddPositionedFieldsSubfields(operation.toDelete, true);
       break;
 
     case "move_field":
       var newDirection = "up";
       var newPosition = operation.field_position + 1;
       if (operation.direction == "up"){
         newDirection = "down";
         newPosition = operation.field_position - 1;
       }
 
       ajaxData = performMoveField(operation.tag, newPosition, newDirection, true);
       break;
     case "move_subfield":
 
       var newDirection = "up";
       var newPosition = operation.subfield_position + 1;
       if (operation.direction == "up"){
         newDirection = "down";
         newPosition = operation.subfield_position - 1;
       }
       ajaxData = performMoveSubfield(operation.tag, operation.field_position,
         newPosition, newDirection, true);
       break;
     case "bulk_operation":
       ajaxData = performBulkOperation(operation.handlers, true);
       break;
     case "apply_hp_change":
       ajaxData = preparePerformUndoOperations([operation.handler]);
       ajaxData[0]["hpChange"] = {};
       ajaxData[0]["hpChange"]["toEnable"] = [operation.changeNo]; // reactivate
       isMultiple = true;
       revertViewedChange(operation.changeNo);
       break;
     case "visualize_hp_changeset":
       ajaxData = prepareUndoVisualizeChangeset(operation.changesetNumber,
         operation.oldChangesList);
       break;
     case "change_field":
       ajaxData = urPerformChangeField(operation.tag, operation.fieldPos,
                                       operation.oldInd1, operation.oldInd2,
                                       operation.oldSubfields,
                                       operation.oldIsControlField,
                                       operation.oldValue , true);
       break;
     case "remove_all_hp_changes":
       ajaxData = performModifyHPChanges(operation.old_changes_list, true);
       break;
     default:
       alert("Error: wrong operation to undo");
     }
 
     if (isMultiple){
       // in this case we have to merge lists rather than include inside
       for (elInd in ajaxData){
         ajaxRequestsData.push(ajaxData[elInd]);
       }
     }
     else{
       ajaxRequestsData.push(ajaxData);
     }
   }
 
   return ajaxRequestsData;
 }
 
 function performUndoOperations(operations){
   var ajaxRequestsData = preparePerformUndoOperations(operations);
   // now submitting the ajax request
   var optArgs={
 //    undoRedo: "undo"
   };
 
   createBulkReq(ajaxRequestsData, function(json){
     updateStatus('report', gRESULT_CODES[json['resultCode']]);
   }, optArgs);
 }
 
 function prepareUndoHandlerMoveField(tag, fieldPosition, direction){
   var result = {};
   result.tag = tag;
   result.operation_type = "move_field";
   result.field_position = fieldPosition;
   result.direction = direction;
   return result;
 }
 
 function prepareUndoHandlerChangeField(tag, fieldPos,
   oldInd1, oldInd2, oldSubfields, oldIsControlField, oldValue,
   newInd1, newInd2, newSubfields, newIsControlField, newValue){
   /** Function building a handler allowing to undo the operation of
       changing the field structure.
 
       Changing can happen only if tag and position remain the same,
       Otherwise we deal with removal and adding of a field
 
       Arguments:
         tag - tag of a field
         fieldPos - position of a field
 
         oldInd1, oldInd2 - indices of the old field
         oldSubfields - subfields present int the old structure
         oldIsControlField - a boolean value indicating if the field
                             is a control field
         oldValue - a value before change in case of field being a control field.
                    if the field is normal field, this should be equal ""
 
         newInd1, newInd2, newSubfields, newIsControlField, newValue -
            Similar parameters describing new structure of a field
   */
   var result = {};
   result.operation_type = "change_field";
   result.tag = tag;
   result.fieldPos = fieldPos;
   result.oldInd1 = oldInd1;
   result.oldInd2 = oldInd2;
   result.oldSubfields = oldSubfields;
   result.oldIsControlField = oldIsControlField;
   result.oldValue = oldValue;
   result.newInd1 = newInd1;
   result.newInd2 = newInd2;
   result.newSubfields = newSubfields;
   result.newIsControlField = newIsControlField;
   result.newValue = newValue;
 
   return result;
 }
 
 function showRedoPreview(){
   $("#redoOperationVisualisationField").removeClass("bibEditHiddenElement");
 }
 
 function deleteFields(toDeleteStruct, undoRedo){
   // a function deleting the specified fields on both client and server sides
   //
   // toDeleteFields : a structure describing fields and subfields to delete
   //   this structure is the same as for the function createFields
 
   var toDelete = {};
 
   // first we convert the data into a different format, loosing the informations about
   //   subfields of entirely removed fields
 
   // first the entirely deleted fields
   for (tag in toDeleteStruct.fields){
     if (toDelete[tag] == undefined){
       toDelete[tag] = {};
     }
     for (fieldPos in toDeleteStruct.fields[tag]){
       toDelete[tag][fieldPos] = [];
     }
   }
 
   for (tag in toDeleteStruct.subfields){
     if (toDelete[tag] == undefined){
       toDelete[tag] = {};
     }
     for (fieldPos in toDeleteStruct.subfields[tag]){
       toDelete[tag][fieldPos] = [];
       for (subfieldPos in toDeleteStruct.subfields[tag][fieldPos]){
         toDelete[tag][fieldPos].push(subfieldPos);
       }
     }
   }
 
   var tagsToRedraw = [];
 
   // reColorTable is true if any field are completely deleted.
   var reColorTable = false;
 
   // first we have to encode all the data in a single dictionary
 
   // Create Ajax request.
   var ajaxData = {
     recID: gRecID,
     requestType: 'deleteFields',
     toDelete: toDelete,
     undoRedo: (undoRedo == true) ? "undo" : ((undoRedo == false) ? "redo" : undoRedo)
   };
 
   // Continue local updating.
   // Parse data structure and delete accordingly in record.
   var fieldsToDelete, subfieldIndexesToDelete, field, subfields, subfieldIndex;
   for (var tag in toDelete) {
     tagsToRedraw.push(tag);
     fieldsToDelete = toDelete[tag];
     // The fields should be treated in the decreasing order (during the removal, indices may change)
     traversingOrder = [];
     for (fieldPosition in fieldsToDelete) {
       traversingOrder.push(fieldPosition);
     }
     // normal sorting will do this in a lexycographical order ! (problems if > 10 subfields
     // function provided, allows sorting in the reversed order
     var traversingOrder = traversingOrder.sort(function(a, b){
       return b - a;
     });
 
     for (var fieldInd in traversingOrder) {
       var fieldPosition = traversingOrder[fieldInd];
       var fieldID = tag + '_' + fieldPosition;
       subfieldIndexesToDelete = fieldsToDelete[fieldPosition];
       if (subfieldIndexesToDelete.length == 0)
         deleteFieldFromTag(tag, fieldPosition);
       else {
         // normal sorting will do this in a lexycographical order ! (problems if > 10 subfields
         subfieldIndexesToDelete.sort(function(a, b){
           return a - b;
         });
         field = gRecord[tag][fieldPosition];
         subfields = field[0];
         for (var j = subfieldIndexesToDelete.length - 1; j >= 0; j--){
           subfields.splice(subfieldIndexesToDelete[j], 1);
         }
       }
     }
   }
 
   // If entire fields has been deleted, redraw all fields with the same tag
   // and recolor the full table.
   for (tag in tagsToRedraw)
       redrawFields(tagsToRedraw[tag]);
   reColorFields();
 
   return ajaxData;
 }
 
 function getSelectedFields(){
   /** Function returning a list of selected fields
     Returns all the fields and subfields that are slected.
     The structure of a result is following:
     {
       "fields" : { tag: {fieldsPosition: field_structure_similar_to_on_from_gRecord}}
       "subfields" : {tag: { fieldPosition: { subfieldPosition: [code, value]}}}
     }
   */
   var selectedFields = {};
   var selectedSubfields = {};
 
   var checkedFieldBoxes = $('input[class="bibEditBoxField"]:checked');
   var checkedSubfieldBoxes = $('input[class="bibEditBoxSubfield"]:checked');
 
   if (!checkedFieldBoxes.length && !checkedSubfieldBoxes.length)
     // No fields selected
     return;
 
   // Collect fields to be deleted in toDelete.
   $(checkedFieldBoxes).each(function(){
     var tmpArray = this.id.split('_');
     var tag = tmpArray[1], fieldPosition = tmpArray[2];
     if (!selectedFields[tag]) {
       selectedFields[tag] = {};
     }
     selectedFields[tag][fieldPosition] = gRecord[tag][fieldPosition];
   });
 
   // Collect subfields to be deleted in toDelete.
   $(checkedSubfieldBoxes).each(function(){
     var tmpArray = this.id.split('_');
     var tag = tmpArray[1], fieldPosition = tmpArray[2], subfieldIndex = tmpArray[3];
     if (selectedFields[tag] == undefined || selectedFields[tag][fieldPosition] == undefined){
       // this field has not been selected entirely, we can proceed with processing subfield slection
       if (!selectedSubfields[tag]) {
         selectedSubfields[tag] = {};
         selectedSubfields[tag][fieldPosition] = {};
         selectedSubfields[tag][fieldPosition][subfieldIndex] =
           gRecord[tag][fieldPosition][0][subfieldIndex];
       }
       else {
         if (!selectedSubfields[tag][fieldPosition])
           selectedSubfields[tag][fieldPosition] = {};
         selectedSubfields[tag][fieldPosition][subfieldIndex] =
           gRecord[tag][fieldPosition][0][subfieldIndex];
       }
     } else {
       // this subfield is a part of entirely selected field... we have already included the information about subfields
     }
   });
   var result={};
   result.fields = selectedFields;
   result.subfields = selectedSubfields;
   return result;
 }
 
 function urPerformChangeSubfieldContent(tag, fieldPos, subfieldPos, code, val, isUndo){
   // changing the server side model
   var ajaxData = {
     recID: gRecID,
     requestType: 'modifyContent',
     tag: tag,
     fieldPosition: fieldPos,
     subfieldIndex: subfieldPos,
     subfieldCode: code,
     value: val,
     undoRedo: (isUndo ? "undo": "redo")
   };
 
   // changing the model
   gRecord[tag][fieldPos][0][subfieldPos][0] = code;
   gRecord[tag][fieldPos][0][subfieldPos][1] = val;
 
   // changing the display .... what if being edited right now ?
   redrawFields(tag);
   reColorFields();
 
   return ajaxData;
 }
 
 function urPerformChangeSubfieldCode(tag, fieldPos, subfieldPos, code, val, isUndo){
   // changing the server side model
   var ajaxData = {
     recID: gRecID,
     requestType: 'modifySubfieldTag',
     tag: tag,
     fieldPosition: fieldPos,
     subfieldIndex: subfieldPos,
     subfieldCode: code,
     value: val,
     undoRedo: (isUndo ? "undo": "redo")
   };
 
   gRecord[tag][fieldPos][0][subfieldPos][0] = code;
   gRecord[tag][fieldPos][0][subfieldPos][1] = val;
 
   // changing the display .... what if being edited right now ?
   redrawFields(tag);
   reColorFields();
 
   return ajaxData;
 }
 
 function urPerformChangeFieldCode(oldTag, oldInd1, oldInd2, newTag, ind1, ind2,
                                   fieldPos, isUndo){
   // changing the server side model
   var ajaxData = {
     recID: gRecID,
     requestType: 'modifyFieldTag',
     oldTag: newTag,
     oldInd1: ind1,
     oldInd2: ind2,
     newTag: oldTag,
     fieldPosition: fieldPos,
     ind1: oldInd1,
     ind2: oldInd2,
     undoRedo: (isUndo ? "undo": "redo")
   };
 
   // updating the local model
   var currentField = gRecord[newTag][fieldPos];
   currentField[1] = oldInd1;
   currentField[2] = oldInd2;
   gRecord[newTag].splice(fieldPos,1);
   if (gRecord[newTag].length == 0){
       delete gRecord[newTag];
   }
   var fieldNewPos;
   if (gRecord[oldTag] == undefined) {
       fieldNewPos = 0;
       gRecord[oldTag] = [];
       gRecord[oldTag][fieldNewPos] = currentField;
   }
   else {
       fieldNewPos = gRecord[oldTag].length;
       gRecord[oldTag].splice(fieldNewPos, 0, currentField);
   }
   // changing the display .... what if being edited right now ?
   redrawFields(newTag);
   redrawFields(oldTag);
   reColorFields();
 
   return ajaxData;
 }
 
 function performChangeField(tag, fieldPos, ind1, ind2, subFields, isControlfield,
   value, undoRedo){
   /** Function changing the field structure and generating an appropriate AJAX
       request handler
       Arguments:
         tag, fieldPos, ind1, ind2, subFields, isControlfield, value - standard
           values describing a field. tag, fieldPos are used to locate the field
           instance (which has to exist) and its content is modified accordingly.
         undoRedo - a undoRedo Handler or one of the words "undo"/"redo"
    */
   var ajaxData = {
     recID: gRecID,
     requestType: "modifyField",
     controlfield : isControlfield,
     fieldPosition : fieldPos,
     ind1: ind1,
     ind2: ind2,
     tag: tag,
     subFields: subFields,
     undoRedo : undoRedo,
     hpChanges: {}
   }
 
   // local changes
   gRecord[tag][fieldPos][0] = subFields;
   gRecord[tag][fieldPos][1] = ind1;
   gRecord[tag][fieldPos][2] = ind2;
   gRecord[tag][fieldPos][3] = value;
   redrawFields(tag);
   reColorFields();
 
   return ajaxData;
 }
 
 function urPerformChangeField(tag, fieldPos, ind1, ind2, subFields,
   isControlfield, value, isUndo){
   /**
    */
   return performChangeField(tag, fieldPos, ind1, ind2, subFields,
     isControlfield, value, (isUndo ? "undo" : "redo"));
 }
 
 function performMoveField(tag, oldFieldPosition, direction, undoRedo){
   var newFieldPosition = oldFieldPosition + (direction == "up" ? -1 : 1);
   // Create Ajax request.
   var ajaxData = {
     recID: gRecID,
     requestType: 'moveField',
     tag: tag,
     fieldPosition: oldFieldPosition,
     direction: direction,
     undoRedo: (undoRedo == true) ? "undo" : ((undoRedo == false) ? "redo" : undoRedo)
   };
 
   //continue updating locally
   var currentField = gRecord[tag][oldFieldPosition];
   gRecord[tag][oldFieldPosition] = gRecord[tag][newFieldPosition];
   gRecord[tag][newFieldPosition] = currentField;
 
   $('tbody#rowGroup_'+tag+'_'+(newFieldPosition)).replaceWith(
       createField(tag, gRecord[tag][newFieldPosition], newFieldPosition));
   $('tbody#rowGroup_'+tag+'_'+oldFieldPosition).replaceWith(
       createField(tag, gRecord[tag][oldFieldPosition], oldFieldPosition));
 
   reColorFields();
 
   // Now taking care of having the new field selected and the rest unselected
   setAllUnselected();
   setFieldSelected(tag, newFieldPosition);
 //$('#boxField_'+tag+'_'+(newFieldPosition)).click();
   return ajaxData;
 }
 
 function setSelectionStatusField(tag, fieldPos, status){
   var fieldCheckbox = $('#boxField_' + tag + '_' + fieldPos);
   var subfieldCheckboxes = $('#rowGroup_' + tag + '_' + fieldPos + ' .bibEditBoxSubfield');
 
   fieldCheckbox.each(function(ind){
       if (fieldCheckbox[ind].checked != status)
       {
           fieldCheckbox[ind].click();
       }
   });
 }
 
 function urPerformDeletePositionedFieldsSubfields(toDelete, isUndo){
   return deleteFields(toDelete, isUndo);
 }
 
 /** General Undo/Redo treatment lists */
 
 function setSelectionStatusSubfield(tag, fieldPos, subfieldPos, status){
   var subfieldCheckbox = $('#boxSubfield_' + tag + '_' + fieldPos + '_' + subfieldPos);
   if (subfieldCheckbox[0].checked != status)
   {
       subfieldCheckbox[0].click();
   }
 }
 
 function createFields(toCreateFields, isUndo){
   // a function adding fields.
   // toCreateFields : a structure describing fields and subfields to create
   //   this structure is the same as for the function deleteFields
 
   // 1) Preparing the AJAX request
   var tagsToRedraw = {}
   var ajaxData = {
     recID: gRecID,
     requestType: 'addFieldsSubfieldsOnPositions',
     fieldsToAdd: toCreateFields.fields,
     subfieldsToAdd: toCreateFields.subfields
   };
 
   if (isUndo != undefined){
     ajaxData['undoRedo'] = (isUndo ? "undo": "redo");
   }
 
   // 2) local processing -> creating the fields locally
   //   - first creating the missing fields so all the subsequent field indices are correcr
   for (tag in toCreateFields.fields){
     if (gRecord[tag] == undefined){
       gRecord[tag] = [];
     }
     tagsToRedraw[tag] = true;
     var fieldIndices = [];
     for (fieldPos in toCreateFields.fields[tag]){
       fieldIndices.push(fieldPos);
     }
     fieldIndices.sort(); // we have to add fields in the increasing order
       for (indInd in fieldIndices){
         var fieldIndexToAdd = fieldIndices[indInd]; // index of the field index to add in the indices array
         var newField = toCreateFields.fields[tag][fieldIndexToAdd];
         gRecord[tag].splice(fieldIndexToAdd, 0, newField);
       }
   }
 
   //   - now appending the remaining subfields
 
   for (tag in toCreateFields.subfields){
     tagsToRedraw[tag] = true;
     for (fieldPos in toCreateFields.subfields[tag]){
       var subfieldPositions = [];
       for (subfieldPos in toCreateFields.subfields[tag][fieldPos]){
         subfieldPositions.push(subfieldPos);
       }
       subfieldPositions.sort();
       for (subfieldInd in subfieldPositions){
         subfieldPosition = subfieldPositions[subfieldInd];
         gRecord[tag][fieldPos][0].splice(
           subfieldPosition, 0,
           toCreateFields.subfields[tag][fieldPos][subfieldPosition]);
       }
     }
   }
 
   // - redrawint the affected tags
 
   for (tag in tagsToRedraw){
    redrawFields(tag);
   }
   reColorFields();
 
   return ajaxData;
 }
 
 
 /* Bibcirculation Panel functions */
 
 function isBibCirculationPanelNecessary(){
   /** A function checking if the BibCirculation connectivity panel should
       be displayed. This information is derieved from the state of the record.
       Returns true or false
   */
 
   if (gRecID === null){
     return false;
   }
 
   // only if the record is saved and exists in the database and belongs
   // to a particular colelction
   return gDisplayBibCircPanel;
 }
 
 
 function updateBibCirculationPanel(){
   /** Updates the BibCirculation panel contents and visibility
   */
   if (gDisplayBibCircPanel === false){
     // in case, the panel is present, should be hidden
     $("#bibEditBibCircConnection").addClass("bibEditHiddenElement");
   }
   else {
     // the panel must be present - we have to show it
     $(".bibEditBibCircConnection").removeClass("bibEditHiddenElement");
   }
 
   var interfaceElement = $("#bibEditBibCircConnection");
   if (isBibCirculationPanelNecessary()){
     interfaceElement.removeClass("bibEditHiddenElement");
   } else {
     interfaceElement.addClass("bibEditHiddenElement");
   }
 
   // updating the content
   var copiesCountElement = $('#bibEditBibCirculationCopies');
   copiesCountElement.attr("innerHTML", gPhysCopiesNum);
 }
 
 function bibCircIntGetEditCopyUrl(recId){
   /**A function returning the address under which, the edition of a
       given record is possible
   */
 
 //  return "/admin/bibcirculation/bibcirculationadmin.py/get_item_details?recid=" + recId;
   return gBibCircUrl;
 }
 
 
 function onBibCirculationBtnClicked(e){
   /** A function redirecting the user to the BibCiculation web interface
   */
   var link = bibCircIntGetEditCopyUrl(gRecID);
   window.open(link);
 }
diff --git a/modules/bibedit/lib/bibedit_engine.py b/modules/bibedit/lib/bibedit_engine.py
index 2b9c625d3..f47b0f49b 100644
--- a/modules/bibedit/lib/bibedit_engine.py
+++ b/modules/bibedit/lib/bibedit_engine.py
@@ -1,1307 +1,1322 @@
 ## This file is part of Invenio.
 ## Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 CERN.
 ##
 ## Invenio is free software; you can redistribute it and/or
 ## modify it under the terms of the GNU General Public License as
 ## published by the Free Software Foundation; either version 2 of the
 ## License, or (at your option) any later version.
 ##
 ## Invenio is distributed in the hope that it will be useful, but
 ## WITHOUT ANY WARRANTY; without even the implied warranty of
 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 ## General Public License for more details.
 ##
 ## You should have received a copy of the GNU General Public License
 ## along with Invenio; if not, write to the Free Software Foundation, Inc.,
 ## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 
 # pylint: disable=C0103
 """Invenio BibEdit Engine."""
 
 __revision__ = "$Id"
 
 from datetime import datetime
 
 import re
 import difflib
 import zlib
 
 from invenio import bibrecord
 from invenio import bibformat
 
 from invenio.jsonutils import json, CFG_JSON_AVAILABLE
 from invenio.bibedit_config import CFG_BIBEDIT_AJAX_RESULT_CODES, \
     CFG_BIBEDIT_JS_CHECK_SCROLL_INTERVAL, CFG_BIBEDIT_JS_HASH_CHECK_INTERVAL, \
     CFG_BIBEDIT_JS_CLONED_RECORD_COLOR, \
     CFG_BIBEDIT_JS_CLONED_RECORD_COLOR_FADE_DURATION, \
     CFG_BIBEDIT_JS_NEW_ADD_FIELD_FORM_COLOR, \
     CFG_BIBEDIT_JS_NEW_ADD_FIELD_FORM_COLOR_FADE_DURATION, \
     CFG_BIBEDIT_JS_NEW_CONTENT_COLOR, \
     CFG_BIBEDIT_JS_NEW_CONTENT_COLOR_FADE_DURATION, \
     CFG_BIBEDIT_JS_NEW_CONTENT_HIGHLIGHT_DELAY, \
     CFG_BIBEDIT_JS_STATUS_ERROR_TIME, CFG_BIBEDIT_JS_STATUS_INFO_TIME, \
     CFG_BIBEDIT_JS_TICKET_REFRESH_DELAY, CFG_BIBEDIT_MAX_SEARCH_RESULTS, \
     CFG_BIBEDIT_TAG_FORMAT, CFG_BIBEDIT_AJAX_RESULT_CODES_REV, \
     CFG_BIBEDIT_AUTOSUGGEST_TAGS, CFG_BIBEDIT_AUTOCOMPLETE_TAGS_KBS,\
     CFG_BIBEDIT_KEYWORD_TAXONOMY, CFG_BIBEDIT_KEYWORD_TAG, \
     CFG_BIBEDIT_KEYWORD_RDFLABEL
 
 from invenio.config import CFG_SITE_LANG, CFG_DEVEL_SITE
 from invenio.bibedit_dblayer import get_name_tags_all, reserve_record_id, \
     get_related_hp_changesets, get_hp_update_xml, delete_hp_change, \
     get_record_last_modification_date, get_record_revision_author, \
     get_marcxml_of_record_revision, delete_related_holdingpen_changes, \
     get_record_revisions
 
 from invenio.bibedit_utils import cache_exists, cache_expired, \
     create_cache_file, delete_cache_file, get_bibrecord, \
     get_cache_file_contents, get_cache_mtime, get_record_templates, \
     get_record_template, latest_record_revision, record_locked_by_other_user, \
     record_locked_by_queue, save_xml_record, touch_cache_file, \
     update_cache_file_contents, get_field_templates, get_marcxml_of_revision, \
     revision_to_timestamp, timestamp_to_revision, \
     get_record_revision_timestamps, record_revision_exists, \
     can_record_have_physical_copies, extend_record_with_template, \
     merge_record_with_template
 
 from invenio.bibrecord import create_record, print_rec, record_add_field, \
     record_add_subfield_into, record_delete_field, \
     record_delete_subfield_from, \
     record_modify_subfield, record_move_subfield, \
     create_field, record_replace_field, record_move_fields, \
     record_modify_controlfield, record_get_field_values, \
     record_get_subfields
 from invenio.config import CFG_BIBEDIT_PROTECTED_FIELDS, CFG_CERN_SITE, \
     CFG_SITE_URL, CFG_SITE_RECORD, CFG_BIBEDIT_KB_SUBJECTS, \
     CFG_BIBEDIT_KB_INSTITUTIONS, CFG_BIBEDIT_AUTOCOMPLETE_INSTITUTIONS_FIELDS
 from invenio.search_engine import record_exists, search_pattern
 from invenio.webuser import session_param_get, session_param_set
 from invenio.bibcatalog import bibcatalog_system
 from invenio.webpage import page
 from invenio.htmlutils import get_mathjax_header
 from invenio.textutils import wash_for_xml
 from invenio.bibknowledge import get_kbd_values_for_bibedit, get_kbr_values, \
      get_kbt_items_for_bibedit, kb_exists
 
 from invenio.bibcirculation_dblayer import get_number_copies, has_copies
 from invenio.bibcirculation_utils import create_item_details_url
 
 import invenio.template
 bibedit_templates = invenio.template.load('bibedit')
 
 re_revdate_split = re.compile('^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)')
 
 def get_empty_fields_templates():
     """
     Returning the templates of empty fields::
      -an empty data field
      -an empty control field
     """
     return [{
                 "name": "Empty field",
                 "description": "The data field not containing any " + \
                                "information filled in",
                 "tag" : "",
                 "ind1" : "",
                 "ind2" : "",
                 "subfields" : [("","")],
                 "isControlfield" : False
             },{
                 "name" : "Empty control field",
                 "description" : "The controlfield not containing any " + \
                                 "data or tag description",
                 "isControlfield" : True,
                 "tag" : "",
                 "value" : ""
             }]
 
 def get_available_fields_templates():
     """
     A method returning all the available field templates
     Returns a list of descriptors. Each descriptor has
     the same structure as a full field descriptor inside the
     record
     """
     templates = get_field_templates()
     #result = get_empty_fields_templates()
     result = []
     for template in templates:
         tplTag = template[3].keys()[0]
         field = template[3][tplTag][0]
 
         if (field[0] == []):
         # if the field is a controlField, add different structure
             result.append({
                     "name" : template[1],
                     "description" : template[2],
                     "isControlfield" : True,
                     "tag" : tplTag,
                     "value" : field[3]
                 })
         else:
             result.append({
                     "name": template[1],
                     "description": template[2],
                     "tag" : tplTag,
                     "ind1" : field[1],
                     "ind2" : field[2],
                     "subfields" : field[0],
                     "isControlfield" : False
                     })
     return result
 
 def perform_request_init(uid, ln, req, lastupdated):
     """Handle the initial request by adding menu and JavaScript to the page."""
     errors   = []
     warnings = []
     body = ''
 
     # Add script data.
     record_templates = get_record_templates()
     record_templates.sort()
     tag_names = get_name_tags_all()
     protected_fields = ['001']
     protected_fields.extend(CFG_BIBEDIT_PROTECTED_FIELDS.split(','))
     history_url = '"' + CFG_SITE_URL + '/admin/bibedit/bibeditadmin.py/history"'
     cern_site = 'false'
 
     if not CFG_JSON_AVAILABLE:
         title = 'Record Editor'
         body = '''Sorry, the record editor cannot operate when the
                 `simplejson' module is not installed.  Please see the INSTALL
                 file.'''
         return page(title       = title,
                     body        = body,
                     errors      = [],
                     warnings    = [],
                     uid         = uid,
                     language    = ln,
                     navtrail    = "",
                     lastupdated = lastupdated,
                     req         = req)
 
 
     if CFG_CERN_SITE:
         cern_site = 'true'
     data = {'gRECORD_TEMPLATES': record_templates,
             'gTAG_NAMES': tag_names,
             'gPROTECTED_FIELDS': protected_fields,
             'gSITE_URL': '"' + CFG_SITE_URL + '"',
             'gSITE_RECORD': '"' + CFG_SITE_RECORD + '"',
             'gHISTORY_URL': history_url,
             'gCERN_SITE': cern_site,
             'gHASH_CHECK_INTERVAL': CFG_BIBEDIT_JS_HASH_CHECK_INTERVAL,
             'gCHECK_SCROLL_INTERVAL': CFG_BIBEDIT_JS_CHECK_SCROLL_INTERVAL,
             'gSTATUS_ERROR_TIME': CFG_BIBEDIT_JS_STATUS_ERROR_TIME,
             'gSTATUS_INFO_TIME': CFG_BIBEDIT_JS_STATUS_INFO_TIME,
             'gCLONED_RECORD_COLOR':
                 '"' + CFG_BIBEDIT_JS_CLONED_RECORD_COLOR + '"',
             'gCLONED_RECORD_COLOR_FADE_DURATION':
                 CFG_BIBEDIT_JS_CLONED_RECORD_COLOR_FADE_DURATION,
             'gNEW_ADD_FIELD_FORM_COLOR':
                 '"' + CFG_BIBEDIT_JS_NEW_ADD_FIELD_FORM_COLOR + '"',
             'gNEW_ADD_FIELD_FORM_COLOR_FADE_DURATION':
                 CFG_BIBEDIT_JS_NEW_ADD_FIELD_FORM_COLOR_FADE_DURATION,
             'gNEW_CONTENT_COLOR': '"' + CFG_BIBEDIT_JS_NEW_CONTENT_COLOR + '"',
             'gNEW_CONTENT_COLOR_FADE_DURATION':
                 CFG_BIBEDIT_JS_NEW_CONTENT_COLOR_FADE_DURATION,
             'gNEW_CONTENT_HIGHLIGHT_DELAY':
                 CFG_BIBEDIT_JS_NEW_CONTENT_HIGHLIGHT_DELAY,
             'gTICKET_REFRESH_DELAY': CFG_BIBEDIT_JS_TICKET_REFRESH_DELAY,
             'gRESULT_CODES': CFG_BIBEDIT_AJAX_RESULT_CODES,
             'gAUTOSUGGEST_TAGS' : CFG_BIBEDIT_AUTOSUGGEST_TAGS,
             'gAUTOCOMPLETE_TAGS' : CFG_BIBEDIT_AUTOCOMPLETE_TAGS_KBS.keys(),
             'gKEYWORD_TAG' : '"' + CFG_BIBEDIT_KEYWORD_TAG  + '"',
             'gAVAILABLE_KBS': get_available_kbs(),
             'gTagsToAutocomplete': CFG_BIBEDIT_AUTOCOMPLETE_INSTITUTIONS_FIELDS
             }
     body += '<script type="text/javascript">\n'
     for key in data:
         body += '    var %s = %s;\n' % (key, data[key])
     body += '    </script>\n'
 
     # Adding the information about field templates
     fieldTemplates = get_available_fields_templates()
     body += "<script>\n" + \
             "   var fieldTemplates = %s\n" % (json.dumps(fieldTemplates), ) + \
             "</script>\n"
     # Add scripts (the ordering is NOT irrelevant).
     scripts = ['jquery.jeditable.mini.js', 'jquery.hotkeys.js', 'json2.js',
                'bibedit_display.js', 'bibedit_engine.js', 'bibedit_keys.js',
                'bibedit_menu.js', 'bibedit_holdingpen.js', 'marcxml.js',
                'bibedit_clipboard.js','jquery-ui.min.js']
 
     for script in scripts:
         body += '    <script type="text/javascript" src="%s/js/%s">' \
             '</script>\n' % (CFG_SITE_URL, script)
 
     body += '<link rel="stylesheet" type="text/css" href="/img/jquery-ui.css" />'
 
     # Build page structure and menu.
     # rec = create_record(format_record(235, "xm"))[0]
     #oaiId = record_extract_oai_id(rec)
 
     body += bibedit_templates.menu()
     body += '    <div id="bibEditContent"></div>\n'
 
     return body, errors, warnings
 
 def get_available_kbs():
     """
     Return list of KBs that are available in the system to be used with
     BibEdit
     """
     kb_list = [CFG_BIBEDIT_KB_INSTITUTIONS, CFG_BIBEDIT_KB_SUBJECTS]
     available_kbs = [kb for kb in kb_list if kb_exists(kb)]
     return available_kbs
 
 def get_xml_comparison(header1, header2, xml1, xml2):
     """
     Return diffs of two MARCXML records.
     """
     return "".join(difflib.unified_diff(xml1.splitlines(1),
         xml2.splitlines(1), header1, header2))
 
 def get_marcxml_of_revision_id(recid, revid):
     """
     Return MARCXML string with corresponding to revision REVID
     (=RECID.REVDATE) of a record.  Return empty string if revision
     does not exist.
     """
     res = ""
     job_date = "%s-%s-%s %s:%s:%s" % re_revdate_split.search(revid).groups()
     tmp_res = get_marcxml_of_record_revision(recid, job_date)
     if tmp_res:
         for row in tmp_res:
             res += zlib.decompress(row[0]) + "\n"
     return res
 
 def perform_request_compare(ln, recid, rev1, rev2):
     """Handle a request for comparing two records"""
     body = ""
     errors = []
     warnings = []
 
     if (not record_revision_exists(recid, rev1)) or \
        (not record_revision_exists(recid, rev2)):
         body = "The requested record revision does not exist !"
     else:
         xml1 = get_marcxml_of_revision_id(recid, rev1)
         xml2 = get_marcxml_of_revision_id(recid, rev2)
         fullrevid1 = "%i.%s" % (recid, rev1)
         fullrevid2 = "%i.%s" % (recid, rev2)
         comparison = bibedit_templates.clean_value(
             get_xml_comparison(fullrevid1, fullrevid2, xml1, xml2),
             'text').replace('\n', '<br />\n           ')
         job_date1 = "%s-%s-%s %s:%s:%s" % re_revdate_split.search(rev1).groups()
         job_date2 = "%s-%s-%s %s:%s:%s" % re_revdate_split.search(rev2).groups()
         body += bibedit_templates.history_comparebox(ln, job_date1,
                                                  job_date2, comparison)
     return body, errors, warnings
 
 def perform_request_newticket(recid, uid):
     """create a new ticket with this record's number
     @param recid: record id
     @param uid: user id
     @return: (error_msg, url)
 
     """
     t_url = ""
     errmsg = ""
     if bibcatalog_system is not None:
         t_id = bibcatalog_system.ticket_submit(uid, "", recid, "")
         if t_id:
             #get the ticket's URL
             t_url = bibcatalog_system.ticket_get_attribute(uid, t_id, 'url_modify')
         else:
             errmsg = "ticket_submit failed"
     else:
         errmsg = "No ticket system configured"
     return (errmsg, t_url)
 
 def perform_request_ajax(req, recid, uid, data, isBulk = False, \
                          ln = CFG_SITE_LANG):
     """Handle Ajax requests by redirecting to appropriate function."""
     response = {}
     request_type = data['requestType']
     undo_redo = None
     if data.has_key("undoRedo"):
         undo_redo = data["undoRedo"]
     # Call function based on request type.
     if request_type == 'searchForRecord':
         # Search request.
         response.update(perform_request_search(data))
     elif request_type in ['changeTagFormat']:
         # User related requests.
         response.update(perform_request_user(req, request_type, recid, data))
     elif request_type in ('getRecord', 'submit', 'cancel', 'newRecord',
         'deleteRecord', 'deleteRecordCache', 'prepareRecordMerge', 'revert'):
         # 'Major' record related requests.
         response.update(perform_request_record(req, request_type, recid, uid,
                                                data))
     elif request_type in ('addField', 'addSubfields', \
                           'addFieldsSubfieldsOnPositions', 'modifyContent', \
                           'modifySubfieldTag', 'modifyFieldTag', \
                           'moveSubfield', 'deleteFields', 'moveField', \
                           'modifyField', 'otherUpdateRequest', \
                           'disableHpChange', 'deactivateHoldingPenChangeset'):
         # Record updates.
         cacheMTime = data['cacheMTime']
         if data.has_key('hpChanges'):
             hpChanges = data['hpChanges']
         else:
             hpChanges = {}
 
         response.update(perform_request_update_record(request_type, recid, \
                                                       uid, cacheMTime, data, \
                                                       hpChanges, undo_redo, \
                                                       isBulk, ln))
     elif request_type in ('autosuggest', 'autocomplete', 'autokeyword'):
         response.update(perform_request_autocomplete(request_type, recid, uid, \
                                                      data))
 
     elif request_type in ('getTickets', ):
         # BibCatalog requests.
         response.update(perform_request_bibcatalog(request_type, recid, uid))
     elif request_type in ('getHoldingPenUpdates', ):
         response.update(perform_request_holdingpen(request_type, recid))
 
     elif request_type in ('getHoldingPenUpdateDetails', \
                           'deleteHoldingPenChangeset'):
         updateId = data['changesetNumber']
         response.update(perform_request_holdingpen(request_type, recid, \
                                                    updateId))
     elif request_type in ('applyBulkUpdates', ):
         # a general version of a bulk request
         changes = data['requestsData']
         cacheMTime = data['cacheMTime']
         response.update(perform_bulk_request_ajax(req, recid, uid, changes, \
                                                   undo_redo, cacheMTime))
     elif request_type in ('preview', ):
         response.update(perform_request_preview_record(request_type, recid, uid))
 
     return response
 
 def perform_bulk_request_ajax(req, recid, uid, reqsData, undoRedo, cacheMTime):
     """ An AJAX handler used when treating bulk updates """
     lastResult = {}
     lastTime = cacheMTime
     isFirst = True
     for data in reqsData:
         assert data != None
         data['cacheMTime'] = lastTime
         if isFirst and undoRedo != None:
             # we add the undo/redo handler to the first operation in order to
             # save the handler on the server side !
             data['undoRedo'] = undoRedo
             isFirst = False
         lastResult = perform_request_ajax(req, recid, uid, data, True)
         # now we have to update the cacheMtime in next request !
 #        if lastResult.has_key('cacheMTime'):
         try:
             lastTime = lastResult['cacheMTime']
         except:
             raise Exception(str(lastResult))
     return lastResult
 
 def perform_request_search(data):
     """Handle search requests."""
     response = {}
     searchType = data['searchType']
     if searchType is None:
         searchType = "anywhere"
     searchPattern = data['searchPattern']
     if searchType == 'anywhere':
         pattern = searchPattern
     else:
         pattern = searchType + ':' + searchPattern
     result_set = list(search_pattern(p=pattern))
     response['resultCode'] = 1
     response['resultSet'] = result_set[0:CFG_BIBEDIT_MAX_SEARCH_RESULTS]
     return response
 
 def perform_request_user(req, request_type, recid, data):
     """Handle user related requests."""
     response = {}
     if request_type == 'changeTagFormat':
         try:
             tagformat_settings = session_param_get(req, 'bibedit_tagformat')
         except KeyError:
             tagformat_settings = {}
         tagformat_settings[recid] = data['tagFormat']
         session_param_set(req, 'bibedit_tagformat', tagformat_settings)
         response['resultCode'] = 2
     return response
 
 def perform_request_holdingpen(request_type, recId, changeId=None):
     """
     A method performing the holdingPen ajax request. The following types of
     requests can be made::
      -getHoldingPenUpdates: retrieving the holding pen updates pending
                             for a given record
     """
     response = {}
     if request_type == 'getHoldingPenUpdates':
         changeSet = get_related_hp_changesets(recId)
         changes = []
         for change in changeSet:
             changes.append((str(change[0]), str(change[1])))
         response["changes"] = changes
     elif request_type == 'getHoldingPenUpdateDetails':
         # returning the list of changes related to the holding pen update
         # the format based on what the record difference xtool returns
 
         assert(changeId != None)
         hpContent = get_hp_update_xml(changeId)
         holdingPenRecord = create_record(hpContent[0], "xm")[0]
 #        databaseRecord = get_record(hpContent[1])
         response['record'] = holdingPenRecord
         response['changeset_number'] = changeId
     elif request_type == 'deleteHoldingPenChangeset':
         assert(changeId != None)
         delete_hp_change(changeId)
     return response
 
 def perform_request_record(req, request_type, recid, uid, data, ln=CFG_SITE_LANG):
     """Handle 'major' record related requests like fetching, submitting or
     deleting a record, cancel editing or preparing a record for merging.
 
     """
     response = {}
 
     if request_type == 'newRecord':
         # Create a new record.
         new_recid = reserve_record_id()
         new_type = data['newType']
         if new_type == 'empty':
             # Create a new empty record.
             create_cache_file(recid, uid)
             response['resultCode'], response['newRecID'] = 6, new_recid
 
         elif new_type == 'template':
             # Create a new record from XML record template.
             template_filename = data['templateFilename']
             template = get_record_template(template_filename)
             if not template:
                 response['resultCode']  = 108
             else:
                 record = create_record(template)[0]
                 if not record:
                     response['resultCode']  = 109
                 else:
                     record_add_field(record, '001',
                                      controlfield_value=str(new_recid))
                     create_cache_file(new_recid, uid, record, True)
                     response['resultCode'], response['newRecID']  = 7, new_recid
 
         elif new_type == 'clone':
             # Clone an existing record (from the users cache).
             existing_cache = cache_exists(recid, uid)
             if existing_cache:
                 try:
                     record = get_cache_file_contents(recid, uid)[2]
                 except:
                     # if, for example, the cache format was wrong (outdated)
                     record = get_bibrecord(recid)
             else:
                 # Cache missing. Fall back to using original version.
                 record = get_bibrecord(recid)
             record_delete_field(record, '001')
             record_add_field(record, '001', controlfield_value=str(new_recid))
             create_cache_file(new_recid, uid, record, True)
             response['resultCode'], response['newRecID'] = 8, new_recid
     elif request_type == 'getRecord':
         # Fetch the record. Possible error situations:
         # - Non-existing record
         # - Deleted record
         # - Record locked by other user
         # - Record locked by queue
         # A cache file will be created if it does not exist.
         # If the cache is outdated (i.e., not based on the latest DB revision),
         # cacheOutdated will be set to True in the response.
         record_status = record_exists(recid)
         existing_cache = cache_exists(recid, uid)
         read_only_mode = False
 
         if data.has_key("inReadOnlyMode"):
             read_only_mode = data['inReadOnlyMode']
 
         if record_status == 0:
             response['resultCode'] = 102
         elif record_status == -1:
             response['resultCode'] = 103
         elif not read_only_mode and not existing_cache and \
                 record_locked_by_other_user(recid, uid):
             response['resultCode'] = 104
         elif not read_only_mode and existing_cache and \
                 cache_expired(recid, uid) and \
                 record_locked_by_other_user(recid, uid):
             response['resultCode'] = 104
         elif not read_only_mode and record_locked_by_queue(recid):
             response['resultCode'] = 105
         else:
             if data.get('deleteRecordCache'):
                 delete_cache_file(recid, uid)
                 existing_cache = False
                 pending_changes = []
                 disabled_hp_changes = {}
             if read_only_mode:
                 if data.has_key('recordRevision') and data['recordRevision'] != 'sampleValue':
                     record_revision_ts = data['recordRevision']
                     record_xml = get_marcxml_of_revision(recid, \
                                                          record_revision_ts)
                     record = create_record(record_xml)[0]
                     record_revision = timestamp_to_revision(record_revision_ts)
                     pending_changes = []
                     disabled_hp_changes = {}
                 else:
                     # a normal cacheless retrieval of a record
                     record = get_bibrecord(recid)
                     record_revision = get_record_last_modification_date(recid)
                     if record_revision == None:
                         record_revision = datetime.now().timetuple()
                     pending_changes = []
                     disabled_hp_changes = {}
                 cache_dirty = False
                 mtime = 0
                 undo_list = []
                 redo_list = []
             elif not existing_cache:
                 record_revision, record = create_cache_file(recid, uid)
                 mtime = get_cache_mtime(recid, uid)
                 pending_changes = []
                 disabled_hp_changes = {}
                 undo_list = []
                 redo_list = []
                 cache_dirty = False
             else:
                 #TODO: This try except should be replaced with something nicer,
                 #      like an argument indicating if a new cache file is to
                 #      be created
                 try:
                     cache_dirty, record_revision, record, pending_changes, \
                         disabled_hp_changes, undo_list, redo_list = \
                         get_cache_file_contents(recid, uid)
                     touch_cache_file(recid, uid)
                     mtime = get_cache_mtime(recid, uid)
                     if not latest_record_revision(recid, record_revision) and \
                             get_record_revisions(recid) != ():
                         # This sould prevent from using old cache in case of
                         # viewing old version. If there are no revisions,
                         # it means we should skip this step because this
                         # is a new record
                         response['cacheOutdated'] = True
 
                 except:
                     record_revision, record = create_cache_file(recid, uid)
                     mtime = get_cache_mtime(recid, uid)
                     pending_changes = []
                     disabled_hp_changes = {}
                     cache_dirty = False
                     undo_list = []
                     redo_list = []
             if data.get('clonedRecord',''):
                 response['resultCode'] = 9
             else:
                 response['resultCode'] = 3
             revision_author = get_record_revision_author(recid, record_revision)
             latest_revision = get_record_last_modification_date(recid)
             if latest_revision == None:
                 latest_revision = datetime.now().timetuple()
             last_revision_ts = revision_to_timestamp(latest_revision)
 
             revisions_history = get_record_revision_timestamps(recid)
             number_of_physical_copies = get_number_copies(recid)
             bibcirc_details_URL = create_item_details_url(recid, ln)
             can_have_copies = can_record_have_physical_copies(recid)
 
             # For some collections, merge template with record
             template_to_merge = extend_record_with_template(recid)
             if template_to_merge:
                 record = merge_record_with_template(record, template_to_merge)
                 create_cache_file(recid, uid, record, True)
                 
             response['cacheDirty'], response['record'], \
                 response['cacheMTime'], response['recordRevision'], \
                 response['revisionAuthor'], response['lastRevision'], \
                 response['revisionsHistory'], response['inReadOnlyMode'], \
                 response['pendingHpChanges'], response['disabledHpChanges'], \
                 response['undoList'], response['redoList'] = cache_dirty, \
                 record, mtime, revision_to_timestamp(record_revision), \
                 revision_author, last_revision_ts, revisions_history, \
                 read_only_mode, pending_changes, disabled_hp_changes, \
                 undo_list, redo_list
             response['numberOfCopies'] = number_of_physical_copies
             response['bibCirculationUrl'] = bibcirc_details_URL
             response['canRecordHavePhysicalCopies'] = can_have_copies
             # Set tag format from user's session settings.
             try:
                 tagformat_settings = session_param_get(req, 'bibedit_tagformat')
                 tagformat = tagformat_settings[recid]
             except KeyError:
                 tagformat = CFG_BIBEDIT_TAG_FORMAT
             response['tagFormat'] = tagformat
             # KB information
             response['KBSubject'] = CFG_BIBEDIT_KB_SUBJECTS
             response['KBInstitution'] = CFG_BIBEDIT_KB_INSTITUTIONS
 
     elif request_type == 'submit':
         # Submit the record. Possible error situations:
         # - Missing cache file
         # - Cache file modified in other editor
         # - Record locked by other user
         # - Record locked by queue
         # - Invalid XML characters
         # If the cache is outdated cacheOutdated will be set to True in the
         # response.
         if not cache_exists(recid, uid):
             response['resultCode'] = 106
         elif not get_cache_mtime(recid, uid) == data['cacheMTime']:
             response['resultCode'] = 107
         elif cache_expired(recid, uid) and \
                 record_locked_by_other_user(recid, uid):
             response['resultCode'] = 104
         elif record_locked_by_queue(recid):
             response['resultCode'] = 105
         else:
             try:
                 tmp_result = get_cache_file_contents(recid, uid)
                 record_revision = tmp_result[1]
                 record = tmp_result[2]
                 pending_changes = tmp_result[3]
 #                disabled_changes = tmp_result[4]
 
                 xml_record = wash_for_xml(print_rec(record))
                 record, status_code, list_of_errors = create_record(xml_record)
                 if status_code == 0:
                     response['resultCode'], response['errors'] = 110, \
                         list_of_errors
                 elif not data['force'] and \
                         not latest_record_revision(recid, record_revision):
                     response['cacheOutdated'] = True
                     if CFG_DEVEL_SITE:
                         response['record_revision'] = record_revision.__str__()
                         response['newest_record_revision'] = \
                             get_record_last_modification_date(recid).__str__()
                 else:
                     save_xml_record(recid, uid)
                     response['resultCode'] = 4
             except Exception, e:
                 response['resultCode'] = CFG_BIBEDIT_AJAX_RESULT_CODES_REV[ \
                     'error_wrong_cache_file_format']
                 if CFG_DEVEL_SITE: # return debug information in the request
                     response['exception_message'] = e.__str__()
     elif request_type == 'revert':
         revId = data['revId']
         job_date = "%s-%s-%s %s:%s:%s" % re_revdate_split.search(revId).groups()
         revision_xml = get_marcxml_of_revision(recid, job_date)
         save_xml_record(recid, uid, revision_xml)
         if (cache_exists(recid, uid)):
             delete_cache_file(recid, uid)
         response['resultCode'] = 4
 
     elif request_type == 'cancel':
         # Cancel editing by deleting the cache file. Possible error situations:
         # - Cache file modified in other editor
         if cache_exists(recid, uid):
             if get_cache_mtime(recid, uid) == data['cacheMTime']:
                 delete_cache_file(recid, uid)
                 response['resultCode'] = 5
             else:
                 response['resultCode'] = 107
         else:
             response['resultCode'] = 5
 
     elif request_type == 'deleteRecord':
         # Submit the record. Possible error situations:
         # - Record locked by other user
         # - Record locked by queue
         # As the user is requesting deletion we proceed even if the cache file
         # is missing and we don't check if the cache is outdated or has
         # been modified in another editor.
         existing_cache = cache_exists(recid, uid)
         pending_changes = []
 
         if has_copies(recid):
             response['resultCode'] = \
                 CFG_BIBEDIT_AJAX_RESULT_CODES_REV['error_physical_copies_exist']
         elif existing_cache and cache_expired(recid, uid) and \
                 record_locked_by_other_user(recid, uid):
             response['resultCode'] = \
                 CFG_BIBEDIT_AJAX_RESULT_CODES_REV['error_rec_locked_by_user']
         elif record_locked_by_queue(recid):
             response['resultCode'] = \
                 CFG_BIBEDIT_AJAX_RESULT_CODES_REV['error_rec_locked_by_queue']
         else:
             if not existing_cache:
                 record_revision, record, pending_changes, \
                     deactivated_hp_changes, undo_list, redo_list = \
                     create_cache_file(recid, uid)
             else:
                 try:
                     record_revision, record, pending_changes, \
                         deactivated_hp_changes, undo_list, redo_list = \
                         get_cache_file_contents(recid, uid)[1:]
                 except:
                     record_revision, record, pending_changes, \
                         deactivated_hp_changes = create_cache_file(recid, uid)
             record_add_field(record, '980', ' ', ' ', '', [('c', 'DELETED')])
             undo_list = []
             redo_list = []
             update_cache_file_contents(recid, uid, record_revision, record, \
                                        pending_changes, \
                                        deactivated_hp_changes, undo_list, \
                                        redo_list)
             save_xml_record(recid, uid)
             delete_related_holdingpen_changes(recid) # we don't need any changes
                                                    # related to a deleted record
             response['resultCode'] = 10
 
     elif request_type == 'deleteRecordCache':
         # Delete the cache file. Ignore the request if the cache has been
         # modified in another editor.
         if cache_exists(recid, uid) and get_cache_mtime(recid, uid) == \
                 data['cacheMTime']:
             delete_cache_file(recid, uid)
         response['resultCode'] = 11
 
     elif request_type == 'prepareRecordMerge':
         # We want to merge the cache with the current DB version of the record,
         # so prepare an XML file from the file cache, to be used by BibMerge.
         # Possible error situations:
         # - Missing cache file
         # - Record locked by other user
         # - Record locked by queue
         # We don't check if cache is outdated (a likely scenario for this
         # request) or if it has been modified in another editor.
         if not cache_exists(recid, uid):
             response['resultCode'] = 106
         elif cache_expired(recid, uid) and \
                 record_locked_by_other_user(recid, uid):
             response['resultCode'] = 104
         elif record_locked_by_queue(recid):
             response['resultCode'] = 105
         else:
             save_xml_record(recid, uid, to_upload=False, to_merge=True)
             response['resultCode'] = 12
 
     return response
 
 def perform_request_update_record(request_type, recid, uid, cacheMTime, data, \
                                   hpChanges, undoRedoOp, isBulk=False):
     """
     Handle record update requests like adding, modifying, moving or deleting
     of fields or subfields. Possible common error situations::
      - Missing cache file
      - Cache file modified in other editor
     @param undoRedoOp: Indicates in "undo"/"redo"/undo_descriptor operation is
                        performed by a current request.
     """
 
     response = {}
     if not cache_exists(recid, uid):
         response['resultCode'] = 106
     elif not get_cache_mtime(recid, uid) == cacheMTime and isBulk == False:
         # In case of a bulk request, the changes are deliberately performed
         # immediately one after another
         response['resultCode'] = 107
     else:
         try:
             record_revision, record, pending_changes, deactivated_hp_changes, \
                 undo_list, redo_list = get_cache_file_contents(recid, uid)[1:]
         except:
             response['resultCode'] = CFG_BIBEDIT_AJAX_RESULT_CODES_REV[ \
                 'error_wrong_cache_file_format']
             return response
 
         # process all the Holding Pen changes operations ... regardles the
         # request type
 #        import rpdb2;
 #        rpdb2.start_embedded_debugger('password', fAllowRemote=True)
         if hpChanges.has_key("toDisable"):
             for changeId in hpChanges["toDisable"]:
                 pending_changes[changeId]["applied_change"] = True
 
         if hpChanges.has_key("toEnable"):
             for changeId in hpChanges["toEnable"]:
                 pending_changes[changeId]["applied_change"] = False
 
         if hpChanges.has_key("toOverride"):
             pending_changes = hpChanges["toOverride"]
 
         if hpChanges.has_key("changesetsToDeactivate"):
             for changesetId in hpChanges["changesetsToDeactivate"]:
                 deactivated_hp_changes[changesetId] = True
 
         if hpChanges.has_key("changesetsToActivate"):
             for changesetId in hpChanges["changesetsToActivate"]:
                 deactivated_hp_changes[changesetId] = False
 
         # processing the undo/redo entries
         if undoRedoOp == "undo":
             try:
                 redo_list = [undo_list[-1]] + redo_list
                 undo_list = undo_list[:-1]
             except:
                 raise Exception("An exception occured when undoing previous" + \
                                 " operation. Undo list: " + str(undo_list) + \
                                 " Redo list " + str(redo_list))
         elif undoRedoOp == "redo":
             try:
                 undo_list = undo_list + [redo_list[0]]
                 redo_list = redo_list[1:]
             except:
                 raise Exception("An exception occured when redoing previous" + \
                                 " operation. Undo list: " + str(undo_list) + \
                                 " Redo list " + str(redo_list))
         else:
             # This is a genuine operation - we have to add a new descriptor
             # to the undo list and cancel the redo unless the operation is
             # a bulk operation
             if undoRedoOp != None:
                 undo_list = undo_list + [undoRedoOp]
                 redo_list = []
             else:
                 assert isBulk == True
 
         field_position_local = data.get('fieldPosition')
         if field_position_local is not None:
             field_position_local = int(field_position_local)
         if request_type == 'otherUpdateRequest':
             # An empty request. Might be useful if we want to perform
             # operations that require only the actions performed globally,
             # like modifying the holdingPen changes list
             response['resultCode'] = CFG_BIBEDIT_AJAX_RESULT_CODES_REV[ \
                 'editor_modifications_changed']
         elif request_type == 'deactivateHoldingPenChangeset':
             # the changeset has been marked as processed ( user applied it in
             # the editor). Marking as used in the cache file.
             # CAUTION: This function has been implemented here because logically
             #          it fits with the modifications made to the cache file.
             #          No changes are made to the Holding Pen physically. The
             #          changesets are related to the cache because we want to
             #          cancel the removal every time the cache disappears for
             #          any reason
             response['resultCode'] = CFG_BIBEDIT_AJAX_RESULT_CODES_REV[ \
                 'disabled_hp_changeset']
         elif request_type == 'addField':
             if data['controlfield']:
                 record_add_field(record, data['tag'],
                                  controlfield_value=data['value'])
                 response['resultCode'] = 20
             else:
                 record_add_field(record, data['tag'], data['ind1'],
                                  data['ind2'], subfields=data['subfields'],
                                  field_position_local=field_position_local)
                 response['resultCode'] = 21
 
         elif request_type == 'addSubfields':
             subfields = data['subfields']
             for subfield in subfields:
                 record_add_subfield_into(record, data['tag'], subfield[0],
                     subfield[1], subfield_position=None,
                     field_position_local=field_position_local)
             if len(subfields) == 1:
                 response['resultCode'] = 22
             else:
                 response['resultCode'] = 23
         elif request_type == 'addFieldsSubfieldsOnPositions':
             #1) Sorting the fields by their identifiers
             fieldsToAdd = data['fieldsToAdd']
             subfieldsToAdd = data['subfieldsToAdd']
             for tag in fieldsToAdd.keys():
                 positions = fieldsToAdd[tag].keys()
                 positions.sort()
                 for position in positions:
                     # now adding fields at a position
 
                     isControlfield = (len(fieldsToAdd[tag][position][0]) == 0)
                     # if there are n subfields, this is a control field
                     if isControlfield:
                         controlfieldValue = fieldsToAdd[tag][position][3]
                         record_add_field(record, tag, field_position_local = \
                                              int(position), \
                                              controlfield_value = \
                                                  controlfieldValue)
                     else:
                         subfields = fieldsToAdd[tag][position][0]
                         ind1 = fieldsToAdd[tag][position][1]
                         ind2 = fieldsToAdd[tag][position][2]
                         record_add_field(record, tag, ind1, ind2, subfields = \
                                              subfields, field_position_local = \
                                                 int(position))
             # now adding the subfields
             for tag in subfieldsToAdd.keys():
                 for fieldPosition in subfieldsToAdd[tag].keys(): #now the fields
                                                           #order not important !
                     subfieldsPositions = subfieldsToAdd[tag][fieldPosition]. \
                                            keys()
                     subfieldsPositions.sort()
                     for subfieldPosition in subfieldsPositions:
                         subfield = subfieldsToAdd[tag][fieldPosition]\
                             [subfieldPosition]
                         record_add_subfield_into(record, tag, subfield[0], \
                                                  subfield[1], \
                                                  subfield_position = \
                                                      int(subfieldPosition), \
                                                  field_position_local = \
                                                      int(fieldPosition))
 
             response['resultCode'] = \
                 CFG_BIBEDIT_AJAX_RESULT_CODES_REV['added_positioned_subfields']
 
         elif request_type == 'modifyField': # changing the field structure
             # first remove subfields and then add new... change the indices
             subfields = data['subFields'] # parse the JSON representation of
                                           # the subfields here
 
             new_field = create_field(subfields, data['ind1'], data['ind2'])
             record_replace_field(record, data['tag'], new_field, \
                 field_position_local = data['fieldPosition'])
             response['resultCode'] = 26
 
         elif request_type == 'modifyContent':
             if data['subfieldIndex'] != None:
                 record_modify_subfield(record, data['tag'],
                     data['subfieldCode'], data['value'],
                     int(data['subfieldIndex']),
                     field_position_local=field_position_local)
             else:
                 record_modify_controlfield(record, data['tag'], data["value"],
                   field_position_local=field_position_local)
             response['resultCode'] = 24
 
         elif request_type == 'modifySubfieldTag':
             record_add_subfield_into(record, data['tag'], data['subfieldCode'],
             data["value"], subfield_position= int(data['subfieldIndex']),
             field_position_local=field_position_local)
 
             record_delete_subfield_from(record, data['tag'], int(data['subfieldIndex']) + 1,
             field_position_local=field_position_local)
 
             response['resultCode'] = 24
 
         elif request_type == 'modifyFieldTag':
             subfields = record_get_subfields(record, data['oldTag'],
             field_position_local=field_position_local)
 
             record_add_field(record, data['newTag'], data['ind1'],
                              data['ind2'] , subfields=subfields)
 
             record_delete_field(record, data['oldTag'], ind1=data['oldInd1'], \
                                 ind2=data['oldInd2'], field_position_local=field_position_local)
             response['resultCode'] = 32
 
         elif request_type == 'moveSubfield':
             record_move_subfield(record, data['tag'],
                 int(data['subfieldIndex']), int(data['newSubfieldIndex']),
                 field_position_local=field_position_local)
             response['resultCode'] = 25
 
         elif request_type == 'moveField':
             if data['direction'] == 'up':
                 final_position_local = field_position_local-1
             else: # direction is 'down'
                 final_position_local = field_position_local+1
             record_move_fields(record, data['tag'], [field_position_local],
                 final_position_local)
             response['resultCode'] = 32
 
         elif request_type == 'deleteFields':
             to_delete = data['toDelete']
             deleted_fields = 0
             deleted_subfields = 0
             for tag in to_delete:
                 #Sorting the fields in a edcreasing order by the local position!
                 fieldsOrder = to_delete[tag].keys()
                 fieldsOrder.sort(lambda a, b: int(b) - int(a))
                 for field_position_local in fieldsOrder:
                     if not to_delete[tag][field_position_local]:
                         # No subfields specified - delete entire field.
                         record_delete_field(record, tag,
                             field_position_local=int(field_position_local))
                         deleted_fields += 1
                     else:
                         for subfield_position in \
                                 to_delete[tag][field_position_local][::-1]:
                             # Delete subfields in reverse order (to keep the
                             # indexing correct).
                             record_delete_subfield_from(record, tag,
                                 int(subfield_position),
                                 field_position_local=int(field_position_local))
                             deleted_subfields += 1
             if deleted_fields == 1 and deleted_subfields == 0:
                 response['resultCode'] = 26
             elif deleted_fields and deleted_subfields == 0:
                 response['resultCode'] = 27
             elif deleted_subfields == 1 and deleted_fields == 0:
                 response['resultCode'] = 28
             elif deleted_subfields and deleted_fields == 0:
                 response['resultCode'] = 29
             else:
                 response['resultCode'] = 30
 
         response['cacheMTime'], response['cacheDirty'] = \
             update_cache_file_contents(recid, uid, record_revision,
                                        record, \
                                        pending_changes, \
                                        deactivated_hp_changes, \
                                        undo_list, redo_list), \
             True
 
     return response
 
 def perform_request_autocomplete(request_type, recid, uid, data):
     """
     Perfrom an AJAX request associated with the retrieval of autocomplete
     data.
 
     @param request_type: Type of the currently served request
     @param recid: the identifer of the record
     @param uid: The identifier of the user being currently logged in
     @param data: The request data containing possibly important additional
                  arguments
     """
     response = {}
     # get the values based on which one needs to search
     searchby = data['value']
         #we check if the data is properly defined
     fulltag = ''
     if data.has_key('maintag') and data.has_key('subtag1') and \
             data.has_key('subtag2') and data.has_key('subfieldcode'):
         maintag = data['maintag']
         subtag1 = data['subtag1']
         subtag2 = data['subtag2']
         u_subtag1 = subtag1
         u_subtag2 = subtag2
         if (not subtag1) or (subtag1 == ' '):
             u_subtag1 = '_'
         if (not subtag2) or (subtag2 == ' '):
             u_subtag2 = '_'
         subfieldcode = data['subfieldcode']
         fulltag = maintag+u_subtag1+u_subtag2+subfieldcode
     if (request_type == 'autokeyword'):
         #call the keyword-form-ontology function
         if fulltag and searchby:
             items = get_kbt_items_for_bibedit(CFG_BIBEDIT_KEYWORD_TAXONOMY, \
                                               CFG_BIBEDIT_KEYWORD_RDFLABEL, \
                                               searchby)
             response['autokeyword'] = items
     if (request_type == 'autosuggest'):
         #call knowledge base function to put the suggestions in an array..
         if fulltag and searchby and len(searchby) > 3:
             #add trailing '*' wildcard for 'search_unit_in_bibxxx()' if not already present
             suggest_values = get_kbd_values_for_bibedit(fulltag, "", searchby+"*")
             #remove ..
             new_suggest_vals = []
             for sugg in suggest_values:
                 if sugg.startswith(searchby):
                     new_suggest_vals.append(sugg)
             response['autosuggest'] = new_suggest_vals
     if (request_type == 'autocomplete'):
         #call the values function with the correct kb_name
         if CFG_BIBEDIT_AUTOCOMPLETE_TAGS_KBS.has_key(fulltag):
             kbname = CFG_BIBEDIT_AUTOCOMPLETE_TAGS_KBS[fulltag]
             #check if the seachby field has semicolons. Take all
             #the semicolon-separated items..
             items = []
             vals = []
             if searchby:
                 if searchby.rfind(';'):
                     items = searchby.split(';')
                 else:
                     items = [searchby.strip()]
             for item in items:
                 item = item.strip()
                 kbrvals = get_kbr_values(kbname, item, '', 'e') #we want an exact match
                 if kbrvals and kbrvals[0]: #add the found val into vals
                     vals.append(kbrvals[0])
             #check that the values are not already contained in other
             #instances of this field
             record = get_cache_file_contents(recid, uid)[2]
             xml_rec = wash_for_xml(print_rec(record))
             record, status_code, dummy_errors = create_record(xml_rec)
             existing_values = []
             if (status_code != 0):
                 existing_values = record_get_field_values(record,
                                                           maintag,
                                                           subtag1,
                                                           subtag2,
                                                           subfieldcode)
             #get the new values.. i.e. vals not in existing
             new_vals = vals
             for val in new_vals:
                 if val in existing_values:
                     new_vals.remove(val)
             response['autocomplete'] = new_vals
     response['resultCode'] = CFG_BIBEDIT_AJAX_RESULT_CODES_REV['autosuggestion_scanned']
     return response
 
 def perform_request_bibcatalog(request_type, recid, uid):
     """Handle request to BibCatalog (RT).
 
     """
     response = {}
 
     if request_type == 'getTickets':
         # Insert the ticket data in the response, if possible
         if bibcatalog_system is None:
             response['tickets'] = "<!--No ticket system configured-->"
         elif bibcatalog_system and uid:
             bibcat_resp = bibcatalog_system.check_system(uid)
             if bibcat_resp == "":
                 tickets_found = bibcatalog_system.ticket_search(uid, \
                     status=['new', 'open'], recordid=recid)
                 t_url_str = '' #put ticket urls here, formatted for HTML display
                 for t_id in tickets_found:
                     #t_url = bibcatalog_system.ticket_get_attribute(uid, \
                     #    t_id, 'url_display')
                     ticket_info = bibcatalog_system.ticket_get_info( \
                         uid, t_id, ['url_display', 'url_close'])
                     t_url = ticket_info['url_display']
                     t_close_url = ticket_info['url_close']
                     #format..
                     t_url_str += "#" + str(t_id) + '<a href="' + t_url + \
                         '">[read]</a> <a href="' + t_close_url + \
                         '">[close]</a><br/>'
                 #put ticket header and tickets links in the box
                 t_url_str = "<strong>Tickets</strong><br/>" + t_url_str + \
                     "<br/>" + '<a href="new_ticket?recid=' + str(recid) + \
                     '>[new ticket]</a>'
                 response['tickets'] = t_url_str
                 #add a new ticket link
             else:
                 #put something in the tickets container, for debug
                 response['tickets'] = "<!--"+bibcat_resp+"-->"
         response['resultCode'] = 31
     return response
 
 def perform_request_preview_record(request_type, recid, uid):
     """ Handle request to preview record with formatting
 
     """
 
     response = {}
     if request_type == "preview":
         if cache_exists(recid, uid):
             dummy1, dummy2, record, dummy3, dummy4, dummy5, dummy6 = get_cache_file_contents(recid, uid)
         else:
             record = get_bibrecord(recid)
     response['html_preview'] = _get_formated_record(record)
 
     # clean the record from unfilled volatile fields
     record_strip_empty_volatile_subfields(record)
     record_strip_empty_fields(record)
     response['html_preview'] = _get_formated_record(record, data['new_window'])
 
     return response
 
 def perform_request_get_pdf_url(recid):
     """ Handle request to get the URL of the attached PDF
     """
     response = {}
     rec_info = BibRecDocs(recid)
     docs = rec_info.list_bibdocs()
     try:
         doc = docs[0]
         response['pdf_url'] = doc.get_file('pdf').get_url()
     except (IndexError, InvenioWebSubmitFileError):
         # FIXME, return here some information about error.
         # We could allow the user to specify a URl and add the FFT tags automatically
         response['pdf_url'] = ''
     return response
 
 def perform_request_record_has_pdf(recid, uid):
     """ Check if record has a pdf attached
     """
     response = {'record_has_pdf': True}
     rec_info = BibRecDocs(recid)
     docs = rec_info.list_bibdocs()
     try:
         doc = docs[0]
     except IndexError:
         response['record_has_pdf'] = False
     finally:
         return response
 
 def _get_formated_record(record, new_window):
     """Returns a record in a given format
 
     @param record: BibRecord object
     """
+    from invenio.config import CFG_WEBSTYLE_TEMPLATE_SKIN
 
     xml_record = wash_for_xml(bibrecord.record_xml_output(record))
 
-    result =  "<html><head><title>Record preview</title></head>"
+    result =  "<html><head><title>Record preview</title>"
+    result += """<style type="text/css">
+                    #referenceinp_link { display: none; }
+                    #referenceinp_link_span { display: none; }
+                </style></head>
+                <link rel="stylesheet" href="%(cssurl)s/img/invenio%(cssskin)s.css" type="text/css">
+                """%{'cssurl': CFG_SITE_URL,
+                     'cssskin': CFG_WEBSTYLE_TEMPLATE_SKIN != 'default' and '_' + CFG_WEBSTYLE_TEMPLATE_SKIN or ''
+                     }
     result += get_mathjax_header(True)
-    result += "<body><h2> Brief format preview </h2>"
+    result += "<body><h2> Brief format preview </h2><br />"
     result += bibformat.format_record(recID=None,
                                      of="hb",
                                      xml_record=xml_record)
-    result += "<h2> Detailed format preview </h2>"
+    result += "<br /><h2> Detailed format preview </h2><br />"
 
     result += bibformat.format_record(recID=None,
                                      of="hd",
                                      xml_record=xml_record)
+    #Preview references
+    result += "<br /><h2> References </h2><br />"
+
+    result += bibformat.format_record(0,
+                                    'hdref',
+                                    xml_record=xml_record)
 
     result += "</body></html>"
 
 
     return result
 
 ########### Functions related to templates web interface #############
 
 def perform_request_init_template_interface():
     """Handle a request to manage templates"""
     errors   = []
     warnings = []
     body = ''
 
     # Add script data.
     record_templates = get_record_templates()
     record_templates.sort()
 
     data = {'gRECORD_TEMPLATES': record_templates,
             'gSITE_RECORD': '"' + CFG_SITE_RECORD + '"',
             'gSITE_URL': '"' + CFG_SITE_URL + '"'}
 
     body += '<script type="text/javascript">\n'
     for key in data:
         body += '    var %s = %s;\n' % (key, data[key])
     body += '    </script>\n'
 
     # Add scripts (the ordering is NOT irrelevant).
     scripts = ['jquery-ui.min.js',
                'json2.js', 'bibedit_display.js', 'bibedit_template_interface.js']
 
     for script in scripts:
         body += '    <script type="text/javascript" src="%s/js/%s">' \
             '</script>\n' % (CFG_SITE_URL, script)
 
     body += '    <div id="bibEditTemplateList"></div>\n'
     body += '    <div id="bibEditTemplateEdit"></div>\n'
 
     return body, errors, warnings
 
 def perform_request_ajax_template_interface(data):
     """Handle Ajax requests by redirecting to appropriate function."""
     response = {}
     request_type = data['requestType']
 
     if request_type == 'editTemplate':
         # Edit a template request.
         response.update(perform_request_edit_template(data))
 
     return response
 
 def perform_request_edit_template(data):
     """ Handle request to edit a template """
     response = {}
     template_filename = data['templateFilename']
     template = get_record_template(template_filename)
     if not template:
         response['resultCode']  = 1
     else:
         response['templateMARCXML'] = template
 
     return response