123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- /**
- * @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"> </div> Updating terms</div>');
- }
- });
- }
- }
- };
- })(jQuery, Drupal, this, this.document);
|