mirror of
https://github.com/boxgaming/qbjs.git
synced 2024-05-12 08:00:12 +00:00
added support for search and export
This commit is contained in:
parent
72a3757f01
commit
b819f79cc9
72
codemirror/active-line.js
Normal file
72
codemirror/active-line.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://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);
|
||||
}
|
||||
});
|
128
codemirror/annotatescrollbar.js
Normal file
128
codemirror/annotatescrollbar.js
Normal file
|
@ -0,0 +1,128 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://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";
|
||||
|
||||
CodeMirror.defineExtension("annotateScrollbar", function(options) {
|
||||
if (typeof options == "string") options = {className: options};
|
||||
return new Annotation(this, options);
|
||||
});
|
||||
|
||||
CodeMirror.defineOption("scrollButtonHeight", 0);
|
||||
|
||||
function Annotation(cm, options) {
|
||||
this.cm = cm;
|
||||
this.options = options;
|
||||
this.buttonHeight = options.scrollButtonHeight || cm.getOption("scrollButtonHeight");
|
||||
this.annotations = [];
|
||||
this.doRedraw = this.doUpdate = null;
|
||||
this.div = cm.getWrapperElement().appendChild(document.createElement("div"));
|
||||
this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";
|
||||
this.computeScale();
|
||||
|
||||
function scheduleRedraw(delay) {
|
||||
clearTimeout(self.doRedraw);
|
||||
self.doRedraw = setTimeout(function() { self.redraw(); }, delay);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
cm.on("refresh", this.resizeHandler = function() {
|
||||
clearTimeout(self.doUpdate);
|
||||
self.doUpdate = setTimeout(function() {
|
||||
if (self.computeScale()) scheduleRedraw(20);
|
||||
}, 100);
|
||||
});
|
||||
cm.on("markerAdded", this.resizeHandler);
|
||||
cm.on("markerCleared", this.resizeHandler);
|
||||
if (options.listenForChanges !== false)
|
||||
cm.on("changes", this.changeHandler = function() {
|
||||
scheduleRedraw(250);
|
||||
});
|
||||
}
|
||||
|
||||
Annotation.prototype.computeScale = function() {
|
||||
var cm = this.cm;
|
||||
var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) /
|
||||
cm.getScrollerElement().scrollHeight
|
||||
if (hScale != this.hScale) {
|
||||
this.hScale = hScale;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Annotation.prototype.update = function(annotations) {
|
||||
this.annotations = annotations;
|
||||
this.redraw();
|
||||
};
|
||||
|
||||
Annotation.prototype.redraw = function(compute) {
|
||||
if (compute !== false) this.computeScale();
|
||||
var cm = this.cm, hScale = this.hScale;
|
||||
|
||||
var frag = document.createDocumentFragment(), anns = this.annotations;
|
||||
|
||||
var wrapping = cm.getOption("lineWrapping");
|
||||
var singleLineH = wrapping && cm.defaultTextHeight() * 1.5;
|
||||
var curLine = null, curLineObj = null;
|
||||
|
||||
function getY(pos, top) {
|
||||
if (curLine != pos.line) {
|
||||
curLine = pos.line
|
||||
curLineObj = cm.getLineHandle(pos.line)
|
||||
var visual = cm.getLineHandleVisualStart(curLineObj)
|
||||
if (visual != curLineObj) {
|
||||
curLine = cm.getLineNumber(visual)
|
||||
curLineObj = visual
|
||||
}
|
||||
}
|
||||
if ((curLineObj.widgets && curLineObj.widgets.length) ||
|
||||
(wrapping && curLineObj.height > singleLineH))
|
||||
return cm.charCoords(pos, "local")[top ? "top" : "bottom"];
|
||||
var topY = cm.heightAtLine(curLineObj, "local");
|
||||
return topY + (top ? 0 : curLineObj.height);
|
||||
}
|
||||
|
||||
var lastLine = cm.lastLine()
|
||||
if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) {
|
||||
var ann = anns[i];
|
||||
if (ann.to.line > lastLine) continue;
|
||||
var top = nextTop || getY(ann.from, true) * hScale;
|
||||
var bottom = getY(ann.to, false) * hScale;
|
||||
while (i < anns.length - 1) {
|
||||
if (anns[i + 1].to.line > lastLine) break;
|
||||
nextTop = getY(anns[i + 1].from, true) * hScale;
|
||||
if (nextTop > bottom + .9) break;
|
||||
ann = anns[++i];
|
||||
bottom = getY(ann.to, false) * hScale;
|
||||
}
|
||||
if (bottom == top) continue;
|
||||
var height = Math.max(bottom - top, 3);
|
||||
|
||||
var elt = frag.appendChild(document.createElement("div"));
|
||||
elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: "
|
||||
+ (top + this.buttonHeight) + "px; height: " + height + "px";
|
||||
elt.className = this.options.className;
|
||||
if (ann.id) {
|
||||
elt.setAttribute("annotation-id", ann.id);
|
||||
}
|
||||
}
|
||||
this.div.textContent = "";
|
||||
this.div.appendChild(frag);
|
||||
};
|
||||
|
||||
Annotation.prototype.clear = function() {
|
||||
this.cm.off("refresh", this.resizeHandler);
|
||||
this.cm.off("markerAdded", this.resizeHandler);
|
||||
this.cm.off("markerCleared", this.resizeHandler);
|
||||
if (this.changeHandler) this.cm.off("changes", this.changeHandler);
|
||||
this.div.parentNode.removeChild(this.div);
|
||||
};
|
||||
});
|
8
codemirror/matchesonscrollbar.css
Normal file
8
codemirror/matchesonscrollbar.css
Normal file
|
@ -0,0 +1,8 @@
|
|||
.CodeMirror-search-match {
|
||||
background: gold;
|
||||
border-top: 1px solid orange;
|
||||
border-bottom: 1px solid orange;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
opacity: .5;
|
||||
}
|
97
codemirror/matchesonscrollbar.js
Normal file
97
codemirror/matchesonscrollbar.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, options) {
|
||||
if (typeof options == "string") options = {className: options};
|
||||
if (!options) options = {};
|
||||
return new SearchAnnotation(this, query, caseFold, options);
|
||||
});
|
||||
|
||||
function SearchAnnotation(cm, query, caseFold, options) {
|
||||
this.cm = cm;
|
||||
this.options = options;
|
||||
var annotateOptions = {listenForChanges: false};
|
||||
for (var prop in options) annotateOptions[prop] = options[prop];
|
||||
if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match";
|
||||
this.annotation = cm.annotateScrollbar(annotateOptions);
|
||||
this.query = query;
|
||||
this.caseFold = caseFold;
|
||||
this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};
|
||||
this.matches = [];
|
||||
this.update = null;
|
||||
|
||||
this.findMatches();
|
||||
this.annotation.update(this.matches);
|
||||
|
||||
var self = this;
|
||||
cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); });
|
||||
}
|
||||
|
||||
var MAX_MATCHES = 1000;
|
||||
|
||||
SearchAnnotation.prototype.findMatches = function() {
|
||||
if (!this.gap) return;
|
||||
for (var i = 0; i < this.matches.length; i++) {
|
||||
var match = this.matches[i];
|
||||
if (match.from.line >= this.gap.to) break;
|
||||
if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
|
||||
}
|
||||
var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), {caseFold: this.caseFold, multiline: this.options.multiline});
|
||||
var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES;
|
||||
while (cursor.findNext()) {
|
||||
var match = {from: cursor.from(), to: cursor.to()};
|
||||
if (match.from.line >= this.gap.to) break;
|
||||
this.matches.splice(i++, 0, match);
|
||||
if (this.matches.length > maxMatches) break;
|
||||
}
|
||||
this.gap = null;
|
||||
};
|
||||
|
||||
function offsetLine(line, changeStart, sizeChange) {
|
||||
if (line <= changeStart) return line;
|
||||
return Math.max(changeStart, line + sizeChange);
|
||||
}
|
||||
|
||||
SearchAnnotation.prototype.onChange = function(change) {
|
||||
var startLine = change.from.line;
|
||||
var endLine = CodeMirror.changeEnd(change).line;
|
||||
var sizeChange = endLine - change.to.line;
|
||||
if (this.gap) {
|
||||
this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);
|
||||
this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);
|
||||
} else {
|
||||
this.gap = {from: change.from.line, to: endLine + 1};
|
||||
}
|
||||
|
||||
if (sizeChange) for (var i = 0; i < this.matches.length; i++) {
|
||||
var match = this.matches[i];
|
||||
var newFrom = offsetLine(match.from.line, startLine, sizeChange);
|
||||
if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);
|
||||
var newTo = offsetLine(match.to.line, startLine, sizeChange);
|
||||
if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);
|
||||
}
|
||||
clearTimeout(this.update);
|
||||
var self = this;
|
||||
this.update = setTimeout(function() { self.updateAfterChange(); }, 250);
|
||||
};
|
||||
|
||||
SearchAnnotation.prototype.updateAfterChange = function() {
|
||||
this.findMatches();
|
||||
this.annotation.update(this.matches);
|
||||
};
|
||||
|
||||
SearchAnnotation.prototype.clear = function() {
|
||||
this.cm.off("change", this.changeHandler);
|
||||
this.annotation.clear();
|
||||
};
|
||||
});
|
|
@ -3,47 +3,50 @@
|
|||
src: url(lp-dosvga.ttf);
|
||||
} */
|
||||
|
||||
.cm-s-lesser-dark {
|
||||
.cm-s-qbjs {
|
||||
line-height: 1em;
|
||||
}
|
||||
.cm-s-lesser-dark.CodeMirror { background: rgb(0, 0, 39); color: rgb(216, 216, 216); text-shadow: 0 -1px 1px #262626; font-family: dosvga; /*letter-spacing: -1px*/}
|
||||
.cm-s-lesser-dark div.CodeMirror-selected { background: #45443B; } /* 33322B*/
|
||||
.cm-s-lesser-dark .CodeMirror-line::selection, .cm-s-lesser-dark .CodeMirror-line > span::selection, .cm-s-lesser-dark .CodeMirror-line > span > span::selection { background: rgba(0, 49, 78, .99); }
|
||||
.cm-s-lesser-dark .CodeMirror-line::-moz-selection, .cm-s-lesser-dark .CodeMirror-line > span::-moz-selection, .cm-s-lesser-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(69, 68, 59, .99); }
|
||||
.cm-s-lesser-dark .CodeMirror-cursor { border-left: 1px solid #fff; }
|
||||
.cm-s-lesser-dark pre { padding: 0 8px; }/*editable code holder*/
|
||||
.cm-s-qbjs.CodeMirror { background: rgb(0, 0, 39); color: rgb(216, 216, 216); text-shadow: 0 -1px 1px #262626; font-family: dosvga; /*letter-spacing: -1px*/}
|
||||
.cm-s-qbjs div.CodeMirror-selected { background: #45443B; } /* 33322B*/
|
||||
.cm-s-qbjs .CodeMirror-line::selection, .cm-s-qbjs .CodeMirror-line > span::selection, .cm-s-qbjs .CodeMirror-line > span > span::selection { background: rgba(0, 49, 78, .99); }
|
||||
.cm-s-qbjs .CodeMirror-line::-moz-selection, .cm-s-qbjs .CodeMirror-line > span::-moz-selection, .cm-s-qbjs .CodeMirror-line > span > span::-moz-selection { background: rgba(69, 68, 59, .99); }
|
||||
.cm-s-qbjs .CodeMirror-cursor { border-left: 1px solid #fff; }
|
||||
.cm-s-qbjs pre { padding: 0 8px; }/*editable code holder*/
|
||||
|
||||
.cm-s-lesser-dark.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/
|
||||
.cm-s-qbjs.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/
|
||||
|
||||
.cm-s-lesser-dark .CodeMirror-gutters { background: rgb(0, 49, 78); border-right:1px solid #aaa; }
|
||||
.cm-s-lesser-dark .CodeMirror-guttermarker { color: #599eff; }
|
||||
.cm-s-lesser-dark .CodeMirror-guttermarker-subtle { color: #777; }
|
||||
.cm-s-lesser-dark .CodeMirror-linenumber { color: rgb(175, 175, 175); }
|
||||
.cm-s-qbjs .CodeMirror-gutters { background: rgb(0, 49, 78); border-right:1px solid #aaa; }
|
||||
.cm-s-qbjs .CodeMirror-guttermarker { color: #599eff; }
|
||||
.cm-s-qbjs .CodeMirror-guttermarker-subtle { color: #777; }
|
||||
.cm-s-qbjs .CodeMirror-linenumber { color: rgb(175, 175, 175); }
|
||||
|
||||
.cm-s-lesser-dark span.cm-header { color: #a0a; }
|
||||
.cm-s-lesser-dark span.cm-quote { color: #090; }
|
||||
.cm-s-lesser-dark span.cm-keyword { color: rgb(69, 118, 147); }
|
||||
.cm-s-lesser-dark span.cm-atom { color: #C2B470; }
|
||||
.cm-s-lesser-dark span.cm-number { color: rgb(216, 98, 78); }
|
||||
.cm-s-lesser-dark span.cm-def { color: white; }
|
||||
.cm-s-lesser-dark span.cm-variable { color:rgb(216, 216, 216); }
|
||||
.cm-s-lesser-dark span.cm-variable-2 { color: #669199; }
|
||||
.cm-s-lesser-dark span.cm-variable-3, .cm-s-lesser-dark span.cm-type { color: white; }
|
||||
.cm-s-lesser-dark span.cm-property { color: #92A75C; }
|
||||
.cm-s-lesser-dark span.cm-operator { color: #92A75C; }
|
||||
.cm-s-lesser-dark span.cm-comment { color: rgb(98, 98, 98); }
|
||||
.cm-s-lesser-dark span.cm-string { color: rgb(255, 167, 0); }
|
||||
.cm-s-lesser-dark span.cm-string-2 { color: #f50; }
|
||||
.cm-s-lesser-dark span.cm-meta { color: #738C73; }
|
||||
.cm-s-lesser-dark span.cm-qualifier { color: #555; }
|
||||
.cm-s-lesser-dark span.cm-builtin { color: rgb(69, 118, 147); }
|
||||
.cm-s-lesser-dark span.cm-bracket { color: #EBEFE7; }
|
||||
.cm-s-lesser-dark span.cm-tag { color: #669199; }
|
||||
.cm-s-lesser-dark span.cm-attribute { color: #81a4d5; }
|
||||
.cm-s-lesser-dark span.cm-hr { color: #999; }
|
||||
.cm-s-lesser-dark span.cm-link { color: #7070E6; }
|
||||
/*.cm-s-lesser-dark span.cm-error { color: #9d1e15; }*/
|
||||
.cm-s-lesser-dark span.cm-error { color: #999; }
|
||||
.cm-s-qbjs span.cm-header { color: #a0a; }
|
||||
.cm-s-qbjs span.cm-quote { color: #090; }
|
||||
.cm-s-qbjs span.cm-keyword { color: rgb(69, 118, 147); }
|
||||
.cm-s-qbjs span.cm-atom { color: #C2B470; }
|
||||
.cm-s-qbjs span.cm-number { color: rgb(216, 98, 78); }
|
||||
.cm-s-qbjs span.cm-def { color: white; }
|
||||
.cm-s-qbjs span.cm-variable { color:rgb(216, 216, 216); }
|
||||
.cm-s-qbjs span.cm-variable-2 { color: #669199; }
|
||||
.cm-s-qbjs span.cm-variable-3, .cm-s-qbjs span.cm-type { color: white; }
|
||||
.cm-s-qbjs span.cm-property { color: #92A75C; }
|
||||
.cm-s-qbjs span.cm-operator { color: #92A75C; }
|
||||
.cm-s-qbjs span.cm-comment { color: rgb(98, 98, 98); }
|
||||
.cm-s-qbjs span.cm-string { color: rgb(255, 167, 0); }
|
||||
.cm-s-qbjs span.cm-string-2 { color: #f50; }
|
||||
.cm-s-qbjs span.cm-meta { color: #738C73; }
|
||||
.cm-s-qbjs span.cm-qualifier { color: #555; }
|
||||
.cm-s-qbjs span.cm-builtin { color: rgb(69, 118, 147); }
|
||||
.cm-s-qbjs span.cm-bracket { color: #EBEFE7; }
|
||||
.cm-s-qbjs span.cm-tag { color: #669199; }
|
||||
.cm-s-qbjs span.cm-attribute { color: #81a4d5; }
|
||||
.cm-s-qbjs span.cm-hr { color: #999; }
|
||||
.cm-s-qbjs span.cm-link { color: #7070E6; }
|
||||
/*.cm-s-qbjs span.cm-error { color: #9d1e15; }*/
|
||||
.cm-s-qbjs span.cm-error { color: #999; }
|
||||
|
||||
.cm-s-lesser-dark .CodeMirror-activeline-background { background: rgb(0, 49, 78); }
|
||||
.cm-s-lesser-dark .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }
|
||||
.cm-s-qbjs .CodeMirror-activeline-background { background: rgb(0, 49, 78); }
|
||||
.cm-s-qbjs .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }
|
||||
|
||||
.cm-s-qbjs .CodeMirror-search-field { font-family: dosvga; font-size: 1em; }
|
||||
.cm-s-qbjs .cm-searching { color: #333 !important; background-color: #ccc; }
|
|
@ -23,8 +23,8 @@ CodeMirror.defineMode("qbjs", function(conf, parserConf) {
|
|||
var brakets = new RegExp('^[\\(\\)]');
|
||||
var identifiers = new RegExp("^[A-Za-z][_A-Za-z0-9]*");
|
||||
|
||||
var openingKeywords = ['sub','select','while','if','function', 'property', 'with', 'for', 'type'];
|
||||
var middleKeywords = ['else','elseif','case'];
|
||||
var openingKeywords = ['sub','select','while','if','function', 'property', 'with', 'for', 'type\\s'];
|
||||
var middleKeywords = ['else','elseif','case','then'];
|
||||
var endKeywords = ['next','loop','wend'];
|
||||
|
||||
var wordOperators = wordRegexp(['and', 'or', 'not', 'xor', 'is', 'mod', 'eqv', 'imp']);
|
||||
|
@ -45,8 +45,8 @@ CodeMirror.defineMode("qbjs", function(conf, parserConf) {
|
|||
'print', 'pset', 'right', 'rtrim', 'rnd', 'screen', 'shared', 'sgn', 'sin', 'sleep', 'space', 'sqr', 'str', 'swap', 'tan',
|
||||
'timer', 'ubound', 'ucase', 'val',
|
||||
// QBJS-specific
|
||||
'alert', 'confirm', 'domadd', 'domcontainer', 'domcreate', 'domevent','domget', 'domgetimage', 'domremove', 'prompt',
|
||||
'storageclear', 'storageget', 'storagekey', 'storagelength', 'storageset', 'storageremove'];
|
||||
'alert', 'confirm', 'domadd', 'domcontainer', 'domcreate', 'domevent','domget', 'domgetimage', 'domremove', 'export',
|
||||
'from', 'prompt', 'import', 'storageclear', 'storageget', 'storagekey', 'storagelength', 'storageset', 'storageremove'];
|
||||
|
||||
var builtinConsts = ['gx_true', 'gx_false', 'gxevent_init', 'gxevent_update', 'gxevent_drawbg', 'gxevent_drawmap', 'gxevent_drawscreen',
|
||||
'gxevent_mouseinput', 'gxevent_paintbefore', 'gxevent_paintafter', 'gxevent_collision_tile', 'gxevent_collision_entity',
|
||||
|
@ -72,7 +72,7 @@ CodeMirror.defineMode("qbjs", function(conf, parserConf) {
|
|||
'gxdevice_axis', 'gxdevice_wheel', 'gxtype_entity', 'gxtype_font', 'gx_cr', 'gx_lf', 'gx_crlf',
|
||||
'local', 'session'];
|
||||
|
||||
var builtinObjsWords = ['\\$if', '\\$end if'];
|
||||
var builtinObjsWords = ['\\$if', '\\$end if', '\\$touchmouse'];
|
||||
|
||||
// TODO: adjust for QB
|
||||
var knownProperties = ['description', 'firstindex', 'global', 'helpcontext', 'helpfile', 'ignorecase', 'length', 'number', 'pattern', 'source', 'value', 'count'];
|
||||
|
|
292
codemirror/search.js
Normal file
292
codemirror/search.js
Normal file
|
@ -0,0 +1,292 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
// Define search commands. Depends on dialog.js or another
|
||||
// implementation of the openDialog method.
|
||||
|
||||
// Replace works a little oddly -- it will do the replace on the next
|
||||
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
|
||||
// replace by making sure the match is no longer selected when hitting
|
||||
// Ctrl-G.
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
// default search panel location
|
||||
CodeMirror.defineOption("search", {bottom: false});
|
||||
|
||||
function searchOverlay(query, caseInsensitive) {
|
||||
if (typeof query == "string")
|
||||
query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
|
||||
else if (!query.global)
|
||||
query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
|
||||
|
||||
return {token: function(stream) {
|
||||
query.lastIndex = stream.pos;
|
||||
var match = query.exec(stream.string);
|
||||
if (match && match.index == stream.pos) {
|
||||
stream.pos += match[0].length || 1;
|
||||
return "searching";
|
||||
} else if (match) {
|
||||
stream.pos = match.index;
|
||||
} else {
|
||||
stream.skipToEnd();
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
function SearchState() {
|
||||
this.posFrom = this.posTo = this.lastQuery = this.query = null;
|
||||
this.overlay = null;
|
||||
}
|
||||
|
||||
function getSearchState(cm) {
|
||||
return cm.state.search || (cm.state.search = new SearchState());
|
||||
}
|
||||
|
||||
function queryCaseInsensitive(query) {
|
||||
return typeof query == "string" && query == query.toLowerCase();
|
||||
}
|
||||
|
||||
function getSearchCursor(cm, query, pos) {
|
||||
// Heuristic: if the query string is all lowercase, do a case insensitive search.
|
||||
return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});
|
||||
}
|
||||
|
||||
function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
|
||||
cm.openDialog(text, onEnter, {
|
||||
value: deflt,
|
||||
selectValueOnOpen: true,
|
||||
closeOnEnter: false,
|
||||
onClose: function() { clearSearch(cm); },
|
||||
onKeyDown: onKeyDown,
|
||||
bottom: cm.options.search.bottom
|
||||
});
|
||||
}
|
||||
|
||||
function dialog(cm, text, shortText, deflt, f) {
|
||||
if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom});
|
||||
else f(prompt(shortText, deflt));
|
||||
}
|
||||
|
||||
function confirmDialog(cm, text, shortText, fs) {
|
||||
if (cm.openConfirm) cm.openConfirm(text, fs);
|
||||
else if (confirm(shortText)) fs[0]();
|
||||
}
|
||||
|
||||
function parseString(string) {
|
||||
return string.replace(/\\([nrt\\])/g, function(match, ch) {
|
||||
if (ch == "n") return "\n"
|
||||
if (ch == "r") return "\r"
|
||||
if (ch == "t") return "\t"
|
||||
if (ch == "\\") return "\\"
|
||||
return match
|
||||
})
|
||||
}
|
||||
|
||||
function parseQuery(query) {
|
||||
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
|
||||
if (isRE) {
|
||||
try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
|
||||
catch(e) {} // Not a regular expression after all, do a string search
|
||||
} else {
|
||||
query = parseString(query)
|
||||
}
|
||||
if (typeof query == "string" ? query == "" : query.test(""))
|
||||
query = /x^/;
|
||||
return query;
|
||||
}
|
||||
|
||||
function startSearch(cm, state, query) {
|
||||
state.queryText = query;
|
||||
state.query = parseQuery(query);
|
||||
cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
|
||||
state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
|
||||
cm.addOverlay(state.overlay);
|
||||
if (cm.showMatchesOnScrollbar) {
|
||||
if (state.annotate) { state.annotate.clear(); state.annotate = null; }
|
||||
state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
|
||||
}
|
||||
}
|
||||
|
||||
function doSearch(cm, rev, persistent, immediate) {
|
||||
var state = getSearchState(cm);
|
||||
if (state.query) return findNext(cm, rev);
|
||||
var q = cm.getSelection() || state.lastQuery;
|
||||
if (q instanceof RegExp && q.source == "x^") q = null
|
||||
if (persistent && cm.openDialog) {
|
||||
var hiding = null
|
||||
var searchNext = function(query, event) {
|
||||
CodeMirror.e_stop(event);
|
||||
if (!query) return;
|
||||
if (query != state.queryText) {
|
||||
startSearch(cm, state, query);
|
||||
state.posFrom = state.posTo = cm.getCursor();
|
||||
}
|
||||
if (hiding) hiding.style.opacity = 1
|
||||
findNext(cm, event.shiftKey, function(_, to) {
|
||||
var dialog
|
||||
if (to.line < 3 && document.querySelector &&
|
||||
(dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
|
||||
dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
|
||||
(hiding = dialog).style.opacity = .4
|
||||
})
|
||||
};
|
||||
persistentDialog(cm, getQueryDialog(cm), q, searchNext, function(event, query) {
|
||||
var keyName = CodeMirror.keyName(event)
|
||||
var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption("keyMap")][keyName]
|
||||
if (cmd == "findNext" || cmd == "findPrev" ||
|
||||
cmd == "findPersistentNext" || cmd == "findPersistentPrev") {
|
||||
CodeMirror.e_stop(event);
|
||||
startSearch(cm, getSearchState(cm), query);
|
||||
cm.execCommand(cmd);
|
||||
} else if (cmd == "find" || cmd == "findPersistent") {
|
||||
CodeMirror.e_stop(event);
|
||||
searchNext(query, event);
|
||||
}
|
||||
});
|
||||
if (immediate && q) {
|
||||
startSearch(cm, state, q);
|
||||
findNext(cm, rev);
|
||||
}
|
||||
} else {
|
||||
dialog(cm, getQueryDialog(cm), "Search for:", q, function(query) {
|
||||
if (query && !state.query) cm.operation(function() {
|
||||
startSearch(cm, state, query);
|
||||
state.posFrom = state.posTo = cm.getCursor();
|
||||
findNext(cm, rev);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function findNext(cm, rev, callback) {cm.operation(function() {
|
||||
var state = getSearchState(cm);
|
||||
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
|
||||
if (!cursor.find(rev)) {
|
||||
cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
|
||||
if (!cursor.find(rev)) return;
|
||||
}
|
||||
cm.setSelection(cursor.from(), cursor.to());
|
||||
cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
|
||||
state.posFrom = cursor.from(); state.posTo = cursor.to();
|
||||
if (callback) callback(cursor.from(), cursor.to())
|
||||
});}
|
||||
|
||||
function clearSearch(cm) {cm.operation(function() {
|
||||
var state = getSearchState(cm);
|
||||
state.lastQuery = state.query;
|
||||
if (!state.query) return;
|
||||
state.query = state.queryText = null;
|
||||
cm.removeOverlay(state.overlay);
|
||||
if (state.annotate) { state.annotate.clear(); state.annotate = null; }
|
||||
});}
|
||||
|
||||
function el(tag, attrs) {
|
||||
var element = tag ? document.createElement(tag) : document.createDocumentFragment();
|
||||
for (var key in attrs) {
|
||||
element[key] = attrs[key];
|
||||
}
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i]
|
||||
element.appendChild(typeof child == "string" ? document.createTextNode(child) : child);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
function getQueryDialog(cm) {
|
||||
return el("", null,
|
||||
el("span", {className: "CodeMirror-search-label"}, cm.phrase("Search:")), " ",
|
||||
el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field"}), " ",
|
||||
el("span", {style: "color: #888", className: "CodeMirror-search-hint"},
|
||||
cm.phrase("(Use /re/ syntax for regexp search)")));
|
||||
}
|
||||
function getReplaceQueryDialog(cm) {
|
||||
return el("", null, " ",
|
||||
el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field"}), " ",
|
||||
el("span", {style: "color: #888", className: "CodeMirror-search-hint"},
|
||||
cm.phrase("(Use /re/ syntax for regexp search)")));
|
||||
}
|
||||
function getReplacementQueryDialog(cm) {
|
||||
return el("", null,
|
||||
el("span", {className: "CodeMirror-search-label"}, cm.phrase("With:")), " ",
|
||||
el("input", {type: "text", "style": "width: 10em", className: "CodeMirror-search-field"}));
|
||||
}
|
||||
function getDoReplaceConfirm(cm) {
|
||||
return el("", null,
|
||||
el("span", {className: "CodeMirror-search-label"}, cm.phrase("Replace?")), " ",
|
||||
el("button", {}, cm.phrase("Yes")), " ",
|
||||
el("button", {}, cm.phrase("No")), " ",
|
||||
el("button", {}, cm.phrase("All")), " ",
|
||||
el("button", {}, cm.phrase("Stop")));
|
||||
}
|
||||
|
||||
function replaceAll(cm, query, text) {
|
||||
cm.operation(function() {
|
||||
for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
|
||||
if (typeof query != "string") {
|
||||
var match = cm.getRange(cursor.from(), cursor.to()).match(query);
|
||||
cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
|
||||
} else cursor.replace(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function replace(cm, all) {
|
||||
if (cm.getOption("readOnly")) return;
|
||||
var query = cm.getSelection() || getSearchState(cm).lastQuery;
|
||||
var dialogText = all ? cm.phrase("Replace all:") : cm.phrase("Replace:")
|
||||
var fragment = el("", null,
|
||||
el("span", {className: "CodeMirror-search-label"}, dialogText),
|
||||
getReplaceQueryDialog(cm))
|
||||
dialog(cm, fragment, dialogText, query, function(query) {
|
||||
if (!query) return;
|
||||
query = parseQuery(query);
|
||||
dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function(text) {
|
||||
text = parseString(text)
|
||||
if (all) {
|
||||
replaceAll(cm, query, text)
|
||||
} else {
|
||||
clearSearch(cm);
|
||||
var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
|
||||
var advance = function() {
|
||||
var start = cursor.from(), match;
|
||||
if (!(match = cursor.findNext())) {
|
||||
cursor = getSearchCursor(cm, query);
|
||||
if (!(match = cursor.findNext()) ||
|
||||
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
|
||||
}
|
||||
cm.setSelection(cursor.from(), cursor.to());
|
||||
cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
|
||||
confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase("Replace?"),
|
||||
[function() {doReplace(match);}, advance,
|
||||
function() {replaceAll(cm, query, text)}]);
|
||||
};
|
||||
var doReplace = function(match) {
|
||||
cursor.replace(typeof query == "string" ? text :
|
||||
text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
|
||||
advance();
|
||||
};
|
||||
advance();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
|
||||
CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
|
||||
CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
|
||||
CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
|
||||
CodeMirror.commands.findNext = doSearch;
|
||||
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
|
||||
CodeMirror.commands.clearSearch = clearSearch;
|
||||
CodeMirror.commands.replace = replace;
|
||||
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
|
||||
});
|
305
codemirror/searchcursor.js
Normal file
305
codemirror/searchcursor.js
Normal file
|
@ -0,0 +1,305 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://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 Pos = CodeMirror.Pos
|
||||
|
||||
function regexpFlags(regexp) {
|
||||
var flags = regexp.flags
|
||||
return flags != null ? flags : (regexp.ignoreCase ? "i" : "")
|
||||
+ (regexp.global ? "g" : "")
|
||||
+ (regexp.multiline ? "m" : "")
|
||||
}
|
||||
|
||||
function ensureFlags(regexp, flags) {
|
||||
var current = regexpFlags(regexp), target = current
|
||||
for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)
|
||||
target += flags.charAt(i)
|
||||
return current == target ? regexp : new RegExp(regexp.source, target)
|
||||
}
|
||||
|
||||
function maybeMultiline(regexp) {
|
||||
return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
|
||||
}
|
||||
|
||||
function searchRegexpForward(doc, regexp, start) {
|
||||
regexp = ensureFlags(regexp, "g")
|
||||
for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
|
||||
regexp.lastIndex = ch
|
||||
var string = doc.getLine(line), match = regexp.exec(string)
|
||||
if (match)
|
||||
return {from: Pos(line, match.index),
|
||||
to: Pos(line, match.index + match[0].length),
|
||||
match: match}
|
||||
}
|
||||
}
|
||||
|
||||
function searchRegexpForwardMultiline(doc, regexp, start) {
|
||||
if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
|
||||
|
||||
regexp = ensureFlags(regexp, "gm")
|
||||
var string, chunk = 1
|
||||
for (var line = start.line, last = doc.lastLine(); line <= last;) {
|
||||
// This grows the search buffer in exponentially-sized chunks
|
||||
// between matches, so that nearby matches are fast and don't
|
||||
// require concatenating the whole document (in case we're
|
||||
// searching for something that has tons of matches), but at the
|
||||
// same time, the amount of retries is limited.
|
||||
for (var i = 0; i < chunk; i++) {
|
||||
if (line > last) break
|
||||
var curLine = doc.getLine(line++)
|
||||
string = string == null ? curLine : string + "\n" + curLine
|
||||
}
|
||||
chunk = chunk * 2
|
||||
regexp.lastIndex = start.ch
|
||||
var match = regexp.exec(string)
|
||||
if (match) {
|
||||
var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
|
||||
var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length
|
||||
return {from: Pos(startLine, startCh),
|
||||
to: Pos(startLine + inside.length - 1,
|
||||
inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
|
||||
match: match}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lastMatchIn(string, regexp, endMargin) {
|
||||
var match, from = 0
|
||||
while (from <= string.length) {
|
||||
regexp.lastIndex = from
|
||||
var newMatch = regexp.exec(string)
|
||||
if (!newMatch) break
|
||||
var end = newMatch.index + newMatch[0].length
|
||||
if (end > string.length - endMargin) break
|
||||
if (!match || end > match.index + match[0].length)
|
||||
match = newMatch
|
||||
from = newMatch.index + 1
|
||||
}
|
||||
return match
|
||||
}
|
||||
|
||||
function searchRegexpBackward(doc, regexp, start) {
|
||||
regexp = ensureFlags(regexp, "g")
|
||||
for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
|
||||
var string = doc.getLine(line)
|
||||
var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch)
|
||||
if (match)
|
||||
return {from: Pos(line, match.index),
|
||||
to: Pos(line, match.index + match[0].length),
|
||||
match: match}
|
||||
}
|
||||
}
|
||||
|
||||
function searchRegexpBackwardMultiline(doc, regexp, start) {
|
||||
if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start)
|
||||
regexp = ensureFlags(regexp, "gm")
|
||||
var string, chunkSize = 1, endMargin = doc.getLine(start.line).length - start.ch
|
||||
for (var line = start.line, first = doc.firstLine(); line >= first;) {
|
||||
for (var i = 0; i < chunkSize && line >= first; i++) {
|
||||
var curLine = doc.getLine(line--)
|
||||
string = string == null ? curLine : curLine + "\n" + string
|
||||
}
|
||||
chunkSize *= 2
|
||||
|
||||
var match = lastMatchIn(string, regexp, endMargin)
|
||||
if (match) {
|
||||
var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
|
||||
var startLine = line + before.length, startCh = before[before.length - 1].length
|
||||
return {from: Pos(startLine, startCh),
|
||||
to: Pos(startLine + inside.length - 1,
|
||||
inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
|
||||
match: match}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var doFold, noFold
|
||||
if (String.prototype.normalize) {
|
||||
doFold = function(str) { return str.normalize("NFD").toLowerCase() }
|
||||
noFold = function(str) { return str.normalize("NFD") }
|
||||
} else {
|
||||
doFold = function(str) { return str.toLowerCase() }
|
||||
noFold = function(str) { return str }
|
||||
}
|
||||
|
||||
// Maps a position in a case-folded line back to a position in the original line
|
||||
// (compensating for codepoints increasing in number during folding)
|
||||
function adjustPos(orig, folded, pos, foldFunc) {
|
||||
if (orig.length == folded.length) return pos
|
||||
for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {
|
||||
if (min == max) return min
|
||||
var mid = (min + max) >> 1
|
||||
var len = foldFunc(orig.slice(0, mid)).length
|
||||
if (len == pos) return mid
|
||||
else if (len > pos) max = mid
|
||||
else min = mid + 1
|
||||
}
|
||||
}
|
||||
|
||||
function searchStringForward(doc, query, start, caseFold) {
|
||||
// Empty string would match anything and never progress, so we
|
||||
// define it to match nothing instead.
|
||||
if (!query.length) return null
|
||||
var fold = caseFold ? doFold : noFold
|
||||
var lines = fold(query).split(/\r|\n\r?/)
|
||||
|
||||
search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {
|
||||
var orig = doc.getLine(line).slice(ch), string = fold(orig)
|
||||
if (lines.length == 1) {
|
||||
var found = string.indexOf(lines[0])
|
||||
if (found == -1) continue search
|
||||
var start = adjustPos(orig, string, found, fold) + ch
|
||||
return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
|
||||
to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
|
||||
} else {
|
||||
var cutFrom = string.length - lines[0].length
|
||||
if (string.slice(cutFrom) != lines[0]) continue search
|
||||
for (var i = 1; i < lines.length - 1; i++)
|
||||
if (fold(doc.getLine(line + i)) != lines[i]) continue search
|
||||
var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
|
||||
if (endString.slice(0, lastLine.length) != lastLine) continue search
|
||||
return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
|
||||
to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function searchStringBackward(doc, query, start, caseFold) {
|
||||
if (!query.length) return null
|
||||
var fold = caseFold ? doFold : noFold
|
||||
var lines = fold(query).split(/\r|\n\r?/)
|
||||
|
||||
search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {
|
||||
var orig = doc.getLine(line)
|
||||
if (ch > -1) orig = orig.slice(0, ch)
|
||||
var string = fold(orig)
|
||||
if (lines.length == 1) {
|
||||
var found = string.lastIndexOf(lines[0])
|
||||
if (found == -1) continue search
|
||||
return {from: Pos(line, adjustPos(orig, string, found, fold)),
|
||||
to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}
|
||||
} else {
|
||||
var lastLine = lines[lines.length - 1]
|
||||
if (string.slice(0, lastLine.length) != lastLine) continue search
|
||||
for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++)
|
||||
if (fold(doc.getLine(start + i)) != lines[i]) continue search
|
||||
var top = doc.getLine(line + 1 - lines.length), topString = fold(top)
|
||||
if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search
|
||||
return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),
|
||||
to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function SearchCursor(doc, query, pos, options) {
|
||||
this.atOccurrence = false
|
||||
this.afterEmptyMatch = false
|
||||
this.doc = doc
|
||||
pos = pos ? doc.clipPos(pos) : Pos(0, 0)
|
||||
this.pos = {from: pos, to: pos}
|
||||
|
||||
var caseFold
|
||||
if (typeof options == "object") {
|
||||
caseFold = options.caseFold
|
||||
} else { // Backwards compat for when caseFold was the 4th argument
|
||||
caseFold = options
|
||||
options = null
|
||||
}
|
||||
|
||||
if (typeof query == "string") {
|
||||
if (caseFold == null) caseFold = false
|
||||
this.matches = function(reverse, pos) {
|
||||
return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
|
||||
}
|
||||
} else {
|
||||
query = ensureFlags(query, "gm")
|
||||
if (!options || options.multiline !== false)
|
||||
this.matches = function(reverse, pos) {
|
||||
return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
|
||||
}
|
||||
else
|
||||
this.matches = function(reverse, pos) {
|
||||
return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SearchCursor.prototype = {
|
||||
findNext: function() {return this.find(false)},
|
||||
findPrevious: function() {return this.find(true)},
|
||||
|
||||
find: function(reverse) {
|
||||
var head = this.doc.clipPos(reverse ? this.pos.from : this.pos.to);
|
||||
if (this.afterEmptyMatch && this.atOccurrence) {
|
||||
// do not return the same 0 width match twice
|
||||
head = Pos(head.line, head.ch)
|
||||
if (reverse) {
|
||||
head.ch--;
|
||||
if (head.ch < 0) {
|
||||
head.line--;
|
||||
head.ch = (this.doc.getLine(head.line) || "").length;
|
||||
}
|
||||
} else {
|
||||
head.ch++;
|
||||
if (head.ch > (this.doc.getLine(head.line) || "").length) {
|
||||
head.ch = 0;
|
||||
head.line++;
|
||||
}
|
||||
}
|
||||
if (CodeMirror.cmpPos(head, this.doc.clipPos(head)) != 0) {
|
||||
return this.atOccurrence = false
|
||||
}
|
||||
}
|
||||
var result = this.matches(reverse, head)
|
||||
this.afterEmptyMatch = result && CodeMirror.cmpPos(result.from, result.to) == 0
|
||||
|
||||
if (result) {
|
||||
this.pos = result
|
||||
this.atOccurrence = true
|
||||
return this.pos.match || true
|
||||
} else {
|
||||
var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)
|
||||
this.pos = {from: end, to: end}
|
||||
return this.atOccurrence = false
|
||||
}
|
||||
},
|
||||
|
||||
from: function() {if (this.atOccurrence) return this.pos.from},
|
||||
to: function() {if (this.atOccurrence) return this.pos.to},
|
||||
|
||||
replace: function(newText, origin) {
|
||||
if (!this.atOccurrence) return
|
||||
var lines = CodeMirror.splitLines(newText)
|
||||
this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)
|
||||
this.pos.to = Pos(this.pos.from.line + lines.length - 1,
|
||||
lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
|
||||
}
|
||||
}
|
||||
|
||||
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
|
||||
return new SearchCursor(this.doc, query, pos, caseFold)
|
||||
})
|
||||
CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
|
||||
return new SearchCursor(this, query, pos, caseFold)
|
||||
})
|
||||
|
||||
CodeMirror.defineExtension("selectMatches", function(query, caseFold) {
|
||||
var ranges = []
|
||||
var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold)
|
||||
while (cur.findNext()) {
|
||||
if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break
|
||||
ranges.push({anchor: cur.from(), head: cur.to()})
|
||||
}
|
||||
if (ranges.length)
|
||||
this.setSelections(ranges, 0)
|
||||
})
|
||||
});
|
96
index.html
96
index.html
|
@ -1,6 +1,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: dosvga;
|
||||
|
@ -25,7 +26,6 @@
|
|||
top: 10px;
|
||||
}
|
||||
#code {
|
||||
/*width: 600px;*/
|
||||
margin-bottom: 5px;
|
||||
border: 1px solid #666;
|
||||
}
|
||||
|
@ -163,13 +163,31 @@
|
|||
#share-dialog a { color: #333; }
|
||||
#share-dialog a:hover { color: #000; }
|
||||
|
||||
#logo {
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
top: 1px;
|
||||
cursor: pointer;
|
||||
opacity: 0.5;
|
||||
}
|
||||
#logo:hover { opacity: 1.0; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.css"></link>
|
||||
<link rel="stylesheet" href="codemirror/codemirror.min.css"></link>
|
||||
<link rel="stylesheet" href="codemirror/dialog.css">
|
||||
<link rel="stylesheet" href="codemirror/matchesonscrollbar.css">
|
||||
<link rel="stylesheet" href="codemirror/qb-ide.css"></link>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/codemirror.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="codemirror/codemirror.min.js"></script>
|
||||
<script type="text/javascript" src="codemirror/qb-lang.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.52.2/addon/selection/active-line.js"></script>
|
||||
<script type="text/javascript" src="codemirror/active-line.js"></script>
|
||||
<script type="text/javascript" src="util/shorty.min.js"></script>
|
||||
<script type="text/javascript" src="util/jszip.min.js"></script>
|
||||
|
||||
<script type="text/javascript" src="codemirror/dialog.js"></script>
|
||||
<script type="text/javascript" src="codemirror/searchcursor.js"></script>
|
||||
<script type="text/javascript" src="codemirror/search.js"></script>
|
||||
<script type="text/javascript" src="codemirror/annotatescrollbar.js"></script>
|
||||
<script type="text/javascript" src="codemirror/matchesonscrollbar.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="code-container">
|
||||
|
@ -185,6 +203,7 @@
|
|||
<div id="game-container">
|
||||
<div id="gx-container">
|
||||
<a href="javascript:runProgram()" id="gx-load-screen"> </a>
|
||||
<img id="logo" src="logo.png" onclick="window.open('https://github.com/boxgaming/qbjs/wiki', '_blank')">
|
||||
</div>
|
||||
<div id="output-container">
|
||||
<div id="tabs">
|
||||
|
@ -195,7 +214,9 @@
|
|||
<div id="js-code"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="show-js-container"><a id="toggle-console" href="javascript:showConsole()">Show Console</a></div> <!-- <input type="checkbox" id="show-js" onclick="window.onresize()"/> Show Javascript</div> -->
|
||||
<div id="show-js-container">
|
||||
<a id="toggle-console" href="javascript:showConsole()">Show Console</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="gx-footer"></div>
|
||||
|
||||
|
@ -209,14 +230,16 @@
|
|||
<option value="auto">Auto</option>
|
||||
</select>
|
||||
</div>
|
||||
<a href="javascript:closeDialog()" style="display:block; float:right; margin-top: 5px">Close</a>
|
||||
<a href="javascript:testShare()" style="display: block; float:right; margin-top: 5px; margin-right: 10px">Test</a>
|
||||
<a id="export-button" href="javascript:exportProgram()" style="display: none; float:left; margin-top: 7px; margin-left: 10px">Export</a>
|
||||
<a href="javascript:closeDialog()" style="display:block; float:right; margin-top: 7px">Close</a>
|
||||
<a href="javascript:testShare()" style="display: block; float:right; margin-top: 7px; margin-right: 10px">Test</a>
|
||||
</dialog>
|
||||
</body>
|
||||
<script language="javascript" src="gx/gx.js"></script>
|
||||
<script language="javascript" src="qb.js"></script>
|
||||
<script language="javascript" src="qb2js.js"></script>
|
||||
<script language="javascript">
|
||||
|
||||
var QBCompiler = null;
|
||||
// if code has been passed on the query string load it into the editor
|
||||
var qbcode = "";
|
||||
|
@ -236,6 +259,9 @@
|
|||
qbcode = zin.inflate(atob(nv[1]));
|
||||
break;
|
||||
}
|
||||
else if (nv[0] == "code") {
|
||||
qbcode = LZUTF8.decompress(nv[1], { inputEncoding: "Base64" });
|
||||
}
|
||||
else if (nv[0] == "mode") {
|
||||
appMode = nv[1];
|
||||
}
|
||||
|
@ -252,9 +278,10 @@
|
|||
indentUnit: 4,
|
||||
value: qbcode,
|
||||
module: "qbjs",
|
||||
theme: "lesser-dark",
|
||||
theme: "qbjs",
|
||||
height: "auto",
|
||||
styleActiveLine: true,
|
||||
smartIndent: false,
|
||||
extraKeys: {
|
||||
"Tab": function(cm) {
|
||||
cm.replaceSelection(" ", "end");
|
||||
|
@ -319,7 +346,7 @@
|
|||
|
||||
function shareProgram() {
|
||||
var zout = new Shorty();
|
||||
var b64 = btoa(zout.deflate(editor.getValue()));
|
||||
var b64 = LZUTF8.compress(editor.getValue(), { outputEncoding: "Base64" });
|
||||
var baseUrl = location.href.split('?')[0];
|
||||
|
||||
var mode = document.getElementById("share-mode").value;
|
||||
|
@ -328,11 +355,57 @@
|
|||
if (mode) {
|
||||
url += "mode=" + mode + "&";
|
||||
}
|
||||
url += "qbcode=" + b64;
|
||||
url += "code=" + b64;
|
||||
codeShare.value = url;
|
||||
document.getElementById("share-dialog").showModal();
|
||||
var shareDialog = document.getElementById("share-dialog");
|
||||
if (!shareDialog.open) {
|
||||
shareDialog.showModal();
|
||||
}
|
||||
codeShare.focus();
|
||||
codeShare.select();
|
||||
|
||||
var exportVisible = (mode == "play" || mode == "auto");
|
||||
document.getElementById("export-button").style.display = (exportVisible) ? "block" : "none";
|
||||
}
|
||||
|
||||
async function exportProgram() {
|
||||
var zip = new JSZip();
|
||||
|
||||
var qbCode = editor.getValue();
|
||||
if (!QBCompiler) { QBCompiler = await _QBCompiler(); }
|
||||
var jsCode = "async function __qbjs_run() {\n" + await QBCompiler.compile(qbCode) + "\n}";
|
||||
|
||||
var mode = document.getElementById("share-mode").value;
|
||||
zip.file("index.html", await getFile("export/" + mode + ".html", "text"));
|
||||
zip.file("program.js", jsCode);
|
||||
zip.file("fullscreen.png", await getFile("export/fullscreen.png", "blob"));
|
||||
zip.file("logo.png", await getFile("logo.png", "blob"));
|
||||
zip.file("lp-dosvga.ttf", await getFile("lp-dosvga.ttf", "blob"));
|
||||
zip.file("play.png", await getFile("play.png", "blob"));
|
||||
zip.file("qbjs.css", await getFile("export/qbjs.css", "text"));
|
||||
zip.file("qb.js", await getFile("qb.js", "text"));
|
||||
|
||||
zip.file("gx/gx.js", await getFile("gx/gx.js", "text"));
|
||||
zip.file("gx/__gx_font_default.png", await getFile("gx/__gx_font_default.png", "blob"));
|
||||
zip.file("gx/__gx_font_default_black.png", await getFile("gx/__gx_font_default_black.png", "blob"));
|
||||
|
||||
zip.generateAsync({type:"blob"}).then(function(content) {
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(content);
|
||||
link.download = "program.zip";
|
||||
link.click();
|
||||
link.remove();
|
||||
});
|
||||
}
|
||||
|
||||
async function getFile(path, type, contentType) {
|
||||
var file = await fetch(path);
|
||||
if (type == "text") {
|
||||
return await file.text();
|
||||
}
|
||||
else if (type == "blob") {
|
||||
return await file.blob();
|
||||
}
|
||||
}
|
||||
|
||||
function testShare() {
|
||||
|
@ -432,6 +505,7 @@
|
|||
document.getElementById("game-container").style.left = "0px";
|
||||
document.getElementById("game-container").style.top = "0px";
|
||||
jsDiv.style.display = "none";
|
||||
document.getElementById("logo").style.display = "none";
|
||||
}
|
||||
else {
|
||||
var cmwidth = 600;
|
||||
|
|
Loading…
Reference in a new issue