Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
gilour
/
generator
/
generator-assets
/
lib
/
php-diff
:
jquery.phpdiffmerge.js
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
/*! * jQuery-Merge-for-php-diff - A jQuery plugin for handling the conflicts between two documents. * v0.3.2 - 2014-05-13 3:53:31 PM UTC * Copyright (c) 2014 Hannes Diercks <github@xiphe.net>; License: MIT */ (function($) { 'use strict'; var pluginName = 'phpdiffmerge', defaults = { failedToMergeMsg: 'Unable to merge: not all conflicts have been resolved.', left: '', right: '', debug: false, pupupResult: false, pupupSources: false, merged: $.noop() }, count = 0; /* PHPDiffMerge constructor */ function PHPDiffMerge(element, options) { var self = this; self._id = 1 + count++; self.$el = $(element); self.options = $.extend({}, defaults, options); self._defaults = defaults; self._name = pluginName; /*** PUBLIC VARIABLES ***/ /* The conflict elements */ self.$conflicts = $(); /* Collection of conflict classes */ self.conflicts = []; /* Counters of conflicts left */ self.toResolve = 0; self.toMerge = 0; /* Indicator if the diff is inline or side-by-side */ self.inline = false; /* The resulting merge */ self.result = []; self.lineOffset = 0; self._tmpLineOffset = 0; self._init(); } PHPDiffMerge.prototype = { /*** CONSTRUCTION ***/ _init: function() { var self = this; /* Say Hello */ self._debug('PHPDiffMerge about to be initiated with options:', self.options); /* Find conflict containers */ self.$conflicts = self.$el.find('.change-rep, .change-del, .change-ins'); /* Set the counters */ self.toResolve = self.$conflicts.length; /* Check if we have enough data to work */ if (self.toResolve <= 0 || (self.options.left === '' && self.options.right === '')) { self._debug('Nothing to merge or merge sources not available - ' + 'Please submit left and right on plug-in initiation!'); return false; } /* Check if table style is inline */ this.inline = self.$el.hasClass('diff-inline'); self._ensurePresenceOfMergeButton(); /* Initiate Conflicts */ $.each(self.$conflicts, function() { self.conflicts.push(new Conflict(this, self)); }); /* Register event listeners for completion actions */ self.$conflicts.on('xiphe_phpdiffmerge_resolved', $.proxy(self._conflictResolved, self)); self.$conflicts.on('xiphe_phpdiffmerge_merged', $.proxy(self._conflictMerged, self)); self.$conflicts.on('xiphe_phpdiffmerge_merged', $.proxy(self._updateLineOffset, self)); self._debug('PHPDiffMerge initiated', self); }, /*** PUBLIC METHODS ***/ /** * Select all changes on the right side. */ useNew: function() { var self = this; self.$conflicts.find('td.new').click(); if (self.$el.hasClass('diff-inline')) { self.$el.find('.change-del td.old').click().click(); } }, /** * Selects all changes on the right. */ useOld: function() { var self = this; self.$conflicts.find('td.old').click(); if (self.$el.hasClass('diff-inline')) { self.$el.find('.change-ins td.new').click().click(); } }, /** * Start the merge process */ merge: function(event) { var self = this; if (typeof event !== 'undefined' && event !== null) { event.preventDefault(); } /* Don't work, if any conflicts are unresolved */ if (self.options.button.attr('data-disabled') === 'disabled') { alert(self.options.failedToMergeMsg); self._debug(self.options.failedToMergeMsg); return; } /* Initiate the end by cloning the left side. */ self.result = self.options.left.slice(0); /* Reset line offset */ self.lineOffset = 0; /* Reset the todo counter */ self.toMerge = self.$conflicts.length; for (var i = 0; i < self.conflicts.length; i++) { self.conflicts[i].merge(); } }, /** * change options AFTER initialization * * @param {object} options a plain options object */ option: function(options) { var self = this; if ($.isPlainObject(options)) { self.options = $.extend(true, self.options, options); } }, /*** PROTECTED METHODS ***/ /* * Check if a merge button is available or generate one. */ _ensurePresenceOfMergeButton: function() { var self = this; if (typeof self.options.button === 'undefined' || !$(self.options.button).length) { self.options.button = $('<button />') .html('Merge') .attr({ 'disabled': 'disabled', 'data-disabled': 'disabled', 'class': 'btn btn-lg btn-primary' }) .css({ display: 'block', height: '50px', width: '200px', margin: '50px auto' }); self.$el.after(self.options.button); } else { self.options.button = $(self.options.button).attr('data-disabled', 'disabled'); } /* Merge on click */ self.options.button.click($.proxy(self.merge, self)); }, _debug: function() { if (this.options.debug && window.console && window.console.log) { window.console.log(Array.prototype.slice.call(arguments)); } }, /* callback for whenever a conflict is resolved */ _conflictResolved: function() { var self = this; self.toResolve--; if (self.toResolve === 0) { self.options.button.removeAttr('data-disabled'); } }, /* callback for whenever a conflict is merged */ _conflictMerged: function() { var self = this; self.toMerge--; if (self.toMerge === 0) { if (self.options.pupupResult) { popup('end', self.result.join('\n')); } /* Pup-up the sources if set in configuration */ if (self.options.pupupSources) { popup('left', self.options.left.join('\n')); popup('right', self.options.right.join('\n')); } /* Call the merged-callback if callable */ if (typeof self.options.merged === 'function') { self.options.merged.call(self, self.result, self.options.left, self.options.right); } } }, /* Delete a given amount of rows at a specific index from the result. */ _deleteResult: function(index, length) { var self = this; /* Delete the left rows */ self._debug('Deleting old: ' + index + ' - ' + (index + length - 1) + '.'); var out = self.result.splice((index + self.lineOffset - 1), length); if (self.options.debug) { out = out.map(function(value) { return $.trim(value).substring(0, 10) + '...'; }); self._debug('Content: ', out); } /* Set new Line Offset */ self._tmpLineOffset -= length; }, /* Insert a given amount of rows at a specific index from the right side to the result. */ _insertResult: function(index, length, targetIndex) { var self = this; var insert = []; /* Insert the right rows. */ for (var i = 0; i < length; i++) { /* Get the content from the right site */ var line = self.options.right[index - 1 + i]; insert.push(line); /* inject it to the following line on the left side */ self.result.splice((targetIndex - 1 + self.lineOffset + i), 0, line); } if (self.options.debug) { self._debug('old line prior to insertion: ' + (targetIndex - 1)); self._debug( 'Content: ', $.trim(self.result[targetIndex - 2 + self.lineOffset]).substring(0, 10) ); self._debug('Inserted new: Row ' + index + ' - ' + (index + length - 1) + '.'); insert = insert.map(function(value) { return $.trim(value).substring(0, 10) + '...'; }); self._debug('Content: (' + insert.join(', ') + ')'); } /* Set new Line Offset */ self._tmpLineOffset += length; }, _updateLineOffset: function() { var self = this; self._debug('Change lineOffset from: ' + self.lineOffset + ' to ' + (self.lineOffset + self._tmpLineOffset) + '.'); self.lineOffset += self._tmpLineOffset; self._tmpLineOffset = 0; } }; /* Conflict Constructor */ function Conflict(element, master) { var self = this; self.$el = $(element); self.master = master; self._resolved = false; self.type = ''; self.useOld = false; self.leftLine = 0; self.rightLine = 0; self.rowsOld = 0; self.rowsNew = 0; self._init(); } Conflict.prototype = { /*** CONSTRUCTION ***/ _init: function() { var self = this; self.type = self.$el.attr('class').match(/change-([\w]+)/)[1]; self.$el.find('td') .click($.proxy(self._clicked, self)) .hover($.proxy(self._hoverIn, self), $.proxy(self._hoverOut, self)); self._setLine(); self._setRows(); self.master._debug('Conflict initiated:', self); }, /*** PUBLIC METHODS ***/ merge: function() { var self = this; if (self.useOld) { self.master._debug( 'Ignoring lines ' + self.leftLine + ' - ' + (self.leftLine + self.rowsOld - 1) + '.' ); self.$el.trigger('xiphe_phpdiffmerge_merged'); return; } self.master._debug('Merging Conflict:', self); switch (self.type) { case 'rep': self._delete(); self._insert(); break; case 'ins': self._insert(); break; case 'del': self._delete(); break; default: logError('Undefined merge method "' + self.type + '".'); return; } self.$el.trigger('xiphe_phpdiffmerge_merged'); }, /*** PRIVATE METHODS ***/ _hoverIn: function(event) { var $target = $(event.delegateTarget); var h = $target.hasClass('old') ? 'old' : 'new'; this.$el.find('td.' + h).addClass('hover'); }, _hoverOut: function() { this.$el.find('td.hover').removeClass('hover'); }, _clicked: function(event) { var self = this; var $target = $(event.delegateTarget), use, dont; if (!self.master.inline) { self.useOld = $target.hasClass('old'); } else { self.useOld = $target.hasClass('use'); if ($target.hasClass('old')) { self.useOld = !self.useOld; } } use = self.useOld ? 'old' : 'new'; dont = self.useOld ? 'new' : 'old'; /* Highlight the current clicked change. */ if (self.master.inline && (self.type === 'del' || self.type === 'ins') ) { var $c = self.$el.find('td'); $c.toggleClass('use'); $c.toggleClass('dontUse', !$c.hasClass('use')); } else { self.$el.find('td.' + use).removeClass('dontUse').addClass('use'); self.$el.find('td.' + dont).removeClass('use').addClass('dontUse'); } /* Consider this conflict as resolved if it is clicked for the first time. */ if (!self._resolved) { self._resolved = true; self.$el.trigger('xiphe_phpdiffmerge_resolved'); } }, _setLine: function() { var self = this; /* * Get the first line of the conflict from the previous table * because there was a bug with the line numbers in php-diff. */ var previousRow = self.$el.prev('tbody').find('tr').last(); self.leftLine = parseInt((previousRow.find('th').first().html() || 0), 10) + 1; self.rightLine = parseInt((previousRow.find('th').last().html() || 0), 10) + 1; }, _setRows: function() { var self = this; if (self.master.inline) { self.rowsOld = self.$el.find('.old').length; self.rowsNew = self.$el.find('.new').length; } else { self.rowsOld = 0; self.$el.find('.old').each(function() { if ($(this).prev('th').html() !== ' ') { self.rowsOld++; } }); self.rowsNew = 0; self.$el.find('.new').each(function() { if ($(this).prev('th').html() !== ' ') { self.rowsNew++; } }); } }, _insert: function() { var self = this; self.master._insertResult(self.rightLine, self.rowsNew, self.leftLine); }, _delete: function() { var self = this; self.master._deleteResult(self.leftLine, self.rowsOld); } }; /** * Generate a pup-up window with the given content. * * @param {string} title the title for the new window * @param {string} content the content for the new window */ function popup(title, content) { var newWin = window.open('popup.html'); // http://css-tricks.com/snippets/javascript/htmlentities-for-javascript/ content = content.replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"'); newWin.document.write('<html><head><title>' + title + '</title></head><body><pre>' + content + '</pre></body></html>'); } // helper function for logging errors // $.error breaks jQuery chaining // From jquery.isotope.js var logError = function(message) { if (window.console) { window.console.error(message); } }; // ======================= Plugin bridge =============================== // From jquery.isotope.js // https://github.com/desandro/isotope/blob/master/jquery.isotope.js#L1363 // // A bit from jQuery UI // https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js // A bit from jcarousel // https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js $.fn[pluginName] = function(options) { var self = this; if (typeof options === 'string') { // call method var args = Array.prototype.slice.call(arguments, 1); self.each(function() { var instance = $.data(this, 'plugin_' + pluginName); if (!instance) { logError('cannot call methods on ' + pluginName + ' prior to initialization; ' + 'attempted to call method "' + options + '"'); return; } if (!$.isFunction(instance[options]) || options.charAt(0) === '_') { logError('no such method "' + options + '" for ' + pluginName + ' instance'); return; } // apply method instance[options].apply(instance, args); }); } else { self.each(function() { var instance = $.data(this, 'plugin_' + pluginName); if (instance) { // apply options & init instance.option(options); instance._init(); } else { // initialize new instance $.data(this, 'plugin_' + pluginName, new PHPDiffMerge(this, options)); } }); } // return jQuery object // so plugin methods do not have to return self; }; })(jQuery);