Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
lightbox.js 16.96 KiB
/*global checkSaveStatuses, console, deparam, extractSource, getFullCartItems, hexEncode, htmlEncode, path, rc4Encrypt, refreshCommentList, vufindString */

/**
 * We save the URL and POST data every time we call getLightboxByUrl.
 * If we don't have a target a form submission, we use these variables
 * to replicate empty target behaviour by submitting to the current "page".
 */
var lastLightboxURL,lastLightboxPOST;
var lightboxShown = false; // Is the lightbox deployed?
var modalXHR; // Used for current in-progress XHR lightbox request
var modalOpenStack = [];
var modalCloseStack = [];
var modalFormHandlers = {};

/**********************************/
/* ======    INTERFACE     ====== */
/**********************************/
/**
 * Register custom open event handlers
 */
function addLightboxOnOpen(func) {
  modalOpenStack.push(func);
}
/**
 * Register custom close event handlers
 */
function addLightboxOnClose(func) {
  modalCloseStack.push(func);
}
/**
 * Register custom form handlers
 */
function addLightboxFormHandler(formName, func) {
  modalFormHandlers[formName] = func;
}

/**********************************/
/* ====== LIGHTBOX ACTIONS ====== */
/**********************************/
/**
 * Change the content of the lightbox.
 *
 * Hide the header if it's empty to make more
 * room for content and avoid double headers.
 */
function changeModalContent(html) {
  var header = $('#modal .modal-header');
  if(header.find('h3').html().length == 0) {
    header.css('border-bottom-width', '0');
  } else {
    header.css('border-bottom-width', '1px');
  }
  $('#modal .modal-body').html(html).modal({'show':true,'backdrop':false});
}

/**
 * This is the function you call to manually close the lightbox
 */
function closeLightbox() {
  $('#modal').modal('hide');
}
/**
 * This function is attached to the lightbox close event,
 * so it always runs when the lightbox is closed.
 */
function closeLightboxActions() {
  lightboxShown = false;
  // Clean out stack
  while(modalCloseStack.length > 0) {
    var f = modalCloseStack.pop();
    f();
  }
  // Abort requests triggered by the lightbox
  if(modalXHR) { modalXHR.abort(); }
  // Reset content so we start fresh when we open a lightbox
  $('#modal').removeData('modal');
  $('#modal').find('.modal-header h3').html('');
  $('#modal').find('.modal-body').html(vufindString.loading + "...");
  
  /**
   * Below here, we're doing content updates (sample events that affect content)
   */ 
  var recordId = $('#record_id').val();
  var recordSource = $('.hiddenSource').val();
   
  // Update the "Saved In" lists (add favorite, login)
  if(typeof checkSaveStatuses === 'function') {
    checkSaveStatuses();
  }
  
  // Update the comment list (add comment, login)
  if(typeof refreshCommentList === 'function') {
    refreshCommentList(recordId, recordSource);
  }
  // Update tag list (add tag)
  var tagList = $('#tagList');
  if (tagList.length > 0) {
      tagList.empty();
      var url = path + '/AJAX/JSON?' + $.param({method:'getRecordTags',id:recordId,'source':recordSource});
      $.ajax({
        dataType: 'json',
        url: url,
        success: function(response) {
          if (response.status == 'OK') {
            $.each(response.data, function(i, tag) {
              var href = path + '/Tag?' + $.param({lookfor:tag.tag});
              var html = (i>0 ? ', ' : ' ') + '<a href="' + htmlEncode(href) + '">' + htmlEncode(tag.tag) +'</a> (' + htmlEncode(tag.cnt) + ')';
              tagList.append(html);
            });
          } else if (response.data && response.data.length > 0) {
            tagList.append(response.data);
          }
        }
      });
  }
  
  // Update cart items (add to cart, remove from cart, cart lightbox interface)
  var cartCount = $('#cartItems strong');
  if(cartCount.length > 0) {
    var cart = getFullCartItems();
    var id = $('#cartId');
    if(id.length > 0) {
      id = id.val();
      $('#cart-add,#cart-remove').addClass('hidden');
      if(cart.indexOf(id) > -1) {
        $('#cart-remove').removeClass('hidden');
      } else {
        $('#cart-add').removeClass('hidden');
      }
    }
    cartCount.html(cart.length);
  }
}
/**
 * This function changes the content of the lightbox to a message.
 */
