mirror of
https://github.com/gorhill/uMatrix.git
synced 2024-09-30 00:56:32 +13:00
integrate CodeMirror's MergeView to the 'My rules' pane
This commit is contained in:
parent
747748c9ba
commit
d825b5562e
17 changed files with 12088 additions and 430 deletions
|
@ -29,6 +29,10 @@ ul {
|
||||||
<li><span data-i18n="aboutIssueContributors"></span> <a href="https://github.com/gorhill/uMatrix/issues?q=is%3Aissue">uMatrix</a>, <a href="https://github.com/gorhill/httpswitchboard/issues?q=is%3Aissue">HTTP Switchboard</a>
|
<li><span data-i18n="aboutIssueContributors"></span> <a href="https://github.com/gorhill/uMatrix/issues?q=is%3Aissue">uMatrix</a>, <a href="https://github.com/gorhill/httpswitchboard/issues?q=is%3Aissue">HTTP Switchboard</a>
|
||||||
<li><span data-i18n="aboutTranslationContributors"></span> <a href="https://github.com/gorhill/uMatrix/wiki/Translation-work-contributors">Crowdin</a>
|
<li><span data-i18n="aboutTranslationContributors"></span> <a href="https://github.com/gorhill/uMatrix/wiki/Translation-work-contributors">Crowdin</a>
|
||||||
</ul>
|
</ul>
|
||||||
|
<li><a href="https://github.com/bestiejs/punycode.js" target="_blank">Punycode.js</a> by <a href="https://github.com/mathiasbynens">Mathias Bynens</a>
|
||||||
|
<li><a href="https://fontawesome.com/" target="_blank">Font Awesome</a> by <a href="https://github.com/davegandy">Dave Gandy</a>
|
||||||
|
<li><a href="https://codemirror.net/" target="_blank">CodeMirror</a> by <a href="https://github.com/marijnh">Marijn Haverbeke</a>
|
||||||
|
<li><a href="https://github.com/Swatinem/diff" target="_blank">An implementation of Myers' diff algorithm</a> by <a href="https://github.com/Swatinem">Arpad Borsos</a>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2 data-i18n="aboutUserDataHeader"></h2>
|
<h2 data-i18n="aboutUserDataHeader"></h2>
|
||||||
|
|
29
src/css/codemirror.css
Normal file
29
src/css/codemirror.css
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
.codeMirrorContainer {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.25;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.CodeMirror {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For when panels are used */
|
||||||
|
.codeMirrorContainer > div:not([class^="CodeMirror"]) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-l-deleted {
|
||||||
|
background-image: none;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-l-inserted {
|
||||||
|
background-image: none;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
body {
|
body {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
box-sizing: border-box;
|
||||||
color: #000;
|
color: #000;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 0.5em 0 0.5em;
|
padding: 0 0.5em 0.5em 0.5em;
|
||||||
font: 14px/1.4 sans-serif;
|
font: 14px/1.4 sans-serif;
|
||||||
}
|
}
|
||||||
body > *:first-child {
|
body > *:first-child {
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
div > p:first-child {
|
body {
|
||||||
margin-top: 0;
|
height: 100vh;
|
||||||
}
|
overflow: hidden;
|
||||||
div > p:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
}
|
||||||
#diff {
|
#diff {
|
||||||
border: 0;
|
border: 0;
|
||||||
|
@ -10,152 +8,85 @@ div > p:last-child {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#diff > .pane {
|
#diff .tools > * {
|
||||||
border: 0;
|
margin-bottom: 0.5em;
|
||||||
box-sizing: box-border;
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
position: relative;
|
|
||||||
vertical-align: top;
|
|
||||||
width: calc(50% - 2px);
|
|
||||||
}
|
}
|
||||||
#diff > .pane > div {
|
#diff .ruleActions {
|
||||||
padding: 0 0 1em 0;
|
border: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: top;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
#diff .ruleActions h3 {
|
||||||
|
font-weight: normal;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
}
|
||||||
|
#ruleFilter {
|
||||||
|
direction: ltr;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
#diff > .pane > div > span {
|
#ruleFilter .fa {
|
||||||
float: left;
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
#revertButton:after,
|
||||||
|
#commitButton:before {
|
||||||
|
font-family: FontAwesome;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1;
|
||||||
|
vertical-align: baseline;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
body[dir="ltr"] #revertButton:after {
|
body[dir="ltr"] #revertButton:after {
|
||||||
content: '\2009\f061';
|
content: '\2009\f061';
|
||||||
font-family: FontAwesome;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1;
|
|
||||||
vertical-align: baseline;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
body[dir="rtl"] #revertButton:after {
|
body[dir="rtl"] #revertButton:after {
|
||||||
content: '\2009\f060';
|
content: '\2009\f060';
|
||||||
font-family: FontAwesome;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1;
|
|
||||||
vertical-align: baseline;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
body[dir="ltr"] #commitButton:before {
|
body[dir="ltr"] #commitButton:before {
|
||||||
content: '\f060\2009';
|
content: '\f060\2009';
|
||||||
font-family: FontAwesome;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1;
|
|
||||||
vertical-align: baseline;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
body[dir="rtl"] #commitButton:before {
|
body[dir="rtl"] #commitButton:before {
|
||||||
content: '\f061\2009';
|
content: '\f061\2009';
|
||||||
font-family: FontAwesome;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: 1;
|
|
||||||
vertical-align: baseline;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
#revertButton,
|
#revertButton,
|
||||||
#commitButton,
|
#commitButton,
|
||||||
#diff.edit #editEnterButton {
|
#editSaveButton,
|
||||||
|
#diff.editing #exportButton,
|
||||||
|
#diff.editing #importButton {
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
#editStopButton,
|
#diff.dirty:not(.editing) #revertButton,
|
||||||
#editCancelButton {
|
#diff.dirty:not(.editing) #commitButton,
|
||||||
display: none;
|
#diff.editing #editSaveButton {
|
||||||
}
|
|
||||||
#diff.dirty:not(.edit) #revertButton,
|
|
||||||
#diff.dirty:not(.edit) #commitButton {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
#diff.edit #editStopButton,
|
|
||||||
#diff.edit #editCancelButton {
|
.codeMirrorContainer {
|
||||||
display: initial;
|
box-sizing: border-box;
|
||||||
|
padding: 0 0 0.5em 0;
|
||||||
}
|
}
|
||||||
#diff.edit #importButton,
|
.CodeMirror-merge, .CodeMirror-merge-pane, .CodeMirror-merge .CodeMirror {
|
||||||
#diff.edit #exportButton {
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
#diff.editing .CodeMirror-merge-copy,
|
||||||
|
#diff.editing .CodeMirror-merge-copy-reverse {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#diff ul {
|
#diff.editing .CodeMirror-merge-left .CodeMirror {
|
||||||
border: 0;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
padding: 1em 0 0 0;
|
|
||||||
}
|
|
||||||
#diff ul,
|
|
||||||
#diff textarea {
|
|
||||||
font: 12px/1.8 monospace;
|
|
||||||
}
|
|
||||||
#diff.edit .right ul {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
#diff .left {
|
|
||||||
padding: 0 0 0 0;
|
|
||||||
}
|
|
||||||
#diff .right > ul {
|
|
||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
#diff li {
|
#diff.editing .CodeMirror-merge-editor .CodeMirror {
|
||||||
background-color: white;
|
background-color: #ffe;
|
||||||
direction: ltr;
|
|
||||||
padding: 0;
|
|
||||||
text-align: left;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
#diff li:nth-of-type(2n+0) {
|
body[dir="rtl"] .CodeMirror-merge-pane-rightmost {
|
||||||
background-color: #eee;
|
right: unset;
|
||||||
}
|
|
||||||
#diff .right li {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
#diff .right li:hover {
|
|
||||||
background-color: #ffc;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
#diff .right li.notLeft {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
#diff .right li.notLeft:hover {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
#diff .right li.notRight {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
#diff .right li.toRemove {
|
|
||||||
color: #000;
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
#diff textarea {
|
|
||||||
border: 0;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
direction: ltr;
|
|
||||||
height: 100%;
|
|
||||||
left: 0;
|
left: 0;
|
||||||
margin: 0;
|
}
|
||||||
overflow: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 1em 0 0 0;
|
|
||||||
position: absolute;
|
|
||||||
resize: none;
|
|
||||||
visibility: hidden;
|
|
||||||
white-space: pre;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
#diff.edit textarea {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
191
src/js/matrix.js
191
src/js/matrix.js
|
@ -141,6 +141,18 @@ var isIPAddress = function(hostname) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var punycodeIf = function(hn) {
|
||||||
|
return reNotASCII.test(hn) ? punycode.toASCII(hn) : hn;
|
||||||
|
};
|
||||||
|
|
||||||
|
var unpunycodeIf = function(hn) {
|
||||||
|
return hn.indexOf('xn--') !== -1 ? punycode.toUnicode(hn) : hn;
|
||||||
|
};
|
||||||
|
|
||||||
|
var reNotASCII = /[^\x20-\x7F]/;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
var toBroaderHostname = function(hostname) {
|
var toBroaderHostname = function(hostname) {
|
||||||
if ( hostname === '*' ) { return ''; }
|
if ( hostname === '*' ) { return ''; }
|
||||||
if ( isIPAddress(hostname) ) {
|
if ( isIPAddress(hostname) ) {
|
||||||
|
@ -591,6 +603,51 @@ Matrix.prototype.extractAllSourceHostnames = (function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uMatrix/issues/759
|
||||||
|
// Backward compatibility: 'plugin' => 'media'
|
||||||
|
|
||||||
|
Matrix.prototype.partsFromLine = function(line) {
|
||||||
|
let fields = line.split(/\s+/);
|
||||||
|
if ( fields.length < 3 ) { return; }
|
||||||
|
|
||||||
|
// Switches
|
||||||
|
if ( this.reSwitchRule.test(fields[0]) ) {
|
||||||
|
fields[0] = fields[0].slice(0, -1);
|
||||||
|
if ( switchBitOffsets.has(fields[0]) === false ) { return; }
|
||||||
|
fields[1] = punycodeIf(fields[1]);
|
||||||
|
fields[2] = nameToSwitchStateMap.get(fields[2]);
|
||||||
|
if ( fields[2] === undefined ) { return; }
|
||||||
|
fields.length = 3;
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rules
|
||||||
|
if ( fields.length < 4 ) { return; }
|
||||||
|
fields[0] = punycodeIf(fields[0]);
|
||||||
|
fields[1] = punycodeIf(fields[1]);
|
||||||
|
if ( fields[2] === 'plugin' ) { fields[2] = 'media'; }
|
||||||
|
if ( typeBitOffsets.get(fields[2]) === undefined ) { return; }
|
||||||
|
if ( nameToStateMap.hasOwnProperty(fields[3]) === false ) { return; }
|
||||||
|
fields[3] = nameToStateMap[fields[3]];
|
||||||
|
fields.length = 4;
|
||||||
|
return fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix.prototype.reSwitchRule = /^[0-9a-z-]+:$/;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
Matrix.prototype.fromArray = function(lines, append) {
|
||||||
|
let matrix = append === true ? this : new Matrix();
|
||||||
|
for ( let line of lines ) {
|
||||||
|
matrix.addFromLine(line);
|
||||||
|
}
|
||||||
|
if ( append !== true ) {
|
||||||
|
this.assign(matrix);
|
||||||
|
}
|
||||||
|
this.modifiedTime = Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
Matrix.prototype.toArray = function() {
|
Matrix.prototype.toArray = function() {
|
||||||
let out = [];
|
let out = [];
|
||||||
for ( let rule of this.rules.keys() ) {
|
for ( let rule of this.rules.keys() ) {
|
||||||
|
@ -600,8 +657,8 @@ Matrix.prototype.toArray = function() {
|
||||||
let val = this.evaluateCell(srcHostname, desHostname, type);
|
let val = this.evaluateCell(srcHostname, desHostname, type);
|
||||||
if ( val === 0 ) { continue; }
|
if ( val === 0 ) { continue; }
|
||||||
out.push(
|
out.push(
|
||||||
punycode.toUnicode(srcHostname) + ' ' +
|
unpunycodeIf(srcHostname) + ' ' +
|
||||||
punycode.toUnicode(desHostname) + ' ' +
|
unpunycodeIf(desHostname) + ' ' +
|
||||||
type + ' ' +
|
type + ' ' +
|
||||||
stateToNameMap.get(val)
|
stateToNameMap.get(val)
|
||||||
);
|
);
|
||||||
|
@ -623,25 +680,6 @@ Matrix.prototype.toArray = function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
Matrix.prototype.fromArray = function(lines, append) {
|
|
||||||
let matrix = append === true ? this : new Matrix();
|
|
||||||
for ( let line of lines ) {
|
|
||||||
matrix.fromLine(line);
|
|
||||||
}
|
|
||||||
if ( append !== true ) {
|
|
||||||
this.assign(matrix);
|
|
||||||
}
|
|
||||||
this.modifiedTime = Date.now();
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
Matrix.prototype.toString = function() {
|
|
||||||
return this.toArray().join('\n');
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
Matrix.prototype.fromString = function(text, append) {
|
Matrix.prototype.fromString = function(text, append) {
|
||||||
let matrix = append === true ? this : new Matrix();
|
let matrix = append === true ? this : new Matrix();
|
||||||
let textEnd = text.length;
|
let textEnd = text.length;
|
||||||
|
@ -657,14 +695,12 @@ Matrix.prototype.fromString = function(text, append) {
|
||||||
}
|
}
|
||||||
let line = text.slice(lineBeg, lineEnd).trim();
|
let line = text.slice(lineBeg, lineEnd).trim();
|
||||||
lineBeg = lineEnd + 1;
|
lineBeg = lineEnd + 1;
|
||||||
|
|
||||||
let pos = line.indexOf('# ');
|
let pos = line.indexOf('# ');
|
||||||
if ( pos !== -1 ) {
|
if ( pos !== -1 ) {
|
||||||
line = line.slice(0, pos).trim();
|
line = line.slice(0, pos).trim();
|
||||||
}
|
}
|
||||||
if ( line === '' ) { continue; }
|
if ( line === '' ) { continue; }
|
||||||
|
matrix.addFromLine(line);
|
||||||
matrix.fromLine(line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( append !== true ) {
|
if ( append !== true ) {
|
||||||
|
@ -674,77 +710,38 @@ Matrix.prototype.fromString = function(text, append) {
|
||||||
this.modifiedTime = Date.now();
|
this.modifiedTime = Date.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
Matrix.prototype.toString = function() {
|
||||||
|
return this.toArray().join('\n');
|
||||||
// https://github.com/gorhill/uMatrix/issues/759
|
|
||||||
// Backward compatibility: 'plugin' => 'media'
|
|
||||||
|
|
||||||
Matrix.prototype.fromLine = function(line) {
|
|
||||||
let fields = line.split(/\s+/);
|
|
||||||
if ( fields.length < 3 ) { return false; }
|
|
||||||
let field0 = fields[0];
|
|
||||||
|
|
||||||
// Switches
|
|
||||||
if ( this.reSwitchRule.test(field0) ) {
|
|
||||||
let switchName = field0.slice(0, -1);
|
|
||||||
let srcHostname = punycode.toASCII(fields[1]);
|
|
||||||
let state = fields[2];
|
|
||||||
if (
|
|
||||||
switchBitOffsets.has(switchName) === false ||
|
|
||||||
nameToSwitchStateMap.has(state) === false
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.setSwitch(
|
|
||||||
switchName,
|
|
||||||
srcHostname,
|
|
||||||
nameToSwitchStateMap.get(state)
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rules
|
|
||||||
if ( fields.length < 4 ) { return false; }
|
|
||||||
|
|
||||||
let srcHostname = punycode.toASCII(fields[0]);
|
|
||||||
let desHostname = punycode.toASCII(fields[1]);
|
|
||||||
let type = fields[2];
|
|
||||||
|
|
||||||
if ( type !== undefined ) {
|
|
||||||
if ( type === 'plugin' ) {
|
|
||||||
type = 'media';
|
|
||||||
} else if ( typeBitOffsets.has(type) === false ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
type = '*';
|
|
||||||
}
|
|
||||||
|
|
||||||
let state = fields[3];
|
|
||||||
|
|
||||||
if ( state !== undefined ) {
|
|
||||||
if ( nameToStateMap.hasOwnProperty(state) === false ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
state = nameToStateMap[state];
|
|
||||||
} else {
|
|
||||||
state = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setCell(srcHostname, desHostname, type, state);
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Matrix.prototype.reSwitchRule = /^[0-9a-z-]+:$/;
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
Matrix.prototype.toSelfie = function() {
|
Matrix.prototype.addFromLine = function(line) {
|
||||||
return {
|
let fields = this.partsFromLine(line);
|
||||||
version: selfieVersion,
|
if ( fields !== undefined ) {
|
||||||
switches: Array.from(this.switches),
|
// Switches
|
||||||
rules: Array.from(this.rules)
|
if ( fields.length === 3 ) {
|
||||||
};
|
return this.setSwitch(fields[0], fields[1], fields[2]);
|
||||||
|
}
|
||||||
|
// Rules
|
||||||
|
if ( fields.length === 4 ) {
|
||||||
|
return this.setCell(fields[0], fields[1], fields[2], fields[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Matrix.prototype.removeFromLine = function(line) {
|
||||||
|
let fields = this.partsFromLine(line);
|
||||||
|
if ( fields !== undefined ) {
|
||||||
|
// Switches
|
||||||
|
if ( fields.length === 3 ) {
|
||||||
|
return this.setSwitch(fields[0], fields[1], 0);
|
||||||
|
}
|
||||||
|
// Rules
|
||||||
|
if ( fields.length === 4 ) {
|
||||||
|
return this.setCell(fields[0], fields[1], fields[2], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -757,6 +754,14 @@ Matrix.prototype.fromSelfie = function(selfie) {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Matrix.prototype.toSelfie = function() {
|
||||||
|
return {
|
||||||
|
version: selfieVersion,
|
||||||
|
switches: Array.from(this.switches),
|
||||||
|
rules: Array.from(this.rules)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
Matrix.prototype.diff = function(other, srcHostname, desHostnames) {
|
Matrix.prototype.diff = function(other, srcHostname, desHostnames) {
|
||||||
|
@ -790,9 +795,7 @@ Matrix.prototype.diff = function(other, srcHostname, desHostnames) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
srcHostname = toBroaderHostname(srcHostname);
|
srcHostname = toBroaderHostname(srcHostname);
|
||||||
if ( srcHostname === '' ) {
|
if ( srcHostname === '' ) { break; }
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|
|
@ -677,6 +677,22 @@ vAPI.messaging.listen('cloud-ui.js', onMessage);
|
||||||
|
|
||||||
var µm = µMatrix;
|
var µm = µMatrix;
|
||||||
|
|
||||||
|
var modifyRuleset = function(details) {
|
||||||
|
let ruleset = details.permanent ? µm.pMatrix : µm.tMatrix,
|
||||||
|
modifiedTime = ruleset.modifiedTime;
|
||||||
|
let toRemove = new Set(details.toRemove.trim().split(/\s*[\n\r]+\s*/));
|
||||||
|
for ( let rule of toRemove ) {
|
||||||
|
ruleset.removeFromLine(rule);
|
||||||
|
}
|
||||||
|
let toAdd = new Set(details.toAdd.trim().split(/\s*[\n\r]+\s*/));
|
||||||
|
for ( let rule of toAdd ) {
|
||||||
|
ruleset.addFromLine(rule);
|
||||||
|
}
|
||||||
|
if ( details.permanent && ruleset.modifiedTime !== modifiedTime ) {
|
||||||
|
µm.saveMatrix();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var onMessage = function(request, sender, callback) {
|
var onMessage = function(request, sender, callback) {
|
||||||
|
@ -691,24 +707,14 @@ var onMessage = function(request, sender, callback) {
|
||||||
var response;
|
var response;
|
||||||
|
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
case 'getUserRules':
|
case 'modifyRuleset':
|
||||||
response = {
|
modifyRuleset(request);
|
||||||
temporaryRules: µm.tMatrix.toString(),
|
/* falls through */
|
||||||
permanentRules: µm.pMatrix.toString()
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'setUserRules':
|
case 'getRuleset':
|
||||||
if ( typeof request.temporaryRules === 'string' ) {
|
|
||||||
µm.tMatrix.fromString(request.temporaryRules);
|
|
||||||
}
|
|
||||||
if ( typeof request.permanentRules === 'string' ) {
|
|
||||||
µm.pMatrix.fromString(request.permanentRules);
|
|
||||||
µm.saveMatrix();
|
|
||||||
}
|
|
||||||
response = {
|
response = {
|
||||||
temporaryRules: µm.tMatrix.toString(),
|
temporaryRules: µm.tMatrix.toArray(),
|
||||||
permanentRules: µm.pMatrix.toString()
|
permanentRules: µm.pMatrix.toArray()
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
|
|
||||||
uMatrix - a Chromium browser extension to block requests.
|
uMatrix - a Chromium browser extension to block requests.
|
||||||
Copyright (C) 2014-2017 Raymond Hill
|
Copyright (C) 2014-2018 Raymond Hill
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
Home: https://github.com/gorhill/uMatrix
|
Home: https://github.com/gorhill/uMatrix
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* global uDom */
|
/* global diff_match_patch, CodeMirror, uDom */
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -29,11 +29,174 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// Switches before, rules after
|
// Move to dashboard-common.js if needed
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
if ( document.querySelector('.vfill-available') === null ) { return; }
|
||||||
|
var timer;
|
||||||
|
var resize = function() {
|
||||||
|
timer = undefined;
|
||||||
|
let prect = document.body.getBoundingClientRect();
|
||||||
|
let child = document.querySelector('.vfill-available');
|
||||||
|
let crect = child.getBoundingClientRect();
|
||||||
|
let height = Math.max(prect.bottom - crect.top, 80);
|
||||||
|
child.style.height = height + 'px';
|
||||||
|
};
|
||||||
|
resize();
|
||||||
|
window.addEventListener('resize', function() {
|
||||||
|
if ( timer === undefined ) {
|
||||||
|
timer = vAPI.setTimeout(resize, 66);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var mergeView = new CodeMirror.MergeView(
|
||||||
|
document.querySelector('.codeMirrorMergeContainer'),
|
||||||
|
{
|
||||||
|
allowEditingOriginals: true,
|
||||||
|
connect: 'align',
|
||||||
|
inputStyle: 'contenteditable',
|
||||||
|
lineNumbers: true,
|
||||||
|
lineWrapping: false,
|
||||||
|
origLeft: '',
|
||||||
|
revertButtons: true,
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
);
|
||||||
|
mergeView.editor().setOption('styleActiveLine', true);
|
||||||
|
mergeView.editor().setOption('lineNumbers', false);
|
||||||
|
mergeView.leftOriginal().setOption('readOnly', 'nocursor');
|
||||||
|
|
||||||
|
var unfilteredRules = {
|
||||||
|
orig: { doc: mergeView.leftOriginal(), rules: [] },
|
||||||
|
edit: { doc: mergeView.editor(), rules: [] }
|
||||||
|
};
|
||||||
|
|
||||||
|
var cleanEditToken = 0;
|
||||||
|
var cleanEditText = '';
|
||||||
|
|
||||||
|
var differ;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Borrowed from...
|
||||||
|
// https://github.com/codemirror/CodeMirror/blob/3e1bb5fff682f8f6cbfaef0e56c61d62403d4798/addon/search/search.js#L22
|
||||||
|
// ... and modified as needed.
|
||||||
|
|
||||||
|
var updateOverlay = (function() {
|
||||||
|
var reFilter;
|
||||||
|
var mode = {
|
||||||
|
token: function(stream) {
|
||||||
|
if ( reFilter !== undefined ) {
|
||||||
|
reFilter.lastIndex = stream.pos;
|
||||||
|
var match = reFilter.exec(stream.string);
|
||||||
|
if ( match !== null ) {
|
||||||
|
if ( match.index === stream.pos ) {
|
||||||
|
stream.pos += match[0].length || 1;
|
||||||
|
return 'searching';
|
||||||
|
}
|
||||||
|
stream.pos = match.index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stream.skipToEnd();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return function(filter) {
|
||||||
|
reFilter = typeof filter === 'string' && filter !== '' ?
|
||||||
|
new RegExp(filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi') :
|
||||||
|
undefined;
|
||||||
|
return mode;
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Incrementally update text in a CodeMirror editor for best user experience:
|
||||||
|
// - Scroll position preserved
|
||||||
|
// - Minimum amount of text updated
|
||||||
|
|
||||||
|
var rulesToDoc = function(clearHistory) {
|
||||||
|
for ( var key in unfilteredRules ) {
|
||||||
|
if ( unfilteredRules.hasOwnProperty(key) === false ) { continue; }
|
||||||
|
var doc = unfilteredRules[key].doc;
|
||||||
|
var rules = filterRules(key);
|
||||||
|
if ( doc.lineCount() === 1 && doc.getValue() === '' || rules.length === 0 ) {
|
||||||
|
doc.setValue(rules.length !== 0 ? rules.join('\n') : '');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( differ === undefined ) { differ = new diff_match_patch(); }
|
||||||
|
var beforeText = doc.getValue();
|
||||||
|
var afterText = rules.join('\n');
|
||||||
|
var diffs = differ.diff_main(beforeText, afterText);
|
||||||
|
doc.startOperation();
|
||||||
|
var i = diffs.length,
|
||||||
|
iedit = beforeText.length;
|
||||||
|
while ( i-- ) {
|
||||||
|
var diff = diffs[i];
|
||||||
|
if ( diff[0] === 0 ) {
|
||||||
|
iedit -= diff[1].length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var end = doc.posFromIndex(iedit);
|
||||||
|
if ( diff[0] === 1 ) {
|
||||||
|
doc.replaceRange(diff[1], end, end);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* diff[0] === -1 */
|
||||||
|
iedit -= diff[1].length;
|
||||||
|
var beg = doc.posFromIndex(iedit);
|
||||||
|
doc.replaceRange('', beg, end);
|
||||||
|
}
|
||||||
|
doc.endOperation();
|
||||||
|
}
|
||||||
|
cleanEditText = mergeView.editor().getValue().trim();
|
||||||
|
cleanEditToken = mergeView.editor().changeGeneration();
|
||||||
|
if ( clearHistory ) {
|
||||||
|
mergeView.editor().clearHistory();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var filterRules = function(key) {
|
||||||
|
var rules = unfilteredRules[key].rules;
|
||||||
|
var filter = uDom('#ruleFilter input').val();
|
||||||
|
if ( filter !== '' ) {
|
||||||
|
rules = rules.slice();
|
||||||
|
var i = rules.length;
|
||||||
|
while ( i-- ) {
|
||||||
|
if ( rules[i].indexOf(filter) === -1 ) {
|
||||||
|
rules.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var renderRules = (function() {
|
||||||
|
var firstVisit = true;
|
||||||
|
|
||||||
|
return function(details) {
|
||||||
|
unfilteredRules.orig.rules = details.permanentRules.sort(directiveSort);
|
||||||
|
unfilteredRules.edit.rules = details.temporaryRules.sort(directiveSort);
|
||||||
|
rulesToDoc(firstVisit);
|
||||||
|
if ( firstVisit ) {
|
||||||
|
firstVisit = false;
|
||||||
|
mergeView.editor().execCommand('goNextDiff');
|
||||||
|
}
|
||||||
|
onTextChanged(true);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Switches before, rules after
|
||||||
var directiveSort = function(a, b) {
|
var directiveSort = function(a, b) {
|
||||||
var aIsSwitch = a.indexOf(':') !== -1;
|
var aIsSwitch = a.indexOf(': ') !== -1;
|
||||||
var bIsSwitch = b.indexOf(':') !== -1;
|
var bIsSwitch = b.indexOf(': ') !== -1;
|
||||||
if ( aIsSwitch === bIsSwitch ) {
|
if ( aIsSwitch === bIsSwitch ) {
|
||||||
return a.localeCompare(b);
|
return a.localeCompare(b);
|
||||||
}
|
}
|
||||||
|
@ -42,72 +205,49 @@ var directiveSort = function(a, b) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var processUserRules = function(response) {
|
var applyDiff = function(permanent, toAdd, toRemove) {
|
||||||
var rules, rule, i;
|
vAPI.messaging.send(
|
||||||
var allRules = {};
|
'user-rules.js',
|
||||||
var permanentRules = {};
|
{
|
||||||
var temporaryRules = {};
|
what: 'modifyRuleset',
|
||||||
var onLeft, onRight;
|
permanent: permanent,
|
||||||
|
toAdd: toAdd,
|
||||||
|
toRemove: toRemove
|
||||||
|
},
|
||||||
|
renderRules
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
rules = response.permanentRules.split(/\n+/);
|
/******************************************************************************/
|
||||||
i = rules.length;
|
|
||||||
while ( i-- ) {
|
// CodeMirror quirk: sometimes fromStart.ch and/or toStart.ch is undefined.
|
||||||
rule = rules[i].trim();
|
// When this happens, use 0.
|
||||||
if ( rule.length !== 0 ) {
|
|
||||||
permanentRules[rule] = allRules[rule] = true;
|
mergeView.options.revertChunk = function(
|
||||||
}
|
mv,
|
||||||
|
from, fromStart, fromEnd,
|
||||||
|
to, toStart, toEnd
|
||||||
|
) {
|
||||||
|
// https://github.com/gorhill/uBlock/issues/3611
|
||||||
|
if ( document.body.getAttribute('dir') === 'rtl' ) {
|
||||||
|
var tmp;
|
||||||
|
tmp = from; from = to; to = tmp;
|
||||||
|
tmp = fromStart; fromStart = toStart; toStart = tmp;
|
||||||
|
tmp = fromEnd; fromEnd = toEnd; toEnd = tmp;
|
||||||
}
|
}
|
||||||
rules = response.temporaryRules.split(/\n+/);
|
if ( typeof fromStart.ch !== 'number' ) { fromStart.ch = 0; }
|
||||||
i = rules.length;
|
if ( fromEnd.ch !== 0 ) { fromEnd.line += 1; }
|
||||||
while ( i-- ) {
|
var toAdd = from.getRange(
|
||||||
rule = rules[i].trim();
|
{ line: fromStart.line, ch: 0 },
|
||||||
if ( rule.length !== 0 ) {
|
{ line: fromEnd.line, ch: 0 }
|
||||||
temporaryRules[rule] = allRules[rule] = true;
|
);
|
||||||
}
|
if ( typeof toStart.ch !== 'number' ) { toStart.ch = 0; }
|
||||||
}
|
if ( toEnd.ch !== 0 ) { toEnd.line += 1; }
|
||||||
|
var toRemove = to.getRange(
|
||||||
var permanentList = document.createDocumentFragment(),
|
{ line: toStart.line, ch: 0 },
|
||||||
temporaryList = document.createDocumentFragment(),
|
{ line: toEnd.line, ch: 0 }
|
||||||
li;
|
);
|
||||||
|
applyDiff(from === mv.editor(), toAdd, toRemove);
|
||||||
rules = Object.keys(allRules).sort(directiveSort);
|
|
||||||
for ( i = 0; i < rules.length; i++ ) {
|
|
||||||
rule = rules[i];
|
|
||||||
onLeft = permanentRules.hasOwnProperty(rule);
|
|
||||||
onRight = temporaryRules.hasOwnProperty(rule);
|
|
||||||
if ( onLeft && onRight ) {
|
|
||||||
li = document.createElement('li');
|
|
||||||
li.textContent = rule;
|
|
||||||
permanentList.appendChild(li);
|
|
||||||
li = document.createElement('li');
|
|
||||||
li.textContent = rule;
|
|
||||||
temporaryList.appendChild(li);
|
|
||||||
} else if ( onLeft ) {
|
|
||||||
li = document.createElement('li');
|
|
||||||
li.textContent = rule;
|
|
||||||
permanentList.appendChild(li);
|
|
||||||
li = document.createElement('li');
|
|
||||||
li.textContent = rule;
|
|
||||||
li.className = 'notRight toRemove';
|
|
||||||
temporaryList.appendChild(li);
|
|
||||||
} else if ( onRight ) {
|
|
||||||
li = document.createElement('li');
|
|
||||||
li.textContent = '\xA0';
|
|
||||||
permanentList.appendChild(li);
|
|
||||||
li = document.createElement('li');
|
|
||||||
li.textContent = rule;
|
|
||||||
li.className = 'notLeft';
|
|
||||||
temporaryList.appendChild(li);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: build incrementally.
|
|
||||||
|
|
||||||
uDom('#diff > .left > ul > li').remove();
|
|
||||||
document.querySelector('#diff > .left > ul').appendChild(permanentList);
|
|
||||||
uDom('#diff > .right > ul > li').remove();
|
|
||||||
document.querySelector('#diff > .right > ul').appendChild(temporaryList);
|
|
||||||
uDom('#diff').toggleClass('dirty', response.temporaryRules !== response.permanentRules);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -117,9 +257,7 @@ var processUserRules = function(response) {
|
||||||
|
|
||||||
var fromRequestPolicy = function(content) {
|
var fromRequestPolicy = function(content) {
|
||||||
var matches = /\[origins-to-destinations\]([^\[]+)/.exec(content);
|
var matches = /\[origins-to-destinations\]([^\[]+)/.exec(content);
|
||||||
if ( matches === null || matches.length !== 2 ) {
|
if ( matches === null || matches.length !== 2 ) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
return matches[1].trim()
|
return matches[1].trim()
|
||||||
.replace(/\|/g, ' ')
|
.replace(/\|/g, ' ')
|
||||||
.replace(/\n/g, ' * allow\n');
|
.replace(/\n/g, ' * allow\n');
|
||||||
|
@ -153,12 +291,8 @@ var fromNoScript = function(content) {
|
||||||
var directive, matches;
|
var directive, matches;
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
directive = directives[i].trim();
|
directive = directives[i].trim();
|
||||||
if ( directive === '' ) {
|
if ( directive === '' ) { continue; }
|
||||||
continue;
|
if ( reBad.test(directive) ) { continue; }
|
||||||
}
|
|
||||||
if ( reBad.test(directive) ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
matches = reURL.exec(directive);
|
matches = reURL.exec(directive);
|
||||||
if ( matches !== null ) {
|
if ( matches !== null ) {
|
||||||
directive = matches[1];
|
directive = matches[1];
|
||||||
|
@ -185,16 +319,10 @@ var handleImportFilePicker = function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( this.result === '' ) { return; }
|
if ( this.result === '' ) { return; }
|
||||||
var request = {
|
applyDiff(false, result, '');
|
||||||
'what': 'setUserRules',
|
|
||||||
'temporaryRules': rulesFromHTML('#diff .right li') + '\n' + result
|
|
||||||
};
|
|
||||||
vAPI.messaging.send('user-rules.js', request, processUserRules);
|
|
||||||
};
|
};
|
||||||
var file = this.files[0];
|
var file = this.files[0];
|
||||||
if ( file === undefined || file.name === '' ) {
|
if ( file === undefined || file.name === '' ) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( file.type.indexOf('text') !== 0 && file.type !== 'application/json') {
|
if ( file.type.indexOf('text') !== 0 && file.type !== 'application/json') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -218,121 +346,174 @@ var startImportFilePicker = function() {
|
||||||
|
|
||||||
function exportUserRulesToFile() {
|
function exportUserRulesToFile() {
|
||||||
vAPI.download({
|
vAPI.download({
|
||||||
'url': 'data:text/plain,' + encodeURIComponent(rulesFromHTML('#diff .left li') + '\n'),
|
url: 'data:text/plain,' + encodeURIComponent(
|
||||||
'filename': uDom('[data-i18n="userRulesDefaultFileName"]').text()
|
mergeView.leftOriginal().getValue().trim() + '\n'
|
||||||
|
),
|
||||||
|
filename: uDom('[data-i18n="userRulesDefaultFileName"]').text()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var rulesFromHTML = function(selector) {
|
var onFilterChanged = (function() {
|
||||||
var rules = [];
|
var timer,
|
||||||
var lis = uDom(selector);
|
overlay = null,
|
||||||
var li;
|
last = '';
|
||||||
for ( var i = 0; i < lis.length; i++ ) {
|
|
||||||
li = lis.at(i);
|
var process = function() {
|
||||||
if ( li.hasClassName('toRemove') ) {
|
timer = undefined;
|
||||||
rules.push('');
|
if ( mergeView.editor().isClean(cleanEditToken) === false ) { return; }
|
||||||
|
var filter = uDom('#ruleFilter input').val();
|
||||||
|
if ( filter === last ) { return; }
|
||||||
|
last = filter;
|
||||||
|
if ( overlay !== null ) {
|
||||||
|
mergeView.leftOriginal().removeOverlay(overlay);
|
||||||
|
mergeView.editor().removeOverlay(overlay);
|
||||||
|
overlay = null;
|
||||||
|
}
|
||||||
|
if ( filter !== '' ) {
|
||||||
|
overlay = updateOverlay(filter);
|
||||||
|
mergeView.leftOriginal().addOverlay(overlay);
|
||||||
|
mergeView.editor().addOverlay(overlay);
|
||||||
|
}
|
||||||
|
rulesToDoc(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return function() {
|
||||||
|
if ( timer !== undefined ) { clearTimeout(timer); }
|
||||||
|
timer = vAPI.setTimeout(process, 773);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var onTextChanged = (function() {
|
||||||
|
var timer;
|
||||||
|
|
||||||
|
var process = function(now) {
|
||||||
|
timer = undefined;
|
||||||
|
var isClean = mergeView.editor().isClean(cleanEditToken);
|
||||||
|
var diff = document.getElementById('diff');
|
||||||
|
if (
|
||||||
|
now &&
|
||||||
|
isClean === false &&
|
||||||
|
mergeView.editor().getValue().trim() === cleanEditText
|
||||||
|
) {
|
||||||
|
cleanEditToken = mergeView.editor().changeGeneration();
|
||||||
|
isClean = true;
|
||||||
|
}
|
||||||
|
diff.classList.toggle('editing', isClean === false);
|
||||||
|
diff.classList.toggle('dirty', mergeView.leftChunks().length !== 0);
|
||||||
|
var input = document.querySelector('#ruleFilter input');
|
||||||
|
if ( isClean ) {
|
||||||
|
input.removeAttribute('disabled');
|
||||||
|
CodeMirror.commands.save = undefined;
|
||||||
} else {
|
} else {
|
||||||
rules.push(li.text());
|
input.setAttribute('disabled', '');
|
||||||
|
CodeMirror.commands.save = editSaveHandler;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return function(now) {
|
||||||
|
if ( timer !== undefined ) { clearTimeout(timer); }
|
||||||
|
timer = now ? process(now) : vAPI.setTimeout(process, 57);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var revertAllHandler = function() {
|
||||||
|
var toAdd = [], toRemove = [];
|
||||||
|
var left = mergeView.leftOriginal(),
|
||||||
|
edit = mergeView.editor();
|
||||||
|
for ( var chunk of mergeView.leftChunks() ) {
|
||||||
|
var addedLines = left.getRange(
|
||||||
|
{ line: chunk.origFrom, ch: 0 },
|
||||||
|
{ line: chunk.origTo, ch: 0 }
|
||||||
|
);
|
||||||
|
var removedLines = edit.getRange(
|
||||||
|
{ line: chunk.editFrom, ch: 0 },
|
||||||
|
{ line: chunk.editTo, ch: 0 }
|
||||||
|
);
|
||||||
|
toAdd.push(addedLines.trim());
|
||||||
|
toRemove.push(removedLines.trim());
|
||||||
|
}
|
||||||
|
applyDiff(false, toAdd.join('\n'), toRemove.join('\n'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var commitAllHandler = function() {
|
||||||
|
var toAdd = [], toRemove = [];
|
||||||
|
var left = mergeView.leftOriginal(),
|
||||||
|
edit = mergeView.editor();
|
||||||
|
for ( var chunk of mergeView.leftChunks() ) {
|
||||||
|
var addedLines = edit.getRange(
|
||||||
|
{ line: chunk.editFrom, ch: 0 },
|
||||||
|
{ line: chunk.editTo, ch: 0 }
|
||||||
|
);
|
||||||
|
var removedLines = left.getRange(
|
||||||
|
{ line: chunk.origFrom, ch: 0 },
|
||||||
|
{ line: chunk.origTo, ch: 0 }
|
||||||
|
);
|
||||||
|
toAdd.push(addedLines.trim());
|
||||||
|
toRemove.push(removedLines.trim());
|
||||||
|
}
|
||||||
|
applyDiff(true, toAdd.join('\n'), toRemove.join('\n'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var editSaveHandler = function() {
|
||||||
|
var editor = mergeView.editor();
|
||||||
|
var editText = editor.getValue().trim();
|
||||||
|
if ( editText === cleanEditText ) {
|
||||||
|
onTextChanged(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( differ === undefined ) { differ = new diff_match_patch(); }
|
||||||
|
var toAdd = [], toRemove = [];
|
||||||
|
var diffs = differ.diff_main(cleanEditText, editText);
|
||||||
|
for ( var diff of diffs ) {
|
||||||
|
if ( diff[0] === 1 ) {
|
||||||
|
toAdd.push(diff[1]);
|
||||||
|
} else if ( diff[0] === -1 ) {
|
||||||
|
toRemove.push(diff[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rules.join('\n');
|
applyDiff(false, toAdd.join(''), toRemove.join(''));
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var revertHandler = function() {
|
|
||||||
var request = {
|
|
||||||
'what': 'setUserRules',
|
|
||||||
'temporaryRules': rulesFromHTML('#diff .left li')
|
|
||||||
};
|
|
||||||
vAPI.messaging.send('user-rules.js', request, processUserRules);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var commitHandler = function() {
|
|
||||||
var request = {
|
|
||||||
'what': 'setUserRules',
|
|
||||||
'permanentRules': rulesFromHTML('#diff .right li')
|
|
||||||
};
|
|
||||||
vAPI.messaging.send('user-rules.js', request, processUserRules);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var editStartHandler = function() {
|
|
||||||
uDom('#diff .right textarea').val(rulesFromHTML('#diff .right li'));
|
|
||||||
var parent = uDom(this).ancestors('#diff');
|
|
||||||
parent.toggleClass('edit', true);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var editStopHandler = function() {
|
|
||||||
var parent = uDom(this).ancestors('#diff');
|
|
||||||
parent.toggleClass('edit', false);
|
|
||||||
var request = {
|
|
||||||
'what': 'setUserRules',
|
|
||||||
'temporaryRules': uDom('#diff .right textarea').val()
|
|
||||||
};
|
|
||||||
vAPI.messaging.send('user-rules.js', request, processUserRules);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var editCancelHandler = function() {
|
|
||||||
var parent = uDom(this).ancestors('#diff');
|
|
||||||
parent.toggleClass('edit', false);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var temporaryRulesToggler = function() {
|
|
||||||
var li = uDom(this);
|
|
||||||
li.toggleClass('toRemove');
|
|
||||||
var request = {
|
|
||||||
'what': 'setUserRules',
|
|
||||||
'temporaryRules': rulesFromHTML('#diff .right li')
|
|
||||||
};
|
|
||||||
vAPI.messaging.send('user-rules.js', request, processUserRules);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
self.cloud.onPush = function() {
|
self.cloud.onPush = function() {
|
||||||
return rulesFromHTML('#diff .left li');
|
return mergeView.leftOriginal().getValue().trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
self.cloud.onPull = function(data, append) {
|
self.cloud.onPull = function(data, append) {
|
||||||
if ( typeof data !== 'string' ) { return; }
|
if ( typeof data !== 'string' ) { return; }
|
||||||
if ( append ) {
|
applyDiff(
|
||||||
data = rulesFromHTML('#diff .right li') + '\n' + data;
|
false,
|
||||||
}
|
data,
|
||||||
var request = {
|
append ? '' : mergeView.editor().getValue().trim()
|
||||||
'what': 'setUserRules',
|
);
|
||||||
'temporaryRules': data
|
|
||||||
};
|
|
||||||
vAPI.messaging.send('user-rules.js', request, processUserRules);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
uDom.onLoad(function() {
|
// Handle user interaction
|
||||||
// Handle user interaction
|
uDom('#exportButton').on('click', exportUserRulesToFile);
|
||||||
uDom('#importButton').on('click', startImportFilePicker);
|
uDom('#revertButton').on('click', revertAllHandler);
|
||||||
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
uDom('#commitButton').on('click', commitAllHandler);
|
||||||
uDom('#exportButton').on('click', exportUserRulesToFile);
|
uDom('#importButton').on('click', startImportFilePicker);
|
||||||
uDom('#revertButton').on('click', revertHandler);
|
uDom('#importFilePicker').on('change', handleImportFilePicker);
|
||||||
uDom('#commitButton').on('click', commitHandler);
|
uDom('#editSaveButton').on('click', editSaveHandler);
|
||||||
uDom('#editEnterButton').on('click', editStartHandler);
|
uDom('#ruleFilter input').on('input', onFilterChanged);
|
||||||
uDom('#editStopButton').on('click', editStopHandler);
|
|
||||||
uDom('#editCancelButton').on('click', editCancelHandler);
|
|
||||||
uDom('#diff > .right > ul').on('click', 'li', temporaryRulesToggler);
|
|
||||||
|
|
||||||
vAPI.messaging.send('user-rules.js', { what: 'getUserRules' }, processUserRules);
|
// https://groups.google.com/forum/#!topic/codemirror/UQkTrt078Vs
|
||||||
});
|
mergeView.editor().on('updateDiff', function() { onTextChanged(); });
|
||||||
|
|
||||||
|
vAPI.messaging.send('user-rules.js', { what: 'getRuleset' }, renderRules);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
21
src/lib/codemirror/LICENSE
Normal file
21
src/lib/codemirror/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
35
src/lib/codemirror/README.md
Normal file
35
src/lib/codemirror/README.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# CodeMirror
|
||||||
|
[![Build Status](https://travis-ci.org/codemirror/CodeMirror.svg)](https://travis-ci.org/codemirror/CodeMirror)
|
||||||
|
[![NPM version](https://img.shields.io/npm/v/codemirror.svg)](https://www.npmjs.org/package/codemirror)
|
||||||
|
[![Join the chat at https://gitter.im/codemirror/CodeMirror](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/codemirror/CodeMirror)
|
||||||
|
[Funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png?again)](https://marijnhaverbeke.nl/fund/)
|
||||||
|
|
||||||
|
CodeMirror is a versatile text editor implemented in JavaScript for
|
||||||
|
the browser. It is specialized for editing code, and comes with over
|
||||||
|
100 language modes and various addons that implement more advanced
|
||||||
|
editing functionality. Every language comes with fully-featured code
|
||||||
|
and syntax highlighting to help with reading and editing complex code.
|
||||||
|
|
||||||
|
A rich programming API and a CSS theming system are available for
|
||||||
|
customizing CodeMirror to fit your application, and extending it with
|
||||||
|
new functionality.
|
||||||
|
|
||||||
|
You can find more information (and the
|
||||||
|
[manual](http://codemirror.net/doc/manual.html)) on the [project
|
||||||
|
page](http://codemirror.net). For questions and discussion, use the
|
||||||
|
[discussion forum](https://discuss.codemirror.net/).
|
||||||
|
|
||||||
|
See
|
||||||
|
[CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md)
|
||||||
|
for contributing guidelines.
|
||||||
|
|
||||||
|
The CodeMirror community aims to be welcoming to everybody. We use the
|
||||||
|
[Contributor Covenant
|
||||||
|
(1.1)](http://contributor-covenant.org/version/1/1/0/) as our code of
|
||||||
|
conduct.
|
||||||
|
|
||||||
|
### Quickstart
|
||||||
|
|
||||||
|
To build the project, make sure you have Node.js installed (at least version 6)
|
||||||
|
and then `npm install`. To run, just open `index.html` in your
|
||||||
|
browser (you don't need to run a webserver). Run the tests with `npm test`.
|
119
src/lib/codemirror/addon/merge/merge.css
vendored
Normal file
119
src/lib/codemirror/addon/merge/merge.css
vendored
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
.CodeMirror-merge {
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge, .CodeMirror-merge .CodeMirror {
|
||||||
|
height: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; }
|
||||||
|
.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; }
|
||||||
|
.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }
|
||||||
|
.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }
|
||||||
|
|
||||||
|
.CodeMirror-merge-pane {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: normal;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-pane-rightmost {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-gap {
|
||||||
|
z-index: 2;
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
position: relative;
|
||||||
|
background: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-scrolllock-wrap {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0; left: 50%;
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-scrolllock {
|
||||||
|
position: relative;
|
||||||
|
left: -50%;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #555;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-scrolllock:after {
|
||||||
|
content: "\21db\00a0\00a0\21da";
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-scrolllock.CodeMirror-merge-scrolllock-enabled:after {
|
||||||
|
content: "\21db\21da";
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {
|
||||||
|
position: absolute;
|
||||||
|
left: 0; top: 0;
|
||||||
|
right: 0; bottom: 0;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-copy {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #44c;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-copy-reverse {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #44c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
|
||||||
|
.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
|
||||||
|
|
||||||
|
.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
|
||||||
|
background-position: bottom left;
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {
|
||||||
|
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
|
||||||
|
background-position: bottom left;
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-merge-r-chunk { background: #ffffe0; }
|
||||||
|
.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; }
|
||||||
|
.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; }
|
||||||
|
.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; }
|
||||||
|
|
||||||
|
.CodeMirror-merge-l-chunk { background: #eef; }
|
||||||
|
.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }
|
||||||
|
.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }
|
||||||
|
.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }
|
||||||
|
|
||||||
|
.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
|
||||||
|
.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
|
||||||
|
.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
|
||||||
|
|
||||||
|
.CodeMirror-merge-collapsed-widget:before {
|
||||||
|
content: "(...)";
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-collapsed-widget {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #88b;
|
||||||
|
background: #eef;
|
||||||
|
border: 1px solid #ddf;
|
||||||
|
font-size: 90%;
|
||||||
|
padding: 0 3px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }
|
1002
src/lib/codemirror/addon/merge/merge.js
vendored
Normal file
1002
src/lib/codemirror/addon/merge/merge.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
72
src/lib/codemirror/addon/selection/active-line.js
vendored
Normal file
72
src/lib/codemirror/addon/selection/active-line.js
vendored
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||||
|
// Distributed under an MIT license: http://codemirror.net/LICENSE
|
||||||
|
|
||||||
|
(function(mod) {
|
||||||
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
|
mod(require("../../lib/codemirror"));
|
||||||
|
else if (typeof define == "function" && define.amd) // AMD
|
||||||
|
define(["../../lib/codemirror"], mod);
|
||||||
|
else // Plain browser env
|
||||||
|
mod(CodeMirror);
|
||||||
|
})(function(CodeMirror) {
|
||||||
|
"use strict";
|
||||||
|
var WRAP_CLASS = "CodeMirror-activeline";
|
||||||
|
var BACK_CLASS = "CodeMirror-activeline-background";
|
||||||
|
var GUTT_CLASS = "CodeMirror-activeline-gutter";
|
||||||
|
|
||||||
|
CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
|
||||||
|
var prev = old == CodeMirror.Init ? false : old;
|
||||||
|
if (val == prev) return
|
||||||
|
if (prev) {
|
||||||
|
cm.off("beforeSelectionChange", selectionChange);
|
||||||
|
clearActiveLines(cm);
|
||||||
|
delete cm.state.activeLines;
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
cm.state.activeLines = [];
|
||||||
|
updateActiveLines(cm, cm.listSelections());
|
||||||
|
cm.on("beforeSelectionChange", selectionChange);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function clearActiveLines(cm) {
|
||||||
|
for (var i = 0; i < cm.state.activeLines.length; i++) {
|
||||||
|
cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS);
|
||||||
|
cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS);
|
||||||
|
cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sameArray(a, b) {
|
||||||
|
if (a.length != b.length) return false;
|
||||||
|
for (var i = 0; i < a.length; i++)
|
||||||
|
if (a[i] != b[i]) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateActiveLines(cm, ranges) {
|
||||||
|
var active = [];
|
||||||
|
for (var i = 0; i < ranges.length; i++) {
|
||||||
|
var range = ranges[i];
|
||||||
|
var option = cm.getOption("styleActiveLine");
|
||||||
|
if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())
|
||||||
|
continue
|
||||||
|
var line = cm.getLineHandleVisualStart(range.head.line);
|
||||||
|
if (active[active.length - 1] != line) active.push(line);
|
||||||
|
}
|
||||||
|
if (sameArray(cm.state.activeLines, active)) return;
|
||||||
|
cm.operation(function() {
|
||||||
|
clearActiveLines(cm);
|
||||||
|
for (var i = 0; i < active.length; i++) {
|
||||||
|
cm.addLineClass(active[i], "wrap", WRAP_CLASS);
|
||||||
|
cm.addLineClass(active[i], "background", BACK_CLASS);
|
||||||
|
cm.addLineClass(active[i], "gutter", GUTT_CLASS);
|
||||||
|
}
|
||||||
|
cm.state.activeLines = active;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectionChange(cm, sel) {
|
||||||
|
updateActiveLines(cm, sel.ranges);
|
||||||
|
}
|
||||||
|
});
|
346
src/lib/codemirror/lib/codemirror.css
vendored
Normal file
346
src/lib/codemirror/lib/codemirror.css
vendored
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
/* BASICS */
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
/* Set height, width, borders, and global font properties here */
|
||||||
|
font-family: monospace;
|
||||||
|
height: 300px;
|
||||||
|
color: black;
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PADDING */
|
||||||
|
|
||||||
|
.CodeMirror-lines {
|
||||||
|
padding: 4px 0; /* Vertical padding around content */
|
||||||
|
}
|
||||||
|
.CodeMirror pre {
|
||||||
|
padding: 0 4px; /* Horizontal padding of content */
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||||
|
background-color: white; /* The little square between H and V scrollbars */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GUTTER */
|
||||||
|
|
||||||
|
.CodeMirror-gutters {
|
||||||
|
border-right: 1px solid #ddd;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.CodeMirror-linenumbers {}
|
||||||
|
.CodeMirror-linenumber {
|
||||||
|
padding: 0 3px 0 5px;
|
||||||
|
min-width: 20px;
|
||||||
|
text-align: right;
|
||||||
|
color: #999;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-guttermarker { color: black; }
|
||||||
|
.CodeMirror-guttermarker-subtle { color: #999; }
|
||||||
|
|
||||||
|
/* CURSOR */
|
||||||
|
|
||||||
|
.CodeMirror-cursor {
|
||||||
|
border-left: 1px solid black;
|
||||||
|
border-right: none;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
/* Shown when moving in bi-directional text */
|
||||||
|
.CodeMirror div.CodeMirror-secondarycursor {
|
||||||
|
border-left: 1px solid silver;
|
||||||
|
}
|
||||||
|
.cm-fat-cursor .CodeMirror-cursor {
|
||||||
|
width: auto;
|
||||||
|
border: 0 !important;
|
||||||
|
background: #7e7;
|
||||||
|
}
|
||||||
|
.cm-fat-cursor div.CodeMirror-cursors {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.cm-fat-cursor-mark {
|
||||||
|
background-color: rgba(20, 255, 20, 0.5);
|
||||||
|
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||||
|
-moz-animation: blink 1.06s steps(1) infinite;
|
||||||
|
animation: blink 1.06s steps(1) infinite;
|
||||||
|
}
|
||||||
|
.cm-animate-fat-cursor {
|
||||||
|
width: auto;
|
||||||
|
border: 0;
|
||||||
|
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||||
|
-moz-animation: blink 1.06s steps(1) infinite;
|
||||||
|
animation: blink 1.06s steps(1) infinite;
|
||||||
|
background-color: #7e7;
|
||||||
|
}
|
||||||
|
@-moz-keyframes blink {
|
||||||
|
0% {}
|
||||||
|
50% { background-color: transparent; }
|
||||||
|
100% {}
|
||||||
|
}
|
||||||
|
@-webkit-keyframes blink {
|
||||||
|
0% {}
|
||||||
|
50% { background-color: transparent; }
|
||||||
|
100% {}
|
||||||
|
}
|
||||||
|
@keyframes blink {
|
||||||
|
0% {}
|
||||||
|
50% { background-color: transparent; }
|
||||||
|
100% {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can style cursor different in overwrite (non-insert) mode */
|
||||||
|
.CodeMirror-overwrite .CodeMirror-cursor {}
|
||||||
|
|
||||||
|
.cm-tab { display: inline-block; text-decoration: inherit; }
|
||||||
|
|
||||||
|
.CodeMirror-rulers {
|
||||||
|
position: absolute;
|
||||||
|
left: 0; right: 0; top: -50px; bottom: -20px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.CodeMirror-ruler {
|
||||||
|
border-left: 1px solid #ccc;
|
||||||
|
top: 0; bottom: 0;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DEFAULT THEME */
|
||||||
|
|
||||||
|
.cm-s-default .cm-header {color: blue;}
|
||||||
|
.cm-s-default .cm-quote {color: #090;}
|
||||||
|
.cm-negative {color: #d44;}
|
||||||
|
.cm-positive {color: #292;}
|
||||||
|
.cm-header, .cm-strong {font-weight: bold;}
|
||||||
|
.cm-em {font-style: italic;}
|
||||||
|
.cm-link {text-decoration: underline;}
|
||||||
|
.cm-strikethrough {text-decoration: line-through;}
|
||||||
|
|
||||||
|
.cm-s-default .cm-keyword {color: #708;}
|
||||||
|
.cm-s-default .cm-atom {color: #219;}
|
||||||
|
.cm-s-default .cm-number {color: #164;}
|
||||||
|
.cm-s-default .cm-def {color: #00f;}
|
||||||
|
.cm-s-default .cm-variable,
|
||||||
|
.cm-s-default .cm-punctuation,
|
||||||
|
.cm-s-default .cm-property,
|
||||||
|
.cm-s-default .cm-operator {}
|
||||||
|
.cm-s-default .cm-variable-2 {color: #05a;}
|
||||||
|
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
|
||||||
|
.cm-s-default .cm-comment {color: #a50;}
|
||||||
|
.cm-s-default .cm-string {color: #a11;}
|
||||||
|
.cm-s-default .cm-string-2 {color: #f50;}
|
||||||
|
.cm-s-default .cm-meta {color: #555;}
|
||||||
|
.cm-s-default .cm-qualifier {color: #555;}
|
||||||
|
.cm-s-default .cm-builtin {color: #30a;}
|
||||||
|
.cm-s-default .cm-bracket {color: #997;}
|
||||||
|
.cm-s-default .cm-tag {color: #170;}
|
||||||
|
.cm-s-default .cm-attribute {color: #00c;}
|
||||||
|
.cm-s-default .cm-hr {color: #999;}
|
||||||
|
.cm-s-default .cm-link {color: #00c;}
|
||||||
|
|
||||||
|
.cm-s-default .cm-error {color: #f00;}
|
||||||
|
.cm-invalidchar {color: #f00;}
|
||||||
|
|
||||||
|
.CodeMirror-composing { border-bottom: 2px solid; }
|
||||||
|
|
||||||
|
/* Default styles for common addons */
|
||||||
|
|
||||||
|
div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
|
||||||
|
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
|
||||||
|
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
|
||||||
|
.CodeMirror-activeline-background {background: #e8f2ff;}
|
||||||
|
|
||||||
|
/* STOP */
|
||||||
|
|
||||||
|
/* The rest of this file contains styles related to the mechanics of
|
||||||
|
the editor. You probably shouldn't touch them. */
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-scroll {
|
||||||
|
overflow: scroll !important; /* Things will break if this is overridden */
|
||||||
|
/* 30px is the magic margin used to hide the element's real scrollbars */
|
||||||
|
/* See overflow: hidden in .CodeMirror */
|
||||||
|
margin-bottom: -30px; margin-right: -30px;
|
||||||
|
padding-bottom: 30px;
|
||||||
|
height: 100%;
|
||||||
|
outline: none; /* Prevent dragging from highlighting the element */
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.CodeMirror-sizer {
|
||||||
|
position: relative;
|
||||||
|
border-right: 30px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
||||||
|
before actual scrolling happens, thus preventing shaking and
|
||||||
|
flickering artifacts. */
|
||||||
|
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 6;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.CodeMirror-vscrollbar {
|
||||||
|
right: 0; top: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
.CodeMirror-hscrollbar {
|
||||||
|
bottom: 0; left: 0;
|
||||||
|
overflow-y: hidden;
|
||||||
|
overflow-x: scroll;
|
||||||
|
}
|
||||||
|
.CodeMirror-scrollbar-filler {
|
||||||
|
right: 0; bottom: 0;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-filler {
|
||||||
|
left: 0; bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-gutters {
|
||||||
|
position: absolute; left: 0; top: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter {
|
||||||
|
white-space: normal;
|
||||||
|
height: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
margin-bottom: -30px;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 4;
|
||||||
|
background: none !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0; bottom: 0;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-elt {
|
||||||
|
position: absolute;
|
||||||
|
cursor: default;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
|
||||||
|
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
|
||||||
|
|
||||||
|
.CodeMirror-lines {
|
||||||
|
cursor: text;
|
||||||
|
min-height: 1px; /* prevents collapsing before first draw */
|
||||||
|
}
|
||||||
|
.CodeMirror pre {
|
||||||
|
/* Reset some styles that the rest of the page might have set */
|
||||||
|
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
|
||||||
|
border-width: 0;
|
||||||
|
background: transparent;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre;
|
||||||
|
word-wrap: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
color: inherit;
|
||||||
|
z-index: 2;
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
-webkit-font-variant-ligatures: contextual;
|
||||||
|
font-variant-ligatures: contextual;
|
||||||
|
}
|
||||||
|
.CodeMirror-wrap pre {
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-linebackground {
|
||||||
|
position: absolute;
|
||||||
|
left: 0; right: 0; top: 0; bottom: 0;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-linewidget {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
padding: 0.1px; /* Force widget margins to stay inside of the container */
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-widget {}
|
||||||
|
|
||||||
|
.CodeMirror-rtl pre { direction: rtl; }
|
||||||
|
|
||||||
|
.CodeMirror-code {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force content-box sizing for the elements where we expect it */
|
||||||
|
.CodeMirror-scroll,
|
||||||
|
.CodeMirror-sizer,
|
||||||
|
.CodeMirror-gutter,
|
||||||
|
.CodeMirror-gutters,
|
||||||
|
.CodeMirror-linenumber {
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-measure {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-cursor {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.CodeMirror-measure pre { position: static; }
|
||||||
|
|
||||||
|
div.CodeMirror-cursors {
|
||||||
|
visibility: hidden;
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
div.CodeMirror-dragcursors {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-focused div.CodeMirror-cursors {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-selected { background: #d9d9d9; }
|
||||||
|
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
||||||
|
.CodeMirror-crosshair { cursor: crosshair; }
|
||||||
|
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
|
||||||
|
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
|
||||||
|
|
||||||
|
.cm-searching {
|
||||||
|
background-color: #ffa;
|
||||||
|
background-color: rgba(255, 255, 0, .4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used to force a border model for a node */
|
||||||
|
.cm-force-border { padding-right: .1px; }
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
/* Hide the cursor when printing */
|
||||||
|
.CodeMirror div.CodeMirror-cursors {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See issue #2901 */
|
||||||
|
.cm-tab-wrap-hack:after { content: ''; }
|
||||||
|
|
||||||
|
/* Help users use markselection to safely style text background */
|
||||||
|
span.CodeMirror-selectedtext { background: none; }
|
9660
src/lib/codemirror/lib/codemirror.js
vendored
Normal file
9660
src/lib/codemirror/lib/codemirror.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
34
src/lib/diff/README.md
Normal file
34
src/lib/diff/README.md
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
# diff
|
||||||
|
|
||||||
|
implementation of myers diff algorithm
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/Swatinem/diff.png?branch=master)](https://travis-ci.org/Swatinem/diff)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/Swatinem/diff/badge.png?branch=master)](https://coveralls.io/r/Swatinem/diff)
|
||||||
|
[![Dependency Status](https://gemnasium.com/Swatinem/diff.png)](https://gemnasium.com/Swatinem/diff)
|
||||||
|
|
||||||
|
|
||||||
|
This uses the [*An O(ND) Difference Algorithm and Its Variations*](http://www.xmailserver.org/diff2.pdf)
|
||||||
|
Also see http://simplygenius.net/Article/DiffTutorial2 and
|
||||||
|
http://www.mathertel.de/Diff/ViewSrc.aspx for more inspiration
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
$ npm install diff
|
||||||
|
$ component install Swatinem/diff
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### diff(a, b, [eql(a, b)])
|
||||||
|
|
||||||
|
Given two arrays (or array-likes, such as strings) `a` and `b` and an optional
|
||||||
|
equal function `eql`, this will return an array with the following operations:
|
||||||
|
* *nop* the element is in both arrays
|
||||||
|
* *ins* the element is only in array `b` and will be inserted
|
||||||
|
* *del* the element in only in array `a` and will be removed
|
||||||
|
* *rep* the element from `a` will be replaced by the element from `b`.
|
||||||
|
This is essentially the same as a del+ins
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
LGPLv3
|
||||||
|
|
207
src/lib/diff/swatinem_diff.js
Normal file
207
src/lib/diff/swatinem_diff.js
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
Key portions of code below was borrowed from:
|
||||||
|
https://github.com/Swatinem/diff
|
||||||
|
|
||||||
|
License is LGPL3 (thanks!) as per:
|
||||||
|
https://github.com/Swatinem/diff/blob/b58391504759/README.md
|
||||||
|
|
||||||
|
I chose to pick this implementation over
|
||||||
|
https://github.com/google/diff-match-patch as suggested by CodeMirror
|
||||||
|
because:
|
||||||
|
|
||||||
|
- Code is clean and simple to read -- useful when unfamiliar with the diff
|
||||||
|
algorithm, this makes changing the code easier if/when needed.
|
||||||
|
|
||||||
|
- Smaller -- diff_match_patch comes with an extended API most of which is
|
||||||
|
of no use to the current project.
|
||||||
|
- diff_match_patch uncompressed: 74.7 KB
|
||||||
|
- Swatinem's diff uncompressed: 3.66 KB
|
||||||
|
|
||||||
|
- I can easily adapt Swatinem's diff to deal with arrays of strings, which
|
||||||
|
is best suited for the current project -- it natively work with arrays.
|
||||||
|
|
||||||
|
I removed portions of code which are of no use for the current project.
|
||||||
|
|
||||||
|
I modified the diff script generator (Diff.prototype.editscript) since I
|
||||||
|
need to generate a script which is compatible with the output of the
|
||||||
|
diff_match_patch, as expected by CodeMirror.
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
(function(context) {
|
||||||
|
|
||||||
|
// CodeMirror expect these globals:
|
||||||
|
context.DIFF_INSERT = 1;
|
||||||
|
context.DIFF_DELETE = -1;
|
||||||
|
context.DIFF_EQUAL = 0;
|
||||||
|
context.diff_match_patch = function(){};
|
||||||
|
|
||||||
|
context.diff_match_patch.prototype.diff_main = function(a, b) {
|
||||||
|
if ( a === b ) { return [ [ 0, a ] ]; }
|
||||||
|
var aa = a.match(/\n|[^\n]+\n?/g) || [];
|
||||||
|
var bb = b.match(/\n|[^\n]+\n?/g) || [];
|
||||||
|
var d = new Diff(aa, bb, eqlDefault);
|
||||||
|
return d.editscript();
|
||||||
|
};
|
||||||
|
|
||||||
|
function eqlDefault(a, b) { return a === b; }
|
||||||
|
|
||||||
|
function Diff(a, b, eql) {
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
this.eql = eql;
|
||||||
|
|
||||||
|
this.moda = Array.apply(null, new Array(a.length)).map(true.valueOf, false);
|
||||||
|
this.modb = Array.apply(null, new Array(b.length)).map(true.valueOf, false);
|
||||||
|
|
||||||
|
// just to save some allocations:
|
||||||
|
this.down = {};
|
||||||
|
this.up = {};
|
||||||
|
|
||||||
|
this.lcs(0, a.length, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
Diff.prototype.editscript = function Diff_editscript() {
|
||||||
|
var moda = this.moda, modb = this.modb;
|
||||||
|
var astart = 0, aend = moda.length;
|
||||||
|
var bstart = 0, bend = modb.length;
|
||||||
|
var result = [];
|
||||||
|
while (astart < aend || bstart < bend) {
|
||||||
|
if (astart < aend && bstart < bend) {
|
||||||
|
if (!moda[astart] && !modb[bstart]) {
|
||||||
|
result.push([ 0, this.a[astart] ]);
|
||||||
|
astart++; bstart++;
|
||||||
|
continue;
|
||||||
|
} else if (moda[astart] && modb[bstart]) {
|
||||||
|
result.push([ -1, this.a[astart] ]);
|
||||||
|
result.push([ 1, this.b[bstart] ]);
|
||||||
|
astart++; bstart++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (astart < aend && (bstart >= bend || moda[astart])) {
|
||||||
|
result.push([ -1, this.a[astart] ]);
|
||||||
|
astart++;
|
||||||
|
}
|
||||||
|
if (bstart < bend && (astart >= aend || modb[bstart])) {
|
||||||
|
result.push([ 1, this.b[bstart] ]);
|
||||||
|
bstart++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
Diff.prototype.lcs = function Diff_lcs(astart, aend, bstart, bend) {
|
||||||
|
var a = this.a, b = this.b, eql = this.eql;
|
||||||
|
// separate common head
|
||||||
|
while (astart < aend && bstart < bend && eql(a[astart], b[bstart])) {
|
||||||
|
astart++; bstart++;
|
||||||
|
}
|
||||||
|
// separate common tail
|
||||||
|
while (astart < aend && bstart < bend && eql(a[aend - 1], b[bend - 1])) {
|
||||||
|
aend--; bend--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (astart === aend) {
|
||||||
|
// only insertions
|
||||||
|
while (bstart < bend) {
|
||||||
|
this.modb[bstart] = true;
|
||||||
|
bstart++;
|
||||||
|
}
|
||||||
|
} else if (bend === bstart) {
|
||||||
|
// only deletions
|
||||||
|
while (astart < aend) {
|
||||||
|
this.moda[astart] = true;
|
||||||
|
astart++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var snake = this.snake(astart, aend, bstart, bend);
|
||||||
|
|
||||||
|
this.lcs(astart, snake.x, bstart, snake.y);
|
||||||
|
this.lcs(snake.u, aend, snake.v, bend);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Diff.prototype.snake = function Diff_snake(astart, aend, bstart, bend) {
|
||||||
|
var a = this.a, b = this.b, eql = this.eql;
|
||||||
|
|
||||||
|
var N = aend - astart,
|
||||||
|
M = bend - bstart;
|
||||||
|
|
||||||
|
var kdown = astart - bstart;
|
||||||
|
var kup = aend - bend;
|
||||||
|
|
||||||
|
var delta = N - M;
|
||||||
|
var deltaOdd = delta & 1;
|
||||||
|
|
||||||
|
var down = this.down;
|
||||||
|
down[kdown + 1] = astart;
|
||||||
|
var up = this.up;
|
||||||
|
up[kup - 1] = aend;
|
||||||
|
|
||||||
|
var Dmax = (N + M + 1) / 2;
|
||||||
|
for (var D = 0; D <= Dmax; D++) {
|
||||||
|
var k, x, y;
|
||||||
|
// forward path
|
||||||
|
for (k = kdown - D; k <= kdown + D; k += 2) {
|
||||||
|
if (k === kdown - D) {
|
||||||
|
x = down[k + 1]; // down
|
||||||
|
} else {
|
||||||
|
x = down[k - 1] + 1; // right
|
||||||
|
if ((k < kdown + D) && (down[k + 1] >= x)) {
|
||||||
|
x = down[k + 1]; // down
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y = x - k;
|
||||||
|
|
||||||
|
while (x < aend && y < bend && eql(a[x], b[y])) {
|
||||||
|
x++; y++; // diagonal
|
||||||
|
}
|
||||||
|
down[k] = x;
|
||||||
|
|
||||||
|
if (deltaOdd && (kup - D < k) && (k < kup + D) &&
|
||||||
|
up[k] <= down[k]) {
|
||||||
|
return {
|
||||||
|
x: down[k],
|
||||||
|
y: down[k] - k,
|
||||||
|
u: up[k],
|
||||||
|
v: up[k] - k,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse path
|
||||||
|
for (k = kup - D; k <= kup + D; k += 2) {
|
||||||
|
if (k === kup + D) {
|
||||||
|
x = up[k - 1]; // up
|
||||||
|
} else {
|
||||||
|
x = up[k + 1] - 1; // left
|
||||||
|
if ((k > kup - D) && (up[k - 1] < x)) {
|
||||||
|
x = up[k - 1]; // up
|
||||||
|
}
|
||||||
|
}
|
||||||
|
y = x - k;
|
||||||
|
|
||||||
|
while (x > astart && y > bstart && eql(a[x - 1], b[y - 1])) {
|
||||||
|
x--; y--; // diagonal
|
||||||
|
}
|
||||||
|
up[k] = x;
|
||||||
|
|
||||||
|
if (!deltaOdd && (kdown - D <= k) && (k <= kdown + D) &&
|
||||||
|
up[k] <= down[k]) {
|
||||||
|
return {
|
||||||
|
x: down[k],
|
||||||
|
y: down[k] - k,
|
||||||
|
u: up[k],
|
||||||
|
v: up[k] - k,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Diff;
|
||||||
|
})(self);
|
|
@ -1,12 +1,17 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>uMatrix — Your rules</title>
|
<title>uMatrix — Your rules</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="lib/codemirror/lib/codemirror.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="lib/codemirror/addon/merge/merge.css">
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
|
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">
|
<link rel="stylesheet" type="text/css" href="css/cloud-ui.css">
|
||||||
<link rel="stylesheet" type="text/css" href="css/user-rules.css">
|
<link rel="stylesheet" type="text/css" href="css/user-rules.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/codemirror.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -15,30 +20,32 @@
|
||||||
|
|
||||||
<!-- <p data-i18n="userRulesFormatHint"></p> -->
|
<!-- <p data-i18n="userRulesFormatHint"></p> -->
|
||||||
<div id="diff">
|
<div id="diff">
|
||||||
<div class="pane left">
|
<div class="tools">
|
||||||
<div>
|
<div class="ruleActions">
|
||||||
<h2 data-i18n="userRulesPermanentHeader"></h2>
|
<h3 data-i18n="userRulesPermanentHeader"></h3>
|
||||||
<button type="button" id="exportButton" data-i18n="userRulesExport"></button>
|
<button type="button" id="exportButton" data-i18n="userRulesExport"></button>
|
||||||
<button type="button" id="revertButton" data-i18n="userRulesRevert"></button>
|
<button type="button" id="revertButton" data-i18n="userRulesRevert"></button>
|
||||||
</div>
|
|
||||||
<ul></ul>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="pane right">
|
<div class="ruleActions">
|
||||||
<div>
|
<h3 data-i18n="userRulesTemporaryHeader"></h3>
|
||||||
<h2 data-i18n="userRulesTemporaryHeader"></h2>
|
|
||||||
<button type="button" id="commitButton" data-i18n="userRulesCommit"></button>
|
<button type="button" id="commitButton" data-i18n="userRulesCommit"></button>
|
||||||
<button type="button" id="editEnterButton" data-i18n="userRulesEdit"></button>
|
|
||||||
<button type="button" id="editStopButton" data-i18n="userRulesEditSave"></button>
|
|
||||||
<button type="button" id="editCancelButton" data-i18n="userRulesEditDicard"></button>
|
|
||||||
<button type="button" id="importButton" data-i18n="userRulesImport"></button>
|
<button type="button" id="importButton" data-i18n="userRulesImport"></button>
|
||||||
</div>
|
<button type="button" id="editSaveButton" data-i18n="userRulesEditSave"></button>
|
||||||
<textarea spellcheck="false"></textarea>
|
|
||||||
<ul></ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="ruleFilter"><span class="fa"></span> <input type="text" size="32"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="codeMirrorContainer codeMirrorMergeContainer vfill-available"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input class="hidden" id="importFilePicker" type="file" accept="text/plain">
|
<div style="display: none;">
|
||||||
<span class="hidden" data-i18n="userRulesDefaultFileName"></span>
|
<input id="importFilePicker" type="file" accept="text/plain">
|
||||||
|
<span data-i18n="userRulesDefaultFileName"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="lib/diff/swatinem_diff.js"></script>
|
||||||
|
<script src="lib/codemirror/lib/codemirror.js"></script>
|
||||||
|
<script src="lib/codemirror/addon/merge/merge.js"></script>
|
||||||
|
<script src="lib/codemirror/addon/selection/active-line.js"></script>
|
||||||
|
|
||||||
<script src="js/vapi-common.js"></script>
|
<script src="js/vapi-common.js"></script>
|
||||||
<script src="js/vapi-client.js"></script>
|
<script src="js/vapi-client.js"></script>
|
||||||
|
|
Loading…
Reference in a new issue