forked from bschug/poedit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patheditor.js
163 lines (135 loc) · 5.25 KB
/
editor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
function Editor() {
this.codeWindow = null;
this.highlightLines = [];
this.scrollTarget = null;
this.scrollSpeed = 0;
var SCROLL_ANIM_INTERVAL = 30;
var SCROLL_DURATION_FACTOR = 1;
var SCROLL_ACC = 0.1;
var MIN_SCROLL_DISTANCE = 10;
this.init = function() {
this.codeWindow = document.getElementById( 'code-window' );
var self = this;
setInterval( function() { self.updateAnimation(); }, SCROLL_ANIM_INTERVAL );
}
this.updateAnimation = function() {
if (this.scrollTarget !== null) {
var distance = this.scrollTarget - this.codeWindow.scrollTop;
if (Math.abs( distance ) < MIN_SCROLL_DISTANCE) {
this.codeWindow.scrollTop = this.scrollTarget;
this.scrollTarget = null;
return;
}
var targetScrollSpeed = Math.abs( distance ) / SCROLL_DURATION_FACTOR * Math.sign( distance );
var scrollSpeed = this.scrollSpeed * (1 - SCROLL_ACC) + targetScrollSpeed * SCROLL_ACC;
if (Math.abs( distance ) < Math.abs( scrollSpeed )) {
scrollSpeed = distance;
}
this.codeWindow.scrollTop += scrollSpeed;
this.scrollSpeed = scrollSpeed;
}
}
this.formatCode = function (rawLines, lineTypes) {
if (rawLines.length != lineTypes.length) {
console.log( rawLines.length.toString() + ' code lines, ' + lineTypes.length.toString() + ' line types' );
return;
}
removeTrailingNewlines( rawLines, lineTypes, 3 );
var codeHTML = '';
var indent = false;
var selection = DomUtils.saveSelection( this.codeWindow );
var selectionOffset = 0;
var generatedCharacters = 0;
var originalCharacters = 0;
for (var i=0; i < rawLines.length; i++) {
var lineCharacters = 0;
var className = {
Empty: 'code-empty',
Comment: 'code-comment',
Visibility: 'code-visibility',
Filter: 'code-filter',
Modifier: 'code-modifier',
Error: 'code-error'
};
var hasHighlight = this.highlightLines.indexOf( i ) >= 0;
var highlightClass = hasHighlight ? ' highlighted' : '';
codeHTML += '<span id="line' + i.toString() + '" class="' + className[lineTypes[i]] + highlightClass + '">';
// Indentation:
// Filters and Modifiers are always indented, Visibility is never indented.
// For erraneous / incomplete lines and empty lines, indentation is guessed based on the previous line.
// If the previous line was visibility, the current line is indented.
// If the previous line was empty, the new line is NOT indented (we assume the user wants a new block).
// If the previous line was erraneous too, we just keep the indentation level from before.
if (lineTypes[i] === 'Visibility') {
indent = false;
}
if (lineTypes[i] === 'Filter' || lineTypes[i] === 'Modifier') {
indent = true;
}
else if (lineTypes[i] === 'Empty') {
indent = false;
}
else if (lineTypes[i] === 'Error' && (i > 0) && lineTypes[i-1] === 'Visibility') {
indent = true;
}
if (indent) {
codeHTML += ' ';
lineCharacters += 4;
}
var trimmedLine = StrUtils.ltrim( rawLines[i] );
codeHTML += trimmedLine + '</span>';
// Must not add <br> for the last line, otherwise we would create more and more newlines at the end
if (i < rawLines.length - 1) {
codeHTML += '<br>';
}
// We need to count now many readable characters we have generated so far,
// and how many readable characters there were in the original, unmodified code.
// Otherwise, we could not know if and by how much to move the cursor.
// Doing this, we must keep in mind that rangy counts all trailing whitespace as one.
lineCharacters += removeAllButOneTrailingWhitespace( trimmedLine ).length;
generatedCharacters += lineCharacters + 1; // add 1 for the newline
var originalLineStart = originalCharacters;
originalCharacters += removeAllButOneTrailingWhitespace( rawLines[i] ).length + 1; // add 1 for the newline
if (selection !== null && selection.length > 0) {
var selectionStart = selection[0].characterRange.start;
if (selectionStart > originalLineStart && selectionStart <= originalCharacters) {
selectionOffset = generatedCharacters - originalCharacters;
}
}
}
this.codeWindow.innerHTML = codeHTML;
DomUtils.restoreSelection( this.codeWindow, selection, selectionOffset );
}
this.scrollToLine = function (lineNr) {
var line = document.getElementById( 'line' + lineNr.toString() );
var codeWindowHeight = this.codeWindow.getBoundingClientRect().height;
var centeredScrollPos = line.offsetTop - codeWindowHeight / 2;
var bottomScrollPos = Math.max( 0, this.codeWindow.scrollHeight - codeWindowHeight );
this.scrollTarget = MathUtils.clamp( centeredScrollPos, 0, bottomScrollPos );
}
function removeTrailingNewlines (rawLines, lineTypes, numAllowed) {
var linesToRemove = 0;
for (var i = rawLines.length - 1; i >= 0; i--) {
if (lineTypes[i] != 'Empty') {
break;
}
linesToRemove++;
}
if (linesToRemove > numAllowed) {
linesToRemove -= numAllowed;
rawLines.splice( -linesToRemove, linesToRemove );
lineTypes.splice( -linesToRemove, linesToRemove );
}
}
function removeAllButOneTrailingWhitespace (line) {
var rTrimmedLine = StrUtils.rtrim( line );
var originalLength = line.length;
var rTrimmedLength = rTrimmedLine.length;
if (originalLength === rTrimmedLength) {
return line;
}
else {
return rTrimmedLine + ' ';
}
}
}