Conflicts:
	lib/mergely.js
This commit is contained in:
Jamie Peabody
2014-08-17 15:35:16 +01:00
9 changed files with 269 additions and 45 deletions

View File

@@ -27,7 +27,9 @@ body { margin: 0; }
.icon-swap { background-image: url(images/swap.png); }
.icon-arrow-right { background-image: url(images/arrow-right.png); }
.icon-arrow-right-v { background-image: url(images/arrow-right-v.png); }
.icon-arrow-right-vv { background-image: url(images/arrow-right-vv.png); }
.icon-arrow-left-v { background-image: url(images/arrow-left-v.png); }
.icon-arrow-left-vv { background-image: url(images/arrow-left-vv.png); }
.icon-arrow-up { background-image: url(images/arrow-up-v.png); }
.icon-arrow-down { background-image: url(images/arrow-down-v.png); }
.icon-x-mark { background-image: url(images/x-mark.png); }

View File

@@ -249,6 +249,9 @@ $(document).ready(function() {
handleFind(ed.find('#mergely-editor-lhs'));
}
else if (id == 'edit-left-merge-right') {
ed.mergely('mergeCurrentDiff', 'rhs');
}
else if (id == 'edit-left-merge-right-file') {
ed.mergely('merge', 'rhs');
}
else if ([
@@ -272,6 +275,9 @@ $(document).ready(function() {
handleFind(ed.find('#mergely-editor-rhs'));
}
else if (id == 'edit-right-merge-left') {
ed.mergely('mergeCurrentDiff', 'lhs');
}
else if (id == 'edit-right-merge-left-file') {
ed.mergely('merge', 'lhs');
}
else if (id == 'edit-right-clear') {
@@ -286,6 +292,12 @@ $(document).ready(function() {
else if (id == 'view-refresh') {
ed.mergely('update');
}
else if (id == 'view-change-next') {
ed.mergely('scrollToDiff', 'next');
}
else if (id == 'view-change-prev') {
ed.mergely('scrollToDiff', 'prev');
}
else if (id == 'view-clear') {
ed.mergely('unmarkup');
}

View File

@@ -1,6 +1,6 @@
<?php
$key = '';
$debug = False;
$debug = True;
if (isset($_GET['key'])) {
$key = $_GET['key'];
}
@@ -97,7 +97,8 @@ if (isset($_GET['key'])) {
<li id="edit-left-redo" accesskey="y" data-hotkey="Ctrl+Y" data-icon="icon-redo">Redo</li>
<li id="edit-left-find">Find</li>
<li class="separator"></li>
<li id="edit-left-merge-right" data-icon="icon-arrow-right-v">Merge right</li>
<li id="edit-left-merge-right" data-hotkey="Alt+&rarr;" data-icon="icon-arrow-right-v">Merge change right</li>
<li id="edit-left-merge-right-file" data-icon="icon-arrow-right-vv">Merge file right</li>
<li id="edit-left-readonly">Read only</li>
<li class="separator"></li>
<li id="edit-left-clear">Clear</li>
@@ -110,7 +111,8 @@ if (isset($_GET['key'])) {
<li id="edit-right-redo" accesskey="y" data-hotkey="Ctrl+Y" data-icon="icon-redo">Redo</li>
<li id="edit-right-find">Find</li>
<li class="separator"></li>
<li id="edit-right-merge-left" data-icon="icon-arrow-left-v">Merge left</li>
<li id="edit-right-merge-left" data-hotkey="Alt+&larr;" data-icon="icon-arrow-left-v">Merge change left</li>
<li id="edit-right-merge-left-file" data-icon="icon-arrow-left-vv">Merge file left</li>
<li id="edit-right-readonly">Read only</li>
<li class="separator"></li>
<li id="edit-right-clear">Clear</li>
@@ -123,6 +125,9 @@ if (isset($_GET['key'])) {
<li class="separator"></li>
<li id="view-refresh" accesskey="v" data-hotkey="Alt+V" title="Generates diff markup">Render diff view</li>
<li id="view-clear" accesskey="c" data-hotkey="Alt+C" title="Clears diff markup">Clear render</li>
<li class="separator"></li>
<li id="view-change-prev" data-hotkey="Alt+&uarr;" title="View previous change">View prev change</li>
<li id="view-change-next" data-hotkey="Alt+&darr;" title="View next change">View next change</li>
</ul>
</li>
<li accesskey="o">
@@ -188,8 +193,11 @@ if (isset($_GET['key'])) {
<li id="tb-file-import" data-icon="icon-import" title="Import">Import</li>
<li id="tb-file-save" data-icon="icon-save" title="Save .diff">Save .diff</li>
<li class="separator"></li>
<li id="tb-edit-right-merge-left" data-icon="icon-arrow-left-v" title="Merge left">Merge left</li>
<li id="tb-edit-left-merge-right" data-icon="icon-arrow-right-v" title="Merge right">Merge right</li>
<li id="tb-view-change-prev" data-icon="icon-arrow-up" title="Previous change">Previous change</li>
<li id="tb-view-change-next" data-icon="icon-arrow-down" title="Next change">Next change</li>
<li class="separator"></li>
<li id="tb-edit-right-merge-left" data-icon="icon-arrow-left-v" title="Merge change left">Merge change left</li>
<li id="tb-edit-left-merge-right" data-icon="icon-arrow-right-v" title="Merge change right">Merge change right</li>
<li id="tb-view-swap" data-icon="icon-swap" title="Swap sides">Swap sides</li>
</ul>

BIN
editor/images/arrow-left-vv.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

BIN
editor/images/arrow-right-vv.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

View File

@@ -75,7 +75,7 @@ THE SOFTWARE.
.wicked-menu ul li:hover > ul { display: block; position: absolute; }
/* drop menu */
.wicked-menu ul { z-index: 7; min-width: 200px; top: 2.1em; left: -1px; }
.wicked-menu ul { z-index: 7; min-width: 210px; top: 2.1em; left: -1px; }
.wicked-menu > li.hover {
/* for the top-level menu, after hovering do not show background */
@@ -93,7 +93,7 @@ padding-left: 31px;
background-repeat: no-repeat;
}
.wicked-menu ul li:hover > ul { min-width:200px; top: 0px; left: 100%; }
.wicked-menu ul li:hover > ul { min-width: 210px; top: 0px; left: 100%; }
.wicked-menu li.separator { border-top: 1px solid #e5e5e5; height: 3px; display: block; line-height: 3em; margin-top: 3px; }
.wicked-menu li.separator:hover { background: transparent; cursor:default; }

View File

@@ -24,6 +24,24 @@ THE SOFTWARE.
;(function( $, window, document, undefined ){
var pluginName = 'wickedtoolbar';
var SHIFT = new RegExp(/shift/i);
var ALT = new RegExp(/alt/i);
var CTRL = new RegExp(/ctrl/i);
var ARROW_DOWN = new RegExp(/↓/);
var ARROW_UP = new RegExp(/↑/);
var ARROW_LEFT = new RegExp(/←/);
var ARROW_RIGHT = new RegExp(/→/);
var keys = {
shift: 16,
alt: 17,
ctrl: 18,
meta: 91,
arrow_up: 38,
arrow_down: 40,
arrow_left: 37,
arrow_right: 39
};
var defaults = {
hasIcon: function(id) { },
@@ -34,7 +52,8 @@ THE SOFTWARE.
var self = this;
this.element = $(element);
this.settings = $.extend({}, defaults, options) ;
this.settings = $.extend({}, defaults, options);
this.bindings = [];
// for each menu item, modify and wrap in div
this.element.find('li').each(function () {
var tthis = $(this);
@@ -53,10 +72,10 @@ THE SOFTWARE.
// change: <li>Text</li>
// to: <li>
// <a>
// <span>Text</span>
// </a>
// </li>
// <a>
// <span>Text</span>
// </a>
// </li>
var li = tthis;
var div = $('<a class="menu-item" href="#">');
div.click(function(ev) {
@@ -92,18 +111,50 @@ THE SOFTWARE.
if (hotkey) {
tthis.removeAttr('data-hotkey');
div.append('<span class="hotkey">' + hotkey + '</span>');
if (!accesskey) {
// add our own handler
var parts = hotkey.split('+');
var bind = {};
for (var i = 0; i < parts.length; ++i) {
if (SHIFT.test(parts[i])) {
bind.shiftKey = true;
}
else if (ALT.test(parts[i])) {
bind.altKey = true;
}
else if (CTRL.test(parts[i])) {
bind.ctrlKey = true;
}
else if (ARROW_DOWN.test(parts[i])) {
bind.which = keys.arrow_down;
}
else if (ARROW_UP.test(parts[i])) {
bind.which = keys.arrow_up;
}
else if (ARROW_RIGHT.test(parts[i])) {
bind.which = keys.arrow_right;
}
else if (ARROW_LEFT.test(parts[i])) {
bind.which = keys.arrow_left;
}
}
bind.target = div;
self.bindings.push(bind);
}
}
}
// icon
var id = tthis.attr('id');
var id = tthis.attr('id'), icon;
if (self.settings.hasIcon(id)) {
span.addClass('icon');
if (icon = self.settings.getIcon(id)) {
icon = self.settings.getIcon(id);
if (icon) {
span.addClass(icon);
}
}
var icon = tthis.attr('data-icon');
icon = tthis.attr('data-icon');
if (icon) {
tthis.removeAttr('data-icon');
span.addClass('icon ' + icon);
@@ -111,22 +162,35 @@ THE SOFTWARE.
else if (icons) {
span.addClass('icon');
}
li.prepend(div);
});
$(document).on('keydown', function(ev) {
for (var i = 0; i < self.bindings.length; ++i) {
var bind = self.bindings[i];
// handle custom key events
if ((bind.shiftKey === undefined ? true : (bind.shiftKey === ev.shiftKey)) &&
(bind.ctrlKey === undefined ? true : (bind.ctrlKey === ev.ctrlKey)) &&
(bind.altKey === undefined ? true : (bind.altKey === ev.altKey)) &&
bind.which && ev.which && (bind.which === ev.which)) {
bind.target.trigger('click');
ev.preventDefault();
}
}
});
}
MenuBase.prototype.update = function (id) {
var li = this.element.find('#' + id);
var li = this.element.find('#' + id), icon;
var span = li.find('span:first-child');
if (this.settings.hasIcon(id)) {
span.removeClass(); // this could be brutally unfair
span.addClass('icon');
if (icon = this.settings.getIcon(id)) {
icon = this.settings.getIcon(id);
if (icon) {
span.addClass(icon);
}
}
}
};
// ------------
// Menu
@@ -139,6 +203,7 @@ THE SOFTWARE.
Menu.prototype.constructor = function () {
this.element.addClass('wicked-ui wicked-menu');
var self = this;
var dohover = function(ev) {
$(this).parent().addClass('hover');
if ($(this).closest('ul').hasClass('wicked-menu')) {
@@ -170,7 +235,6 @@ THE SOFTWARE.
}
);
});
var self = this;
this.element.find('> li > a.menu-item').hover(
function() {
if (!self.accessing) return;
@@ -178,7 +242,7 @@ THE SOFTWARE.
$.proxy(dohover, this)();
}
);
}
};
// ------------
// Toolbar
@@ -195,12 +259,12 @@ THE SOFTWARE.
function(){ $(this).parent().addClass('hover'); },
function(){ $(this).parent().removeClass('hover'); }
);
}
};
var plugins = { wickedmenu: Menu, wickedtoolbar: Toolbar };
for (var key in plugins) {
(function(name, Plugin){
$.fn[name] = function ( options ) {
$.fn[name] = function (options) {
var args = arguments;
return this.each(function () {
if (typeof options === 'object' || !options) {
@@ -217,7 +281,7 @@ THE SOFTWARE.
return d[options](Array.prototype.slice.call(args, 1));
}
});
}
};
})(key, plugins[key])
}

View File

@@ -12,42 +12,121 @@ This example demonstrates how to set left and right editors using ajax.
<meta name="author" content="Jamie Peabody" />
<!-- Requires jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js" type="text/javascript"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js" type="text/javascript"></script>
<!-- Requires CodeMirror -->
<script type="text/javascript" src="../lib/codemirror.js"></script>
<link type="text/css" rel="stylesheet" href="../lib/codemirror.css" />
<!-- Requires Mergely -->
<script type="text/javascript" src="../lib/mergely.js"></script>
<link type="text/css" rel="stylesheet" href="../lib/mergely.css" />
<style type='text/css'>
.drop_zone {
border: 2px dashed #BBBBBB;
border-radius: 5px 5px 5px 5px;
color: #BBBBBB;
padding: 10px 25px;
text-align: center;
align: center;
width: 80%;
}
</style>
<script type="text/javascript">
$(document).ready(function () {
$(document).ready(function () {
$('#compare').mergely({
width: 'auto',
height: 'auto', // containing div must be given a height
cmsettings: { readOnly: false },
});
var lhs_url = 'lhs.txt';
var rhs_url = 'rhs.txt'
$.ajax({
type: 'GET', async: true, dataType: 'text',
url: 'lhs.txt',
url: lhs_url,
success: function (response) {
$('#path-lhs').text(lhs_url);
$('#compare').mergely('lhs', response);
}
});
$.ajax({
type: 'GET', async: true, dataType: 'text',
url: 'rhs.txt',
url: rhs_url,
success: function (response) {
$('#path-rhs').text(rhs_url);
$('#compare').mergely('rhs', response);
}
});
function checkFileList(files) {
if (typeof window.FileReader !== 'function')
error_msg("The file API isn't supported on this browser yet.");
if (files.length>0) readFile(files[0], "lhs");
if (files.length>1) readFile(files[1], "rhs");
}
function readFile(file, side) {
var reader = new FileReader();
reader.onload = function file_onload() {
// document.getElementById('td1').innerHTML = ..
$('#path-'+side).text(file.name);
$('#compare').mergely(side, reader.result);
}
reader.readAsBinaryString(file);
}
function handleDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
}
function handleFileSelect(evt) {
document.getElementById('drop_zone').visibility = "collapse";
evt.stopPropagation();
evt.preventDefault();
var files = evt.dataTransfer.files; // FileList object.
checkFileList(files);
}
var dropZone = document.getElementById('drop_zone');
document.body.addEventListener('dragover', handleDragOver, false);
document.body.addEventListener('drop', handleFileSelect, false);
function download_content(a, side) {
//a.innerHTML = "preparing content..";
var txt = $('#compare').mergely('get', side);
var datauri = "data:plain/text;charset=UTF-8," + encodeURIComponent(txt);
a.setAttribute('download', side+".txt");
a.setAttribute('href', datauri);
//a.innerHTML = "content ready.";
}
document.getElementById('save-lhs').addEventListener('mouseover', function() { download_content(this, "lhs"); }, false);
document.getElementById('save-rhs').addEventListener('mouseover', function() { download_content(this, "lhs"); }, false);
//document.getElementById('save-lhs').addEventListener('click', function() { download_content(this, "lhs"); }, false);
//document.getElementById('save-rhs').addEventListener('click', function() { download_content(this, "lhs"); }, false);
document.getElementById('ignorews').addEventListener('change', function() {
$('#compare').mergely('options', { ignorews: this.checked });
}, false);
});
</script>
</head>
<body>
<body style="width: 100%;">
<table style="width: 100%;"><tr>
<td style="width: 50%;"><div id="drop_zone" class="drop_zone">Drop files here</div></td>
<td style="width: 50%;"><input type="checkbox" id="ignorews">ignore witespaces</td>
</tr></table>
<br/>
<div id="mergely-resizer">
<table style="width: 100%;"><tr>
<td style="width: 50%;"><tt id="path-lhs"></tt> &nbsp; <a id="save-lhs" class="save-link" href="#">save</a></td>
<td style="width: 50%;"><tt id="path-rhs"></tt> &nbsp; <a id="save-rhs" class="save-link" href="#">save</a></td>
</tr></table>
<div id="mergely-resizer" style="height: 450px;"">
<div id="compare">
</div>
</div>

View File

@@ -386,11 +386,12 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
viewport: false,
ignorews: false,
fadein: 'fast',
editor_width: '400px',
editor_width: '650px',
editor_height: '400px',
resize_timeout: 500,
change_timeout: 150,
fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f'},
fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f', // color for differences (soft color)
ca:'#4b73ff',cc:'#434343',cd:'#ff4f4f'}, // color for currently active difference (bright color)
bgcolor: '#eee',
vpcolor: 'rgba(0, 0, 200, 0.5)',
lhs: function(setValue) { },
@@ -481,6 +482,26 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
unmarkup: function() {
this._clear();
},
scrollToDiff: function(direction) {
if (!this.changes.length) return;
if (direction == 'next') {
this._current_diff = Math.min(++this._current_diff, this.changes.length - 1);
}
else {
this._current_diff = Math.max(--this._current_diff, 0);
}
this._scroll_to_change(this.changes[this._current_diff]);
this._changed(this.id + '-lhs', this.id + '-rhs');
},
mergeCurrentDiff: function(side) {
if (!this.changes.length) return;
if (side == 'lhs' && !this.lhs_cmsettings.readOnly) {
this._merge_change(this.changes[this._current_diff], 'rhs', 'lhs');
}
else if (side == 'rhs' && !this.rhs_cmsettings.readOnly) {
this._merge_change(this.changes[this._current_diff], 'lhs', 'rhs');
}
},
scrollTo: function(side, num) {
var le = this.editor[this.id + '-lhs'];
var re = this.editor[this.id + '-rhs'];
@@ -605,7 +626,7 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
}
else {
// homebrew
var style = 'opacity:0.4;width:10px;height:15px;background-color:#888;cursor:pointer;text-align:center;color:#eee;border:1px solid: #222;margin-right:5px;';
var style = 'opacity:0.4;width:10px;height:15px;background-color:#888;cursor:pointer;text-align:center;color:#eee;border:1px solid: #222;margin-right:5px;margin-top: -2px;';
merge_lhs_button = '<div style="' + style + '" title="Merge left">&lt;</div>';
merge_rhs_button = '<div style="' + style + '" title="Merge right">&gt;</div>';
}
@@ -688,7 +709,31 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
this.settings.rhs(setv.bind(this.editor[this.id + '-rhs'].getDoc()));
}
},
_scroll_to_change : function(change) {
if (!change) return;
var self = this;
var led = self.editor[self.id+'-lhs'];
var red = self.editor[self.id+'-rhs'];
var yref = led.getScrollerElement().offsetHeight * 1/2; // center between >0 and 1/2
// set cursors
led.setCursor(Math.max(change["lhs-line-from"],0), 0); // use led.getCursor().ch ?
red.setCursor(Math.max(change["rhs-line-from"],0), 0);
// using directly CodeMirror breaks canvas alignment
// var ly = led.charCoords({line: Math.max(change["lhs-line-from"],0), ch: 0}, "local").top;
// calculate scroll offset for current change. Warning: returns relative y position so we scroll to 0 first.
led.scrollTo(null, 0);
red.scrollTo(null, 0);
self._calculate_offsets(self.id+'-lhs', self.id+'-rhs', [change]);
led.scrollTo(null, Math.max(change["lhs-y-start"]-yref, 0));
red.scrollTo(null, Math.max(change["rhs-y-start"]-yref, 0));
// right pane should simply follows
},
_scrolling: function(editor_name) {
if (this._skipscroll[editor_name] === true) {
// scrolling one side causes the other to event - ignore it
@@ -845,6 +890,12 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
this.trace('change', 'diff time', timer.stop());
this.changes = Mgly.DiffParser(d.normal_form());
this.trace('change', 'parse time', timer.stop());
if (this._current_diff === undefined) {
// go to first difference on start-up
this._current_diff = 0;
this._scroll_to_change(this.changes[0]);
}
this.trace('change', 'scroll_to_change time', timer.stop());
this._calculate_offsets(editor_name1, editor_name2, this.changes);
this.trace('change', 'offsets time', timer.stop());
this._markup_changes(editor_name1, editor_name2, this.changes);
@@ -1230,8 +1281,17 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
}
});
var change = self.changes[cid];
self._merge_change(change, side, oside);
return false;
});
this.trace('change', 'markup buttons time', timer.stop());
},
_merge_change : function(change, side, oside) {
if (!change) return;
var led = this.editor[this.id+'-lhs'];
var red = this.editor[this.id+'-rhs'];
var ed = {lhs:led, rhs:red};
var line = {lhs: ed['lhs'].lineInfo(llt), rhs: ed['rhs'].lineInfo(rlt)};
var text = ed[side].getRange(
CodeMirror.Pos(change[side + '-line-from'], 0),
@@ -1272,9 +1332,8 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
//reset
ed['lhs'].setValue(ed['lhs'].getValue());
ed['rhs'].setValue(ed['rhs'].getValue());
return false;
});
this.trace('change', 'markup buttons time', timer.stop());
this._scroll_to_change(change)
},
_draw_info: function(editor_name1, editor_name2) {
var visible_page_height = jQuery(this.editor[editor_name1].getScrollerElement()).height();
@@ -1345,14 +1404,14 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
this.trace('draw', 'marker calculated', lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end);
ctx_lhs.beginPath();
ctx_lhs.fillStyle = this.settings.fgcolor[change['op']];
ctx_lhs.fillStyle = this.settings.fgcolor[(this._current_diff==i?'c':'')+change['op']];
ctx_lhs.strokeStyle = '#000';
ctx_lhs.lineWidth = 0.5;
ctx_lhs.fillRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5));
ctx_lhs.strokeRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5));
ctx_rhs.beginPath();
ctx_rhs.fillStyle = this.settings.fgcolor[change['op']];
ctx_rhs.fillStyle = this.settings.fgcolor[(this._current_diff==i?'c':'')+change['op']];
ctx_rhs.strokeStyle = '#000';
ctx_rhs.lineWidth = 0.5;
ctx_rhs.fillRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5));
@@ -1371,8 +1430,8 @@ jQuery.extend(Mgly.CodeMirrorDiffView.prototype, {
// draw left box
ctx.beginPath();
ctx.strokeStyle = this.settings.fgcolor[change['op']];
ctx.lineWidth = 1;
ctx.strokeStyle = this.settings.fgcolor[(this._current_diff==i?'c':'')+change['op']];
ctx.lineWidth = (this._current_diff==i) ? 1.5 : 1;
var rectWidth = this.draw_lhs_width;
var rectHeight = lhs_y_end - lhs_y_start - 1;