c9-core/plugins/c9.ide.find.replace/findreplace.js

1026 wiersze
38 KiB
JavaScript

define(function(require, exports, module) {
"use strict";
main.consumes = [
"Plugin", "settings", "ui", "layout",
"menus", "tabManager", "commands", "tooltip", "apf"
];
main.provides = ["findreplace"];
return main;
function main(options, imports, register) {
var Plugin = imports.Plugin;
var settings = imports.settings;
var ui = imports.ui;
var menus = imports.menus;
var layout = imports.layout;
var commands = imports.commands;
var tooltip = imports.tooltip;
var tabs = imports.tabManager;
var apf = imports.apf;
var css = require("text!./findreplace.css");
var skin = require("text!./skin.xml");
var markup = require("text!./findreplace.xml");
var lib = require("plugins/c9.ide.find.replace/libsearch");
var asyncSearch = require("./async_search");
var Range = require("ace/range").Range;
/***** Initialization *****/
var plugin = new Plugin("Ajax.org", main.consumes);
var emit = plugin.getEmitter();
var libsearch = lib(settings, execFind, toggleDialog, restore, toggleOption);
var searchRow, txtFind, winSearchReplace, txtReplace;
var tooltipSearchReplace, divSearchCount;
var btnPrev, btnNext, btnReplace, btnReplaceAll, hbox, btnCollapse;
var chk = {};
var currentRange, lastSearchOptions;
var timer, startPos = {};
function toggleOption() {
var ch;
switch (this.name) {
case "regex": ch = chk.regEx; break;
case "wholeWords": ch = chk.wholeWords; break;
case "matchCase": ch = chk.matchCase; break;
}
ch.change(!ch.checked, true);
execFind();
}
var loaded = false;
function load() {
if (loaded) return false;
loaded = true;
function isSupported(editor) {
if (apf.activeElement === txtFind || apf.activeElement === txtReplace)
return true;
return editor && editor.ace;
}
function isSupportedRW(editor) {
var ace = isSupported(editor);
return ace === true || !ace ? ace : !ace.getOption("readOnly");
}
commands.addCommands({
replace: {
bindKey: { mac: "Option-Command-F", win: "Alt-Shift-F|Ctrl-H" },
hint: "search for a string inside the active document and replace it",
isAvailable: isSupportedRW,
exec: function(env, args, request) {
toggleDialog(1, true);
}
},
replaceall: {
bindKey: { mac: "", win: "" },
hint: "search for a string inside the active document and replace all",
isAvailable: isSupportedRW,
exec: function(env, args, request) {
replaceAll();
}
},
replacenext: {
isAvailable: isSupportedRW,
exec: function(env, args, request) {
replace();
}
},
replaceprevious: {
isAvailable: isSupportedRW,
exec: function(env, args, request) {
replace(true);
}
},
findAll: {
isAvailable: isSupported,
bindKey: { mac: "Ctrl-Alt-G", win: "Ctrl-Alt-K" },
exec: function(editor) {
findAgain(editor.ace, 0);
},
},
findnext: {
isAvailable: isSupported,
bindKey: { mac: "Command-G", win: "Ctrl-K" },
exec: function(editor) {
findAgain(editor.ace, 1);
},
},
findprevious: {
isAvailable: isSupported,
bindKey: { mac: "Command-Shift-G", win: "Ctrl-Shift-K" },
exec: function(editor) {
findAgain(editor.ace, -1);
},
},
find: {
hint: "open the quicksearch dialog to quickly search for a phrase",
bindKey: { mac: "Command-F", win: "Ctrl-F" },
isAvailable: isSupported,
exec: function(env, args, request) {
toggleDialog(1, false);
}
},
hidesearchreplace: {
bindKey: { mac: "ESC", win: "ESC" },
isAvailable: function(editor) {
return winSearchReplace && winSearchReplace.visible;
},
exec: function(env, args, request) {
toggleDialog(-1);
}
}
}, plugin);
menus.addItemByPath("Find/Find...", new ui.item({
command: "find"
}), 100, plugin);
menus.addItemByPath("Find/Find Next", new ui.item({
command: "findnext"
}), 200, plugin);
menus.addItemByPath("Find/Find Previous", new ui.item({
command: "findprevious"
}), 300, plugin);
menus.addItemByPath("Find/~", new ui.divider(), 400, plugin);
menus.addItemByPath("Find/Replace...", new ui.item({
command: "replace"
}), 500, plugin);
menus.addItemByPath("Find/Replace Next", new ui.item({
command: "replacenext",
}), 600, plugin);
menus.addItemByPath("Find/Replace Previous", new ui.item({
command: "replaceprevious",
}), 700, plugin);
menus.addItemByPath("Find/Replace All", new ui.item({
command: "replaceall"
}), 800, plugin);
tabs.on("focus", function(e) {
if (winSearchReplace && winSearchReplace.visible) {
if (e.tab && e.tab.editor.ace) {
winSearchReplace.enable();
execFind(false, "highlight");
}
else {
winSearchReplace.disable();
btnCollapse.enable();
updateCounter();
}
}
});
}
var drawn = false;
function draw() {
if (drawn) return;
drawn = true;
// Import CSS
ui.insertCss(css, null, plugin);
// Import Skin
ui.insertSkin({
name: "searchreplace",
data: skin,
}, plugin);
// Create UI elements
searchRow = layout.findParent(plugin);
ui.insertMarkup(null, markup, plugin);
txtFind = plugin.getElement("txtFind");
winSearchReplace = plugin.getElement("winSearchReplace");
txtReplace = plugin.getElement("txtReplace");
tooltipSearchReplace = plugin.getElement("tooltipSearchReplace");
chk.searchSelection = plugin.getElement("chkSearchSelection");
divSearchCount = plugin.getElement("divSearchCount");
hbox = plugin.getElement("hbox");
chk.regEx = plugin.getElement("chkRegEx");
chk.wrapAround = plugin.getElement("chkWrapAround");
chk.matchCase = plugin.getElement("chkMatchCase");
chk.wholeWords = plugin.getElement("chkWholeWords");
chk.preserveCase = plugin.getElement("chkPreserveCase");
btnPrev = plugin.getElement("btnPrev");
btnNext = plugin.getElement("btnNext");
btnReplace = plugin.getElement("btnReplace");
btnReplaceAll = plugin.getElement("btnReplaceAll");
btnCollapse = plugin.getElement("btnCollapse");
btnNext.on("click", function() { findNext(false); });
btnPrev.on("click", function() { findNext(true); });
btnReplace.on("click", function() { replace(); });
btnReplaceAll.on("click", function() { replaceAll(); });
btnCollapse.on("click", function() { toggleDialog(-1); });
txtFind.$ext.appendChild(divSearchCount.$ext);
txtFind.$ext.appendChild(btnPrev.$ext);
txtFind.$ext.appendChild(btnNext.$ext);
var first = 0;
function resize() {
if (first++ < 2) { return; } // Skip first 2 calls
var h = winSearchReplace.$ext.scrollHeight;
if (Math.abs(winSearchReplace.height - h) < 1) { return; }
winSearchReplace.setHeight(h);
winSearchReplace.$ext.style.height = "";
ui.layout.forceResize(null, true);
}
txtFind.ace.renderer.on("autosize", resize);
txtReplace.ace.renderer.on("autosize", resize);
var timer, control;
txtReplace.on("focus", function() {
if (control) control.stop();
control = {};
// I'd rather use css anims, but they didn't seem to work
apf.tween.single(txtReplace.$ext.parentNode, {
type: "boxFlex",
from: txtReplace.$ext.parentNode.style[apf.CSS_FLEX_PROP] || 1,
to: 3,
anim: apf.tween.easeOutCubic,
control: control,
steps: 15,
interval: 1,
onfinish: function() {
ui.layout.forceResize(null, true);
}
});
});
txtReplace.on("blur", function() {
if (txtReplace.getValue())
return;
if (control) control.stop();
control = {};
// I'd rather use css anims, but they didn't seem to work
apf.tween.single(txtReplace.$ext.parentNode, {
type: "boxFlex",
from: txtReplace.$ext.parentNode.style[apf.CSSPREFIX + "BoxFlex"] || 3,
to: 1,
anim: apf.tween.easeOutCubic,
control: control,
steps: 15,
interval: 1,
onfinish: function() {
ui.layout.forceResize(null, true);
}
});
});
settings.on("read", function(e) {
settings.setDefaults("state/ace/search", [
["regex", "false"],
["matchcase", "false"],
["wholeword", "false"],
["backwards", "false"],
["wraparound", "true"],
["highlightmatches", "true"],
["preservecase", "false"]
]);
}, plugin);
var kb = libsearch.addSearchKeyboardHandler(txtReplace, "replace");
kb.bindKeys({
"Return": function(codebox) { replace(); },
"Shift-Return": function(codebox) { replace(true); }
});
document.body.appendChild(tooltipSearchReplace.$ext);
chk.regEx.on("prop.value", function(e) {
libsearch.setRegexpMode(txtFind, ui.isTrue(e.value));
});
libsearch.setRegexpMode(txtFind, chk.regEx.checked);
libsearch.setReplaceFieldMode(txtReplace, "extended");
decorateCheckboxes(hbox);
[txtReplace].forEach(function(node) {
tooltip.add(node.$ext, {
message: node.label,
width: "auto",
timeout: 0,
tooltip: tooltipSearchReplace.$ext,
animate: false,
getPosition: function() {
var pos = ui.getAbsolutePosition(winSearchReplace.$ext);
var pos2 = ui.getAbsolutePosition(node.$ext, winSearchReplace.$ext);
var left = pos[0] + pos2[0];
var top = pos[1];
return [left, top - 16];
}
}, plugin);
});
libsearch.addSearchKeyboardHandler(txtFind, "search");
txtFind.ace.session.on("change", function(e) {
clearTimeout(timer);
var find = !libsearch.keyStroke;
timer = setTimeout(function() {
execFind(false, find ? false : "highlight");
}, 20);
});
txtFind.ace.commands.on("exec", function(e) {
if (/centerselection|fold|comment/i.test(e.command.name)) {
getAce().execCommand(e.command.name);
e.preventDefault();
e.stopPropagation();
}
});
initFindInRange();
emit("draw");
}
/***** Methods *****/
function decorateCheckboxes(parent) {
var cbs = parent.selectNodes("//a:checkbox");
cbs.forEach(function(cb) {
cb.on("click", function(e) {
if (this.name == "chkSearchSelection") {
if (chk.searchSelection.checked && !txtReplace.ace.isFocused())
txtFind.focus();
updateFindInRangeMarker(e);
}
execFind(undefined, "highlight");
});
tooltip.add(cb.$ext, {
message: cb.label,
width: "auto",
timeout: 0,
tooltip: tooltipSearchReplace.$ext,
animate: false,
getPosition: function() {
var pos = ui.getAbsolutePosition(winSearchReplace.$ext);
var left = cb.$ext.getBoundingClientRect().left;
var top = pos[1];
return [left, top - 16];
}
}, plugin);
});
}
function updateCounter(total, current, msg, wrapped) {
var oIter = divSearchCount.$ext;
if (!oIter) return;
msg = msg || "";
var color = wrapped ? "blue" : "";
if (typeof total == "number" && typeof current == "number") {
if (!total) {
current = 0;
color = "red";
} else {
current = getOptions().backwards ? total - current : current + 1;
}
msg = current + " of " + total + msg;
}
oIter.style.color = color;
oIter.textContent = msg;
}
function setStartPos(ace, force) {
if (!startPos.range || force) {
startPos.range = ace.getSelectionRange();
}
if (chk.searchSelection.checked) {
var range = ace.getSelectionRange();
var isValid = ace.session.getTextRange(range).length > 100;
if (!isValid || currentRange && range.isEqual(currentRange))
range = null;
if (marker || !range && startPos.id == getSessionId(ace.session))
range = startPos.searchRange;
if (range && !range.isEmpty())
startPos.searchRange = range;
else
startPos.searchRange = null;
}
startPos.scrollTop = ace.session.getScrollTop();
startPos.scrollLeft = ace.session.getScrollLeft();
startPos.id = getSessionId(ace.session);
}
function initFromEditor(ace) {
if (!ace.selection.isEmpty() && !ace.selection.isMultiLine())
txtFind.setValue(ace.getCopyText());
}
function toggleDialog(force, isReplace, noselect, callback) {
var tab = tabs.focussedTab;
var editor = tab && tab.editor;
draw();
tooltipSearchReplace.$ext.style.display = "none";
if (!force && !winSearchReplace.visible || force > 0) {
if (!editor || !editor.ace)
return;
winSearchReplace.enable();
var ace = getAce();
var fromEditor = ace && editor && editor.ace == ace;
if (fromEditor) {
if (!isReplace)
initFromEditor(ace);
setStartPos(ace, ace.selection.isEmpty());
}
if (!winSearchReplace.visible)
showUi(callback);
// chk.searchSelection.uncheck();
var input = isReplace ? txtReplace : txtFind;
input.focus();
input.select();
}
else if (winSearchReplace.visible) {
txtFind.ace.saveHistory();
if (!noselect)
tabs.focusTab(tab);
hideUi(null, callback);
}
else if (callback)
callback();
return false;
}
function showUi(callback) {
btnReplaceAll.setCaption(searchRow.getWidth() < 800 ? "All" : "Replace All");
layout.setFindArea(winSearchReplace, {}, callback);
btnCollapse.setValue(1);
}
function hideUi(animate, callback) {
layout.setFindArea(null, {}, callback);
btnCollapse.setValue(0);
}
function restore() {
if (!startPos)
return false;
var editor = getAce();
editor.selection.setSelectionRange(startPos.range || startPos.searchRange);
editor.session.setScrollTop(startPos.scrollTop);
editor.session.setScrollLeft(startPos.scrollLeft);
}
function getOptions() {
var options = {
backwards: false,
wrap: chk.wrapAround.checked,
caseSensitive: chk.matchCase.checked,
wholeWord: chk.wholeWords.checked,
regExp: chk.regEx.checked
};
var ace = getAce();
if (chk.searchSelection.checked) {
options.range = startPos.searchRange;
}
else {
options.range = null;
}
var newLineMode = ace.session.getNewLineMode();
txtFind.ace.session.setNewLineMode(newLineMode);
txtReplace.ace.session.setNewLineMode(newLineMode);
return options;
}
function findNext(backwards) {
execFind(backwards, true);
}
/*
* type can be highlight-> only update highlighting,
* next|true -> skip current selection
* falsy -> do not skip current
*/
function execFind(reverseBackwards, type, options, callback) {
var ace = getAce();
if (!ace || !txtFind)
return;
if (timer)
timer = clearTimeout(timer);
var searchTxt = txtFind.getValue();
if (!options)
options = getOptions();
if (reverseBackwards)
options.backwards = !options.backwards;
if (options.regExp) {
libsearch.checkRegExp(txtFind,
tooltipSearchReplace, winSearchReplace);
}
var range = ace.selection.getRange();
if (type === true)
type = "next";
if (type == "next")
txtFind.ace.saveHistory();
if (type == "next" || !currentRange)
currentRange = range;
options.skipCurrent = type == "next";
options.start = currentRange;
options.needle = searchTxt;
if (options.range && type != "highlight")
addFindInRangeMarker(options.range, ace.session);
else if (!options.range)
removeFindInRangeMarker();
var re = ace.$search.$assembleRegExp(options, true);
if (!re) {
updateCounter();
if (type != "highlight") {
var pos = options.start[options.backwards ? "end" : "start"];
var newRange = options.range || Range.fromPoints(pos, pos);
ace.revealRange(newRange);
}
return callback && callback();
}
if (type != "highlight") {
lastSearchOptions = options;
}
options.re = re;
options.source = re.source;
options.flags = re.ignoreCase ? "igm" : "gm";
asyncSearch.execFind(ace.session, options, function(result) {
if (result == "waiting")
return updateCounter("...");
result = result || { total: 0, current: 0 };
updateCounter(result.total, result.current, null, result.wrapped);
if (!result.start || !result.end) {
result.start =
result.end = range[!options.backwards ? "start" : "end"];
}
var newRange = Range.fromPoints(result.start, result.end);
if (options.range && newRange.isEmpty())
newRange = options.range;
if (type == "next")
currentRange = newRange;
if (type != "highlight")
ace.revealRange(newRange);
if (options.findAll) {
selectAll(result);
} else {
// highlight
ace.session.highlight(re);
ace.session._signal("changeBackMarker");
}
callback && callback(result);
});
function selectAll(result) {
var indexArray = result.matches;
var value = result.value;
var startIndex = result.offset;
var re = options.re;
if (!indexArray.length)
return;
var doc = ace.session.doc;
var ranges = [];
var startPos = { row: 0, column: 0 };
var endPos = { row: 0, column: 0 };
var start = 0, end = 0, offset = 0;
for (var i = 0; i < indexArray.length; i++) {
var index = indexArray[i] + startIndex;
re.lastIndex = index;
var match = re.exec(value);
var txt = match[0];
var len = txt.length;
startPos = doc.indexToPosition(index + offset - start + startPos.column, startPos.row);
start = index + offset;
end = index + len + offset;
endPos = doc.indexToPosition(end - start + startPos.column, startPos.row);
ranges.push(Range.fromPoints(startPos, endPos));
}
ace.selection.fromJSON(ranges);
}
}
function findAgain(ace, direction) {
if (!ace.selection.isEmpty() && lastSearchOptions) {
var text = ace.session.getTextRange();
var match = lastSearchOptions.re && lastSearchOptions.re.exec(text);
if (!match || match[0] != text)
lastSearchOptions = null;
}
if (lastSearchOptions) {
if (chk.searchSelection.checked) {
chk.searchSelection.uncheck();
removeFindInRangeMarker(true);
delete lastSearchOptions.range;
delete lastSearchOptions.indexRange;
marker = null;
}
lastSearchOptions.backwards = direction == -1;
lastSearchOptions.findAll = direction == 0;
execFind(null, true, lastSearchOptions);
} else if (direction == -1) {
ace.findPrevious();
} else if (direction == 1) {
ace.findNext();
} else if (direction == 0) {
ace.findAll();
}
}
function replace(backwards) {
var ace = getAce();
if (!ace)
return;
var options = getOptions();
options.needle = txtFind.getValue();
var re = ace.$search.$assembleRegExp(options, true);
var replaceFn = getReplaceFunction(options);
var range = ace.selection.getRange();
execFind(backwards, false, options, function(result) {
if (!ace.selection.getRange().isEqual(range))
return; // found new one
if (result && result.total) {
re.lastIndex = result.startIndex;
var match = re.exec(result.value);
var replacement = match && replaceFn(match);
if (match[0] != replacement) {
range.end = ace.session.replace(range, replacement);
}
if (options.backwards) {
range.end = range.start;
} else {
range.start = range.end;
}
ace.selection.setRange(range);
}
findNext(backwards);
});
txtReplace.ace.saveHistory();
}
function replaceAll(cb) {
var ace = getAce();
if (!ace)
return;
var options = getOptions();
options.needle = txtFind.getValue();
var re = ace.$search.$assembleRegExp(options, true);
if (!re) {
return updateCounter();
}
options.re = re;
options.source = re.source;
options.flags = re.ignoreCase ? "igm" : "gm";
options.findAll = true;
var replaceFn = getReplaceFunction(options);
ace.$search.set({ preserveCase: chk.preserveCase.checked });
asyncSearch.execFind(ace.session, options, function(result) {
var replaced = 0;
var indexArray = result.matches;
var value = result.value;
var startIndex = result.offset;
var re = options.re;
if (!indexArray.length)
return replaced;
var doc = ace.session.doc;
var startPos = { row: 0, column: 0 };
var endPos = { row: 0, column: 0 };
var start = 0, end = 0, offset = 0;
var range = new Range();
for (var i = 0; i < indexArray.length; i++) {
var index = indexArray[i] + startIndex;
re.lastIndex = index;
var match = re.exec(value);
var txt = match[0];
var len = txt.length;
startPos = doc.indexToPosition(index + offset - start + startPos.column, startPos.row);
start = index + offset;
end = index + len + offset;
endPos = doc.indexToPosition(end - start + startPos.column, startPos.row);
range.start = startPos;
range.end = endPos;
var replacement = replaceFn(match);
if (txt != replacement) {
doc.replace(range, replacement);
offset += replacement.length - txt.length;
}
}
updateCounter();
cb && cb();
});
txtReplace.ace.saveHistory();
}
function getReplaceFunction(options) {
var val = txtReplace.getValue();
options.preserveCase = chk.preserveCase.checked;
if (options.replaceMode == "literal")
return function() { return val; };
var fmtParts = [];
function add(p) {
var last = fmtParts.length - 1;
if (p && typeof p == "string" && typeof fmtParts[last] == "string")
fmtParts[last] += p;
else if (typeof p == "number" || p)
fmtParts.push(p);
}
var lut = { n: "\n", t: "\t", r: "\r", "&": 0, U: -1, L: -2, E: -3, u: -4, l: -5 };
var re = /\$([\$&\d])|\\([\\ULulEntr\d])/g;
var index = 0, m;
while ((m = re.exec(val))) {
add(val.substring(index, m.index));
index = re.lastIndex;
var part = m[1] || m[2];
if (/\d/.test(part))
part = options.regExp ? parseInt(part, 10) : part;
else if (part in lut)
part = lut[part];
add(part);
}
add(val.substr(index));
if (fmtParts.length == 1 && typeof fmtParts[0] == "string" && !options.preserveCase)
return function() { return fmtParts[0]; };
return function(match) {
var gChangeCase = 0;
var changeCase = 0;
var result = "";
for (var i = 0; i < fmtParts.length; i++) {
var ch = fmtParts[i];
if (typeof ch === "number") {
if (ch < 0) {
switch (ch) {
case -1: gChangeCase = 1; break;
case -2: gChangeCase = 2; break;
case -3: gChangeCase = 0; break;
case -4: changeCase = 1; break;
case -5: changeCase = 2; break;
}
continue;
}
ch = match[ch] || "";
}
if (gChangeCase)
ch = gChangeCase === 1 ? ch.toUpperCase() : ch.toLowerCase();
if (changeCase && ch) {
result += changeCase === 1 ? ch[0].toUpperCase() : ch[0].toLowerCase();
ch = ch.substr(1);
changeCase = 0;
}
result += ch;
}
if (options.preserveCase) {
var input = match[0];
var replacement = result.split("");
for (var i = Math.min(input.length, replacement.length); i--;) {
var ch = input[i];
if (ch && ch.toLowerCase() != ch)
replacement[i] = replacement[i].toUpperCase();
else
replacement[i] = replacement[i].toLowerCase();
}
result = replacement.join("");
}
return result;
};
}
function getAce() {
var tab = tabs.focussedTab;
var editor = tab && tab.editor;
return editor && editor.ace;
}
function getSessionId(session) {
return (session.c9doc || session.c9session).name;
}
var marker;
function addFindInRangeMarker(range, session) {
removeFindInRangeMarker();
if (!range || !session || range.isEmpty())
return;
var start = new Range(0, 0, range.start.row, range.start.column);
var end = new Range(range.end.row, range.end.column, Number.MAX_VALUE, Number.MAX_VALUE);
start.id = session.addMarker(start, "findInRangeMarker", true, "line", true);
end.id = session.addMarker(end, "findInRangeMarker", true, "line", true);
range.start = start.end = session.doc.createAnchor(start.end.row, start.end.column);
range.end = end.start = session.doc.createAnchor(end.start.row, end.start.column);
return marker = { start: start, end: end, session: session };
}
function removeFindInRangeMarker(reset) {
if (!marker) return;
var session = marker.session;
session.removeMarker(marker.start.id);
session.removeMarker(marker.end.id);
marker.start.end.detach();
marker.end.start.detach();
marker = null;
}
function updateFindInRangeMarker(e) {
var changeFocusInside = false;
var isBlur = e.name == "blur";
var target = isBlur ? e.toElement : e.fromElement;
if (target && target.$ext) {
changeFocusInside = winSearchReplace.$ext.contains(target.$ext);
}
if (changeFocusInside)
return;
var ace = getAce();
if (isBlur || !ace)
return removeFindInRangeMarker();
if (e.fromElement && e.fromElement.editor) {
if (e.fromElement.editor.ace == ace)
setStartPos(ace);
execFind(false, "highlight");
}
if (!startPos.searchRange || startPos.id !== getSessionId(ace.session) || e.name == "click")
setStartPos(ace);
if (chk.searchSelection.checked)
addFindInRangeMarker(getOptions().range, ace.session);
else
removeFindInRangeMarker();
if (e.name)
currentRange = null;
}
function initFindInRange() {
winSearchReplace.addEventListener("focus", updateFindInRangeMarker);
winSearchReplace.addEventListener("blur", updateFindInRangeMarker);
}
/***** Lifecycle *****/
plugin.on("load", function() {
load();
});
plugin.on("enable", function() {
});
plugin.on("disable", function() {
});
plugin.on("unload", function() {
loaded = false;
asyncSearch.terminateWorker();
searchRow = txtFind = winSearchReplace = txtReplace = null;
tooltipSearchReplace = divSearchCount = null;
btnPrev = btnNext = btnReplace = btnReplaceAll = hbox = btnCollapse = null;
currentRange = lastSearchOptions = timer = null;
startPos = {};
});
/***** Register and define API *****/
/**
* Implements the search and replace UI for Cloud9.
* @singleton
*/
/**
* Fetches a ui element. You can use this method both sync and async.
*
* The search in files plugin has the following elements:
*
* * txtFind - `{ui.textbox}`
* * winSearchReplace - `{ui.window}`
* * txtReplace - `{ui.textbox}`
* * tooltipSearchReplace - `{ui.label}`
* * chkSearchSelection - `{ui.checkbox}`
* * chkRegEx - `{ui.checkbox}`
* * chkWrapAround - `{ui.checkbox}`
* * chkMatchCase - `{ui.checkbox}`
* * chkWholeWords - `{ui.checkbox}`
* * chkPreserveCase - `{ui.checkbox}`
* * btnPrev - `{ui.button}`
* * btnNext - `{ui.button}`
* * btnReplace - `{ui.button}`
* * btnReplaceAll - `{ui.button}`
* * btnCollapse - `{ui.button}`
*
* @method getElement
* @param {String} name the id of the element to fetch.
* @param {Function} [callback] the function to call when the
* element is available (could be immediately)
*/
plugin.freezePublicAPI({
/**
*
*/
get aml() { return winSearchReplace; },
/**
* Toggles the visibility of the search and replace panel.
* @param {Number} force Set to -1 to force hide the panel,
* or set to 1 to force show the panel.
*/
toggle: toggleDialog,
/**
* Return the cursor and selection to where it was, prior to
* starting searching.
*/
restore: restore,
/**
* Find the next occurance of the search query. If wrap around is
* turned on, the search will continue from the beginning when it
* reaches the end of the file.
* @param {Boolean} backwards When set to true the search direction is reversed.
*/
findNext: findNext,
/**
* Replace the next occurance of the query with whatever the user
* entered in the replace textbox.
* @param {Boolean} backwards When set to true the search direction is reversed.
*/
replace: replace,
/**
* Replace all occurences of the query with whatever the user
* entered in the replace textbox.
*/
replaceAll: replaceAll
});
register(null, {
findreplace: plugin
});
}
});