827 строки
32 KiB
TypeScript
827 строки
32 KiB
TypeScript
///<reference path='refs.ts'/>
|
|
|
|
module TDev
|
|
{
|
|
export interface HTMLElementWithIntelliItem extends HTMLElement
|
|
{
|
|
intelliItem: IntelliItem;
|
|
}
|
|
|
|
export class SearchApi
|
|
extends SideTab {
|
|
constructor (public calc: Calculator) {
|
|
super()
|
|
this.autoUpdate = KeyboardAutoUpdate.mkInput(this.searchBox, null);
|
|
}
|
|
public icon() { return "svg:search,black"; }
|
|
public name() { return "results"; }
|
|
public keyShortcut() { return "Ctrl-F"; }
|
|
public isModal() { return true; }
|
|
public isNav() { return true; }
|
|
private lastSearchValue = "";
|
|
private lastHistoryVersion = 0;
|
|
private lastSelectedIdx = -1;
|
|
private lastEntryCount = -1;
|
|
private wasSelected: any = {};
|
|
private searchTerms: string[] = [];
|
|
private progressBar = HTML.mkProgressBar();
|
|
|
|
private searchBox = HTML.mkTextInput("text", lf("Search..."), "search");
|
|
private autoUpdate: KeyboardAutoUpdate;
|
|
private backContainer = div("apiBackContainer");
|
|
private searchDiv = div("apiTopDiv");
|
|
private snapshotId: Promise = null;
|
|
|
|
private dismissBtn:HTMLElement;
|
|
private runBtn:HTMLElement;
|
|
private toCodeBtn:HTMLElement;
|
|
public visible = false;
|
|
public prepopulate: string;
|
|
public artKind: Kind; // restricting to picture or sounds
|
|
private doInsertOnDismissing = false;
|
|
|
|
private dismissDiv = div("apiDismiss");
|
|
|
|
public listenToSpeech() {
|
|
var recognition = TDev.RT.WebSpeechManager.createRecognition();
|
|
if(recognition) {
|
|
recognition.continuous = true; // stop when user stops talking
|
|
recognition.interimResults = true; // only report final results
|
|
recognition.lang = 'en-US';
|
|
recognition.onresult = (e : any) => {
|
|
Util.log('reco:result');
|
|
var res = '';
|
|
for (var i = e.resultIndex; i < e.results.length; ++i)
|
|
res += e.results[i][0].transcript;
|
|
if (res)
|
|
this.show(res, false);
|
|
}
|
|
recognition.onerror = (e:any) => {
|
|
Util.log('reco:error' + e);
|
|
}
|
|
recognition.onstart = () => {
|
|
Util.log('reco:start');
|
|
}
|
|
recognition.onend = () => {
|
|
Util.log('reco:end');
|
|
}
|
|
recognition.start();
|
|
}
|
|
}
|
|
|
|
|
|
public navigatedTo() {
|
|
super.navigatedTo();
|
|
this.navRefreshPending = true; // we want to always refresh
|
|
}
|
|
|
|
public cancel()
|
|
{
|
|
this.doInsertOnDismissing = false;
|
|
this.searchBox.blur();
|
|
this.calc.display();
|
|
}
|
|
|
|
public cancelImplicitInsert()
|
|
{
|
|
this.doInsertOnDismissing = false;
|
|
}
|
|
|
|
public setSize() {
|
|
var off = this.searchDiv.offsetHeight;
|
|
if (TheEditor.autoHide()) off = 0;
|
|
this.scrollRoot.style.top = off + "px";
|
|
this.scrollRoot.style.height = this.visualRoot.offsetHeight - off + "px";
|
|
}
|
|
|
|
public handleKey(e: KeyboardEvent) {
|
|
if (e.keyName == "Esc") {
|
|
this.cancel();
|
|
return true;
|
|
}
|
|
|
|
if (this.handleCarretKeys(e))
|
|
return true;
|
|
|
|
var charName = String.fromCharCode(e.charCode);
|
|
if (!this.getVerb(this.searchBox.value) &&
|
|
this.carretIdx != -1 &&
|
|
(e.keyName == 'Tab' || /^[:()\-+\/*=<>|&\'\".,]$/.test(charName))) {
|
|
this.onEnter();
|
|
e.fromTextBox = false;
|
|
Util.setTimeout(1, () => this.calc.handleKey(e));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public resetState() {
|
|
this.progressBar.reset();
|
|
this.searchDiv.removeSelf();
|
|
this.snapshotId = null;
|
|
}
|
|
|
|
public onEnter() {
|
|
if (!super.onEnter()) {
|
|
this.carretIdx = 0;
|
|
this.highlightCarret();
|
|
return true;
|
|
}
|
|
this.searchBox.blur();
|
|
return true;
|
|
}
|
|
|
|
private setVR() {
|
|
//this.searchDiv.removeSelf();
|
|
this.searchDiv.setChildren(<any[]>[this.backContainer, this.searchBox, this.progressBar]);
|
|
// IEMobile seems to pass some clicks through here
|
|
this.searchDiv.withClick(() => {});
|
|
if (TheEditor.autoHide()) {
|
|
this.searchDiv.appendChild(
|
|
TheEditor.mkTabMenuItem("svg:search,black", "all APIs", null, Ticks.calcBtnApiSearch, () => {
|
|
if (TheEditor.sidePaneVisible())
|
|
this.cancel();
|
|
else
|
|
this.startSearch(false)
|
|
}));
|
|
elt("leftBtnRow").appendChild(this.searchDiv);
|
|
} else {
|
|
this.visualRoot.setChildrenIfNeeded([this.searchDiv, this.scrollRoot]);
|
|
}
|
|
}
|
|
|
|
public refreshCore() {
|
|
this.setVR();
|
|
// scrollRoot.addEventListener("scroll", function => calc.onIntelliScroll());
|
|
this.setupList();
|
|
/*
|
|
if (justNavigatedTo) {
|
|
// this will not show the keyboard on the iPad
|
|
// without the timeout the keyboard shows for a split second and then hides
|
|
// I have no idea why
|
|
Util.setTimeout(1, () => { Util.setKeyboardFocus(searchBox); });
|
|
// Util.log("keyboard focused");
|
|
}
|
|
*/
|
|
}
|
|
|
|
private startSearch(focus:boolean) {
|
|
tick(Ticks.calcStartSearch)
|
|
|
|
this.carretIdx = focus ? 0 : -1;
|
|
this.calc.onIntelliScroll();
|
|
this.lastSearchValue = null;
|
|
var selectAll = false;
|
|
if (this.prepopulate) {
|
|
this.searchBox.value = this.prepopulate;
|
|
this.prepopulate = null;
|
|
this.lastSearchValue = "";
|
|
focus = true;
|
|
selectAll = true;
|
|
}
|
|
this.searchKey();
|
|
Util.showLeftPanel(this.scrollRoot);
|
|
if (focus)
|
|
Util.setKeyboardFocus(this.searchBox, selectAll);
|
|
//this.setSize();
|
|
}
|
|
|
|
public show(value = "", focus = true) {
|
|
this.searchBox.value = value;
|
|
this.startSearch(focus);
|
|
}
|
|
|
|
private newItems() {
|
|
this.searchBox.blur();
|
|
Util.setTimeout(1, () => { this.searchBox.blur() });
|
|
this.lastSearchValue = null;
|
|
this.searchBox.value = "";
|
|
this.carretIdx = 0;
|
|
this.searchKey();
|
|
}
|
|
|
|
public displayPlaceholder() {
|
|
this.searchBox.value = "";
|
|
this.lastSearchValue = null;
|
|
this.setChildren([this.dismissDiv]);
|
|
this.setPlaceholder();
|
|
this.setVR();
|
|
}
|
|
|
|
public dismissing()
|
|
{
|
|
this.artKind = null;
|
|
if (this.doInsertOnDismissing) {
|
|
this.doInsertOnDismissing = false;
|
|
return super.onEnter();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private setPlaceholder() {
|
|
this.searchBox.placeholder = lf("Search...");
|
|
this.searchBox.setAttribute("aria-label", this.searchBox.placeholder);
|
|
}
|
|
|
|
public updateRunButton() {
|
|
if (this.runBtn) {
|
|
if (TheEditor.currentRt && TheEditor.currentRt.canResume())
|
|
this.runBtn.setChildren([Editor.mkTopMenuItem("svg:resume,black", lf("resume"), Ticks.calcSearchRun, "Ctrl-P", () => {
|
|
TheEditor.dismissSidePane();
|
|
TheEditor.resumeExecution();
|
|
})]);
|
|
else
|
|
this.runBtn.setChildren([Editor.mkTopMenuItem("svg:play,black", lf("run"), Ticks.calcSearchRun, "Ctrl-P", () => {
|
|
TheEditor.dismissSidePane();
|
|
TheEditor.runMainAction();
|
|
})]);
|
|
}
|
|
}
|
|
|
|
public init(e: Editor) {
|
|
super.init(e);
|
|
|
|
this.searchBox.id = "apiSearchBox";
|
|
this.setPlaceholder();
|
|
|
|
this.dismissDiv.withClick(() => TheEditor.dismissSidePane());
|
|
|
|
this.searchBox.onkeyup = Util.catchErrors("sideSearch-searchKey", () => this.searchKey());
|
|
this.searchBox.onclick = Util.catchErrors("sideSearch-on-focus", () => this.startSearch(false));
|
|
HTML.enableSpeech(this.searchBox, Util.catchErrors("sideSearch-onspeechchange", () => this.searchKey()));
|
|
this.setVR();
|
|
|
|
this.toCodeBtn =
|
|
Editor.mkTopMenuItem("svg:back,black", lf("to code"), Ticks.calcSearchBack, "Esc", () => {
|
|
this.cancel();
|
|
});
|
|
this.dismissBtn =
|
|
Editor.mkTopMenuItem("svg:back,black", lf("dismiss"), Ticks.calcSearchBack, "", () => {
|
|
TheEditor.dismissSidePane()
|
|
});
|
|
this.runBtn = div("");
|
|
this.updateRunButton();
|
|
this.runBtn.style.zIndex = "1"; // above apiDismiss
|
|
this.runBtn.style.position = "relative";
|
|
|
|
this.toCodeBtn.style.display = "none";
|
|
this.backContainer.setChildren([
|
|
this.dismissBtn,
|
|
this.runBtn,
|
|
this.toCodeBtn
|
|
]);
|
|
}
|
|
|
|
public moveCarret(d: number) {
|
|
super.moveCarret(d);
|
|
this.calc.onIntelliScroll();
|
|
}
|
|
|
|
private searchKey() {
|
|
this.searchBox.placeholder = "";
|
|
if (this.lastSearchValue != this.searchBox.value) {
|
|
this.lastSearchValue = this.searchBox.value;
|
|
this.setupList();
|
|
this.highlightCarret();
|
|
}
|
|
}
|
|
|
|
public setVisible(v:boolean)
|
|
{
|
|
this.visible = v;
|
|
if (this.visible) {
|
|
this.doInsertOnDismissing = true;
|
|
this.toCodeBtn.style.display = "inline-block";
|
|
this.dismissBtn.style.display = "none";
|
|
this.runBtn.style.display = "none";
|
|
} else {
|
|
this.toCodeBtn.style.display = "none";
|
|
this.dismissBtn.style.display = "inline-block";
|
|
this.runBtn.style.display = "inline-block";
|
|
}
|
|
}
|
|
|
|
public carretBound() { return -1 }
|
|
|
|
public highlightCarret() {
|
|
super.highlightCarret();
|
|
|
|
this.calc.displayTokenPlaceholder(null, this.searchTerms);
|
|
if (this.searchTerms.length > 0) {
|
|
var e = <HTMLElementWithIntelliItem>this.htmlEntries[this.carretIdx];
|
|
if (e) this.calc.displayTokenPlaceholder(e.intelliItem, this.searchTerms);
|
|
}
|
|
}
|
|
|
|
private getVerb(s: string) {
|
|
var verb = "";
|
|
var mtch = /^\?([a-zA-Z]+)/.exec(s);
|
|
if (mtch) {
|
|
switch (mtch[1].toLowerCase()) {
|
|
case 'd': return 'd';
|
|
case 'a': return 'a';
|
|
case 'l': return 'l';
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
private stripVerb(s: string) {
|
|
var v = this.getVerb(s);
|
|
if (v) return s.slice(v.length + 1);
|
|
return s;
|
|
}
|
|
|
|
private setupList() {
|
|
var maxLen = 20;
|
|
var hasMore = false;
|
|
|
|
var items: HTMLElement[] = [];
|
|
|
|
var allTerms = this.searchBox.value;
|
|
var verb = this.getVerb(allTerms);
|
|
|
|
var terms = allTerms.split(/\s+/).map((s: string) => s.toLowerCase()).filter((s) => s != "");
|
|
var fullName = terms.join("");
|
|
|
|
this.searchTerms = terms;
|
|
var isSpecificKind = false;
|
|
|
|
if (terms.length == 0) maxLen = 100;
|
|
|
|
var updateScore = (score: number, prop: IProperty) => {
|
|
if (!prop || !score) return score;
|
|
score *= 1e-1;
|
|
var isSingleton = prop.parentKind.singleton || prop.parentKind instanceof AST.LibraryRefKind;
|
|
if ((!isSpecificKind && !isSingleton) || prop.parentKind.getName() == "Invalid")
|
|
score *= 1e-5;
|
|
if (!Script.canUseProperty(prop))
|
|
score *= 1e-10;
|
|
return score;
|
|
}
|
|
|
|
var addList = (entries: IntelliItem[]) => {
|
|
var cmpName = (a: IntelliItem, b: IntelliItem) =>
|
|
b.alphaOverride() - a.alphaOverride() ||
|
|
Util.stringCompare(a.getName(), b.getName());
|
|
var cmpScoreThenName = (a: IntelliItem, b: IntelliItem) =>
|
|
b.lastMatchScore - a.lastMatchScore || b.score - a.score || cmpName(a, b);
|
|
|
|
if (entries.length == 0) return;
|
|
if (terms.length > 0) {
|
|
var totalProps = 0;
|
|
entries = entries.filter((it: IntelliItem) => {
|
|
it.lastMatchScore = updateScore(it.match(terms, fullName), it.prop);
|
|
if (it.lowSearch) it.lastMatchScore *= 0.05;
|
|
return it.lastMatchScore > 0;
|
|
});
|
|
entries.sort(cmpScoreThenName);
|
|
} else {
|
|
entries.sort(cmpName);
|
|
}
|
|
if (entries.length == 0) return;
|
|
// if (!!n) items.push(div("navHeader", n));
|
|
if (entries.length > maxLen) {
|
|
entries = entries.slice(0, maxLen);
|
|
hasMore = true
|
|
}
|
|
entries.forEach((it: IntelliItem) => {
|
|
var b = it.mkBox();
|
|
if (terms.length > 0)
|
|
Util.highlightWords(b, terms);
|
|
(<HTMLElementWithIntelliItem>b).intelliItem = it;
|
|
if (it.prop && !Script.canUseProperty(it.prop))
|
|
b.className += " disabledItem";
|
|
items.push(b);
|
|
});
|
|
|
|
if (hasMore) items.push(IntelliItem.thereIsMore());
|
|
}
|
|
|
|
var its = this.calc.currentIntelliItems;
|
|
var singletons: IntelliItem[] = its.filter((it: IntelliItem) => it.decl instanceof AST.SingletonDef);
|
|
|
|
var propScore = (p: IProperty) => p.forwardsTo() ? 10 : 0;
|
|
|
|
var getProps = () =>
|
|
{
|
|
var props: IProperty[] = [];
|
|
|
|
function addProp(p:IProperty) {
|
|
var s = updateScore(IntelliItem.matchProp(p, terms, fullName), p);
|
|
if (s > 0) {
|
|
props.push(p);
|
|
p.lastMatchScore = s;
|
|
}
|
|
}
|
|
|
|
var profile = this.editor.intelliProfile;
|
|
Script.getKinds().filter(k => (!profile || profile.hasKind(k))).forEach((k) => {
|
|
k.listProperties()
|
|
.filter(prop => prop.isBrowsable() && (!profile || profile.hasProperty(prop)))
|
|
.forEach(addProp);
|
|
});
|
|
Script.libraries().forEach((l) => {
|
|
l.getKind().listProperties().filter(p => p.isBrowsable() && !(<AST.LibraryRefAction>p)._extensionAction).forEach(addProp);
|
|
});
|
|
props.sort((a: IProperty, b: IProperty) => {
|
|
if (a.lastMatchScore != b.lastMatchScore) return b.lastMatchScore - a.lastMatchScore;
|
|
var aa = propScore(a);
|
|
var bb = propScore(b);
|
|
if (aa != bb) return bb - aa;
|
|
// This is super slow on Chrome
|
|
// return an.localeCompare(bn);
|
|
return Util.nameCompare(a, b);
|
|
});
|
|
return props;
|
|
}
|
|
|
|
var allProps = () =>
|
|
{
|
|
var props: IProperty[] = getProps();
|
|
|
|
/*
|
|
// about 3ms per full search on IE, Core i7
|
|
Util.time("search-props", () => {
|
|
for (var i = 0; i < 100; ++i) {
|
|
props = getProps();
|
|
}
|
|
});
|
|
*/
|
|
|
|
if (props.length > maxLen) {
|
|
hasMore = true;
|
|
props = props.slice(0, maxLen);
|
|
}
|
|
var propIts: IntelliItem[] = props.map((p: IProperty) => {
|
|
var it = new IntelliItem();
|
|
it.prop = p;
|
|
it.isAttachedTo = p.parentKind;
|
|
it.score = propScore(p);
|
|
it.tick = Ticks.calcIntelliProperty;
|
|
return it;
|
|
});
|
|
|
|
propIts.pushRange(its.filter((it: IntelliItem) => !it.prop));
|
|
|
|
var insertLit = (name: string, tp = "literal") => {
|
|
var tr = new IntelliItem();
|
|
tr.tick = Ticks.calcIntelliLiteral;
|
|
tr.nameOverride = name;
|
|
tr.descOverride = lf("Insert '{0}' {1}", name, tp);
|
|
tr.cbOverride = () => { this.calc.insertOp(name); };
|
|
tr.score = 10;
|
|
propIts.push(tr);
|
|
}
|
|
insertLit("true");
|
|
insertLit("false");
|
|
insertLit("not", "operator");
|
|
if (asyncEnabled && TheEditor.widgetEnabled("async")) {
|
|
insertLit("async", "operator");
|
|
}
|
|
propIts.pushRange(TheEditor.selector.getStmtIntelliItems());
|
|
|
|
addList(propIts);
|
|
}
|
|
|
|
if (terms.length > 0)
|
|
this.calc.onIntelliScroll();
|
|
|
|
items = [];
|
|
|
|
if ((singletons.length > 0 || TheEditor.calculator.inSelectionMode()) && terms.length > 0) {
|
|
isSpecificKind = false;
|
|
allProps();
|
|
} else {
|
|
isSpecificKind = true;
|
|
addList(its); //.filter((it:IntelliItem) => !!it.prop));
|
|
}
|
|
|
|
var singleton = TheEditor.calculator.getSingletonBeforeCursor()
|
|
var verbOverride = null
|
|
|
|
if (singleton && singleton.getName() == "art")
|
|
verbOverride = "a"
|
|
if (singleton && singleton.getName() == AST.libSymbol)
|
|
verbOverride = "l"
|
|
|
|
var autoOnlineSearch = (!isSpecificKind || verbOverride) && terms.length > 0 && allTerms.length > 1 && items.length < 15;
|
|
|
|
if (verb || autoOnlineSearch) {
|
|
this.autoUpdate.lastValue = "";
|
|
this.autoUpdate.update = (s) => this.runOnlineSearchAsync(verbOverride, s);
|
|
this.autoUpdate.keypress();
|
|
}
|
|
if (!verb && !isSpecificKind) {
|
|
//items.push(this.onlineBox("d", "do as i mean", "tell us what you want and we'll try to generate a program that does it"));
|
|
//items.push(this.onlineBox("a", "search online art", "find pictures and sounds uploaded by you and others"));
|
|
//gets in the way on small screens
|
|
//items.push(this.onlineBox("l", "search for libraries", "find libraries online doing useful stuff"));
|
|
}
|
|
|
|
if (!verb && !autoOnlineSearch) // synthesis might be on...
|
|
{
|
|
this.autoUpdate.update = null;
|
|
this.autoUpdate.keypress();
|
|
}
|
|
|
|
this.htmlEntries = items.slice(0);
|
|
|
|
this.setChildren(items);
|
|
this.setSize();
|
|
}
|
|
|
|
private onlineBox(verb: string, name: string, desc: string) {
|
|
var dwim = new IntelliItem();
|
|
dwim.nameOverride = name;
|
|
dwim.descOverride = "?" + verb + ": " + desc;
|
|
dwim.colorOverride = "#FF7518";
|
|
dwim.cbOverride = () => {
|
|
var terms = this.stripVerb(this.searchBox.value);
|
|
this.searchBox.value = "?" + verb + " " + terms;
|
|
Util.setKeyboardFocus(this.searchBox);
|
|
this.searchKey();
|
|
};
|
|
return dwim.mkBox();
|
|
}
|
|
|
|
private runOnlineSearchAsync(verbOverride:string, terms: string) : Promise {
|
|
if (Cloud.isOffline()) return Promise.as(); // better offline experience
|
|
|
|
var verb = this.getVerb(terms);
|
|
if (verbOverride) verb = verbOverride
|
|
|
|
var itemCount = (Browser.isMobile ? 5 : 20) + terms.length * 2;
|
|
/* SYNTHESIS if (verb[0] == 'd') {
|
|
tick(Ticks.searchApiSynthesis);
|
|
return this.runSynthesisAsync(terms);
|
|
} else */
|
|
if (verb[0] == 'a') {
|
|
tick(Ticks.searchApiSearchArt);
|
|
return this.searchAzureSearchArtAsync(terms, itemCount);
|
|
// return this.searchCoreAsync("art?count=" + itemCount + "&q=", terms);
|
|
} else if (verb[0] == 'l') {
|
|
tick(Ticks.searchApiSearchLib);
|
|
return this.searchCoreAsync("scripts?count=" + itemCount + "&q=" + encodeURIComponent("*library "), terms, "", p => (<Browser.ScriptInfo>p).isLibrary());
|
|
} else {
|
|
tick(Ticks.searchApiSearchAuto);
|
|
/* SYNTHESIS this.runSynthesisAsync(terms).then(() => )*/
|
|
return this.searchAzureSearchArtAsync(terms, itemCount);
|
|
// return this.searchCoreAsync("art?count=" + itemCount + "&q=", terms);
|
|
}
|
|
}
|
|
|
|
private searchMessage(msg: string) {
|
|
this.setChildren([<HTMLElement>div("navMessage", msg)].concat(this.htmlEntries))
|
|
}
|
|
|
|
private searchAzureSearchArtAsync(terms: string, itemCount: number, kind: string = undefined) {
|
|
if (!this.editor.widgetEnabled("calcSearchArt") || !this.autoUpdate.resultsCurrent(terms)) {
|
|
return Promise.as();
|
|
}
|
|
if (!kind && this.artKind) kind = this.artKind.getName().toLowerCase();
|
|
|
|
var uploadPicBtn, uploadSndBtn;
|
|
this.progressBar.start();
|
|
|
|
return Meta.searchArtAsync(terms, kind).then(arts => {
|
|
this.progressBar.stop();
|
|
if (!this.autoUpdate.resultsCurrent(terms)) {
|
|
return;
|
|
}
|
|
|
|
var els = [];
|
|
arts.map(art => {
|
|
var el = art.mkSmallBoxNoClick().withClick(() => {
|
|
art.getJsonAsync().done(() => {
|
|
tick(Ticks.searchApiInsertArt);
|
|
this.appendArt(art.art);
|
|
});
|
|
});
|
|
els.push(el);
|
|
});
|
|
if (this.editor.widgetEnabled('uploadArtInSearchButton')) {
|
|
if (uploadPicBtn) this.removeBtn(uploadPicBtn);
|
|
uploadPicBtn = HTML.mkButton(lf("upload picture"), () => {
|
|
this.removeBtn(uploadPicBtn);
|
|
ArtUtil.uploadPictureDialogAsync()
|
|
.done((a: JsonArt) => {
|
|
tick(Ticks.searchApiUploadArt);
|
|
if (a) this.appendArt(a);
|
|
});
|
|
});
|
|
els.push(uploadPicBtn);
|
|
|
|
uploadSndBtn = HTML.mkButton(lf("upload sound"), () => {
|
|
this.removeBtn(uploadSndBtn);
|
|
ArtUtil.uploadSoundDialogAsync()
|
|
.done((a: JsonArt) => {
|
|
tick(Ticks.searchApiUploadArt);
|
|
if (a) this.appendArt(a);
|
|
});
|
|
});
|
|
els.push(uploadSndBtn);
|
|
}
|
|
if (els.length > 0) {
|
|
els.forEach((c) => this.htmlEntries.push(c));
|
|
this.setChildren(this.htmlEntries);
|
|
}
|
|
});
|
|
}
|
|
|
|
private appendArt(a : JsonArt) {
|
|
this.editor.undoMgr.pushMainUndoState();
|
|
this.editor.undoMgr.clearCalc();
|
|
var n = null;
|
|
var appendPlay = false;
|
|
if (a && a.pictureurl) {
|
|
n = this.editor.freshPictureResource(a.name);
|
|
n.url = a.pictureurl;
|
|
} else {
|
|
n = this.editor.freshSoundResource(a.name);
|
|
n.url = a.wavurl;
|
|
appendPlay = true;
|
|
}
|
|
Script.addDecl(n);
|
|
this.editor.queueNavRefresh();
|
|
this.cancelImplicitInsert();
|
|
var artDecl = TDev.api.getThing("art")
|
|
if (this.calc.getSingletonBeforeCursor() != artDecl)
|
|
this.calc.insertThing(artDecl);
|
|
this.calc.insertProp(n);
|
|
if (appendPlay)
|
|
this.calc.insertProp(TDev.api.getKind("Sound").getProperty("play"));
|
|
}
|
|
|
|
private removeBtn(btn : HTMLElement) {
|
|
btn.removeSelf();
|
|
var idx = this.htmlEntries.indexOf(btn);
|
|
if (idx >= 0)
|
|
this.htmlEntries.splice(idx, 1);
|
|
}
|
|
|
|
private searchCoreAsync(pref: string, terms: string, continuation : string = undefined, filter : (p:Browser.BrowserPage) => boolean = p => true) : Promise {
|
|
if (!this.autoUpdate.resultsCurrent(terms)) {
|
|
return Promise.as();
|
|
}
|
|
|
|
this.progressBar.start();
|
|
var uri = pref + encodeURIComponent(this.stripVerb(terms));
|
|
if (continuation) uri += "&continuation=" + encodeURIComponent(continuation);
|
|
return Browser.TheHost.getLocationList(uri, (itms: Browser.BrowserPage[], cont: string) => {
|
|
this.progressBar.stop();
|
|
if (!this.autoUpdate.resultsCurrent(terms)) {
|
|
return;
|
|
}
|
|
|
|
var els = [];
|
|
itms.forEach((itm: Browser.BrowserPage) => {
|
|
if (!filter(itm)) return;
|
|
if (itm instanceof Browser.ArtInfo) {
|
|
var art = <Browser.ArtInfo>itm;
|
|
var el = art.mkSmallBoxNoClick().withClick(() => {
|
|
art.getJsonAsync().done(() => {
|
|
tick(Ticks.searchApiInsertArt);
|
|
this.appendArt(art.art);
|
|
});
|
|
});
|
|
els.push(el);
|
|
} else if (itm instanceof Browser.ScriptInfo) {
|
|
var scr = <Browser.ScriptInfo>itm;
|
|
var el = scr.mkSmallBoxNoClick().withClick(() => {
|
|
if (scr.app && scr.app.isLibrary) {
|
|
tick(Ticks.searchApiInsertLib);
|
|
this.editor.undoMgr.pushMainUndoState();
|
|
this.editor.undoMgr.clearCalc();
|
|
var lib = this.editor.freshLibrary();
|
|
Script.addDecl(lib);
|
|
LibraryRefProperties.bindLibraryAsync(lib, scr).done(() => {
|
|
this.cancelImplicitInsert();
|
|
var libDecl = TDev.api.getThing(AST.libSymbol)
|
|
if (this.calc.getSingletonBeforeCursor() != libDecl)
|
|
this.calc.insertThing(libDecl);
|
|
this.calc.insertProp(lib);
|
|
this.editor.queueNavRefresh();
|
|
});
|
|
}
|
|
});
|
|
els.push(el);
|
|
}
|
|
});
|
|
|
|
if (els.length > 0) {
|
|
els.forEach((c) => this.htmlEntries.push(c));
|
|
this.setChildren(this.htmlEntries);
|
|
}
|
|
}, true);
|
|
}
|
|
|
|
/* SYNTHESIS
|
|
private runSynthesisAsync(search: string): Promise {
|
|
if (!Cloud.hasAccessToken() || Cloud.isOffline() || !Browser.canLogin) return Promise.as(); // better offline experience
|
|
|
|
if (!this.calc || !this.calc.expr)
|
|
return Promise.as();
|
|
|
|
var urlPref = "me/installed/" + Script.localGuid + "/";
|
|
|
|
this.progressBar.start();
|
|
|
|
if (!this.snapshotId) {
|
|
var tw = AST.TokenWriter.forStorage()
|
|
tw.skipActionBodies = true
|
|
Script.writeTo(tw);
|
|
var req =
|
|
{
|
|
script: tw.finalize(),
|
|
actionname: TheEditor.lastDecl.getName(),
|
|
locals: (this.calc.expr.locals || []).map((l: AST.LocalDef) => { return { name: l.getName(), kind: l.getKind().toString() } }),
|
|
culture: "en-US"
|
|
}
|
|
this.snapshotId =
|
|
Cloud.postPrivateApiAsync(urlPref + "snapshot", req).then((resp) => {
|
|
if (resp && resp.snapshotid) return resp.snapshotid;
|
|
else {
|
|
HTML.showErrorNotification("wrong synthesis snapshot response");
|
|
return "";
|
|
}
|
|
});
|
|
}
|
|
|
|
return this.snapshotId
|
|
.then((snapshotId) => {
|
|
if (!this.autoUpdate.resultsCurrent(search)) {
|
|
this.progressBar.stop();
|
|
return Promise.as();
|
|
}
|
|
var url = urlPref + "synthesis?snapshotid=" + encodeURIComponent(snapshotId) +
|
|
"&query=" + encodeURIComponent(this.stripVerb(search));
|
|
return Util.httpGetJsonAsync(Cloud.getPrivateApiUrl(url));
|
|
})
|
|
.then((resp) => {
|
|
if (!resp || !this.autoUpdate.resultsCurrent(search)) {
|
|
this.progressBar.stop();
|
|
return;
|
|
}
|
|
|
|
if (Array.isArray(resp.results)) {
|
|
var renderer = new TDev.EditorRenderer()
|
|
var children = <HTMLElement[]>resp.results.slice(0, 1).map((res: string, idx: number) => {
|
|
var stmt = AST.Parser.parseStmt(res)
|
|
var stmtDiv = renderer.renderStmt(stmt)
|
|
stmtDiv.className = "codeSearchResult";
|
|
|
|
var icon = div("navImg", HTML.mkImg("svg:Wand,white"));
|
|
icon.style.backgroundColor = "blue";
|
|
var innerElt = div("navItemInner", icon, div("navContent", stmtDiv));
|
|
var elt = HTML.mkButtonElt("navItem codeSearchItem", innerElt);
|
|
|
|
elt.withClick(() => {
|
|
var url = urlPref + "synthesis?synthesisid=" + encodeURIComponent(resp.synthesisid) + "&index=" + idx;
|
|
Cloud.postPrivateApiAsync(url, {}).done(undefined, () => { });
|
|
stmt.accept(new SynthesisCleaner());
|
|
TheEditor.selector.injectBelow(stmt);
|
|
});
|
|
return elt;
|
|
});
|
|
|
|
if (children.length > 0) {
|
|
children.forEach((c) => this.htmlEntries.push(c));
|
|
this.setChildren(this.htmlEntries);
|
|
}
|
|
} else {
|
|
HTML.showErrorNotification("wrong synthesis response");
|
|
}
|
|
this.progressBar.stop();
|
|
});
|
|
} */
|
|
}
|
|
|
|
/* SYNTHESIS
|
|
export class SynthesisCleaner
|
|
extends TDev.AST.NodeVisitor
|
|
{
|
|
constructor () {
|
|
super()
|
|
}
|
|
|
|
visitAction(n: TDev.AST.Action) {
|
|
this.visitChildren(n);
|
|
return null
|
|
}
|
|
|
|
visitBlock(n: TDev.AST.Block) {
|
|
n.stmts = n.stmts.filter((s) => s.nodeType() != "comment");
|
|
this.visitChildren(n);
|
|
}
|
|
|
|
visitStmt(s:TDev.AST.Stmt)
|
|
{
|
|
this.visitChildren(s);
|
|
return null
|
|
}
|
|
} */
|
|
}
|
|
|