function lightboxConfirm(message) {
  changeModalContent('<div class="alert alert-info">'+message+'</div><button class="btn" onClick="closeLightbox()">'+vufindString['close']+'</button>');
}
/**
 * Insert an alert element into the top of the lightbox
 */
function displayLightboxError(message) {
  var alert = $('#modal .modal-body .alert');
  if(alert.length > 0) {
    $(alert).html(message);
  } else {
    $('#modal .modal-body').prepend('<div class="alert alert-error">'+message+'</div>');
  }
  $('.icon-spinner').remove();
}

/***********************************/
/* ====== LIGHTBOX REQUESTS ====== */
/***********************************/
/**
 * This function creates an XHR request to the URL
 * and handles the response according to the callback.
 *
 * Unless there's an error, default callback is changeModalContent
 */
function getLightboxByUrl(url, post, callback) {
  if(typeof callback == "undefined") {
    // No custom handler: display return in lightbox
    callback = changeModalContent;
  }
  // If the lightbox isn't visible, fix that
  if(lightboxShown === false) {
    $('#modal').modal('show');
    lightboxShown = true;
  }
  // Create our AJAX request, store it in case we need to cancel later
  modalXHR = $.ajax({
    type:'POST',
    url:url,
    data:post,
    success:function(html) { // Success!
      callback(html);
    },
    error:function(d,e) {
      console.log(e,d); // Error reporting
      console.log(url,post);
    }
  });
  // Store current "page" context for empty targets
  lastLightboxURL = url;
  lastLightboxPOST = post;
  return false;
}
/**
 * This is the friendly face to the function above.
 * It converts a Controller and Action into a URL with GET
 * and pushes the data and callback to the getLightboxByUrl
 */
function getLightbox(controller, action, get, post, callback) {
  // Build URL
  var url = path+'/AJAX/JSON?method=getLightbox&submodule='+controller+'&subaction='+action;
  if(get && get !== {}) {
    url += '&'+$.param(get);
  }
  return getLightboxByUrl(url, post, callback);
}

/**********************************/
/* ====== FORM SUBMISSIONS ====== */
/**********************************/
/**
 * Call this function after a form is submitted
 */
function getDataFromForm($form) {
  // Gather all the data
  var inputs = $form.find('*[name]');
  var data = {};
  for(var i=0;i<inputs.length;i++) {
    var currentName = inputs[i].name;
    var array = currentName.substring(currentName.length-2) == '[]';
    if(array && !data[currentName.substring(0,currentName.length-2)]) {
      data[currentName.substring(0,currentName.length-2)] = [];
    }
    // Submit buttons
    if(inputs[i].type == 'submit') {
      if($(inputs[i]).attr('clicked') == 'true') {
        data[currentName] = inputs[i].value;
      }
    // Radio buttons
    } else if(inputs[i].type == 'radio') {
      if(inputs[i].checked) {
        if(array) {
          var n = currentName.substring(0,currentName.length-2);
          data[n].push(inputs[i].value);
        } else {
          data[currentName] = inputs[i].value;
        }
      }
    // Checkboxes
    } else if($(inputs[i]).attr('type') != 'checkbox' || inputs[i].checked) {
      if(array) {
        var f = currentName.substring(0,currentName.length-2);
        data[f].push(inputs[i].value);
      } else {
        data[currentName] = inputs[i].value;
      }
    }
  }
  return data;
}
function ajaxSubmit($form, callback) {
  // Default callback is to close
  if(typeof callback == "undefined") {
    callback = closeLightbox;
  }
  var data = getDataFromForm($form);
  // If we have an action: parse
  var POST = $form.attr('method') && $form.attr('method').toUpperCase() == 'POST';
  if($form.attr('action')) {
    // Parse action location
    var action = $form.attr('action').substring($form.attr('action').indexOf(path)+path.length+1);
    var params = action.split('?');
    action = action.split('/');
    var get = params.length > 1 ? deparam(params[1]) : data['id'] ? {id:data['id']} : {};
    if(POST) {
      getLightbox(action[0], action[action.length-1], get, data, callback);
    } else {
      getLightbox(action[0], action[action.length-1], data, {}, callback);
    }
  // If not: fake context by using the previous action
  } else if(POST) {
    getLightboxByUrl(lastLightboxURL, data, callback);
  } else {
    getLightboxByUrl(lastLightboxURL, {}, callback);
  }
  $(this).find('.modal-body').html(vufindString.loading + "...");
}
/**
 * Action specific form submissions
 */
