Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

1607
LINES

< > BotCompany Repo | #1023558 // agi.blue source [LIVE]

JavaX module (desktop) [tags: use-pretranspiled]

Download Jar. Uses 1658K of libraries. Click here for Pure Java version (26075L/154K).

1  
!7
2  
3  
// See #1023660 for the older 99 lines version
4  
5  
!include once #1024472 // agi.blue core db
6  
7  
// global constants
8  
9  
static SS searchTypeToText = litmap(
10  
  leven := "Leven",
11  
  literal := "Literal",
12  
  scored := "Scored");
13  
  
14  
// global vars
15  
16  
static int sourceCodeLines;
17  
18  
static RealAGIBlue agiBlue;
19  
static Renderer renderer = new Renderer1;
20  
21  
set flag AllPublic.
22  
23  
sS classesToShare = [[
24  
  DynamicObject Concept Concepts Page Entry Slice LoadedSlice
25  
  IConceptIndex IFieldIndex IConceptCounter ConceptFieldIndexCI ConceptFieldIndexDesc
26  
  AbstractEntry Signer Session User GoogleUser MinimalUser CookieUser
27  
  BlobEntry
28  
  SliceInfo CentralIndexEntry AGIBlue RealAGIBlue TimedCache
29  
  GlobalID Renderer
30  
  IterableIterator CloseableIterableIterator
31  
]];
32  
33  
// end of global data
34  
35  
p {
36  
  agiBlue = new RealAGIBlue;
37  
}
38  
39  
svoid _onLoad_sourceCodeLines {
40  
  sourceCodeLines = countLines(mySource());
41  
}
42  
43  
svoid cleanMeUp {
44  
  cleanUp(agiBlue);
45  
  agiBlue = null;
46  
}
47  
48  
sinterface Renderer {
49  
  O html(S uri, SS params);
50  
}
51  
52  
set flag NoNanoHTTPD. html {
53  
  printWithTime("Starting request");
54  
  O o = renderer.html(uri, params);
55  
  printWithTime("Done with request");
56  
  ret o;
57  
}
58  
59  
sclass RealAGIBlue > AGIBlue {
60  
  volatile bool started, broken;
61  
  TimedCache<Long> sizeOnDisk = new(60.0, f guessClusteredSizeOfProgramDirWithoutBackups);
62  
  
63  
  *() {
64  
    
65  
    try {
66  
      if (bootstrapDataFrom(#1023558)) deleteMyBackups();
67  
      thinMyBackups();
68  
      fan = slicesLoadedOnDemand(loadedSlices);
69  
      mainConcepts = fan.get("", null);
70  
      
71  
      // index main DB
72  
  
73  
      indexConceptFields(Slice, 'globalID, Slice, 'caseID);
74  
      indexConceptField(CookieUser, 'cookie);
75  
      idx_slicesByName = new ConceptFieldIndexCI(Slice, 'name);
76  
      idx_slicesByModification = new ConceptFieldIndexDesc(Slice, '_modified);
77  
      idx_usersByName = new ConceptFieldIndexCI(GoogleUser, 'googleLastName);
78  
      idx_usersBySeen = new ConceptFieldIndexDesc(User, 'lastSeen);
79  
  
80  
      cset(uniq_returnIfNew Slice(caseID := ""), name := "main slice");
81  
      loadedSlices.get("").initialSetup(toGlobalIDObj(mainSliceGlobalID()));
82  
      loadedSlices.get("").sliceConcept = sliceConceptForCaseID("");
83  
      
84  
      backgroundFan = slicesLoadedOnDemand(backgroundSlices);
85  
      backgroundFan.lock = fan.lock;
86  
      
87  
      // legacy conversions!
88  
      //deleteConcepts(Slice, globalID := GlobalID('wftlawbagrwprywn));
89  
    
90  
      // Approve this machine's key
91  
      PKIKeyPair machineKey = agiBot_trustedKeyForMachine();
92  
      if (machineKey != null) {
93  
        print("Approving this machine's key: " + machineKey.publicKey);
94  
        cset(uniq_sync(Signer, publicKey := machineKey.publicKey), trusted := true, approvedBy := "local");
95  
      }
96  
      
97  
      rst_index.trigger();
98  
    } catch print e {
99  
      set broken;
100  
    } finally {
101  
      set started;
102  
    }
103  
    
104  
    // do stuff after we are officially started
105  
    
106  
    // calculate DB size regularly iff there were changes
107  
    sizeOnDisk.keepValueWhileCalculating = true;
108  
    doEveryAndNow(60.0, rWatcher(() -> db_mainConceptsChangeCount(), r { sizeOnDisk! }));
109  
    
110  
  } // end of main program
111  
112  
  L<Entry> entriesOnPage(Page p) {
113  
    ret p == null ? null : sortedByField count(findBackRefs(p, Entry));
114  
  }
115  
  
116  
  L<Page> pagesSortedByLength(L<Page> l) {
117  
    ret sortedByCalculatedField(l, p -> l(p.q));
118  
  }
119  
  
120  
  sbool agiBlue_isOriginal() {
121  
    ret amProgram(#1023558);
122  
  }
123  
  
124  
  S agiBlueURL(S subUri) { ret agiBlueURL() + prependSlash(subUri); }
125  
  
126  
  S agiBlueURL() {
127  
    ret agiBlue_isOriginal() ? "https://agi.blue" :  "/" + psI(programID()) + "/raw";
128  
  }
129  
130  
  S agiBlueName() {
131  
    ret agiBlue_isOriginal()
132  
      ? "agi.blue"
133  
      : isProgramID(#1024512) ? "agi.blue productive"
134  
      : "agi.blue clone " + programID();
135  
  }
136  
  
137  
  void cleanMeUp {
138  
    cleanUp(fan);
139  
  }
140  
  
141  
  LoadedSlice loadSlice(Slice slice) {
142  
    ret loadSlice(slice.caseID, str(slice.globalID));
143  
  }
144  
  
145  
  LoadedSlice loadSlice(S caseID, S globalID) {
146  
    caseID = unnull(caseID);
147  
    lock fan.lock;
148  
    
149  
    // move slice from background to foreground
150  
    
151  
    Concepts cc = backgroundFan.getIfLoaded(caseID);
152  
    if (cc != null) {
153  
      LoadedSlice slice = assertNotNull(backgroundSlices.get(caseID));
154  
      backgroundFan.loaded.remove(caseID);
155  
      fan.loaded.put(caseID, cc);
156  
      backgroundSlices.remove(caseID);
157  
      loadedSlices.put(caseID, slice);
158  
      ret slice;
159  
    }
160  
    
161  
    fan.get(caseID, globalID);
162  
    ret loadedSlices.get(caseID);
163  
  }
164  
  
165  
  void unloadSlice(S caseID) {
166  
    fan.unloadCase(caseID);
167  
    backgroundFan.unloadCase(caseID);
168  
  }
169  
  
170  
  // load background slice
171  
  LoadedSlice loadBackgroundSlice(Slice slice) {
172  
    ret loadBackgroundSlice(slice.caseID, str(slice.globalID));
173  
  }
174  
  
175  
  LoadedSlice loadBackgroundSlice(S caseID, S globalID) {
176  
    caseID = unnull(caseID);
177  
    lock fan.lock;
178  
    
179  
    // check if in foreground
180  
    
181  
    if (fan.getIfLoaded(caseID) != null)
182  
      ret loadedSlices.get(caseID);
183  
  
184  
    backgroundFan.get(caseID, globalID);
185  
    LoadedSlice slice = assertNotNull(backgroundSlices.get(caseID));
186  
    slice.lastAccess = sysNow();
187  
    while (l(backgroundSlices) > maxBackgroundSlices)
188  
      unloadLeastRecentlyAccessedBackgroundSlice();
189  
    assertNotNull("slice.sliceConcept", slice.sliceConcept);
190  
    ret slice;
191  
  }
192  
  
193  
  // TODO: sync unloading slice with accessors
194  
  void unloadLeastRecentlyAccessedBackgroundSlice() {
195  
    lock fan.lock;
196  
    S caseID = lowestByField lastAccess(keys(backgroundSlices));
197  
    if (caseID != null)
198  
      backgroundFan.unloadCase(caseID);
199  
  }
200  
  
201  
  Slice sliceConceptForGlobalID(S globalID) {
202  
    ret sliceConceptForGlobalID(toGlobalIDObj(globalID));
203  
  }
204  
  
205  
  Slice sliceConceptForGlobalID(GlobalID globalID) {
206  
    ret conceptWhere Slice(+globalID);
207  
  }
208  
  
209  
  Slice sliceConceptForCaseID(S caseID) {
210  
    ret conceptWhere Slice(+caseID);
211  
  }
212  
  
213  
  Slice sliceForName(S name) {
214  
    ret conceptWhereCI Slice(+name);
215  
  }
216  
  
217  
  class Query extends WorksOnSlice {
218  
    SS vars = ciMap();
219  
      
220  
    *(LoadedSlice slice) { super(slice); }
221  
    
222  
    O process(S query) {
223  
      try object processLines(agiBlue_parseQueryScript(query));
224  
      ret serveJSON("No return statement");
225  
    }
226  
      
227  
    O processLines(L<ALQLLine> lines) {
228  
      for (ALQLLine line : lines) {
229  
        if (line cast ALQLSlice) {
230  
          Slice slice = sliceForName(line.slice);
231  
          if (slice == null) ret serveJSON("Slice not found: " + line.slice);
232  
          LoadedSlice oldSlice = Query.this.slice;
233  
          try {
234  
            setSlice(loadSlice(slice));
235  
            try object processLines(line.contents);
236  
          } finally {
237  
            setSlice(oldSlice);
238  
          }
239  
        } else if (line cast ALQLReturn)
240  
          ret serveJSON(ll(getOrKeep(vars, line.var)));
241  
        else if (line cast ALQLReturnTuple)
242  
          ret serveJSON(map(v -> getOrKeep(vars, v), line.vars));
243  
        else if (line cast ALQLTriple) {
244  
          T3S t = line.triple;
245  
          t = tripleMap(t, s -> getOrKeep(vars, s));
246  
          Cl<Page> pages;
247  
          S var;
248  
          if (isDollarVar(t.c)) {
249  
            var = t.c;
250  
            if (isDollarVar(t.a)) {
251  
              if (isDollarVar(t.b)) todo(t);
252  
              Entry e = random(conceptsWhereCI(cc, Entry, key := t.b));
253  
              if (e == null) ret serveJSON("No results for " + var);
254  
              pages = pagesForKeyAndValue(t.b, t.c);
255  
              vars.put(t.a, e.page->q);
256  
              vars.put(t.c, e.value);
257  
              continue;
258  
            } else if (isDollarVar(t.b)) {
259  
              Page page = findPageFromQ(t.a);
260  
              Entry e = random(findBackRefs(page, Entry));
261  
              if (e == null) ret serveJSON("No results for " + var);
262  
              vars.put(t.b, e.key);
263  
              vars.put(t.c, e.value);
264  
              continue;
265  
            } else {
266  
              S val = getValue(t.a, t.b);
267  
              if (val == null) ret serveJSON("No results for " + var);
268  
              pages = ll(pageFromQ(val));
269  
            }
270  
          } else if (isDollarVar(t.b)) {
271  
            var = t.b;
272  
            if (isDollarVar(t.c)) todo(t);
273  
            if (isDollarVar(t.a)) {
274  
              L<Entry> entries = conceptsWhereCI(cc, Entry, value := t.c);
275  
              if (empty(entries)) ret serveJSON("No results for " + var);
276  
              Entry e = random(entries);
277  
              vars.put(t.a, e.page->q);
278  
              vars.put(t.b, e.key);
279  
              continue;
280  
            } else {
281  
              Cl<S> keys = keysForPageAndValue(t.a, t.c);
282  
              if (empty(keys)) ret serveJSON("No results for " + var);
283  
              pages = map(f<S, Page> pageFromQ, keys);
284  
            }
285  
          } else {
286  
            var = t.a;
287  
            if (!isDollarVar(t.a)) todo(t);
288  
            if (isDollarVar(t.b)) todo(t);
289  
            if (isDollarVar(t.c)) todo(t);
290  
            pages = pagesForKeyAndValue(t.b, t.c);
291  
          }
292  
          if (empty(pages)) ret serveJSON("No results for " + var);
293  
          vars.put(var, random(pages).q);
294  
        } else if (line cast ALQLPage) {
295  
          if (eq(line.matchMethod, 'eqic))
296  
            findPageFromQ(line.page);
297  
          else if (eq(line.matchMethod, 'flexMatchDollarVarsIC_first)) {
298  
            new L<SS> l;
299  
            for (Page p : list(cc, Page))
300  
              addIfNotNull(l, flexMatchDollarVarsIC_first(line.page, p.q));
301  
            if (empty(l)) ret serveJSON("No results for " + curly(line.page));
302  
            vars.putAll(random(l));
303  
          } else
304  
            fail("Unknown match method: " + line.matchMethod);
305  
        } else
306  
          fail("Can't interpret: " + line);
307  
      }
308  
      
309  
      null;
310  
    }
311  
  }
312  
  
313  
  /*S globalIDFromCaseID(S caseID) {
314  
    ret assertGlobalID(takeLast(globalIDLength(), caseID));
315  
  }*/
316  
  
317  
  S agiBlue_pageURLWithSlice(Page p) {
318  
    ret p == null ? null : agiBlueURL() + hquery(slice := _get(sliceForPage(p), 'globalID), q := p.q);
319  
  }
320  
  
321  
  Slice sliceForPage(Page p) {
322  
    if (p == null) null;
323  
    SliceInfo info = conceptWhere(p._concepts, SliceInfo);
324  
    ret info == null ? null : sliceConceptForGlobalID(info.globalID);
325  
  }
326  
  
327  
  GlobalID mainSliceGlobalID() {
328  
    ret conceptWhere Slice(caseID := "").globalID;
329  
  }
330  
  
331  
  ConceptsLoadedOnDemand slicesLoadedOnDemand(Map<S, LoadedSlice> loadedSlices) {
332  
    new ConceptsLoadedOnDemand fan;
333  
    fan.onCaseLoaded(voidfunc(S caseID, O globalID, Concepts concepts) {
334  
      LoadedSlice ls = new(caseID, concepts);
335  
      concepts.miscMap = mapPutOrCreate_multi(concepts.miscMap,
336  
        agiBlue := RealAGIBlue.this,
337  
        loadedSlice := ls);
338  
      ls.sliceConcept = sliceConceptForCaseID(caseID);
339  
  
340  
      if (nempty(globalID))
341  
        ls.initialSetup(toGlobalIDObj((S) globalID));
342  
      loadedSlices.put(caseID, ls);
343  
    });
344  
    fan.onUnloadingCase(voidfunc(S caseID, Concepts concepts) {
345  
      loadedSlices.remove(caseID);
346  
    });
347  
    ret fan;
348  
  }
349  
  
350  
  // check if slices are loaded both in foreground and background (BAD)
351  
  S checkDoubleLoads() {
352  
    LS l = sharedKeys(loadedSlices, backgroundSlices);
353  
    ret empty(l) ? null : "ERROR: Slice loaded in background and foreground: " + first(l);
354  
  }
355  
  
356  
  // just use size of concepts.structure.gz
357  
  long estimatedSliceDataSize(Slice slice) {
358  
    ret estimatedSliceDataSize(slice.caseID);
359  
  }
360  
  
361  
  long estimatedSliceDataSize(S caseID) {
362  
    ret l(conceptsFile(fan.dbID(caseID)));
363  
  }
364  
  
365  
  // restart magic!
366  
  void softRestart { // TODO
367  
    print("Cloning...");
368  
    cloningSince = sysNow();
369  
    Class c = hotwireDependent(programID());
370  
371  
    // New strategy: Share AGIBlue & just grab the new renderer :)
372  
373  
    copyFields(mc(), c, 'agiBlue, 'mainConcepts, 'creator_class);
374  
    renderer = (Renderer) get renderer(c);
375  
    print("Got new renderer.");
376  
    
377  
    // TODO: any clean up of this instance?
378  
  }
379  
} // end of RealAGIBlue
380  
381  
sclass WorksOnSlice {
382  
  delegate LoadedSlice to AGIBlue.
383  
    
384  
  Concepts cc;
385  
  LoadedSlice slice;
386  
  
387  
  *() {}
388  
  *(LoadedSlice *slice) { cc = slice.cc; }
389  
  
390  
  void setSlice(LoadedSlice slice) {
391  
    this.slice = slice;
392  
    cc = slice.cc;
393  
  }
394  
  
395  
  Page findPageFromParams(Map map) {
396  
    S q = getString q(map);
397  
    ret empty(q) ? null : findPageFromQ(q);
398  
  }
399  
400  
  Page findOrMakePageFromParams(Map map) {
401  
    ret pageFromQ(getString q(map));
402  
  }
403  
  
404  
  Page findPageFromQ(S q) {
405  
    ret conceptWhereCI(cc, Page, +q);
406  
  }
407  
  
408  
  Page pageFromQ(S q) {
409  
    ret slice.pageFromQ(q);
410  
  }
411  
  
412  
  Set<Page> pagesForKeyAndValue(S key, S value) {
413  
    L<Entry> entries = conceptsWhereIC(cc, Entry, +value, +key);
414  
    ret asSet(ccollect page(entries));
415  
  }
416  
  
417  
  Set<Page> pagesWithKey(S key) {
418  
    L<Entry> entries = conceptsWhereIC(cc, Entry, +key);
419  
    ret asSet(ccollect page(entries));
420  
  }
421  
  
422  
  Cl<S> keysForPageAndValue(S q, S value) {
423  
    Page page = findPageFromQ(q);
424  
    if (page == null) null;
425  
    ret collect key(conceptsWhereIC(cc, Entry, +page, +value));
426  
  }
427  
428  
  // DB functions
429  
430  
  bool hasPage(S q) { ret hasConceptWhereIC(Page, +q); }
431  
  S getValue(Page page, S key) {
432  
    ret page == null || empty(key) ? null : getString value(highestByField count(objectsWhereIC(findBackRefs(page, Entry), +key)));
433  
  }
434  
  S getValue(S page, S key) {
435  
    ret getValue(findPageFromQ(page), key);
436  
  }
437  
  S pageDisplayName(Page page) {
438  
    /*S name = getValue(page, "read as");
439  
    bool unnaturalName = nempty(name) && !eq(makeAGIDomain(name), page.url);
440  
    ret unnaturalName ? name + " " + squareBracketed(page.url) : or2(name, unpackAGIDomainOpt(page.url));*/
441  
    ret page.q;
442  
  }
443  
  
444  
  S renderThing(S s, bool forceURLDisplay) {
445  
    ret forceURLDisplay || isURL(s) || isAGIDomain(s)
446  
      ? ahref(fixAGILink(absoluteURL(s)), htmlencode2(shorten(agiBlue.displayLength, s)))
447  
      : ahref(agiBlue_linkForPhrase(s,
448  
          slice := slice.isMainSlice() ? null : slice.sliceConcept.globalID),
449  
        htmlencode2(shorten(agiBlue.displayLength, s)));
450  
  }
451  
452  
  GlobalID sliceID() { ret slice.globalID(); }
453  
454  
  SliceInfo sliceInfo() { ret slice.sliceInfo; }
455  
456  
} // end of WorksOnSlice
457  
458  
459  
sclass Renderer1 implements Renderer {
460  
  // Serve page
461  
  
462  
  O html(S uri, SS params) {
463  
    sleepWhile(() -> !agiBlue.started);
464  
    if (agiBlue.broken) ret subBot_serve500("Internal error");
465  
    ret new Request().serve(uri, params);
466  
  }
467  
468  
  // forward things to AGIBlue object
469  
  delegate agiBlue_pageURLWithSlice to agiBlue.
470  
  delegate newCentralIndexGet to agiBlue.
471  
  delegate centralIndexMade to agiBlue.
472  
  delegate agiBlueURL to agiBlue.
473  
  delegate dumpSliceToFile to agiBlue.
474  
  delegate checkDoubleLoads to agiBlue.
475  
  delegate asyncSearch to agiBlue.
476  
  delegate centralIndexGetSlices to agiBlue.
477  
  delegate dbLog to agiBlue.
478  
  delegate entryToTriple to agiBlue.
479  
  delegate loadSlice to agiBlue.
480  
  delegate agiBlueName to agiBlue.
481  
  delegate idx_slicesByModification to agiBlue.
482  
  delegate idx_slicesByName to agiBlue.
483  
  delegate idx_usersBySeen to agiBlue.
484  
  delegate idx_usersByName to agiBlue.
485  
  delegate agiBlue_isOriginal to agiBlue.
486  
  delegate loadBackgroundSlice to agiBlue.
487  
  delegate maxSliceNameLength to agiBlue.
488  
  delegate pagesSortedByLength to agiBlue.
489  
  delegate entriesOnPage to agiBlue.
490  
  delegate centralIndex to agiBlue.
491  
  delegate showGoogleLogIn to agiBlue.
492  
  delegate rst_index to agiBlue.
493  
  delegate LoadedSlice to AGIBlue.
494  
  delegate centralIndexMadeIn to agiBlue.
495  
  delegate sideSearchType to agiBlue.
496  
  delegate sideDisplayLength to agiBlue.
497  
  delegate sliceConceptForGlobalID to agiBlue.
498  
  delegate mainSliceGlobalID to agiBlue.
499  
  delegate sliceForName to agiBlue.
500  
  
501  
  class Request extends WorksOnSlice {
502  
    S cookie;
503  
    Session session;
504  
    Slice sliceConcept;
505  
    S uri;
506  
    SS params;
507  
    long started = sysNow();
508  
    S q, domain;
509  
    Set<S> get;
510  
    
511  
    // when serving a concept page, info that may be grabbed
512  
    // by thought bots
513  
    L<Entry> usesAsKey;
514  
    
515  
    // should also work for standalone
516  
    bool isHomeDomain() {
517  
      S domain = domain();
518  
      ret eqic(domain, "www.agi.blue") || !ewic(domain, ".agi.blue");
519  
    }
520  
    
521  
    O serve(S uri, SS params) {
522  
      this.uri = dropMultipleSlashes(uri);
523  
      this.params = params;
524  
      new Matches m;
525  
      
526  
      print(uri + " ? " + unnull(subBot_query()));
527  
      
528  
      // get cookie & session
529  
      
530  
      cookie = cookieFromUser();
531  
      if (cookie != null)
532  
 {
533  
        session = uniq_sync(Session, +cookie);
534  
        if (session.user! == null)
535  
          cset(session, user := uniq_sync CookieUser(+cookie));
536  
      } else
537  
        session = unlistedWithValues(Session);
538  
        
539  
      // check if user wants to change slices
540  
        
541  
      S selectSlice = params.get('slice);
542  
      if (isGlobalID(selectSlice))
543  
        cset(session, selectedSlice := selectSlice);
544  
      if (session.selectedSlice == null)
545  
        cset(session, selectedSlice := str(mainSliceGlobalID()));
546  
  
547  
      // get slice's case ID
548  
      
549  
      S caseID = "";
550  
      sliceConcept = sliceConceptForGlobalID(session.selectedSlice);
551  
      print("Selected slice: " + session.selectedSlice + ", obj? " + (sliceConcept != null));
552  
      if (sliceConcept != null) caseID = sliceConcept.caseID;
553  
      print("caseID: " + caseID);
554  
      
555  
      // load slice
556  
      
557  
      slice = assertNotNull(loadSlice(caseID, session.selectedSlice));
558  
      cc = slice.cc;
559  
      
560  
      // Check for special URIs
561  
  
562  
      if (swic(uri, "/bot/")) ret serveBot();
563  
  
564  
      if (swic(uri, "/user/", m)) {
565  
        User user = conceptWhere User(globalID := toGlobalIDObj(assertGlobalID(m.rest())));
566  
        if (user == null) ret subBot_serve404("User not found");
567  
        S title = "User " + user;
568  
        ret hhtml_miniPage(title, h3(agiBlueNameHTML() + " | " + title)
569  
          + "User type: " + classShortName(user));
570  
      }
571  
  
572  
      // eleu appends a slash to the URI if it's a single identifier, so we drop it again
573  
      S uri2 = dropTrailingSlash(uri);
574  
      
575  
      if (eqic(uri2, "/google-verify")) {
576  
        print("Google-verify started.");
577  
        Payload payload = printStruct(googleVerifyUserToken2(googleSignInID(), params.get("token")));
578  
        if (payload == null) ret print("google-verify", "No");
579  
        S email = payload.getEmail();
580  
        if (empty(email)) ret print("google-verify", "No");
581  
        
582  
        GoogleUser user = uniqCI_sync GoogleUser(googleEmail := email);
583  
        cset(user,
584  
          googleEmailVerified := payload.getEmailVerified(),
585  
          googleFirstName := strOrNull(payload.get("given_name")),
586  
          googleLastName := strOrNull(payload.get("family_name")));
587  
        
588  
        cset(session,
589  
 +user);
590  
          
591  
        ret print("google-verify", payload.getEmail() + " " + (payload.getEmailVerified() ? "(verified)" : "(not verified)"));
592  
      }
593  
  
594  
      cset(session.user!, lastSeen := now());
595  
596  
      if (nempty(params.get('searchAction))) ret serveSearchAction();
597  
      
598  
      if (eqic(uri2, "/users")) ret serveUsersList();
599  
      
600  
      if (eqic(uri2, "/slices")) ret serveSlicesList();
601  
      
602  
      if (eqic(uri2, "/classes")) ret serveClasses();
603  
      
604  
      if (eqic(uri2, "/search")) ret serveScoredSearch();
605  
      if (eqic(uri2, "/literalSearch")) ret serveLiteralSearch();
606  
      if (eqic(uri2, "/levenSearch")) ret serveLevenSearch();
607  
      
608  
      if (eqic(uri2, "/query")) ret serveQueryPage();
609  
      if (eqic(uri2, "/createSlice")) ret serveCreateSlicePage();
610  
      if (eqic(uri2, "/deletePage")) ret serveDeletePage();
611  
      if (eqic(uri2, "/deleteSlice")) ret serveDeleteSlice();
612  
  
613  
      if (eqic(uri2, "/checkboxes")) ret serveCheckboxes();
614  
      if (eqic(uri2, "/multiAdd")) ret serveMultiAdd();
615  
616  
      if (eqic(uri2, "/version")) ret myTranspilationDate();
617  
618  
      if (eqic(uri2, "/restart") && authed()) {
619  
        agiBlue.softRestart();
620  
        ret "OK";
621  
      }
622  
      
623  
      q = params.get('q);
624  
      get = asCISet(nempties(subBot_paramsAsMultiMap().get('get)));
625  
      
626  
      domain = or2(params.get('domain), domain());
627  
      
628  
      S raw = firstKeyWithValue("", params); // agi.blue?something
629  
      if (nempty(raw) && empty(q)) q = raw;
630  
      /*if (nempty(q)) {
631  
        domain = makeAGIDomain(q);
632  
        if (l(domain) > maximumDomainPartLength()) // escape with "domain="
633  
          ret hrefresh(agiBlueURL() + hquery(+domain, key := "read as", value := q));
634  
        ret hrefresh("http://" + domain + (eq(q, domain) ? "" : "/" +  hquery(key := "read as", value := q)));
635  
        //uri = "/"; replaceMapWithParams(params, key := "read as", value := q);
636  
      }*/
637  
      S url = domain + dropTrailingSlash(uri);
638  
      
639  
      // domain to query
640  
      //if (empty(q)) q = url;
641  
      if (empty(q)) {
642  
        S qq = agiBlue_urlToQuery(url);
643  
        if (neqic(qq, "agi.blue")) q = qq;
644  
      }
645  
      
646  
      Page page, bool newPage = unpair uniqCI2_sync(cc, Page, +q);
647  
      if (newPage) dbLog("New page", +q);
648  
      //printStructs(+params, +raw, +q);
649  
    
650  
      if (empty(params.get('q)) && empty(raw) && isHomeDomain()) {
651  
        //L<Page> pages = sortedByFieldDesc _modified(list(cc, Page)); // TODO: use index
652  
        L<Page> pages = cloneList(slice.idx_latestChangedPages.objectIterator());
653  
        int start = parseInt(params.get("start")), step = 100;
654  
        S nav = pageNav2("/", l(pages), start, step, "start");
655  
        S content = hform(b("GIVE ME INPUT: &nbsp; ") + htextinput('q, autofocus := true) + " " + hsubmit("Search", name := 'searchAction) + " " + hsubmit("Add to slice"))
656  
          + h1(sliceAsHTML() + " has " + nPages(countConcepts(cc, Page)) + " and " + nConnections(countConcepts(cc, Entry)))
657  
          + p(nav)
658  
          + p_nemptyLines(map(pageToHTMLLink(), subList(pages, start, start+step)));
659  
  
660  
        ret hhtml_agiBlue(hhead_title("Slice " + sliceConcept().name + " | agi.blue") // SERVE SLICE HOME PAGE
661  
          + hbody(hfullcenterAndTopLeft(top() + content
662  
            + footer(),
663  
            sliceSelector()
664  
          )));
665  
      }
666  
          
667  
      S key = trim(params.get('key)), value = trim(params.get('value));
668  
      L<Entry> entries = agiBlue.new GetEntriesAndPost(cc).go(page, params).entries;
669  
      
670  
      //S get = params.get('get);
671  
      if (nempty(get))
672  
        ret serveJSON(collect value(llNotNulls(firstThat(entries, e -> get.contains(e.key)))));
673  
      
674  
      S key2 = key, value2 = value; if (nempty(key) && nempty(value)) key2 = value2 = ""; // input reset
675  
    
676  
      bool withHidden = eq(params.get('withHidden), "1");
677  
      new Set<Int> hide;
678  
      if (!withHidden) for (Entry e : entries) if (eqic(e.key, 'hide) && isSquareBracketedInt(e.value)) addAll(hide, e.count, parseInt(unSquareBracket(e.value)));
679  
      new MultiMap<Int, S> mmMeta;
680  
      for (Entry e : entries) if (isSquareBracketedInt(e.key)) mmMeta.put(parseInt(unSquareBracket(e.key)), e.value);
681  
      new MultiMap<S> mm;
682  
      for (Entry e : entries) mm.put(e.key, e.value);
683  
      
684  
      //S name = or2(/* ouch */ last(mm.get("read as")), /* end ouch */ unpackAGIDomain(page.url), page.url);
685  
      S name = page.q;
686  
      
687  
      // Find references
688  
      
689  
      L<Entry> refs = concatLists(conceptsWhereIC(cc, Entry, value := name),
690  
        conceptsWhereIC(cc, Entry, key := name));
691  
      Set<Page> refPages = asSet(ccollect page(refs));
692  
      refPages.remove(page); // don't list current page as reference
693  
      
694  
      // Search in page names (depending on default search type)
695  
      
696  
      L<Page> searchResults;
697  
      int totalResults = 0, searchResultsToShow = 50;
698  
      if (eq(sideSearchType, 'leven))
699  
        searchResults = levenSearch(page.q, max := searchResultsToShow);
700  
      else if (eq(sideSearchType, 'literal))
701  
 {
702  
        searchResults = literalSearch(page.q, max := maxInt());
703  
        print("totalResults", totalResults = l(searchResults));
704  
        searchResults = takeFirst(searchResultsToShow, searchResults);
705  
      } else
706  
        searchResults = (L<Page>) dm_call('agiBlueSearch, 'search, page.q, maxResult := clipIntPlus(searchResultsToShow, 1), agiBlueBotID := programID(), +cc);
707  
      searchResults.remove(page);
708  
      
709  
      // Find uses as key
710  
      usesAsKey = conceptsWhereIC(cc, Entry, key := page.q);
711  
  
712  
      // Find page in other slices
713  
      CentralIndexEntry cie = centralIndex.get(page.q);
714  
      L<Slice> pageInOtherSlices = cie == null ? null : listMinus(cie.slices, slice.sliceConcept);
715  
      
716  
      S pageName_html = htmlEncode2(shorten(agiBlue.displayLength, name));
717  
  
718  
      S mainContents =
719  
          top() + h1(ahref_unstyled(agiBlue_pageURLWithSlice(page), pageName_html)
720  
          + (!isURL(page.q) ? "" : " " + targetBlank(page.q, hsnippetimg(#1101876)))
721  
          + (newPage ? " [huh????]" : ""))
722  
        + p_nemptyLines(map(entries, func(Entry e) -> S {
723  
          !withHidden && (hide.contains(e.count) || eqic(e.key, "read as") && eqic(e.value, name)) ? ""
724  
          : "[" + e.count + "] " +
725  
            renderThing(e.key, false) + ": " +
726  
            b(renderThing(e.value, cic(mmMeta.get(e.count), "is a URL")))
727  
          }))
728  
        + hpostform(h3("Add an entry")
729  
          + "Key: " + hinputfield(key := key2) + " Value: " + hinputfield(value := value2) + "<br><br>" + hsubmit("Add")
730  
          )
731  
          
732  
        + p(ahref(agiBlueURL() + "/literalSearch" + hquery(q := page.q), "[literal search]", title := "Search pages with a name containing this page's name literally")
733  
           + " " +
734  
           ahref(agiBlueURL() + "/levenSearch" + hquery(q := page.q), "[leven search 1]", title := "Search pages with a Levenshtein similarity of 1 containing this page's name literally")
735  
           + " " +
736  
           ahref(agiBlueURL() + "/search" + hquery(q := page.q), "[scored search]", title := "Search pages with ScoredSearch")
737  
           + " " +
738  
           ahref(agiBlueURL() + "/deletePage" + hquery(slice := sliceID(), q := page.q), "[delete page]")
739  
         )
740  
         
741  
         // optional "uses as key" section
742  
         
743  
         + (empty(usesAsKey) ? "" : h3(quote(pageName_html) + " as key")
744  
         + p_nemptyLines_showFirst(50, map(usesAsKey, e ->
745  
           pageToHTMLLink().get(e.page!) + unicode_spacedRightPointingTriangle() +
746  
             pageName_html + unicode_spacedRightPointingTriangle() +
747  
           pageToHTMLLink().get(pageFromQ(e.value))
748  
         )))
749  
         
750  
         // optional "in other slices" section
751  
         
752  
         + (empty(pageInOtherSlices) ? "" : h3(quote(pageName_html) + " in other slices")
753  
         + p_nemptyLines_showFirst(50, map(slice ->
754  
           ahref(agiBlueURL() + hquery(slice := slice.globalID, q := page.q), htmlEncode2(slice.name)), pageInOtherSlices)))
755  
         ;
756  
         
757  
        // end of mainContents
758  
          
759  
      S sideContents =
760  
        hform(b("GIVE ME INPUT:") + " "
761  
          + htextinput('q) + " "
762  
          + hsubmit("Ask", onclick := "document.getElementById('newInputForm').target = '';") + " "
763  
          + hsubmit("+Tab", title := "Ask and show result in a new tab", onclick := "document.getElementById('newInputForm').target = '_blank';"),
764  
          id := 'newInputForm)
765  
        
766  
        + h3("References (" + l(refPages) + ")")
767  
        
768  
        + p_nemptyLines_showFirst(10, map(pageToHTMLLink(displayLength := sideDisplayLength), refPages))
769  
        
770  
        + h3(searchTypeToText.get(sideSearchType) + " search results ("
771  
          + (l(searchResults) >= searchResultsToShow ? searchResultsToShow + "+" : str(l(searchResults)))
772  
          + (totalResults > l(searchResults) ? " of " + n2(totalResults) : "") + ")")
773  
        
774  
        + p_nemptyLines_showFirst(searchResultsToShow, map(pageToHTMLLink(displayLength := sideDisplayLength), searchResults))
775  
        
776  
        + hdiv("", id := 'extraStuff);
777  
        
778  
      // TODO: sync search delivery with WebSocket creation
779  
      if (asyncSearch)
780  
        doLater(6.0, r { dm_call('agiBlueSearch, 'searchAndPost, page.q, agiBlueBotID := programID(), +cc) });
781  
        
782  
      // notify local interested parties
783  
      vmBus_send('agiBlue_servingConceptPage, this, page);
784  
      
785  
      S iframe = params.get('render_iframe);
786  
        
787  
      // serve a concept page
788  
      ret hhtml_agiBlue(hhead_title(pageDisplayName(page)) + hbody(
789  
        tag('table,
790  
          tr(
791  
            td(sliceSelector(), valign := 'top)
792  
          + td(sideContents, align := 'right, valign := 'top, rowspan := 2))
793  
        + tr(td(mainContents +
794  
          (empty(iframe) ? "" : iframe(iframe, width := 600, height := 400)) +
795  
        footer(), align := 'center, valign := 'top)),
796  
        width := "100%", height := "100%")));
797  
    }
798  
    
799  
    O servePagesToBot(Iterable<Page> pages) {
800  
      ret serveListToBot(map(pageToMap(wrapMapAsParams(params)), pages));
801  
    }
802  
  
803  
    O serveListToBot(Collection l) {
804  
      if (nempty(params.get('max)))
805  
        l = takeFirst(parseInt(params.get('max)), l);
806  
      ret serveJSON(l);
807  
    }
808  
  
809  
    // uri starts with "/bot/"
810  
    O serveBot() {
811  
      S q = params.get('q);
812  
      
813  
      if (eqic(uri, "/bot/hello")) ret serveJSON("hello");
814  
      if (eqic(uri, "/bot/hasPage")) ret serveJSON(hasPage(q));
815  
      if (eqic(uri, "/bot/randomPageContaining")) {
816  
        assertNempty(q);
817  
        ret servePageToBot(random(filter(list(cc, Page), p -> cic(p.q, q))), params);
818  
      }
819  
      if (eqic(uri, "/bot/allPages"))
820  
        ret servePagesToBot(list(cc, Page));
821  
      if (eqic(uri, "/bot/allPagesStartingWith")) {
822  
        assertNempty(q);
823  
        ret servePagesToBot(filter(list(cc, Page), p -> swic(p.q, q)));
824  
      }
825  
      if (eqic(uri, "/bot/allPagesEndingWith")) {
826  
        assertNempty(q);
827  
        ret servePagesToBot(filter(list(cc, Page), p -> ewic(p.q, q)));
828  
      }
829  
      if (eqic(uri, "/bot/allPagesContaining")) {
830  
        assertNempty(q);
831  
        ret servePagesToBot(filter(list(cc, Page), p -> cic(p.q, q)));
832  
      }
833  
      if (eqic(uri, "/bot/allPagesContainingRegexp")) {
834  
        assertNempty(q);
835  
        Pattern pat = regexpIC(q);
836  
        ret servePagesToBot(filter(list(cc, Page), p -> regexpFindIC(pat, p.q)));
837  
      }
838  
      
839  
      if (eqicOneOf(uri, "/bot/postSigned", /*"/bot/makePhysicalSlice",*/ "/bot/approveTrustRequest")) {
840  
        S text = rtrim(params.get('text));
841  
        S key = getSignerKey(text);
842  
        if (empty(key)) ret subBot_serve500("Please include your public key");
843  
        if (!isSignedWithKey(text, key)) ret subBot_serve500("Signature didn't verify");
844  
        text = dropLastTwoLines(text); // drop signer + sig line
845  
        
846  
        Signer signer = uniq_sync Signer(publicKey := key);
847  
        
848  
        /*if (eqic(uri, "/bot/makePhysicalSlice")) {
849  
          if (!signer.trusted) ret subBot_serve500("Untrusted signer");
850  
          Page page = findPageFromParams(jsonDecodeMap(text));
851  
          if (page == null) ret subBot_serve500("Page not found");
852  
          ret serveJSON(uniq2_sync(PhysicalSlice, slicePage := page).b ? "Slice made" : "Slice exists");
853  
        }*/
854  
      
855  
        if (eqic(uri, "/bot/postSigned")) {
856  
          new L out;
857  
          for (S line : tlft(text)) {
858  
            SS map = jsonDecodeMap(line);
859  
            AGIBlue.GetEntriesAndPost x = agiBlue.new GetEntriesAndPost(cc);
860  
            x.signer = signer;
861  
            Page page = findOrMakePageFromParams(map);
862  
            if (page == null) continue with out.add("Invalid page reference");
863  
            x.go(page, map);
864  
            out.add(x.newEntry ? "Saved" : x.entry != null ? "Entry exists" : "Need key and value");
865  
          }
866  
          ret serveJSON(out);
867  
        }
868  
        
869  
        if (eqic(uri, "/bot/approveTrustRequest")) {
870  
          if (!signer.trusted) ret subBot_serve500("Untrusted signer");
871  
          Signer toApprove = conceptWhere Signer(publicKey := trim(text));
872  
          if (toApprove == null) ret subBot_serve500("Signer to approve not found");
873  
          cset(toApprove, trusted := true, approvedBy := signer.globalID);
874  
          ret serveJSON("Approved: " + trim(text));
875  
        }
876  
        
877  
        ret subBot_serve500("CONFUSION");
878  
      }
879  
      
880  
      if (eqic(uri, "/bot/post")) {
881  
        AGIBlue.GetEntriesAndPost x = agiBlue.new GetEntriesAndPost(cc);
882  
        x.go(pageFromQ(q), params);
883  
        ret serveJSON(x.newEntry ? "Saved" : x.entry != null ? "Entry exists" : "Need key and value");
884  
      }
885  
      
886  
      if (eqic(uri, "/bot/entriesOnPage"))
887  
        ret serveJSON(map(entriesOnPage(findPageFromParams(params)), entryToMap(false)));
888  
        
889  
      if (eqic(uri, "/bot/entriesForKey"))
890  
        ret serveJSON(map(conceptsWhereIC(cc, Entry, key := params.get('key)), entryToMap(true)));
891  
       
892  
      // look up value from page & key in current slice
893  
      if (eqic(uri, "/bot/lookup")) {
894  
        S key = params.get('key);
895  
        if (empty(key)) ret serveJSON("Need key");
896  
        S value = getValue(findPageFromParams(params), key);
897  
        ret serveJSON(empty(value) ? "" : litmap(+value));
898  
      }
899  
  
900  
      if (eqic(uri, "/bot/multiLookup")) {
901  
        S key = params.get('key);
902  
        if (empty(key)) ret serveJSON("Need key");
903  
        ret serveJSON(collect value(objectsWhereIC(findBackRefs(findPageFromParams(params), Entry), +key)));
904  
      }
905  
    
906  
      if (eqic(uri, "/bot/lookupMultipleKeys")) {
907  
        Set<S> keys = asCISet(subBot_paramsAsMultiMap().get('key));
908  
        L<Entry> entries = findBackRefs(findPageFromParams(params), Entry);
909  
        ret serveJSON(map(entryToMap(false), filter(entries, e -> contains(keys, e.key))));
910  
      }
911  
    
912  
      // look up value from page & key in all slices
913  
      if (eqic(uri, "/bot/multiLookupInAllSlices")) {
914  
        S key = params.get('key);
915  
        if (empty(key)) ret serveJSON("Need key");
916  
        Cl<Slice> slices = centralIndexGetSlices(key);
917  
        new L<Map> results;
918  
        fOr (Slice s : slices) {
919  
          WorksOnSlice wos = new(loadBackgroundSlice(s));
920  
          Page page = wos.findPageFromParams(params);
921  
          if (page != null)
922  
            fOr (Entry e : objectsWhereIC(findBackRefs(page, Entry), +key))
923  
              results.add(litorderedmap("c" := e.value, "slice" := str(s.globalID())));
924  
        }
925  
        ret serveJSON(results);
926  
      }
927  
      
928  
      if (eqic(uri, "/bot/latestEntries"))
929  
        ret serveJSON(map(takeFirst(10, slice.idx_latestEntries.objectIterator()), entryToMap(true)));
930  
      if (eqic(uri, "/bot/latestPages"))
931  
        ret serveJSON(map(takeFirst(10, slice.idx_latestCreatedPages.objectIterator()), pageToMap()));
932  
      if (eqic(uri, "/bot/latestChangedPages"))
933  
        ret serveJSON(map(takeFirst(10, slice.idx_latestChangedPages.objectIterator()), pageToMap()));
934  
        
935  
      if (eqic(uri, "/bot/googleUsersCount"))
936  
        ret serveJSON(countConcepts(GoogleUser));
937  
        
938  
      if (eqic(uri, "/bot/totalPageCount"))
939  
        ret serveJSON(countConcepts(Page));
940  
      /*if (eqic(uri, "/bot/pageWithoutPhysicalSliceCount"))
941  
        ret serveJSON(countConceptsWhere(Page, slice := null));
942  
      if (eqic(uri, "/bot/physicalSliceCount"))
943  
        ret serveJSON(countConcepts(PhysicalSlice));*/
944  
      if (eqic(uri, "/bot/trustedSignersCount"))
945  
        ret serveJSON(countConcepts(Signer, trusted := true));
946  
        
947  
      if (eqic(uri, "/bot/valueSearch")) {
948  
        S value = params.get('value);
949  
        L<Entry> entries = conceptsWhereIC(cc, Entry, +value);
950  
        ret serveJSON(map(takeFirst(100, entries), entryToMap(true)));
951  
      }
952  
        
953  
      if (eqic(uri, "/bot/keyAndValueSearch")) {
954  
        S key = params.get('key), value = params.get('value);
955  
        Cl<Page> pages = pagesForKeyAndValue(key, value);
956  
        ret servePagesToBot(pages);
957  
      }
958  
      
959  
      if (eqic(uri, "/bot/pagesWithKey")) {
960  
        S key = params.get('key);
961  
        ret servePagesToBot(pagesWithKey(key));
962  
      }
963  
      
964  
      if (eqic(uri, "/bot/match")) {
965  
        PositionalTokenIndex2 index = slice.positionalIndex;
966  
        if (index == null) ret "No positional index";
967  
        ret serveJSON(fastJoinAll(positionalTokenIndex2_matchAll_returnTokenizations(q, index)));
968  
      }
969  
      
970  
      if (eqic(uri, "/bot/aLookup")) {
971  
        S key = params.get('key), value = params.get('value);
972  
        Cl<Page> pages = pagesForKeyAndValue(key, value);
973  
        ret serveJSON(collect q(pages));
974  
      }
975  
      
976  
      if (eqic(uri, "/bot/bLookup")) {
977  
        S value = params.get('value);
978  
        ret serveJSON(keysForPageAndValue(q, value));
979  
      }
980  
      
981  
      if (eqic(uri, "/bot/keyValuePairsByPopularity")) {
982  
        L<PairS> pairs = map(list(Entry), e -> pair(e.key, e.value));
983  
        LPair<S, Int> pairs2 = multiSetTopPairs(ciMultiSet(map pairToUglyStringForCIComparison(pairs)));
984  
        ret serveJSON(map(pairs2, p -> {
985  
          S key, value = unpair pairFromUglyString(p.a);
986  
          ret litorderedmap(n := p.b, +key, +value);
987  
        }));
988  
      }
989  
      
990  
      // technically a duplicate of entriesForKey
991  
      if (eqic(uri, "/bot/pagesAndValuesForKey")) {
992  
        S key = params.get('key);
993  
        L<Entry> entries = conceptsWhereIC(cc, Entry, +key);
994  
        L<PairS> pairs = map(entries, e -> pair(e.page->q, e.value));
995  
        ret serveJSON(pairsToLists(pairs));
996  
      }
997  
      
998  
      if (eqic(uri, "/bot/allKeys"))
999  
        ret serveListToBot(distinctCIFieldValuesOfConcepts(cc, Entry, 'key));
1000  
        
1001  
      if (eqic(uri, "/bot/allKeysByPopularity"))
1002  
        ret serveListToBot(mapMultiSetByPopularity(distinctCIFieldValuesOfConcepts_multiSet(cc, Entry, 'key), (key, n) -> litorderedmap(+n, +key)));
1003  
        
1004  
      if (eqic(uri, "/bot/dbSize"))
1005  
        ret serveJSON(l(conceptsFile()));
1006  
        
1007  
      if (eqic(uri, "/bot/query"))
1008  
        ret serveBotQuery();
1009  
  
1010  
      if (eqic(uri, "/bot/createSlice")) {
1011  
        Slice slice = createSlice(assertNempty(params.get('name)));
1012  
        ret serveJSON(litorderedmap(id := str(slice.globalID), name := slice.name));
1013  
      }
1014  
      
1015  
      if (eqic(uri, "/bot/dumpAllSlices") && authed()) {
1016  
        new L<Map> out;
1017  
        for (Slice slice) try {
1018  
          out.add(litorderedmap(slice := slice.caseID, ms := returnTimeFor(() -> dumpSliceToFile(loadBackgroundSlice(slice)))));
1019  
        } catch print e {
1020  
          out.add(litorderedmap(slice.caseID, error := exceptionToStringShort(e)));
1021  
        }
1022  
        ret serveJSON(out);
1023  
      }
1024  
      
1025  
      if (eqic(uri, "/bot/dumpSlice")) {
1026  
        File f = dumpSliceToFile(slice);
1027  
        ret "OK, saved as: " + f2s(f);
1028  
      }
1029  
      
1030  
      if (eqic(uri, "/bot/diskStats"))
1031  
        ret serveJSON(litcimap(
1032  
          sizeOnDisk := agiBlue.sizeOnDisk!,
1033  
          freeDiskSpace := freeDiskSpace()
1034  
        ));
1035  
  
1036  
      if (eqic(uri, "/bot/memStats")) {
1037  
        Map<S, ?> fg = cloneMap(agiBlue.loadedSlices);
1038  
        Map<S, ?> bg = cloneMap(agiBlue.backgroundSlices);
1039  
        ret serveJSON(litcimap(
1040  
          error := checkDoubleLoads(),
1041  
          centralIndexEntries := l(centralIndex),
1042  
          centralIndexMade := renderHowLongAgo(centralIndexMade),
1043  
          centralIndexMadeInMS := centralIndexMadeIn,
1044  
          numLoadedForegroundSlices := l(fg),
1045  
          numLoadedBackgroundSlices := l(bg),
1046  
          loadedForegroundSlicesEstimatedSize := longSum(map(s -> agiBlue.estimatedSliceDataSize(s), keys(fg))),
1047  
          loadedBackgroundSlicesEstimatedSize := longSum(map(s -> agiBlue.estimatedSliceDataSize(s), keys(bg))),
1048  
          loadedForegroundSlices := sortedIC(keysList(fg)),
1049  
          loadedBackgroundSlices := sortedIC(keysList(bg)),
1050  
          sessionsWithGoogleUsers := countPred(list(Session), s -> s.user! instanceof GoogleUser),
1051  
          userObjects := countConcepts(User),
1052  
          processSize := getOpt(vmBus_query('processSize), 'processSize),
1053  
          slices := countConcepts(Slice)
1054  
        ));
1055  
      }
1056  
      
1057  
      if (eqic(uri, "/bot/centralIndexGet")) {
1058  
        CentralIndexEntry e = centralIndex.get(q);
1059  
        ret serveJSON(e == null ? ll() : map(e.slices, sliceToMap()));
1060  
      }
1061  
      
1062  
      // return primary triple from each slice
1063  
      if (eqic(uri, "/bot/centralIndexGrab")) {
1064  
        CentralIndexEntry ie = centralIndex.get(q);
1065  
        new L<Map> out;
1066  
        if (ie != null) for (Slice slice : ie.slices) {
1067  
          LoadedSlice ls = loadBackgroundSlice(slice);
1068  
          WorksOnSlice wos = new(ls);
1069  
          Page page = wos.findPageFromQ(q);
1070  
          Iterable<Entry> entries = entriesOnPage(page);
1071  
          print("Page for " + q + " in slice " + ls.caseID + ": " + yesNo(page != null) + " - " + l(entries));
1072  
          Entry e = first(entries);
1073  
          if (e != null)
1074  
            out.add(litorderedmap(a := page.q, b := e.key, c := e.value, slice := slice.caseID));
1075  
        }
1076  
        ret serveJSON(out);
1077  
      }
1078  
      
1079  
      // Search for literal substring in central index entry
1080  
      // Returns only the words found, not the slices
1081  
      if (eqic(uri, "/bot/centralIndexLiteralSearch")) {
1082  
        LS entries = containingIC(keys(centralIndex), q);
1083  
        ret serveJSON(entries);
1084  
      }
1085  
      
1086  
      if (eqic(uri, "/bot/updateCentralIndex") && authed()) {
1087  
        rst_index.trigger();
1088  
        ret "OK";
1089  
      }
1090  
  
1091  
      if (eqic(uri, "/bot/allGoogleEmails") && authed())
1092  
        ret serveJSON(collect googleEmail(list(GoogleUser)));
1093  
  
1094  
      if (eqic(uri, "/bot/words2_spaces_all"))
1095  
        ret serveJSON(mapToValues_ciMap words2_spaces_cached(collect q(conceptsSortedByFieldCI(slice.cc, Page, 'q))));
1096  
      
1097  
      if (eqic(uri, "/bot/words2_spaces_collapse_all"))
1098  
        ret serveJSON(mapToValues_ciMap words2_spaces_collapse_cached(collect q(conceptsSortedByFieldCI(slice.cc, Page, 'q))));
1099  
  
1100  
      if (eqic(uri, "/bot/makeManyPages")) {
1101  
        //LS qs = nempties(subBot_paramsAsMultiMap().get('q));
1102  
        LS qs = tlft(params.get("pages"));
1103  
        slice.haltSliceDumping = true;
1104  
        try {
1105  
          fOr ping (S _q : qs)
1106  
            pageFromQ(_q);
1107  
        } finally {
1108  
          slice.haltSliceDumping = true;
1109  
          slice.rstDumpSlice.trigger();
1110  
        }
1111  
        ret "OK (" + l(qs) + ")";
1112  
      }
1113  
1114  
      if (eqic(uri, "/bot/sliceNamesMap"))
1115  
        ret serveJSON(mapToMap(list(Slice), s -> pair(str(s.globalID), s.name)));
1116  
1117  
      if (eqic(uri, "/bot/sliceForName")) {
1118  
        Slice slice = sliceForName(q);
1119  
        ret serveJSON(slice == null ? null : str(slice.globalID));
1120  
      }
1121  
1122  
      if (eqic(uri, "/bot/allowOpenPosting")) {
1123  
        try object errorIfNotOwnerOfSlice();
1124  
        bool allow = eq("1", params.get('allow));
1125  
        cset(sliceInfo(), openPosting := allow);
1126  
        ret "Set openPosting to " + allow;
1127  
      }
1128  
      
1129  
      if (eqic(uri, "/bot/createUnusedNumberedPage")) {
1130  
        int n = 1;
1131  
        // TODO: sync
1132  
        while (findPageFromQ(q + n) != null) ++n;
1133  
        ret serveJSON(litmap(q := pageFromQ(q + n).q));
1134  
      }
1135  
  
1136  
      // end of bot methods
1137  
    
1138  
      ret subBot_serve404();
1139  
    }
1140  
    
1141  
    O serveBotQuery() {
1142  
      S query = params.get('query);
1143  
      ret agiBlue.new Query(slice).process(query);
1144  
    }
1145  
1146  
    int searchResultsToShow() { ret agiBlue.searchResultsToShow; }
1147  
    
1148  
    O serveLiteralSearch() {
1149  
      S q = params.get('q);
1150  
      L<Page> searchResults = literalSearch(q);
1151  
      ret serveSearchResults("literal search" , q, searchResultsToShow(), searchResults);
1152  
    }
1153  
    
1154  
    L<Page> literalSearch(S q, O... _) {
1155  
      int searchResultsToShow = optPar max(_, 100);
1156  
      
1157  
      // quick search in random order
1158  
      //L<Page> searchResults = takeFirst(clipIntPlus(searchResultsToShow, 1), filterIterator(iterator(list(cc, Page)), p -> cic(p.q, q));
1159  
      
1160  
      // full search, order by length
1161  
      ret takeFirst(clipIntPlus(searchResultsToShow, 1), pagesSortedByLength(filter(list(cc, Page), p -> cic(p.q, q))));
1162  
    }
1163  
    
1164  
    O serveScoredSearch() {
1165  
      S q = params.get('q);
1166  
      L<Page> searchResults = (L<Page>) dm_call('agiBlueSearch, 'search, q, +cc);
1167  
      ret serveSearchResults("scored search" , q, searchResultsToShow(), searchResults);
1168  
    }
1169  
    
1170  
    O serveLevenSearch() {
1171  
      S q = params.get('q);
1172  
      L<Page> searchResults = levenSearch(q);
1173  
      ret serveSearchResults("leven search with distance 1" , q, searchResultsToShow(), searchResults);
1174  
    }
1175  
    
1176  
    L<Page> levenSearch(S q, O... _) {
1177  
      int searchResultsToShow = optPar max(_, 100);
1178  
      int maxEditDistance = 1;
1179  
      
1180  
      new Map<Page, Int> map;
1181  
      for (Page p : list(cc, Page)) {
1182  
        int distance = leven_limitedIC(q, p.q, maxEditDistance+1);
1183  
        if (distance <= maxEditDistance) map.put(p, distance);
1184  
      }
1185  
      ret takeFirst(clipIntPlus(searchResultsToShow, 1), keysSortedByValue(map));
1186  
    }
1187  
    
1188  
    O serveSearchResults(S searchType, S q, int searchResultsToShow, Collection<Page> searchResults) {
1189  
      S title = "agi.blue " + searchType + " for " + htmlEncode2(quote(q)) + " (" + (l(searchResults) >= searchResultsToShow ? searchResultsToShow + "+ results" : nResults(l(searchResults))) + ")";
1190  
      
1191  
      ret hhtml_agiBlue(hhead_title(htmldecode_dropAllTags(title))
1192  
        + hbody(hfullcenter(//top() +
1193  
          h3(title)
1194  
        + p_nemptyLines_showFirst(searchResultsToShow, map(pageToHTMLLink(), searchResults)))));
1195  
    }
1196  
    
1197  
    O serveQueryPage() {
1198  
      S query = params.get('query);
1199  
      if (query == null) query = loadSnippet(#1024258);
1200  
      S title = agiBlueNameHTML() + " | Execute a query script (" + targetBlank("http://code.botcompany.de/1024274", "ALQL") + ")";
1201  
      ret hhtml_miniPage(title, h3(title)
1202  
 + form(
1203  
        htextarea(query, name := 'query, cols := 80, rows := 10, autofocus := true)
1204  
        + "<br><br>"
1205  
        + hsubmit("Execute"), action := "/bot/query"
1206  
      ));
1207  
    }
1208  
  
1209  
    O hhtml_miniPage(S htmlTitle, O contents) {
1210  
      ret hhtml_agiBlue(hhead_title(htmldecode_dropAllTags(htmlTitle))
1211  
        + hbody(hfullcenter(contents)));
1212  
    }
1213  
    
1214  
    S sliceHomeURL() {
1215  
      ret slice == null ? agiBlueURL() : sliceHomeURL(slice.sliceConcept.globalID);
1216  
    }
1217  
    
1218  
    S sliceHomeURL(Slice slice) { ret sliceHomeURL(slice.globalID); }
1219  
  
1220  
    S sliceHomeURL(GlobalID slice) {
1221  
      ret agiBlueURL() + hquery(+slice);
1222  
    }
1223  
  
1224  
    S sliceLinkHTML(Slice slice) {
1225  
      ret slice == null ? "-": ahref(sliceHomeURL(slice), htmlEncode2(slice.name));
1226  
    }
1227  
    
1228  
    O serveCreateSlicePage() {
1229  
      S sliceName = trim(params.get('sliceName));
1230  
      if (eq(params.get('doIt), "1") && nempty(sliceName)) {
1231  
        // TODO: check for existing name
1232  
        Slice slice = createSlice(sliceName);
1233  
        ret hrefresh(sliceHomeURL(slice.globalID));
1234  
      }
1235  
      
1236  
      S title = agiBlueNameHTML() + " | Create slice";
1237  
      ret hhtml_agiBlue(hhead_title(htmldecode_dropAllTags(title))
1238  
        + hbody(hfullcenter(
1239  
          h3(title)
1240  
        + form(
1241  
            hhidden(doIt := 1)
1242  
          + "Slice name: " + htextinput(+sliceName, autofocus := true)
1243  
          + "<br><br>"
1244  
          + hsubmit("Create slice"))
1245  
        )));    
1246  
    }
1247  
  
1248  
    O serveDeletePage() {
1249  
      q = params.get('q);
1250  
      Page page = findPageFromQ(q);
1251  
      if (page == null) ret "Page " + quote(q) + " not found in slice " + htmlEncode(slice.name());
1252  
      try object errorIfNotOwnerOfSlice();
1253  
      deleteConcepts(findBackRefs(page, AbstractEntry));
1254  
      cdelete(page);
1255  
      ret hrefresh(1.0, sliceHomeURL()) + "Page deleted";
1256  
    }
1257  
1258  
    // TODO: delete without loading
1259  
    O serveDeleteSlice() {
1260  
      assertNempty("Need slice param", params.get('slice));
1261  
      try object errorIfNotOwnerOfSlice();
1262  
      S caseID = sliceConcept.caseID;
1263  
      agiBlue.unloadSlice(caseID);
1264  
      File dir = sliceDir(sliceConcept);
1265  
      if (!dirExists(dir)) ret "Dir not found: " + f2s(dir);
1266  
      moveDirectory(dir, programFile("deletedSlices/" + sliceConcept.caseID));
1267  
      cset(session, selectedSlice := null);
1268  
      cdelete(sliceConcept);
1269  
      ret serveText("Slice " + caseID + " deleted (& backed up)");
1270  
    }
1271  
1272  
    O errorIfNotOwnerOfSlice() {
1273  
      bool ownage = slice.sliceConcept.owner! == session.user!;
1274  
      if (!ownage)
1275  
        ret subBot_serve403("Sorry, you don't own slice " + htmlEncode(slice.name()));
1276  
      null;
1277  
    }
1278  
    
1279  
    F1<Page, S> pageToHTMLLink(O... _) {
1280  
      optPar int displayLength = agiBlue.displayLength;
1281  
      ret func(Page p) -> S {
1282  
        S name = pageDisplayName(p);
1283  
        ret ahref(agiBlue_pageURLWithSlice(p),
1284  
          htmlEncode2(shorten(displayLength, name)),
1285  
          title := name);
1286  
      };
1287  
    }
1288  
    
1289  
    // and google log-in
1290  
    S sliceSelector() {
1291  
      ret htag table(tr(
1292  
        (!showGoogleLogIn ? "" : td(
1293  
          googleSignIn_signInButton(agiBlueURL() + "/google-verify", "console.log(data);", ""), style := "padding-right: 10px;")
1294  
          + td(nobr(ahref("javascript:signOut()", "Sign out")), style := "padding-right: 10px; padding-bottom: 0.15em"))
1295  
      
1296  
      + (!agiBlue.showSliceSelector ? "" : hform(td(
1297  
          "Select reality slice: "
1298  
        + hselect(availableSlices(), session.selectedSlice, name := 'slice, onchange := "this.form.submit()")
1299  
        /*+ " " + hsubmit("Go")*/
1300  
        + " &nbsp; | " +
1301  
        ahref(agiBlueURL("/slices"), nSlices(countConcepts(Slice))) + " | " +
1302  
        ahref(agiBlueURL() + "/createSlice", "Create slice...")
1303  
      )))));
1304  
    }
1305  
    
1306  
    S sliceAsHTML() {
1307  
      if (slice.isMainSlice()) ret htmlEncode2(agiBlueName() + "'s main slice");
1308  
      if (slice.sliceConcept == null) ret "Slice ???";
1309  
      ret htmlEncode2("Slice " + quote(slice.sliceConcept.name));
1310  
    }
1311  
    
1312  
    Slice sliceConcept() { ret slice.sliceConcept; }
1313  
    
1314  
    S footer() {
1315  
      ret p(small(elapsedMS_sysNow(started) + " ms"));
1316  
    }
1317  
    
1318  
    S verifiedEmail() {
1319  
      if (session == null || !(session.user! instanceof GoogleUser)) null;
1320  
      ret ((GoogleUser) session.user!).googleEmail;
1321  
    }
1322  
    
1323  
    bool authed() {
1324  
      bool ver = eqic(verifiedEmail(), "stefan.reich.maker.of.eye@googlemail.com");
1325  
      print("Auth check " + verifiedEmail() + " -> " + ver);
1326  
      ret ver;
1327  
    }
1328  
    
1329  
    S googleSignInID() {
1330  
      ret eqic(domain(), "botcompany.de") ? botCompanyGoogleSignInID() : agiBlueGoogleSignInID();
1331  
    }
1332  
  
1333  
    S hhtml_agiBlue(S contents) {
1334  
      ret hhtml(hAddToHead_fast(contents, 
1335  
        hIncludeGoogleFont("Source Sans Pro")
1336  
        //+ [[<meta name="google-site-verification" content="oRvyeTqgSuv-FE_c-2UKM1Vp0oDqx8h9WruHYWqA-NQ" />]]
1337  
        + loadJQuery()
1338  
        + hmobilefix()
1339  
        + googleSignIn_header("", googleSignInID())
1340  
        + hstylesheet("body { font-family: Source Sans Pro }")));
1341  
    }
1342  
    
1343  
    // list classes in current slice
1344  
    O serveClasses() {
1345  
      // find classes & counts
1346  
      
1347  
      L<Entry> entries = conceptsWhereCI(cc, Entry, key := "_class");
1348  
      MultiSet<S> classesAndCount = ciMultiSet();
1349  
      for (Entry e : entries)
1350  
        classesAndCount.add(e.value);
1351  
        
1352  
      S title = agiBlueNameHTML() + " | " + htmlEncode(sliceConcept().name) + " | Classes";
1353  
      
1354  
      ret hhtml_miniPage(title, h3(title) +
1355  
        htmlTable2(map(keys(classesAndCount), c ->
1356  
          litorderedmap(
1357  
            "Class" := pageToHTMLLink().get(findPageFromQ(c)),
1358  
            "Elements" := classesAndCount.get(c),
1359  
          )),
1360  
          htmlEncode := false));
1361  
    }
1362  
    
1363  
    O serveSlicesList() {
1364  
      S sort = getAny(params, 'sort, 'by);
1365  
      L<Slice> slices;
1366  
      S shown;
1367  
      if (eqic(sort, 'name)) {
1368  
        slices = asList(idx_slicesByName.objectIterator());
1369  
        shown = "sorted by name";
1370  
      } else {
1371  
        sort = 'date;
1372  
        slices = asList(idx_slicesByModification.objectIterator());
1373  
        shown = "last modified first";
1374  
      }
1375  
        
1376  
      S title = agiBlueNameHTML() + " has " + nSlices(slices) /*+ " (" + shown + ")"*/;
1377  
      new PreIncLongCounter counter;
1378  
      ret hhtml_agiBlue(hhead_title_decode(title)
1379  
        + hbody(hfullcenter(
1380  
          h1(title)
1381  
        + p(joinWithSpacedVBar(
1382  
          ahrefIf(neqic(sort, 'name), agiBlueURL() + "/slices" + hquery(sort := 'name), "List by name"),
1383  
          ahrefIf(neqic(sort, 'date), agiBlueURL() + "/slices" + hquery(sort := 'date), "List by date"),
1384  
          ahref(agiBlueURL() + "/createSlice", "Create slice...")))
1385  
  
1386  
        + htmlTable2(map(slices, slice ->
1387  
          litorderedmap(
1388  
            "Name" := sliceLinkHTML(slice),
1389  
            "ID" := ahref(sliceHomeURL(slice), str(slice.globalID)),
1390  
            "Owner" := str(slice.owner!)
1391  
          )),
1392  
          htmlEncode := false)
1393  
        )));
1394  
    }
1395  
  
1396  
    O serveUsersList() {
1397  
      S sort = getAny(params, 'sort, 'by);
1398  
      L<User> users;
1399  
      S shown;
1400  
      if (eqic(sort, 'name))
1401  
        users = asList(idx_usersByName.objectIterator());
1402  
      else {
1403  
        sort = 'seen;
1404  
        users= asList(idx_usersBySeen.objectIterator());
1405  
      }
1406  
        
1407  
      S title = agiBlueNameHTML() + " has " + nUsers(users);
1408  
      new PreIncLongCounter counter;
1409  
      ret hhtml_agiBlue(hhead_title_decode(title)
1410  
        + hbody(hfullcenter(
1411  
          h1(title)
1412  
        + p(joinWithSpacedVBar(
1413  
          ahrefIf(neqic(sort, 'name), agiBlueURL() + "/users" + hquery(sort := 'name), "List by name"),
1414  
          ahrefIf(neqic(sort, 'seen), agiBlueURL() + "/users" + hquery(sort := 'seen), "List by last seen"),
1415  
          ahref(agiBlueURL() + "/createSlice", "Create slice...")))
1416  
  
1417  
        + htmlTable2(map(users, user ->
1418  
          litorderedmap(
1419  
            "Name" := ahref(userHomeURL(user), htmlEncode2(str(user))),
1420  
            "Last seen" := renderHowLongAgo(user.lastSeen)
1421  
          )),
1422  
          htmlEncode := false)
1423  
        )));
1424  
    }
1425  
  
1426  
    S sliceInfoHTML() {
1427  
      User owner = slice.sliceConcept.owner!;
1428  
      ret owner == null ? "" : p("This slice is owned by " + userHTML(owner));
1429  
    }
1430  
  
1431  
    S userHomeURL(User user) {
1432  
      ret user == null ? null : agiBlueURL() + "/user/" + user.globalID;
1433  
    }
1434  
    
1435  
    S userHTML(User user) {
1436  
      ret user == null ? "nobody" : ahref(userHomeURL(user), htmlEncode2(str(user)));
1437  
    }
1438  
    
1439  
    S top() {
1440  
      ret nempty(get) ? "" : hcomment("cookie: " + takeFirst(4, session.cookie))
1441  
        + hSilentComputatorWithFlag("agi.blue: " + q)
1442  
        + p(ahref(sliceHomeURL(),
1443  
          //hsnippetimg(#1101682, width := 565/5, height := 800/5, title := "Robot by Peerro @ DeviantArt")
1444  
          //hsnippetimg(#1101778, width := 96, height := 96, title := "agi.blue - a database for everything")
1445  
          hsnippetimg(#1101822, width := 314, height := 125, title := "agi.blue - Wikipedia for robots")
1446  
        ))
1447  
        + h2(ahref_unstyled(sliceHomeURL(), htmlEncode2(slice.sliceConcept.name), style := "color: yellow"))
1448  
        + sliceInfoHTML()
1449  
        + p(small(
1450  
            agiBlueNameHTML_boldWithSize()
1451  
          + (agiBlue_isOriginal() ? "" : " " + targetBlank("http://agi.blue", "[original]"))
1452  
          + " | " + ahref(agiBlueURL("/slices"), nSlices(countConcepts(Slice)))
1453  
          + " | " + targetBlank(progLink(), "source code") + " of this web site (" + nLines(sourceCodeLines) + ") | " +
1454  
          /*targetBlank("https://gitter.im/agi-blue/community", "sponsor https?") + " | " */ + "by " + targetBlank("https://BotCompany.de", "BC") + " | " + targetBlank("http://fiverr.tinybrain.de/", "Fiverr") + " | " + targetBlank("https://discordapp.com/invite/SEAjPqk", "Discord") + " | " + targetBlank("https://www.youtube.com/watch?v=b6jtRdV3Ev8", "Video")
1455  
          + " | " + targetBlank("http://code.botcompany.de/1024233", "Notes")
1456  
          + " | " + ahref(agiBlueURL() + "/query", "Query")
1457  
        ));
1458  
    }
1459  
  
1460  
    Slice createSlice(S name) {
1461  
      name = shorten(name, maxSliceNameLength);
1462  
      if (sliceForName(name) != null)
1463  
        fail("A slice with the name " + quote(name) + " exists, please choose another name.");
1464  
      Slice slice = cnew Slice(+name, owner := session.user);
1465  
      cset(slice, caseID := slice.defaultCaseID());
1466  
      loadSlice(slice).initialSetup(slice.globalID);
1467  
      ret slice;
1468  
    }
1469  
  
1470  
    O serveCheckboxes() {
1471  
      set conceptsSortedByFieldCI_verbose;
1472  
      int showMax = 1000;
1473  
      Cl<Page> pages = takeFirst(showMax, conceptsSortedByFieldCI(slice.cc, Page, 'q));
1474  
      S title = "Select pages";
1475  
      ret hhtml_miniPage(title, h3(title) + hpostform(
1476  
        htmlTable2(map(pages, page -> litorderedmap(
1477  
          "Page" := hcheckbox("page_" + page.globalID()) + " " + pageToHTMLLink().get(page)
1478  
        )), 
1479  
        htmlEncode := false)
1480  
        + h3("Add an entry to all selected pages")
1481  
          + p("Key: " + hinputfield("key") +
1482  
            " Value: " + hinputfield("value"))
1483  
        + p(hsubmit("Add entries")), action := agiBlueURL() + "/multiAdd"));
1484  
    }
1485  
  
1486  
    O serveMultiAdd() {
1487  
      S key = trim(params.get('key)), value = trim(params.get('value));
1488  
      if (empty(key) || empty(value)) ret "Need key and value";
1489  
      new Matches m;
1490  
      new L<Page> pages;
1491  
      for (S a, b : params)
1492  
        if (startsWith(a, "page_", m))
1493  
          addIfNotNull(pages, conceptWhere(cc, Page, globalID := m.rest()));
1494  
      if (empty(pages)) ret "No pages selected";
1495  
      for (Page page : pages)
1496  
        agiBlue.new GetEntriesAndPost(cc).go(page, params);
1497  
      ret "Entry added to " + nPages(pages);
1498  
    }
1499  
  
1500  
    S agiBlueNameHTML() {
1501  
      ret ahref(agiBlueURL(), htmlEncode2(agiBlueName()));
1502  
    }
1503  
  
1504  
    S agiBlueNameHTML_boldWithSize() {
1505  
      Long size = agiBlue.sizeOnDisk.peek();
1506  
      ret b(agiBlueNameHTML()) + (size == null ? "" : " " + spanTitle("Size of agi.blue's database on disk", "(" + toM(size) + " MB)"));
1507  
    }
1508  
1509  
    IF1<Page, Map> pageToMap(O... _) {
1510  
      optPar bool withEntries;
1511  
      
1512  
      bool nameOnly = eqOneOf(optPar nameOnly(_), "1", true);
1513  
      if (nameOnly) ret (IF1<Page, Map>) p -> litmap(q := p.q);
1514  
      
1515  
      ret (IF1<Page, Map>) p -> {
1516  
        L<Entry> entries = findBackRefs(p, Entry);
1517  
        ret litorderedmap(
1518  
          q := p.q,
1519  
          nEntries := l(entries),
1520  
          created := p.created,
1521  
          modified := p._modified,
1522  
          entries := !withEntries ? null : map(entries, entryToMap(false)));
1523  
      };
1524  
    }
1525  
    
1526  
    IF1<Entry, Map> entryToMap(bool withPage) {
1527  
      ret (IF1<Entry, Map>) e -> litorderedmap(
1528  
        created := e.created,
1529  
        i := e.count,
1530  
        key := e.key,
1531  
        value := e.value,
1532  
        q := withPage ? e.page->q : null,
1533  
        signer := getString globalID(e.signer!));
1534  
    }
1535  
    
1536  
    IF1<Slice, Map> sliceToMap() {
1537  
      ret (IF1<Slice, Map>) s -> litorderedmap(
1538  
        created := s.created,
1539  
        globalID := str(s.globalID),
1540  
        name := s.name,
1541  
        caseID := s.caseID);
1542  
    }
1543  
    
1544  
    O servePageToBot(Page page, SS params) {
1545  
      if (page == null) ret serveJSON(null);
1546  
      params = asCIMap(params);
1547  
      Map map = pageToMap(withEntries := valueIs1 withEntries(params)).get(page);
1548  
      ret serveJSON(map);
1549  
    }
1550  
    
1551  
    SS availableSlices() {
1552  
      L<Slice> slices = asList(idx_slicesByName.objectIterator());
1553  
      ret mapToOrderedMap(s -> pair(str(s.globalID), s.name + " [ID: " + s.globalID + "]"), slices);
1554  
    }
1555  
1556  
    O serveSearchAction() {
1557  
      q = trim(params.get('q));
1558  
      if (empty(q)) ret "You are searching for nothing";
1559  
1560  
      Map<S, CentralIndexEntry> ci = centralIndex;
1561  
      LS entries = containingIC(keys(ci), q);
1562  
1563  
      S title = htmlEncode2("Searching for " + quote(q));
1564  
      ret hhtml_miniPage(title, h2(agiBlueNameHTML() + " | " + title)
1565  
         + h3("Results in slice " + htmlEncode2(quote(sliceConcept().name)))
1566  
         + p("TODO")
1567  
         + h3(firstToUpper(nResults(entries)) + " in central index (" + l(ci) + ")")
1568  
         + htmlTable2(map(entries, e -> {
1569  
            CentralIndexEntry ee = ci.get(e);
1570  
            ret litorderedmap(
1571  
              "Phrase" := ahref(
1572  
                empty(ee.slices) ? null : agiBlue_linkForPhrase(e, slice := first(ee.slices).globalID), htmlEncode2(e)),
1573  
              "Slices" := 
1574  
                joinWithComma(map(takeFirst(3, ee.slices), s -> ahref(agiBlue_linkForPhrase(e, slice := s.globalID),
1575  
                  htmlEncode(s.name)))) +
1576  
                (l(ee.slices) > 3 ? " + " + (l(ee.slices)-3) + " more" : "")
1577  
            );
1578  
         }), htmlEncode := false)
1579  
      );
1580  
    }
1581  
    
1582  
  } // end of Request
1583  
  
1584  
} // end of renderer
1585  
1586  
set flag hotwire_here.
1587  
1588  
// share ISpec interface with sub-modules
1589  
static JavaXClassLoader hotwire_makeClassLoader(L<File> files) {
1590  
  ClassLoader cl = myClassLoader();
1591  
  
1592  
  // Avoid class loader chaining, always reference base class loader
1593  
  // (TODO)
1594  
  /*if (agiBlue().isClone && agiBlue().cloningSince != 0) {
1595  
    ClassLoader parent = cast getOpt(cl, 'virtualParent);
1596  
    print("Cloned class loader. " + parent);
1597  
    if (parent != null) cl = parent;
1598  
  }*/
1599  
  
1600  
  ret new JavaXClassLoaderWithParent2(null, files, cl, parseClassesToShareList(classesToShare));
1601  
}
1602  
1603  
static AGIBlue agiBlue() { ret agiBlue; }
1604  
1605  
static File sliceDir(Slice slice) {
1606  
  ret programFile(slice.caseID);
1607  
}

download  show line numbers  debug dex  old transpilations   

Travelled to 8 computer(s): bhatertpkbcr, cfunsshuasjs, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1023558
Snippet name: agi.blue source [LIVE]
Eternal ID of this version: #1023558/784
Text MD5: ca99a83f98aea2ac29a66c6410b044aa
Transpilation MD5: 2ec59d80c671598010de6661779e6b06
Author: stefan
Category: javax / html
Type: JavaX module (desktop)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2019-10-28 14:49:33
Source code size: 61416 bytes / 1607 lines
Pitched / IR pitched: No / No
Views / Downloads: 2007 / 9753
Version history: 783 change(s)
Referenced in: [show references]