added taxonomy wrangler module

This commit is contained in:
Bachir Soussi Chiadmi
2015-11-23 14:47:44 +01:00
parent fc0a779214
commit 7298235d4f
6 changed files with 1313 additions and 0 deletions

View File

@@ -0,0 +1,591 @@
/**
* @file
* js for taxonomy ui tweaks
*/
(function ($, Drupal, window, document, undefined) {
function TaxonomyWranglerUID() {
var prefix = 'taxonomy-wrangler-';
var randomId = prefix + this.count;
while($('#' + randomId).length) {
this.count++;
randomId = prefix + this.count;
}
return randomId;
}
TaxonomyWranglerUID.count = 0;
function TaxonomyWranglerAccordion(table) {
this.table = table;
this.rows = $('tbody tr', table);
this.init();
}
TaxonomyWranglerAccordion.instances = {};
var ctrlDown = false;
$(window).keydown(function(e) {
if (e.keyCode == 17) {
ctrlDown = true;
}
}).keyup(function(e) {
if (e.keyCode == 17) {
ctrlDown = false;
}
});
TaxonomyWranglerAccordion.prototype.init = function() {
var accordion = this;
$('tbody tr').not(':eq(0)').each(function(){
var $row = $(this);
accordion.collapseTree($row);
accordion.attachAccordionEvents($row);
});
};
TaxonomyWranglerAccordion.prototype.attachAccordionEvents = function($row) {
var accordion = this;
$row.find('.taxonomy-wrangler-accordion-item').bind('click.tweaksAccordion', function(e){
e.preventDefault();
if (ctrlDown) {
accordion.toggleTree($row);
}
else {
accordion.toggleRow($row);
}
return false;
});
}
TaxonomyWranglerAccordion.prototype.refreshRows = function(){
var accordion = this;
this.rows.not(':eq(0)').each(function(){
var $this = $(this);
var hasChildren = accordion.rows.filter('[data-parent="'+ $this.data('tid') +'"]').length > 0 ? true : false;
var $collapseLink = $this.find('.taxonomy-wrangler-accordion-item--parent');
//create a new parent
if (hasChildren && !$collapseLink.length) {
$this.find('.tabledrag-handle').after('<a href="#" class="taxonomy-wrangler-accordion-item taxonomy-wrangler-accordion-item--parent"><span></span></a>');
accordion.attachAccordionEvents($this);
}
else if (!hasChildren && $collapseLink.length) {
$collapseLink.remove();
}
});
};
/**
* This is a bit different than a typical accordion in that the
* data structure is hierarchical, but its representation in the DOM is flat
*/
TaxonomyWranglerAccordion.prototype.showRow = function($row) {
$row.css('display', 'table-row').removeClass('tabledrag-hidden');
}
TaxonomyWranglerAccordion.prototype.hideRow = function($row) {
$row.css('display', 'none').addClass('tabledrag-hidden');
}
TaxonomyWranglerAccordion.prototype.setCollapsed = function($row, collapsed, recurse) {
var accordion = this;
var $children = this.rows.filter('[data-parent=' + $row.data('tid') + ']');
if (collapsed && $children.length) {
$row.data('is-collapsed', true);
$row.find('.taxonomy-wrangler-accordion-item').addClass('is-collapsed');
if ($children.length) {
$row.addClass('tabledrag-leaf');
}
}
else {
$row.data('is-collapsed', false).removeClass('tabledrag-leaf');
$row.find('.taxonomy-wrangler-accordion-item').removeClass('is-collapsed');
}
if (recurse) {
accordion.rows.filter('[data-parent=' + $row.data('tid') + ']').each(function(){
accordion.setCollapsed($(this), collapsed, true);
});
}
}
/*
* Hide all children
*/
TaxonomyWranglerAccordion.prototype.hideChildren = function($row) {
var $item = $row.find('.taxonomy-wrangler-accordion-item');
var accordion = this;
this.rows.filter('[data-parent=' + $row.data('tid') + ']').each(function(){
accordion.hideRow($(this));
accordion.hideChildren($(this));
});
};
/**
* Show children if they are not collapsed
*/
TaxonomyWranglerAccordion.prototype.showChildren = function($row) {
var accordion = this;
accordion.rows.filter('[data-parent=' + $row.data('tid') + ']').each(function(){
accordion.showRow($(this));
if (!$(this).data('is-collapsed')) {
accordion.showChildren($(this));
}
});
};
TaxonomyWranglerAccordion.prototype.toggleRow = function($row) {
if ($row.data('is-collapsed')) {
this.setCollapsed($row, false);
this.showChildren($row);
}
else {
this.setCollapsed($row, true);
this.hideChildren($row);
}
};
TaxonomyWranglerAccordion.prototype.expandTree = function($row) {
this.setCollapsed($row, false, true);
this.showChildren($row);
};
TaxonomyWranglerAccordion.prototype.collapseTree = function($row) {
this.setCollapsed($row, true, true);
this.hideChildren($row);
};
TaxonomyWranglerAccordion.prototype.toggleTree = function($row) {
if ($row.data('is-collapsed')) {
this.expandTree($row);
}
else {
this.collapseTree($row);
}
}
Drupal.behaviors.taxonomyWranglerAccordion = {
attach: function(context, settings) {
idCount = 0;
$('.taxonomy-wrangler-accordion', context)
.not('.taxonomy-wrangler-accordion-processed')
.addClass('taxonomy-wrangler-accordion-processed')
.each(function(){
var id = $(this).attr('id')
if (!!!id || id == '') {
$(this).attr('id', TaxonomyWranglerUID());
}
TaxonomyWranglerAccordion.instances[id] = new TaxonomyWranglerAccordion(this);
});
}
};
function TaxonomyWranglerCollection() {
this.updatedTerms = {};
};
TaxonomyWranglerCollection.prototype.updateTerm = function(row) {
var $row = $(row);
var tid = $row.data('tid');
var now = Date.now();
this.updatedTerms[tid] = {
"tid": $row.data('tid'),
"parent": $row.data('parent'),
"depth": $row.data('depth'),
"weight": $row.data('weight'),
"updated": now
}
//el.attr('data-{key}') & el.data('key') are not the same!
$row.attr('data-parent', $row.data('parent'))
.attr('data-depth', $row.data('depth'))
.attr('data-weight', $row.data('weight'))
.data('updated', now);
};
/**
* New TableDrag "class" that inherits Drupal.tableDrag
*/
function TaxonomyWranglerDrag(table, tableSettings) {
Drupal.tableDrag.call(this, table, tableSettings);
}
TaxonomyWranglerDrag.prototype = new Drupal.tableDrag();
TaxonomyWranglerDrag.instances = {};
/**
* Find the target used within a particular row and group.
* This isn't strictly necessary in this case because the data
* is stored on the row element itself.
* Copied and modified from misc/tabledrag.js
*/
TaxonomyWranglerDrag.prototype.rowSettings = function (group, row) {
for (var delta in this.tableSettings[group]) {
// Return a copy of the row settings.
var rowSettings = {};
for (var n in this.tableSettings[group][delta]) {
rowSettings[n] = this.tableSettings[group][delta][n];
}
return rowSettings;
}
};
/**
* After the row is dropped, update the table fields according to the settings
* set for this table.
*
* @param changedRow
* DOM object for the row that was just dropped.
*/
TaxonomyWranglerDrag.prototype.updateFields = function (changedRow) {
var $c = $(changedRow);
for (var group in this.tableSettings) {
// Each group may have a different setting for relationship, so we find
// the source rows for each separately.
this.updateField(changedRow, group);
}
};
/**
* After the row is dropped, update a single table field according to specific
* settings. This code is cribbed from tabledrag.js and modified to
* use the data properties on the row object rather than input fields in the row.
*
* @param changedRow
* DOM object for the row that was just dropped.
* @param group
* The settings group on which field updates will occur.
*/
TaxonomyWranglerDrag.prototype.updateField = function (changedRow, group) {
var rowSettings = this.rowSettings(group, changedRow);
// Set the row as its own target.
if (rowSettings.relationship == 'self' || rowSettings.relationship == 'group') {
var sourceRow = changedRow;
}
// Siblings are easy, check previous and next rows.
else if (rowSettings.relationship == 'sibling') {
var previousRow = $(changedRow).prev('tr').get(0);
var nextRow = $(changedRow).next('tr').get(0);
var sourceRow = changedRow;
if ($(previousRow).is('.draggable') && $('.' + group, previousRow).length) {
if (this.indentEnabled) {
if ($('.indentations', previousRow).length == $('.indentations', changedRow)) {
sourceRow = previousRow;
}
}
else {
sourceRow = previousRow;
}
}
else if ($(nextRow).is('.draggable') && $('.' + group, nextRow).length) {
if (this.indentEnabled) {
if ($('.indentations', nextRow).length == $('.indentations', changedRow)) {
sourceRow = nextRow;
}
}
else {
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 previousRow = $(changedRow).prev('tr');
while (previousRow.length && $('.indentation', previousRow).length >= this.rowObject.indents) {
previousRow = previousRow.prev('tr');
}
// If we found a row.
if (previousRow.length) {
sourceRow = previousRow[0];
}
// Otherwise we went all the way to the left of the table without finding
// a parent, meaning this item has been placed at the root level.
else {
// Use the first row in the table as source, because it's guaranteed to
// be at the root level. Find the first item, then compare this row
// against it as a sibling.
sourceRow = $(this.table).find('tr.draggable:first').get(0);
if (sourceRow == this.rowObject.element) {
sourceRow = $(this.rowObject.group[this.rowObject.group.length - 1]).next('tr.draggable').get(0);
}
var useSibling = true;
}
}
// 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);
rowSettings = this.rowSettings(group, changedRow);
// In the case that we're looking for a parent, but the row is at the top
// of the tree, copy our sibling's values.
if (useSibling) {
rowSettings.relationship = 'sibling';
rowSettings.source = rowSettings.target;
}
var $changedRow = $(changedRow);
// Check if data exists in this row.
if (typeof $(sourceRow).data(rowSettings.target) !== 'undefined') {
switch (rowSettings.action) {
case 'depth':
// Get the depth of the target row.
$changedRow.data(rowSettings.target, $('.indentation', $(sourceRow)).length);
break;
case 'match':
// Update the value.
$changedRow.data(rowSettings.target, $(sourceRow).data(rowSettings.source));
break;
case 'order':
var siblings = this.rowObject.findSiblings(rowSettings);
// We're not using fields for this so we just assume
// weight starting from 0
var weight = 0;
$(siblings).each(function () {
$(this).data('weight', weight);
weight++;
$(this).trigger('taxonomyWranglerTermDrag.rowDataChange', [this]);
});
break;
}
}
$changedRow.trigger('taxonomyWranglerTermDrag.rowDataChange', [changedRow]);
};
TaxonomyWranglerDrag.prototype.dropRow = function (event, self) {
// Drop row functionality shared between mouseup and blur events.
if (self.rowObject != null) {
var droppedRow = self.rowObject.element;
// The row is already in the right place so we just release it.
if (self.rowObject.changed == true) {
// Update the fields in the dropped row.
self.updateFields(droppedRow);
// If a setting exists for affecting the entire group, update all the
// fields in the entire dragged group.
for (var group in self.tableSettings) {
var rowSettings = self.rowSettings(group, droppedRow);
if (rowSettings.relationship == 'group') {
for (var n in self.rowObject.children) {
self.updateField(self.rowObject.children[n], group);
}
}
}
self.rowObject.markChanged();
if (self.changed == false) {
self.changed = true;
}
}
if (self.indentEnabled) {
self.rowObject.removeIndentClasses();
}
if (self.oldRowElement) {
$(self.oldRowElement).removeClass('drag-previous');
}
$(droppedRow).removeClass('drag').addClass('drag-previous');
self.oldRowElement = droppedRow;
self.onDrop();
self.rowObject = null;
}
// Functionality specific only to mouseup event.
if (self.dragObject != null) {
$('.tabledrag-handle', droppedRow).removeClass('tabledrag-handle-hover');
self.dragObject = null;
$('body').removeClass('drag');
clearInterval(self.scrollInterval);
// Hack for IE6 that flickers uncontrollably if select lists are moved.
if (navigator.userAgent.indexOf('MSIE 6.') != -1) {
$('select', this.table).css('display', 'block');
}
}
};
/**
* Indent a row within the legal bounds of the table.
*
* @param indentDiff
* The number of additional indentations proposed for the row (can be
* positive or negative). This number will be adjusted to nearest valid
* indentation level for the row.
*/
TaxonomyWranglerDrag.prototype.row.prototype.indent = function (indentDiff) {
// Determine the valid indentations interval if not available yet.
if (!this.interval) {
var prevRow = $(this.element).prevAll('tr:not(.tabledrag-hidden)').get(0);
var nextRow = $(this.group).filter(':last').nextAll('tr:not(.tabledrag-hidden)').get(0);
this.interval = this.validIndentInterval(prevRow, nextRow);
}
// Adjust to the nearest valid indentation.
var indent = this.indents + indentDiff;
indent = Math.max(indent, this.interval.min);
indent = Math.min(indent, this.interval.max);
indentDiff = indent - this.indents;
for (var n = 1; n <= Math.abs(indentDiff); n++) {
// Add or remove indentations.
if (indentDiff < 0) {
$('.indentation:first', this.group).remove();
this.indents--;
}
else {
$('td:first', this.group).prepend(Drupal.theme('tableDragIndentation'));
this.indents++;
}
}
if (indentDiff) {
// Update indentation for this row.
this.changed = true;
this.groupDepth += indentDiff;
this.onIndent();
}
return indentDiff;
};
/**
* Perform the swap between two rows. Take into account the collapsed
* accordion rows when swapping after.
*
* @param position
* Whether the swap will occur 'before' or 'after' the given row.
* @param row
* DOM element what will be swapped with the row group.
*/
TaxonomyWranglerDrag.prototype.row.prototype.swap = function (position, row) {
var swapRow = row;
if (position == 'after') {
var $row = $(row);
if ($row.data('is-collapsed')) {
var $target = $row.nextUntil(":visible");
if ($target.length) {
swapRow = $target.get($target.length - 1);
}
else {
// If there are no other rows visible, we must be at the bottom
// of the table, get the last row.
swapRow = $row.nextAll(":last").get(0);
}
}
}
Drupal.detachBehaviors(this.group, Drupal.settings, 'move');
$(swapRow)[position](this.group);
Drupal.attachBehaviors(this.group, Drupal.settings);
this.changed = true;
this.onSwap(row);
};
/**
* Ensure that two rows are allowed to be swapped.
*
* @param row
* DOM object for the row being considered for swapping.
*/
TaxonomyWranglerDrag.prototype.row.prototype.isValidSwap = function (row) {
if (this.indentEnabled) {
var prevRow, nextRow;
if (this.direction == 'down') {
prevRow = row;
nextRow = $(row).nextAll('tr:not(:hidden)').get(0);
}
else {
prevRow = $(row).prevAll('tr:not(:hidden)').get(0);
nextRow = row;
}
this.interval = this.validIndentInterval(prevRow, nextRow);
// We have an invalid swap if the valid indentations interval is empty.
if (this.interval.min > this.interval.max) {
return false;
}
}
// Do not let an un-draggable first row have anything put before it.
if (this.table.tBodies[0].rows[0] == row && $(row).is(':not(.draggable)')) {
return false;
}
return true;
};
/**
* Move a block in the blocks table from one region to another via select list.
*
* This behavior is dependent on the tableDrag behavior, since it uses the
* objects initialized in that behavior to update the row.
*/
Drupal.behaviors.taxonomyWranglerTermDrag = {
attach: function (context, settings) {
for (var base in settings.taxonomyWrangler.tables) {
if (typeof TaxonomyWranglerDrag.instances[base] == 'undefined') {
$('#' + base, context).once('tabledrag', function () {
// Create the new tableDrag instance. Save in the Drupal variable
// to allow other scripts access to the object.
TaxonomyWranglerDrag.instances[base] = new TaxonomyWranglerDrag(this, settings.taxonomyWrangler.tables[base]);
});
}
}
var table = $('#taxonomy-wrangler', context);
var tableDrag = TaxonomyWranglerDrag.instances['taxonomy-wrangler']; // Get the blocks tableDrag object.
var $messages = $('#taxonomy-wrangler-messages-js', context);
// Compare server timestamp with javascript changed stamp and remove changed classes.
if (settings.taxonomyWrangler && settings.taxonomyWrangler.updatedTimestamps) {
$.each(settings.taxonomyWrangler.updatedTimestamps, function (tid, stamp) {
var $row = table.find('tr[data-tid="'+ tid +'"]');
if ($row.data('updated') == stamp) {
$row.removeClass('drag-previous').find('span.tabledrag-changed').remove();
}
});
}
if (tableDrag.tm === null) {
$messages.removeClass('is-busy').empty();
}
if (!table.hasClass('taxonomy-wrangler-processed')) {
table.addClass('taxonomy-wrangler-processed');
var rows = $('tr', table).length;
var $termData = $('input[name="term_data"]', context);
var $submit = $('input[name="op"][value="Save"]', context)
var $rows = $('tr', table);
tableDrag.collection = new TaxonomyWranglerCollection();
var updatedTerms = {};
$submit.attr('disabled', 'disabled');
var accordion = !!TaxonomyWranglerAccordion.instances[table.attr('id')] ? TaxonomyWranglerAccordion.instances[table.attr('id')] : null;
$rows.bind('taxonomyWranglerTermDrag.rowDataChange', function(e, row){
tableDrag.collection.updateTerm(row);
var termsArray = [];
var tids = [];
accordion.refreshRows();
$.each(tableDrag.collection.updatedTerms, function(i, val) {
termsArray.push(val);
tids.push(val.tid + ':' + val.weight);
});
$termData.val(JSON.stringify({ "termData": termsArray }));
if (tableDrag.tm) {
clearTimeout(tableDrag.tm);
tableDrag.tm = null;
}
tableDrag.tm = setTimeout(function(){
$submit.removeAttr('disabled').trigger('mousedown').attr('disabled', 'disabled');
tableDrag.tm = null;
}, 1000);
if (!$messages.hasClass('is-busy')) {
$messages.addClass('is-busy').append('<div class="ajax-progress ajax-progress-throbber ajax-progress-throbber--taxonomy-wrangler"><div class="throbber">&nbsp;</div> Updating terms</div>');
}
});
}
}
};
})(jQuery, Drupal, this, this.document);