// Logging in
function ajaxLogin(form) {
  modalXHR = $.ajax({
    url: path + '/AJAX/JSON?method=getSalt',
    dataType: 'json',
    success: function(response) {
      if (response.status == 'OK') {
        var salt = response.data;

        // get the user entered password
        var password = form.password.value;

        // encrypt the password with the salt
        password = rc4Encrypt(salt, password);

        // hex encode the encrypted password
        password = hexEncode(password);

        var params = {password:password};

        // get any other form values
        for (var i = 0; i < form.length; i++) {
            if (form.elements[i].name == 'password') {
                continue;
            }
            params[form.elements[i].name] = form.elements[i].value;
        }

        // login via ajax
        modalXHR = $.ajax({
          type: 'POST',
          url: path + '/AJAX/JSON?method=login',
          dataType: 'json',
          data: params,
          success: function(response) {
            if (response.status == 'OK') {
              // Hide "log in" options and show "log out" options:
              $('#loginOptions').hide();
              $('.logoutOptions').show();
              
              var recordId = $('#record_id').val();

              // Update user save statuses if the current context calls for it:
              if (typeof(checkSaveStatuses) == 'function') {
                checkSaveStatuses();
              }

              // refresh the comment list so the "Delete" links will show
              $('.commentList').each(function(){
                var recordSource = extractSource($('#record'));
                refreshCommentList(recordId, recordSource);
              });
              
              var summon = false;
              $('.hiddenSource').each(function(i, e) {
                if(e.value == 'Summon') {
                  summon = true;
                  // If summon, queue reload for when we close
                  addLightboxOnClose(function(){document.location.reload(true);});
                }
              });
              
              // Refresh tab content
              var recordTabs = $('.recordTabs');
              if(!summon && recordTabs.length > 0) { // If summon, skip: about to reload anyway
                var tab = recordTabs.find('.active a').attr('id');
                $.ajax({ // Shouldn't be cancelled, not assigned to modalXHR
                  type:'POST',
                  url:path+'/AJAX/JSON?method=getLightbox&submodule=Record&subaction=AjaxTab&id='+recordId,
                  data:{tab:tab},
                  success:function(html) {
                    recordTabs.next('.tab-container').html(html);
                  },
                  error:function(d,e) {
                    console.log(d,e); // Error reporting
                  }
                });
              }
              // and we update the modal
              if(lastLightboxPOST && lastLightboxPOST['loggingin']) {
                closeLightbox();
              } else {
                getLightboxByUrl(lastLightboxURL, lastLightboxPOST, changeModalContent);
              }
            } else {
              displayLightboxError(response.data);
            }
          }
        });
      } else {
        displayLightboxError(response.data);
      }
    }
  });
}

/***********************/
/* ====== SETUP ====== */
/***********************/
/**
 * The jQueries add functionality to content in the lightbox.
 *
 * It is called every time the lightbox is finished loading.
 */
function registerModalEvents(modal) {
  // New list
  $('#make-list').click(function() {
    var parts = this.href.split('?');
    var get = deparam(parts[1]);
    get['id'] = 'NEW';
    return getLightbox('MyResearch', 'EditList', get);
  });  
  // Select all checkboxes
  $(modal).find('.checkbox-select-all').change(function() {
    $(this).closest('.modal-body').find('.checkbox-select-item').attr('checked', this.checked);
  });
  $(modal).find('.checkbox-select-item').change(function() {
    if(!this.checked) { // Uncheck all selected if one is unselected
      $(this).closest('.modal-body').find('.checkbox-select-all').attr('checked', false);
    }
  });
  // Highlight which submit button clicked
  $(modal).find("form input[type=submit]").click(function() {
    // Abort requests triggered by the lightbox
    if(modalXHR) { modalXHR.abort(); }
    $('#modal .icon-spinner').remove();
    // Add useful information
    $(this).attr("clicked", "true");
    // Add prettiness
    $(this).after(' <i class="icon-spinner icon-spin"></i> ');
  });
}
/**
 * Prevents default submission, reroutes through ajaxSubmit
 * or a specified action based on form name. Please return false.
 *
 * Called everytime the lightbox is loaded.
 */
