├── .gitignore ├── Gruntfile.coffee ├── LICENSE ├── dist ├── jquery.phpdiffmerge.css ├── jquery.phpdiffmerge.js ├── jquery.phpdiffmerge.min.css └── jquery.phpdiffmerge.min.js ├── example ├── a.txt ├── b.txt ├── composer.json ├── index.php └── style.css ├── karma.conf.coffee ├── package.json ├── readme.md ├── src ├── phpdiffmerge.js └── phpdiffmerge.less └── tests └── phpdiffmergeSpec.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | /example/vendor/ 2 | /node_modules/ 3 | /example/composer.lock 4 | /package-lock.json 5 | -------------------------------------------------------------------------------- /Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (grunt) -> 2 | require('load-grunt-tasks')(grunt) 3 | grunt.loadTasks 'tasks' 4 | 5 | 6 | grunt.initConfig 7 | pkg: grunt.file.readJSON 'package.json' 8 | meta: 9 | banner: """/*! 10 | * <%= pkg.name %> - <%= pkg.description %> 11 | * v<%= pkg.version %> - <%= grunt.template.today("UTC:yyyy-mm-dd h:MM:ss TT Z") %> 12 | * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>; License: <%= pkg.license %> 13 | */ 14 | 15 | """ 16 | minbanner: """/*! 17 | * <%= pkg.name %> v<%= pkg.version %> - Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %> - License: <%= pkg.license %> 18 | */ 19 | """ 20 | 21 | less: 22 | dist: 23 | files: 24 | 'dist/<%= pkg.compactname %>.css': ['src/phpdiffmerge.less'] 25 | 26 | cssmin: 27 | options: 28 | banner: '<%= meta.minbanner %>' 29 | styles: 30 | files: 31 | 'dist/<%= pkg.compactname %>.min.css': ['dist/<%= pkg.compactname %>.css'] 32 | 33 | concat: 34 | options: 35 | stripBanners: true 36 | banner: '<%= meta.banner %>' 37 | dist: 38 | src: ['src/phpdiffmerge.js'] 39 | dest: 'dist/<%= pkg.compactname %>.js' 40 | styles: 41 | src: ['dist/<%= pkg.compactname %>.css'] 42 | dest: 'dist/<%= pkg.compactname %>.css' 43 | 44 | uglify: 45 | options: 46 | banner: '<%= meta.minbanner %>\n' 47 | dist: 48 | files: 49 | 'dist/<%= pkg.compactname %>.min.js': ['dist/<%= pkg.compactname %>.js'] 50 | 51 | karma: 52 | options: 53 | configFile: 'karma.conf.coffee' 54 | 55 | single: 56 | singleRun: true 57 | 58 | watch: 59 | singleRun: false 60 | autoWatch: true 61 | 62 | bump: 63 | options: 64 | files: ['package.json'] 65 | updateConfigs: ['pkg'] 66 | commitFiles: ['package.json', 'dist/'] 67 | tagName: 'v%VERSION%' 68 | pushTo: 'origin' 69 | 70 | 71 | grunt.registerTask 'test', ['karma:single'] 72 | grunt.registerTask 'watch', ['karma:watch'] 73 | grunt.registerTask 'build', ['less', 'cssmin', 'concat', 'uglify'] 74 | grunt.registerTask 'default', ['test', 'build'] 75 | grunt.registerTask 'release', (type) -> grunt.task.run ['test', "bump-only:#{type||'patch'}", 'build', 'bump-commit'] 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (C) 2014 Hannes Diercks 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /dist/jquery.phpdiffmerge.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery-Merge-for-php-diff - A jQuery plugin for handling the conflicts between two documents. 3 | * v0.3.2 - 2014-05-13 3:53:31 PM UTC 4 | * Copyright (c) 2014 Hannes Diercks ; License: MIT 5 | */ 6 | .Differences .ChangeReplace td, 7 | .Differences .ChangeInsert td, 8 | .Differences .ChangeDelete td { 9 | cursor: pointer; 10 | } 11 | .Differences .ChangeReplace td.hover, 12 | .Differences .ChangeInsert td.hover, 13 | .Differences .ChangeDelete td.hover, 14 | .Differences .ChangeReplace td.dontUse.hover, 15 | .Differences .ChangeInsert td.dontUse.hover, 16 | .Differences .ChangeDelete td.dontUse.hover { 17 | background: #000; 18 | color: #fff; 19 | } 20 | .Differences .ChangeReplace td.hover del, 21 | .Differences .ChangeInsert td.hover del, 22 | .Differences .ChangeDelete td.hover del, 23 | .Differences .ChangeReplace td.dontUse.hover del, 24 | .Differences .ChangeInsert td.dontUse.hover del, 25 | .Differences .ChangeDelete td.dontUse.hover del, 26 | .Differences .ChangeReplace td.hover ins, 27 | .Differences .ChangeInsert td.hover ins, 28 | .Differences .ChangeDelete td.hover ins, 29 | .Differences .ChangeReplace td.dontUse.hover ins, 30 | .Differences .ChangeInsert td.dontUse.hover ins, 31 | .Differences .ChangeDelete td.dontUse.hover ins { 32 | background: #000; 33 | color: #fff; 34 | } 35 | .Differences .ChangeReplace td.use.hover, 36 | .Differences .ChangeInsert td.use.hover, 37 | .Differences .ChangeDelete td.use.hover { 38 | cursor: default; 39 | color: #000; 40 | } 41 | .Differences .ChangeReplace td.use.hover del, 42 | .Differences .ChangeInsert td.use.hover del, 43 | .Differences .ChangeDelete td.use.hover del, 44 | .Differences .ChangeReplace td.use.hover ins, 45 | .Differences .ChangeInsert td.use.hover ins, 46 | .Differences .ChangeDelete td.use.hover ins { 47 | background: #6EEF81; 48 | color: #000; 49 | } 50 | .Differences .ChangeReplace td.use, 51 | .Differences .ChangeInsert td.use, 52 | .Differences .ChangeDelete td.use { 53 | background: #6EEF81; 54 | } 55 | .Differences .ChangeReplace td.dontUse, 56 | .Differences .ChangeInsert td.dontUse, 57 | .Differences .ChangeDelete td.dontUse { 58 | background: #F7F7F7; 59 | color: #DDDDDD; 60 | } 61 | .Differences .ChangeReplace td.use del, 62 | .Differences .ChangeReplace td.use.hover del, 63 | .Differences .ChangeReplace td.use ins, 64 | .Differences .ChangeReplace td.use.hover ins { 65 | background: #0CD123; 66 | } 67 | .Differences .ChangeReplace td.dontUse del, 68 | .Differences .ChangeReplace td.dontUse ins { 69 | background: #DDDDDD; 70 | color: #BBBBBB; 71 | } 72 | .Differences.DifferencesInline .ChangeDelete td.use.hover, 73 | .Differences.DifferencesInline .ChangeInsert td.use.hover { 74 | cursor: pointer; 75 | } 76 | .Differences.DifferencesInline .ChangeDelete td.hover, 77 | .Differences.DifferencesInline .ChangeInsert td.hover, 78 | .Differences.DifferencesInline .ChangeDelete td.dontUse.hover, 79 | .Differences.DifferencesInline .ChangeInsert td.dontUse.hover { 80 | background: #000; 81 | color: #fff; 82 | } 83 | .Differences.DifferencesInline .ChangeDelete td.hover del, 84 | .Differences.DifferencesInline .ChangeInsert td.hover del, 85 | .Differences.DifferencesInline .ChangeDelete td.dontUse.hover del, 86 | .Differences.DifferencesInline .ChangeInsert td.dontUse.hover del, 87 | .Differences.DifferencesInline .ChangeDelete td.hover ins, 88 | .Differences.DifferencesInline .ChangeInsert td.hover ins, 89 | .Differences.DifferencesInline .ChangeDelete td.dontUse.hover ins, 90 | .Differences.DifferencesInline .ChangeInsert td.dontUse.hover ins { 91 | background: #000; 92 | color: #fff; 93 | } 94 | -------------------------------------------------------------------------------- /dist/jquery.phpdiffmerge.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery-Merge-for-php-diff - A jQuery plugin for handling the conflicts between two documents. 3 | * v0.3.2 - 2014-05-13 3:53:31 PM UTC 4 | * Copyright (c) 2014 Hannes Diercks ; License: MIT 5 | */ 6 | (function($) { 7 | 'use strict'; 8 | 9 | var pluginName = 'phpdiffmerge', 10 | defaults = { 11 | left: '', 12 | right: '', 13 | debug: false, 14 | pupupResult: false, 15 | pupupSources: false, 16 | merged: $.noop() 17 | }, 18 | count = 0; 19 | 20 | /* PHPDiffMerge constructor */ 21 | function PHPDiffMerge(element, options) { 22 | var self = this; 23 | 24 | self._id = 1 + count++; 25 | self.$el = $(element); 26 | 27 | self.options = $.extend({}, defaults, options); 28 | 29 | self._defaults = defaults; 30 | self._name = pluginName; 31 | 32 | /*** PUBLIC VARIABLES ***/ 33 | 34 | /* The conflict elements */ 35 | self.$conflicts = $(); 36 | 37 | /* Collection of conflict classes */ 38 | self.conflicts = []; 39 | 40 | /* Counters of conflicts left */ 41 | self.toResolve = 0; 42 | self.toMerge = 0; 43 | 44 | /* Indicator if the diff is inline or side-by-side */ 45 | self.inline = false; 46 | 47 | /* The resulting merge */ 48 | self.result = []; 49 | 50 | self.lineOffset = 0; 51 | self._tmpLineOffset = 0; 52 | 53 | 54 | self._init(); 55 | } 56 | 57 | PHPDiffMerge.prototype = { 58 | 59 | /*** CONSTRUCTION ***/ 60 | _init: function() { 61 | var self = this; 62 | 63 | /* Say Hello */ 64 | self._debug('PHPDiffMerge about to be initiated with options:', self.options); 65 | 66 | /* Find conflict containers */ 67 | self.$conflicts = self.$el.find('.ChangeReplace, .ChangeDelete, .ChangeInsert'); 68 | /* Set the counters */ 69 | self.toResolve = self.$conflicts.length; 70 | 71 | /* Check if we have enough data to work */ 72 | if (self.toResolve <= 0 || (self.options.left === '' && self.options.right === '')) { 73 | self._debug('Nothing to merge or merge sources not available - ' + 74 | 'Please submit left and right on plug-in initiation!'); 75 | return false; 76 | } 77 | 78 | /* Check if table style is inline */ 79 | this.inline = self.$el.hasClass('DifferencesInline'); 80 | 81 | self._ensurePresenceOfMergeButton(); 82 | 83 | /* Initiate Conflicts */ 84 | $.each(self.$conflicts, function() { 85 | self.conflicts.push(new Conflict(this, self)); 86 | }); 87 | 88 | /* Register event listeners for completion actions */ 89 | self.$conflicts.on('xiphe_phpdiffmerge_resolved', $.proxy(self._conflictResolved, self)); 90 | self.$conflicts.on('xiphe_phpdiffmerge_merged', $.proxy(self._conflictMerged, self)); 91 | self.$conflicts.on('xiphe_phpdiffmerge_merged', $.proxy(self._updateLineOffset, self)); 92 | 93 | self._debug('PHPDiffMerge initiated', self); 94 | }, 95 | 96 | 97 | /*** PUBLIC METHODS ***/ 98 | 99 | /** 100 | * Select all changes on the right side. 101 | */ 102 | useRight: function() { 103 | var self = this; 104 | 105 | self.$conflicts.find('td.Right').click(); 106 | if (self.$el.hasClass('DifferencesInline')) { 107 | self.$el.find('.ChangeDelete td.Left').click().click(); 108 | } 109 | }, 110 | 111 | /** 112 | * Selects all changes on the right. 113 | */ 114 | useLeft: function() { 115 | var self = this; 116 | 117 | self.$conflicts.find('td.Left').click(); 118 | if (self.$el.hasClass('DifferencesInline')) { 119 | self.$el.find('.ChangeInsert td.Right').click().click(); 120 | } 121 | }, 122 | 123 | /** 124 | * Start the merge process 125 | */ 126 | merge: function(event) { 127 | var self = this; 128 | 129 | if (typeof event !== 'undefined' && event !== null) { 130 | event.preventDefault(); 131 | } 132 | 133 | /* Don't work, if any conflicts are unresolved */ 134 | if (self.options.button.attr('disabled') === 'disabled') { 135 | self._debug('Unable to merge: not all conflicts have been resolved.'); 136 | return; 137 | } 138 | 139 | /* Initiate the end by cloning the left side. */ 140 | self.result = self.options.left.slice(0); 141 | 142 | /* Reset line offset */ 143 | self.lineOffset = 0; 144 | 145 | /* Reset the todo counter */ 146 | self.toMerge = self.$conflicts.length; 147 | 148 | for (var i = 0; i < self.conflicts.length; i++) { 149 | self.conflicts[i].merge(); 150 | } 151 | }, 152 | 153 | /** 154 | * change options AFTER initialization 155 | * 156 | * @param {object} options a plain options object 157 | */ 158 | option: function(options) { 159 | var self = this; 160 | 161 | if ($.isPlainObject(options)) { 162 | self.options = $.extend(true, self.options, options); 163 | } 164 | }, 165 | 166 | 167 | /*** PROTECTED METHODS ***/ 168 | 169 | /* 170 | * Check if a merge button is available or generate one. 171 | */ 172 | _ensurePresenceOfMergeButton: function() { 173 | var self = this; 174 | 175 | if (typeof self.options.button === 'undefined' || !$(self.options.button).length) { 176 | self.options.button = $('