All pastes #748770 Raw Edit

Anonymous

public javascript v1 · immutable
#748770 ·published 2007-10-25 03:20 UTC
rendered paste body
// $Id $Drupal.behaviors.tableDrag = function(context) {  for (var base in Drupal.settings.tabledrag) {    if (!$('#' + base + '.tabledrag-processed').size(), context) {      var fieldSettings = Drupal.settings.tabledrag[base];      $('#' + base + ':not(.tabledrag-processed)').each(function() {        // Create the new tabledrag instance. Save in the Drupal variable        // to allow other scripts access to the object.        Drupal.tabledrag[base] = new Drupal.tabledrag(fieldSettings);        Drupal.tabledrag[base].init(this);      });      $('#' + base).addClass('tabledrag-processed');    }  }}Drupal.tabledrag = function(fieldSettings) {  this.fieldSettings = fieldSettings;  this.dragObject = null; // Keep hold of the current drag object if any.  this.table = null; // The current table.  this.oldY = 0; // Remember the old value of Y so that we don't do too much processing.}// Initialize the drag and drop by capturing mouse move events.Drupal.tabledrag.prototype.init = function(table) {  var self = this;  this.table = table;  // Check if this table contains indentations.  var indents = $('div.indentation', table);  this.indentEnabled = indents.size() > 0 ? true : false;  if (this.indentEnabled) {    var indentSize = indents.css('width');    this.oldX = 0;    this.indentCount = 1; // Total width of indents, set in makeDraggable.    this.indentIncrement = indentSize.replace(/[0-9\.]*/, '');    this.indentAmount = parseInt(indentSize);  }  // Make each applicable row draggable.  $('tr.draggable', table).each(function() { self.makeDraggable(this); });  // Hide columns containing affected form elements.  this.hideColumns();  // Now make the onmousemove method in the context of "self" so that we can get back to tableDnD  $(document).bind('mousemove', { tabledrag: self }, self.dragRow);  $(document).bind('mouseup', { tabledrag: self }, self.dropRow);}/** * Hide the columns containing form elements according to the settings for * this tabledrag instance. */Drupal.tabledrag.prototype.hideColumns = function() {  for (group in this.fieldSettings) {    var hidden = this.fieldSettings[group][0]['hidden'];    var field = $('.' + this.fieldSettings[group][0]['target'] + ':first', this.table);    var cell = field.parents('td:first, th:first');    if (hidden && cell[0] && cell.css('display') != 'none') {      var columnIndex = $('td, th', field.parents('tr:first')).index(cell.get(0));      columnIndex++; // nth-child selector is 1 based, not 0 based.      $('tr', this.table).each(function() {        // Find and hide the cell in the table body.        var cell = $('td:nth-child('+ columnIndex +')', this);        if (cell.size()) {          cell.css('display', 'none');        }        // We might be dealing with a row spanning the entire table.        // Reduce the colspan on the first cell to prevent the cell from        // overshooting the table.        else {          cell = $('td:first', this);          cell.attr('colspan', cell.attr('colspan') - 1);        }        // Remove table header cells entirely (Safari doesn't hide properly).        var th = $('th:nth-child('+ columnIndex +')', this);        if (th.size()) {          th.remove();        }      });    }  }}/** * Find the target used within a particular row and group. */Drupal.tabledrag.prototype.rowSettings = function(group, row) {  var field = $('.' + group, row);  for (delta in this.fieldSettings[group]) {    var targetClass = this.fieldSettings[group][delta]['target'];    if (field.is('.' + targetClass)) {      return this.fieldSettings[group][delta];    }  }};/** * Take an item and add an onmousedown method so that we can make it draggable. */Drupal.tabledrag.prototype.makeDraggable = function(item) {  if (!item) return;  var self = this; // Keep the context of the tabledrag object.  // Create the handle.  handle = $('<div class="tabledrag-handle"><div class="handle"></div></div>');  // Insert after indentations (if any).  var indentAdded = $('td:first .indentation:last', item).after(handle).size();  if (!indentAdded) {    $('td:first', item).prepend(handle);  }  else {    self.indentCount = Math.max($('.indentation', item).size(), self.indentCount);  }  // Add hover action for the handle.  handle.hover(function() {    self.dragObject == null ? $(this).addClass('tabledrag-handle-hover') : null;  }, function() {    self.dragObject == null ? $(this).removeClass('tabledrag-handle-hover') : null;  });  // Add the mousedown action for the handle.  handle.mousedown(function(event) {    self.dragObject = new Object();    self.dragObject.element = item;    self.dragObject.initMouseOffset = self.getMouseOffset(item, event);    self.dragObject.initMousePos = self.mouseCoords(event);    self.dragObject.indentMousePos = self.dragObject.initMousePos;    self.dragObject.indents = $('.indentation', item).size();    $(this).addClass('tabledrag-handle-hover');    // Update the style to show we're dragging.    $(this).parents('tr:first').addClass('highlight');    return false;  });}/** * Mousemove event handler, bound to document. */Drupal.tabledrag.prototype.dragRow = function(event) {  var self = event.data.tabledrag;  if (self.dragObject) {    var mousePos = self.mouseCoords(event);    // Handle swapping rows.    var y = mousePos.y - self.dragObject.initMouseOffset.y;    if (y != self.oldY) {      // Going up or down...      var movingDown = y > self.oldY;      // Update the old value.      self.oldY = y;      // Check if we need to move any rows.      var currentRow = self.findDropTargetRow(y);      if (currentRow && self.dragObject.element != currentRow) {        if (movingDown) {          $(currentRow).after(self.dragObject.element);        }        else if (!movingDown) {          $(currentRow).before(self.dragObject.element);        }        self.restripeTable(self.table);      }    }    // Similar to row swapping, handle indentations.    var x = mousePos.x - self.dragObject.initMouseOffset.x;    if (self.indentEnabled && x != self.oldX) {      self.oldX = x;      var xDiff = mousePos.x - self.dragObject.indentMousePos.x;      var indentDiff = parseInt(xDiff / self.indentAmount);      // Limit the indentation to no less than 0 and no more than      // the total amount of indentation in the table.      indentDiff = indentDiff > 0 ? Math.min(indentDiff, self.indentCount - self.dragObject.indents + 1) : Math.max(indentDiff, -self.dragObject.indents);      if (indentDiff != 0) {        // Further limit the diff        if (indentDiff > 0) {          var prevIndent = $('.indentation', $(self.dragObject.element).prev()).size();          indentDiff = Math.min(prevIndent - self.dragObject.indents + 1, indentDiff);        }        for (var n = 1; n <= Math.abs(indentDiff); n++) {          // Add or remove indentations.          if (indentDiff < 0) {            $('.indentation:first', self.dragObject.element).remove();            self.dragObject.indents--;          }          else {            $('td:first', self.dragObject.element).prepend(Drupal.theme('tabledragIndentation'));            self.dragObject.indents++;          }        }        // Update indentation for this actual row.        self.dragObject.indentMousePos.x += self.indentAmount * indentDiff;        // Update table total indentations.        self.indentCount = Math.max(self.indentCount, self.dragObject.indents);      }    }    return false;  }};/** * Mouseup event handler, bound to document. */Drupal.tabledrag.prototype.dropRow = function(event) {  var self = event.data.tabledrag;  if (self.dragObject != null) {    var droppedRow = self.dragObject.element;    // The row is already in the right place so we just release it.    $(droppedRow).removeClass('highlight');    $('.tabledrag-handle', droppedRow).removeClass('tabledrag-handle-hover');    self.updateFields(droppedRow);    self.markChanged(droppedRow);    self.dragObject = null;    self.onDrop(droppedRow);  }};/** * Stub function. Allows custom javascript to add a handler when the row is * dropped. */Drupal.tabledrag.prototype.onDrop = function(droppedRow) {  return null;}/** * Get the position of an element by going up the DOM tree and adding up all * the offsets. */Drupal.tabledrag.prototype.getPosition = function(event){  var left = 0;  var top  = 0;  // Safari fix -- thanks to Luis Chato for this!  if (event.offsetHeight == 0) {    /* Safari 2 doesn't correctly grab the offsetTop of a table row        this is detailed here:        http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/        the solution is likewise noted there, grab the offset of a table cell        in the row - the firstChild. note that firefox will return a text node        as a first child, so designing a more thorough solution may need to        take that into account, this seems to work in Firefox, Safari, and IE.     */    event = event.firstChild; // a table cell  }  while (event.offsetParent){    left += event.offsetLeft;    top  += event.offsetTop;    event = event.offsetParent;  }  left += event.offsetLeft;  top  += event.offsetTop;  return {x:left, y:top};}/** * Get the mouse coordinates from the event (allowing for browser differences). */Drupal.tabledrag.prototype.mouseCoords = function(event){  if (event.pageX || event.pageY) {    return {x:event.pageX, y:event.pageY};  }  return {    x:event.clientX + document.body.scrollLeft - document.body.clientLeft,    y:event.clientY + document.body.scrollTop  - document.body.clientTop  };}/** * Given a target element and a mouse event, get the mouse offset from that * element. To do this we need the element's position and the mouse position. */Drupal.tabledrag.prototype.getMouseOffset = function(target, event){  event = event || window.event;  var docPos   = this.getPosition(target);  var mousePos = this.mouseCoords(event);  return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};}/** * Find the row the mouse is currently over. This row is then taken and swapped * with the one being dragged. */Drupal.tabledrag.prototype.findDropTargetRow = function(y) {  var rows = this.table.tBodies[0].rows;  for (var n=0; n<rows.length; n++) {    var row = rows[n];    var rowY = this.getPosition(row).y;    var rowHeight = parseInt(row.offsetHeight)/2;    if (row.offsetHeight == 0) {      rowY = this.getPosition(row.firstChild).y;      rowHeight = parseInt(row.firstChild.offsetHeight)/2;    }    // Because we always insert before, we need to offset the height a bit.    if ((y > (rowY - rowHeight)) && (y < (rowY + rowHeight))) {      // We've found the row the mouse just passed over, but it doesn't take      // into account hidden rows. Skip backwards until we find a draggable      // row.      while ($(row).is(':not(.draggable)') && $(row).prev().is(':not(.draggable)')) {        row = $(row).prev().get(0);      }      // Special case for the first row, if it's not draggable, do not allow      // another row to be put before it.      if (!(n == 0 && $(row).is(':not(.draggable)'))) {        return row;      }    }  };  return null;}/** * After the row is dropped, update the fields inside of it according to the * dragtable settings. */Drupal.tabledrag.prototype.updateFields = function() {  changedRow = this.dragObject.element;  for (group in this.fieldSettings) {    // Each group may have a different setting for relationship, so we find    // the source rows for each seperately.    var rowSettings = this.rowSettings(group, changedRow);    // Siblings are easy, check previous and next rows.    if (rowSettings.relationship == 'sibling') {      var previousRow = $(changedRow).prev().get(0);      var nextRow = $(changedRow).next().get(0);      var sourceRow = changedRow;      if ($(previousRow).is('.draggable')) {        sourceRow = previousRow;      }      else if ($(nextRow).is('.draggable')) {        sourceRow = nextRow;      }    }    // Parents, look up the tree until we find a field not in this group.    // Go up as many parents as indentations in the changed row.    else if (rowSettings.relationship == 'parent') {      var parents = this.indentCount - this.dragObject.indents;      var previousRow = $(changedRow).prev();      while (parents > 0) {        parents--;        while (previousRow.length && $('.indentation', previousRow).length < (this.dragObject.indents - parents)) {          previousRow = previousRow.prev();        }      }      sourceRow = previousRow[0] || changedRow;    }    // Because we may have moved the row from one category to another,    // take a look at our sibling and borrow its sources and targets.    this.copyDragClasses(sourceRow, changedRow, group);    for (delta in this.fieldSettings[group]) {      var targetClass = '.' + this.fieldSettings[group][delta]['target'];      var targetElement = $(targetClass, changedRow).get(0);      // Check if a target element exists in this row.      if (targetElement) {        var relationship = this.fieldSettings[group][delta]['relationship'];        var action = this.fieldSettings[group][delta]['action'];        var sourceClass = '.' + this.fieldSettings[group][delta]['source'];        var sourceElement = $(sourceClass, sourceRow).get(0);          switch (action) {          case 'match':            // Update the value.            targetElement.value = sourceElement.value;            break;          case 'order':            if ($(targetElement).is('select')) {              // Get a list of acceptable values.              var values = new Array();              $('option', targetElement).each(function() {                values.push(this.value);              });              $(targetClass, this.table).each(function() {                this.value = values.shift();              });            }            else {              // Assume a numeric input field.              var weight = 0;              $(targetClass, this.table).each(function() {                this.value = weight;                weight++;              });            }            break;        }      }    }  }}/** * Copy all special tabledrag classes from one row's form elements to a * different one, removing any special classes that the destination row * may have had. */Drupal.tabledrag.prototype.copyDragClasses = function(sourceRow, targetRow, group) {  var sourceElement = $('.' + group, sourceRow);  var targetElement = $('.' + group, targetRow);  if (sourceElement.length && targetElement.length) {    targetElement[0].className = sourceElement[0].className;  }}/** * Add an asterisk or other marker to the changed row. */Drupal.tabledrag.prototype.markChanged = function(changedRow) {  var marker = $('<div>' + Drupal.theme('tabledragChangedMarker') + '</div>');  var cell = $('td:first', changedRow);  // Converting marker to a DOM object and using .html() to do string  // comparision ensures consitency across browsers.  if (cell.html().indexOf(marker.html()) == -1) {    cell.append(marker.html());  }}Drupal.tabledrag.prototype.restripeTable = function(table) {  // :even and :odd are reversed because jquery counts from 0 and  // we count from 1, so we're out of sync.  $('tbody tr:not(:hidden)', table)    .filter(':odd').filter('.odd')      .removeClass('odd').addClass('even')    .end().end()    .filter(':even').filter('.even')      .removeClass('even').addClass('odd');}Drupal.theme.prototype.tabledragChangedMarker = function () {  return '<span class="warning">*</span>';};Drupal.theme.prototype.tabledragIndentation = function () {  return '<div class="indentation">&nbsp;</div>';};