function registerModalForms(modal) {
  var $form = $(modal).find('form');
  // Assign form handler based on name
  if(typeof modalFormHandlers[$form.attr('name')] !== "undefined") {
    $form.submit(modalFormHandlers[$form.attr('name')]);
  } else {
    // Default
    $(modal).find('form').submit(function(){
      ajaxSubmit($(this), closeLightbox);
      return false;
    });
  }
}
/**
 * This is where you add click events to open the lightbox.
 * We do it here so that non-JS users still have a good time.
 */
$(document).ready(function() {
  /* --- LIGHTBOX BEHAVIOUR --- */
  // First things first
  addLightboxOnOpen(registerModalEvents);
  addLightboxOnOpen(registerModalForms);
  addLightboxFormHandler('loginForm', function() {
    ajaxLogin(this);
    return false;
  });
  addLightboxFormHandler('newList', function(evt) {
    ajaxSubmit($(evt.target), changeModalContent);
    return false;
  });
  addLightboxFormHandler('placeHold', function(evt) {
    var data = getDataFromForm($(evt.target));
    modalXHR = $.ajax({
      type:'POST',
      url:lastLightboxURL,
      data:data,
      success:function(html) { // Success!
        var fi = html.indexOf('<div class="alert alert-error">');
        if(fi > -1) {
          var li = html.indexOf('</div>', fi+31);
          displayLightboxError(html.substring(fi+31, li));
        } else {
          document.location.href = path+'/MyResearch/Holds';
        }
      },
      error:function(d,e) {
        console.log(e,d); // Error reporting
        console.log(lastLightboxURL,data);
      }
    });
    return false;
  });
  addLightboxFormHandler('saveRecord', function(evt) {
    ajaxSubmit($(evt.target), function(){lightboxConfirm(vufindString['bulk_save_success']);});
    return false;
  });

  // Hijack modal forms
  $('#modal').on('show', function() {
    for(var i=0;i<modalOpenStack.length;i++) {
      modalOpenStack[i](this);
    }
  });  
  // Reset Content
  $('#modal').on('hidden', function() {
    closeLightboxActions();
  });
  // Modal title
  $('.modal-link,.help-link').click(function() {
    var title = $(this).attr('title');
    if(typeof title === "undefined") {
      title = $(this).html();
    }
    $('#modal .modal-header h3').html(title);
  });
  
  /* --- PAGES EVENTS THAT AFFECT THE LIGHTBOX --- */
  // Help links
  $('.help-link').click(function() {
    var split = this.href.split('=');
    return getLightbox('Help','Home',{topic:split[1]});
  });
  // Hierarchy links
  $('.hierarchyTreeLink a').click(function() {
    var id = $(this).parent().parent().parent().find(".hiddenId")[0].value;
    var hierarchyID = $(this).parent().find(".hiddenHierarchyId")[0].value;
    return getLightbox('Record','AjaxTab',{id:id},{hierarchy:hierarchyID,tab:'HierarchyTree'});
  });
  // Login link
  $('#loginOptions a').click(function() {
    return getLightbox('MyResearch','Login',{},{'loggingin':true});
  });
  // Place a Hold
  $('.placehold').click(function() {
    var params = deparam($(this).attr('href'));
    params.hashKey = params.hashKey.split('#')[0]; // Remove #tabnav
    return getLightbox('Record', 'Hold', params, {}, function(html) {
      var fi = html.indexOf('<div class="alert alert-error">');
      if(fi > -1) {
        var li = html.indexOf('</div>', fi+31);
        changeModalContent(html.substring(fi, li));
      } else {
        changeModalContent(html);
      }
    });
  });
  // Save record links
  $('.save-record').click(function() {
    var parts = this.href.split('/');
    return getLightbox(parts[parts.length-3],'Save',{id:$(this).attr('id')});
  });  
  // Tag lightbox
  $('#tagRecord').click(function() {
    var id = $('.hiddenId')[0].value;
    var parts = this.href.split('/');
    return getLightbox(parts[parts.length-3],'AddTag',{id:id});
  });
});