From b7b90800484a61ae340e4d04e2340d3144e43292 Mon Sep 17 00:00:00 2001 From: Dirk Roorda Date: Fri, 25 Jun 2021 20:01:11 +0200 Subject: [PATCH] modularized the javascript --- static/js/.eslintrc => .eslintrc | 10 +- controllers/hebrew.py | 6 +- modules/render.py | 41 +- static/js/app/colorpicker.js | 201 ++ static/js/app/helpers.js | 130 ++ static/js/app/main.js | 33 + static/js/app/material.js | 309 +++ static/js/app/materiallib.js | 295 +++ static/js/app/msg.js | 38 + static/js/{ => app}/notes.js | 105 +- static/js/app/notesm.js | 506 +++++ static/js/app/page.js | 431 ++++ static/js/app/queries.js | 1022 +++++++++ static/js/app/select.js | 623 ++++++ static/js/app/share.js | 365 ++++ static/js/app/sidebars.js | 722 +++++++ static/js/app/viewstate.js | 166 ++ static/js/app/words.js | 80 + static/js/hebrew2021-06-22.js | 3452 ------------------------------ static/js/queries.js | 941 -------- static/js/share.js | 326 --- static/js/words.js | 69 - views/hebrew/note.html | 4 +- views/hebrew/notes.html | 40 +- views/hebrew/queries.html | 7 +- views/hebrew/query.html | 4 +- views/hebrew/sideq.load | 2 +- views/hebrew/text.html | 5 +- views/hebrew/text_body.html | 2 +- views/hebrew/versions.html | 20 +- views/hebrew/word.html | 4 +- views/hebrew/words.html | 16 +- views/layout.html | 1 - 33 files changed, 5045 insertions(+), 4931 deletions(-) rename static/js/.eslintrc => .eslintrc (97%) create mode 100644 static/js/app/colorpicker.js create mode 100644 static/js/app/helpers.js create mode 100644 static/js/app/main.js create mode 100644 static/js/app/material.js create mode 100644 static/js/app/materiallib.js create mode 100644 static/js/app/msg.js rename static/js/{ => app}/notes.js (79%) create mode 100644 static/js/app/notesm.js create mode 100644 static/js/app/page.js create mode 100644 static/js/app/queries.js create mode 100644 static/js/app/select.js create mode 100755 static/js/app/share.js create mode 100644 static/js/app/sidebars.js create mode 100644 static/js/app/viewstate.js create mode 100644 static/js/app/words.js delete mode 100644 static/js/hebrew2021-06-22.js delete mode 100644 static/js/queries.js delete mode 100755 static/js/share.js delete mode 100644 static/js/words.js diff --git a/static/js/.eslintrc b/.eslintrc similarity index 97% rename from static/js/.eslintrc rename to .eslintrc index 42438136..6f5f3a10 100644 --- a/static/js/.eslintrc +++ b/.eslintrc @@ -1,5 +1,5 @@ parserOptions: - sourceType: script + sourceType: module failOnError: true ecmaVersion: 2021 @@ -28,7 +28,8 @@ rules: - warn - ignoreRestSiblings: true no-implicit-globals: off - no-invalid-this: off + no-invalid-this: + - error no-loop-func: warn no-multi-spaces: warn no-new: warn @@ -41,7 +42,7 @@ rules: no-useless-concat: warn no-useless-escape: warn camelcase: - - warn + - off - properties: always comma-dangle: - warn @@ -137,7 +138,8 @@ rules: - enforceForRenamedProperties: false prefer-rest-params: warn prefer-spread: warn - prefer-template: off + prefer-template: + - warn rest-spread-spacing: - warn - never diff --git a/controllers/hebrew.py b/controllers/hebrew.py index 34659e94..47ae9414 100755 --- a/controllers/hebrew.py +++ b/controllers/hebrew.py @@ -65,7 +65,6 @@ def text(): return dict( viewsettings=Viewsettings(cache, passage_dbs, URL, versions), - versions=versions, colorpicker=colorpicker, legend=legend, tp_labels=tp_labels, @@ -1440,8 +1439,11 @@ def words_page(viewsettings, vr, lan=None, letter=None): cache, "words_data_{}_".format(vr), lambda: get_words_data(vr), None ) + version = viewsettings.versionstate() + return dict( - versionstate=viewsettings.versionstate(), + version=version, + viewsettings=viewsettings, lan=lan, letter=letter, letters=letters, diff --git a/modules/render.py b/modules/render.py index 9d4a88f0..290742df 100644 --- a/modules/render.py +++ b/modules/render.py @@ -924,9 +924,11 @@ def __init__(self, cache, passage_dbs, URL, versions): lambda: collections.defaultdict(lambda: {}) ) self.pref = get_request_val("rest", "", "pref") + self.versions = { - k: v for (k, v) in versions.items() if versions[k]["present"] is not None + v: info for (v, info) in versions.items() if info["present"] is not None } + for group in settings: self.state[group] = {} for qw in settings[group]: @@ -994,26 +996,18 @@ def __init__(self, cache, passage_dbs, URL, versions): cache = self.cache passage_dbs = self.passage_dbs - for vr in versions: - if not versions[vr]["present"]: + for (v, vinfo) in self.versions.items(): + if not vinfo["present"]: continue - (books[vr], books_order[vr], book_id[vr], book_name[vr]) = from_cache( - cache, "books_{}_".format(vr), lambda: get_books(passage_dbs, vr), None + (books[v], books_order[v], book_id[v], book_name[v]) = from_cache( + cache, "books_{}_".format(v), lambda: get_books(passage_dbs, v), None ) def theversion(self): return self.state["material"][""]["version"] def versionstate(self): - return """ -var versions = {versions} -var version = '{version}' -""".format( - versions=json.dumps( - dict((v, self.versions[v]["present"]) for v in self.versions) - ), - version=self.state["material"][""]["version"], - ) + return self.state["material"][""]["version"] def writeConfig(self): URL = self.URL @@ -1081,7 +1075,7 @@ def writeConfig(self): dncols=dncols, dnrows=dnrows, versions=json.dumps( - dict((v, self.versions[v]["present"]) for v in self.versions) + [v for (v, inf) in self.versions.items() if inf["present"]] ), tp_labels=json.dumps(tp_labels), tr_labels=json.dumps(tr_labels), @@ -1120,21 +1114,12 @@ def writeConfig(self): pn_url=URL("hebrew", "note_tree", extension="json"), n_url=URL("hebrew", "text", extension=""), upload_url=URL("hebrew", "note_upload", extension="json"), - pq_url=URL('hebrew', 'query_tree', extension='json'), - queriesr_url=URL('hebrew', 'queriesr', extension='json'), - q_url=URL('hebrew', 'text', extension=''), - record_url=URL('hebrew', 'record', extension='json'), + pq_url=URL("hebrew", "query_tree", extension="json"), + queriesr_url=URL("hebrew", "queriesr", extension="json"), + q_url=URL("hebrew", "text", extension=""), + record_url=URL("hebrew", "record", extension="json"), ) - def dynamics(self): - config = self.writeConfig() - - return f""" -{config} -var P = setup() -dynamics(P) -""" - def h_esc(material, fill=True): material = ( diff --git a/static/js/app/colorpicker.js b/static/js/app/colorpicker.js new file mode 100644 index 00000000..b546d4f7 --- /dev/null +++ b/static/js/app/colorpicker.js @@ -0,0 +1,201 @@ +/* eslint-env jquery */ +/* globals Config, P */ + +import { close_dialog, defcolor } from "./helpers.js" + +export class Colorpicker1 { + /* the colorpicker associated with individual items + * + * These pickers show up in lists of items (in mq and mw sidebars) and + * near individual items (in rq and rw sidebars). + * They also have a checkbox, stating whether the color counts as customized. + * Customized colors are held in a global colormap, + * which is saved in a cookie upon every picking action. + * + * All actions are processed by the highlight2 (!) method + * of the associated Settings object. + */ + constructor(qw, iid, is_item, do_highlight) { + const { style, vcolors } = Config + + const pointer = is_item ? "me" : iid + this.code = is_item ? "1a" : "1" + this.qw = qw + this.iid = iid + this.picker = $(`#picker_${qw}${pointer}`) + this.stl = style[qw]["prop"] + this.sel = $(`#sel_${qw}${pointer}`) + this.selw = $(`#sel_${qw}${pointer}>a`) + this.selc = $(`#selc_${qw}${pointer}`) + + this.sel.click(e => { + e.preventDefault() + this.picker.dialog({ + dialogClass: "picker_dialog", + closeOnEscape: true, + modal: true, + title: "choose a color", + position: { my: "right top", at: "left top", of: this.selc }, + width: "200px", + }) + }) + + this.selc.click(() => { + /* process a click on the selectbox of the picker + */ + const { qw, iid, picker } = this + const was_cust = P.vs.iscolor(qw, iid) + close_dialog(picker) + if (was_cust) { + P.vs.cstatex(qw, iid) + } else { + const vals = {} + vals[iid] = defcolor(qw == "q", iid) + P.vs.cstatesv(qw, vals) + const active = P.vs.active(qw) + if (active != "hlcustom" && active != "hlmany") { + P.vs.hstatesv(qw, { active: "hlcustom" }) + } + } + P.vs.addHist() + this.apply(true) + }) + + $(`.c${qw}.${qw}${pointer}>a`).click(e => { + /* process a click on a colored cell of the picker + */ + e.preventDefault() + const elem = $(e.target) + const { picker } = this + close_dialog(picker) + + const { qw, iid } = this + const vals = {} + vals[iid] = elem.html() + P.vs.cstatesv(qw, vals) + P.vs.hstatesv(qw, { active: "hlcustom" }) + P.vs.addHist() + this.apply(true) + }) + + this.picker.hide() + $(`.c${qw}.${qw}${pointer}>a`).each((i, el) => { + /* initialize the individual color cells in the picker + */ + const elem = $(el) + const { qw } = this + const target = qw == "q" ? elem.closest("td") : elem + target.css(this.stl, vcolors[elem.html()][qw]) + }) + this.apply(do_highlight) + } + + adapt(iid, do_highlight) { + this.iid = iid + this.apply(do_highlight) + } + + apply(do_highlight) { + const { vcolors } = Config + + const { qw, iid, stl, sel, selc, selw } = this + const color = P.vs.color(qw, iid) || defcolor(qw == "q", iid) + const target = qw == "q" ? sel : selw + if (color) { + target.css(stl, vcolors[color][qw]) + /* apply state to the selected cell + */ + } + selc.prop("checked", P.vs.iscolor(qw, iid)) + /* apply state to the checkbox + */ + if (do_highlight) { + P.highlight2(this) + } + } +} + +export class Colorpicker2 { + /* the colorpicker associated with the view settings in a sidebar + * + * These pickers show up at the top of the individual sidebars, + * only on mq and mw sidebars. + * They are used to control the uniform color with which + * the results are to be painted. + * They can be configured for dealing with background or foreground painting. + * The paint actions depend on the mode of coloring + * that the user has selected in settings. + * So the paint logic is more involved. + * But there is no associated checkbox. + * The selected color is stored in the highlight settings, + * which are synchronized in a cookie. + * All actions are processed by the highlight2 method + * of the associated Settings object. + */ + constructor(qw, do_highlight) { + const { style, vcolors } = Config + + this.code = "2" + this.qw = qw + this.picker = $(`#picker_${qw}one`) + this.stl = style[this.qw]["prop"] + this.sel = $(`#sel_${qw}one`) + this.selw = $(`#sel_${qw}one>a`) + + this.sel.click(e => { + e.preventDefault() + this.picker.dialog({ + dialogClass: "picker_dialog", + closeOnEscape: true, + modal: true, + title: "choose a color", + position: { my: "right top", at: "left top", of: this.sel }, + width: "200px", + }) + }) + + $(`.c${qw}.${qw}one>a`).click(e => { + /* process a click on a colored cell of the picker + */ + e.preventDefault() + const elem = $(e.target) + const { picker } = this + close_dialog(picker) + const { qw } = this + const current_active = P.vs.active(qw) + if (current_active != "hlone" && current_active != "hlcustom") { + P.vs.hstatesv(qw, { active: "hlcustom", sel_one: elem.html() }) + } else { + P.vs.hstatesv(qw, { sel_one: elem.html() }) + } + P.vs.addHist() + this.apply(true) + }) + + this.picker.hide() + + $(`.c${qw}.${qw}one>a`).each((i, el) => { + /* initialize the individual color cells in the picker + */ + const elem = $(el) + const { qw, stl } = this + const target = qw == "q" ? elem.closest("td") : elem + target.css(stl, vcolors[elem.html()][qw]) + }) + this.apply(do_highlight) + } + + apply(do_highlight) { + const { vcolors } = Config + + const { qw, stl, sel, selw } = this + const color = P.vs.sel_one(qw) || defcolor(qw, null) + const target = qw == "q" ? sel : selw + target.css(stl, vcolors[color][qw]) + /* apply state to the selected cell + */ + if (do_highlight) { + P.highlight2(this) + } + } +} diff --git a/static/js/app/helpers.js b/static/js/app/helpers.js new file mode 100644 index 00000000..140fc3ff --- /dev/null +++ b/static/js/app/helpers.js @@ -0,0 +1,130 @@ +/* eslint-env jquery */ +/* globals Config, markdown */ + +export const escHT = text => { + const chr = { + "&": "&", + "<": "<", + ">": ">", + } + return text.replace(/[&<>]/g, a => chr[a]) +} + +const mEscape = ns => ns.replace(/_/g, "\\_") + +export const markdownEscape = ntxt => ntxt.replace(/\[[^\]\n\t]+\]\([^)]*\)/g, mEscape) + +export const put_markdown = wdg => { + const did = wdg.attr("did") + const src = $(`#dv_${did}`) + const mdw = $(`#dm_${did}`) + mdw.html(special_links(markdown.toHTML(src.val()))) +} + +export const toggle_detail = (wdg, detail, extra) => { + const thedetail = detail == undefined ? wdg.closest("div").find(".detail") : detail + thedetail.toggle() + if (extra != undefined) { + extra(wdg) + } + let thiscl, othercl + if (wdg.hasClass("fa-chevron-right")) { + thiscl = "fa-chevron-right" + othercl = "fa-chevron-down" + } else { + thiscl = "fa-chevron-down" + othercl = "fa-chevron-right" + } + wdg.removeClass(thiscl) + wdg.addClass(othercl) +} + +export const special_links = d_mdGiven => { + const { featurehost, host } = Config + + let d_md = d_mdGiven + d_md = d_md.replace( + /]*href=['"]image[\n\t ]+([^)\n\t '"]+)['"][^>]*>(.*?)(<\/a>)/g, + '

$2
' + ) + d_md = d_md.replace( + /(
]*)href=['"]([^)\n\t '"]+)[\n\t ]+([^:)\n\t '"]+):([^)\n\t '"]+)['"]([^>]*)>(.*?)(<\/a>)/g, + '$1b="$2" c="$3" v="$4" href="#" class="fa fw" $5>$6$7' + ) + d_md = d_md.replace( + /(]*)href=['"]([^)\n\t '"]+)[\n\t ]+([^)\n\t '"]+)['"]([^>]*)>(.*?)(<\/a>)/g, + '$1b="$2" c="$3" v="1" href="#" class="fa fw" $4>$5$6' + ) + d_md = d_md.replace( + /(href=['"])shebanq:([^)\n\t '"]+)(['"])/g, + `$1${host}$2$3 class="fa fw fa-bookmark" ` + ) + d_md = d_md.replace( + /(href=['"])feature:([^)\n\t '"]+)(['"])/g, + `$1${featurehost}/$2$3 target="_blank" class="fa fw fa-file-text" ` + ) + d_md = d_md.replace( + /\[([^\]\n\t]+)\]\(image[\n\t ]+([^)\n\t '"]+)\)/g, + '

$1
' + ) + d_md = d_md.replace( + /\[([^\]\n\t]+)\]\(([^)\n\t '"]+)[\n\t ]+([^:)\n\t '"]+):([^)\n\t '"]+)\)/g, + '
$1' + ) + d_md = d_md.replace( + /\[([^\]\n\t]+)\]\(([^)\n\t '"]+)[\n\t ]+([^)\n\t '"]+)\)/g, + '$1' + ) + d_md = d_md.replace( + /\[([^\]\n\t]+)\]\(shebanq:([^)\n\t '"]+)\)/g, + `$1` + ) + d_md = d_md.replace( + /\[([^\]\n\t]+)\]\(feature:([^)\n\t '"]+)\)/g, + `$1` + ) + d_md = d_md.replace( + /\[([^\]\n\t]+)\]\(([^)\n\t '"]+)\)/g, + '$1' + ) + return d_md +} + +export const defcolor = (qw, iid) => { + /* compute the default color + * + * The data for the computation comes from the server + * and is stored in the javascript global variable Config + * vdefaultcolors, dncols, dnrows + */ + const { style, vdefaultcolors, dncols, dnrows } = Config + + let result + if (qw in style) { + result = style[qw]["default"] + } else if (qw) { + const mod = iid % vdefaultcolors.length + result = vdefaultcolors[dncols * (mod % dnrows) + Math.floor(mod / dnrows)] + } else { + const iidstr = iid == null ? "" : iid + let sumiid = 0 + for (let i = 0; i < iidstr.length; i++) { + sumiid += iidstr.charCodeAt(i) + } + const mod = sumiid % vdefaultcolors.length + result = vdefaultcolors[dncols * (mod % dnrows) + Math.floor(mod / dnrows)] + } + return result +} + +export const close_dialog = dia => { + const was_open = Boolean( + dia && dia.length && dia.dialog("instance") && dia.dialog("isOpen") + ) + if (was_open) { + dia.dialog("close") + } + return was_open +} + + diff --git a/static/js/app/main.js b/static/js/app/main.js new file mode 100644 index 00000000..18a2cc1b --- /dev/null +++ b/static/js/app/main.js @@ -0,0 +1,33 @@ +/* eslint-env jquery */ +/* globals Config */ + +import { Page, LStorage } from "./page.js" +import { ViewState } from "./viewstate.js" + +$.cookie.raw = false +$.cookie.json = true +$.cookie.defaults.expires = 30 +$.cookie.defaults.path = "/" + +const setup = () => { + /* top level, called when the page has loaded + * inserts the Config global var + */ + const { viewinit, pref } = Config + window.L = new LStorage + window.P = new Page(new ViewState(viewinit, pref)) +} + +const dynamics = () => { + /* top level, called when the page has loaded + * inserts the Config global var + * P is the handle to manipulate the whole page + */ + window.P.init() + window.P.go() +} + +$(() => { + setup() + dynamics() +}) diff --git a/static/js/app/material.js b/static/js/app/material.js new file mode 100644 index 00000000..59cf008f --- /dev/null +++ b/static/js/app/material.js @@ -0,0 +1,309 @@ +/* eslint-env jquery */ +/* globals Config, P */ + +import { defcolor, close_dialog } from "./helpers.js" +import { Notes } from "./notesm.js" +import { LSelect, MSelect, PSelect } from "./select.js" +import { MMessage, MContent, MSettings } from "./materiallib.js" + + +export class Material { + /* Object corresponding to everything that controls the material in the main part + * (not in the side bars) + */ + constructor() { + this.name = "material" + this.hid = `#${this.name}` + this.lselect = new LSelect() + this.mselect = new MSelect() + this.pselect = new PSelect() + this.message = new MMessage() + this.content = new MContent() + this.msettings = new MSettings(this.content) + this.message.msg("choose a passage or a query or a word") + } + + adapt() { + this.fetch() + } + + apply() { + /* apply viewsettings to current material + */ + const { booklangs, booktrans } = Config + P.version = P.vs.version() + P.mr = P.vs.mr() + P.qw = P.vs.qw() + P.iid = P.vs.iid() + if ( + P.mr != P.prev["mr"] || + P.qw != P.prev["qw"] || + P.version != P.prev["version"] || + (P.mr == "m" && + (P.vs.book() != P.prev["book"] || + P.vs.chapter() != P.prev["chapter"] || + P.vs.verse() != P.prev["verse"])) || + (P.mr == "r" && (P.iid != P.prev["iid"] || P.vs.page() != P.prev["page"])) + ) { + P.reset_material_status() + const p_mr = P.prev["mr"] + const p_qw = P.prev["qw"] + const p_iid = P.prev["iid"] + if (p_mr == "r" && P.mr == "m") { + const vals = {} + if (p_qw != "n") { + vals[p_iid] = P.vs.colormap(p_qw)[p_iid] || defcolor(p_qw == "q", p_iid) + P.vs.cstatesv(p_qw, vals) + } + } + } + this.lselect.apply() + this.mselect.apply() + this.pselect.apply() + this.msettings.apply() + const book = P.vs.book() + const chapter = P.vs.chapter() + const page = P.vs.page() + $("#thelang").html(booklangs[P.vs.lang()][1]) + $("#thebook").html(book != "x" ? booktrans[P.vs.lang()][book] : "book") + $("#thechapter").html(chapter > 0 ? chapter : "chapter") + $("#thepage").html(page > 0 ? `${page}` : "") + for (const x in P.vs.mstate()) { + P.prev[x] = P.vs.mstate()[x] + } + } + + fetch() { + /* get the material by AJAX if needed, and process the material afterward + */ + const { material_url } = Config + const { material_fetched, material_kind } = P + + let vars = + `?version=${P.version}&mr=${P.mr}&tp=${P.vs.tp()}&tr=${P.vs.tr()}` + + `&qw=${P.qw}&lang=${P.vs.lang()}` + let do_fetch = false + if (P.mr == "m") { + vars += `&book=${P.vs.book()}&chapter=${P.vs.chapter()}` + do_fetch = P.vs.book() != "x" && P.vs.chapter() > 0 + } else { + vars += `&iid=${P.iid}&page=${P.vs.page()}` + do_fetch = P.qw == "q" ? P.iid >= 0 : P.iid != "-1" + } + const tp = P.vs.tp() + const tr = P.vs.tr() + if ( + do_fetch && + (!material_fetched[tp] || !(tp in material_kind) || material_kind[tp] != tr) + ) { + this.message.msg("fetching data ...") + P.sidebars.after_material_fetch() + $.get( + `${material_url}${vars}`, + html => { + const response = $(html) + this.pselect.add(response) + this.message.add(response) + this.content.add(response) + material_fetched[tp] = true + material_kind[tp] = tr + this.process() + this.goto_verse() + }, + "html" + ) + } else { + P.highlight2({ code: "5", qw: "w" }) + P.highlight2({ code: "5", qw: "q" }) + this.msettings.hebrewsettings.apply() + this.goto_verse() + } + } + goto_verse() { + /* go to the selected verse + */ + $(".vhl").removeClass("vhl") + const xxx = P.mr == "r" ? "div[tvid]" : `div[tvid="${P.vs.verse()}"]` + const vtarget = $(`#material_${P.vs.tp()}>${xxx}`).filter(":first") + if (vtarget != undefined && vtarget[0] != undefined) { + vtarget[0].scrollIntoView() + $("#navbar")[0].scrollIntoView() + vtarget.addClass("vhl") + } + } + process() { + /* process new material obtained by an AJAX call + */ + const { material_fetched, material_kind } = P + + let mf = 0 + const tp = P.vs.tp() + const tr = P.vs.tr() + for (const x in material_fetched) { + if (material_fetched[x]) { + mf += 1 + } + } + /* count how many versions of this material already have been fetched + */ + if (material_kind[tp] != "" && material_kind != tr) { + /* and also whether the material has already been fetched + * in another transcription + */ + mf += 1 + } + const newcontent = $(`#material_${tp}`) + const textcontent = $(".txt_p,.txt_tb1,.txt_tb2,.txt_tb3") + const ttextcontent = $(".t1_txt,.lv2") + if (P.vs.tr() == "hb") { + textcontent.removeClass("pho") + textcontent.removeClass("phox") + ttextcontent.removeClass("pho") + textcontent.addClass("heb") + textcontent.addClass("hebx") + ttextcontent.addClass("heb") + } else if (P.vs.tr() == "ph") { + textcontent.removeClass("heb") + textcontent.removeClass("hebx") + ttextcontent.removeClass("heb") + textcontent.addClass("pho") + textcontent.addClass("phox") + ttextcontent.addClass("pho") + } + /* because some processes like highlighting and assignment of verse number clicks + * must be suppressed on first time or on subsequent times + */ + if (P.mr == "r") { + this.pselect.apply() + if (P.qw != "n") { + P.picker1[P.qw].adapt(P.iid, true) + } + $("a.cref").click(e => { + e.preventDefault() + const elem = $(e.target) + P.vs.mstatesv({ + book: elem.attr("book"), + chapter: elem.attr("chapter"), + verse: elem.attr("verse"), + mr: "m", + }) + P.vs.addHist() + P.go() + }) + } else { + this.add_word_actions(newcontent, mf) + } + this.add_vrefs(newcontent, mf) + if (P.vs.tp() == "txt_il") { + this.msettings.hebrewsettings.apply() + } else if (P.vs.tp() == "txt_tb1") { + this.add_cmt(newcontent) + } + } + + add_cmt(newcontent) { + /* add actions for the tab1 view + */ + this.notes = new Notes(newcontent) + } + + add_vrefs(newcontent, mf) { + const { data_url } = Config + + const vrefs = newcontent.find(".vradio") + vrefs.each((i, el) => { + const elem = $(el) + elem.attr("title", "interlinear data") + }) + vrefs.click(e => { + e.preventDefault() + const elem = $(e.target) + const bk = elem.attr("b") + const ch = elem.attr("c") + const vs = elem.attr("v") + const base_tp = P.vs.tp() + const dat = $(`#${base_tp}_txt_il_${bk}_${ch}_${vs}`) + const txt = $(`#${base_tp}_${bk}_${ch}_${vs}`) + const legend = $("#datalegend") + const legendc = $("#datalegend_control") + if (elem.hasClass("ison")) { + elem.removeClass("ison") + elem.attr("title", "interlinear data") + legend.hide() + close_dialog(legend) + legendc.hide() + dat.hide() + txt.show() + } else { + elem.addClass("ison") + elem.attr("title", "text/tab") + legendc.show() + dat.show() + txt.hide() + if (dat.attr("lf") == "x") { + dat.html(`fetching data for ${bk} ${ch}:${vs} ...`) + dat.load( + `${data_url}?version=${P.version}&book=${bk}&chapter=${ch}&verse=${vs}`, + () => { + dat.attr("lf", "v") + this.msettings.hebrewsettings.apply() + if (P.mr == "r") { + if (P.qw != "n") { + P.picker1[P.qw].adapt(P.iid, true) + } + } else { + P.highlight2({ code: "5", qw: "w" }) + P.highlight2({ code: "5", qw: "q" }) + this.add_word_actions(dat, mf) + } + }, + "html" + ) + } + } + }) + } + + add_word_actions(newcontent, mf) { + /* Make words clickable, so that they show up in the sidebar + */ + newcontent.find("span[l]").click(e => { + e.preventDefault() + const elem = $(e.target) + const iid = elem.attr("l") + const qw = "w" + const all = $(`#${qw}${iid}`) + if (P.vs.iscolor(qw, iid)) { + P.vs.cstatex(qw, iid) + all.hide() + } else { + const vals = {} + vals[iid] = defcolor(false, iid) + P.vs.cstatesv(qw, vals) + all.show() + } + const active = P.vs.active(qw) + if (active != "hlcustom" && active != "hlmany") { + P.vs.hstatesv(qw, { active: "hlcustom" }) + } + if (P.vs.get("w") == "v") { + if (iid in P.picker1list["w"]) { + /* should not happen but it happens when changing data versions + */ + P.picker1list["w"][iid].apply(false) + } + } + P.highlight2({ code: "4", qw }) + P.vs.addHist() + }) + if (mf > 1) { + /* Initially, material gets highlighted once the sidebars have been loaded. + * But when we load a different representation of material (data, tab), + * the sidebars are still there, + * and after loading the material, highlighs have to be applied. + */ + P.highlight2({ code: "5", qw: "q" }) + P.highlight2({ code: "5", qw: "w" }) + } + } +} diff --git a/static/js/app/materiallib.js b/static/js/app/materiallib.js new file mode 100644 index 00000000..dd3ae340 --- /dev/null +++ b/static/js/app/materiallib.js @@ -0,0 +1,295 @@ +/* eslint-env jquery */ +/* globals Config, P */ + +import { close_dialog } from "./helpers.js" +import { Colorpicker2 } from "./colorpicker.js" + + +export class MMessage { + /* diagnostic output + */ + constructor() { + this.name = "material_message" + this.hid = `#${this.name}` + } + + add(response) { + $(this.hid).html(response.children(this.hid).html()) + } + + msg(m) { + $(this.hid).html(m) + } +} + +export class MContent { + /* the actual Hebrew content, either plain text or tabbed data + */ + constructor() { + this.name_content = "#material_content" + this.select = () => {} + } + + add(response) { + $(`#material_${P.vs.tp()}`).html(response.children(this.name_content).html()) + } + + show() { + const { next_tp } = Config + + const this_tp = P.vs.tp() + for (const tp in next_tp) { + const this_material = $(`#material_${tp}`) + if (this_tp == tp) { + this_material.show() + } else { + this_material.hide() + } + } + } +} + +/* MATERIAL SETTINGS (for choosing between plain text and tabbed data) + * + */ + +export class MSettings { + constructor(content) { + const { featurehost } = Config + + const { next_tp, next_tr, tab_info, tab_views, tr_info, tr_labels } = Config + + const hltext = $("#mtxt_p") + const hltabbed = $("#mtxt_tb1") + this.legend = $("#datalegend") + this.legendc = $("#datalegend_control") + this.name = "material_settings" + this.hid = `#${this.name}` + this.content = content + this.hebrewsettings = new HebrewSettings() + + hltext.show() + hltabbed.show() + + this.legendc.click(e => { + e.preventDefault() + $("#datalegend") + .find("a[fname]") + .each((i, el) => { + const elem = $(el) + const url = `${featurehost}/${elem.attr("fname")}` + elem.attr("href", url) + }) + this.legend.dialog({ + autoOpen: true, + dialogClass: "legend", + closeOnEscape: true, + modal: false, + title: "legend", + width: "600px", + }) + }) + + $(".mhradio").click(e => { + e.preventDefault() + const elem = $(e.target) + const old_tp = P.vs.tp() + let new_tp = elem.attr("id").substring(1) + if (old_tp == "txt_p") { + if (old_tp == new_tp) { + return + } + } else if (old_tp == new_tp) { + new_tp = next_tp[old_tp] + if (new_tp == "txt_p") { + new_tp = next_tp[new_tp] + } + } + P.vs.mstatesv({ tp: new_tp }) + P.vs.addHist() + this.apply() + }) + + $(".mtradio").click(e => { + e.preventDefault() + const elem = $(e.target) + const old_tr = P.vs.tr() + let new_tr = elem.attr("id").substring(1) + if (old_tr == new_tr) { + new_tr = next_tr[old_tr] + } + + P.vs.mstatesv({ tr: new_tr }) + P.vs.addHist() + this.apply() + }) + + for (let i = 1; i <= tab_views; i++) { + const mc = $(`#mtxt_tb${i}`) + mc.attr("title", tab_info[`txt_tb${i}`]) + if (i == 1) { + mc.show() + } else { + mc.hide() + } + } + + for (const l in tr_labels) { + const t = tr_info[l] + const mc = $(`#m${t}`) + mc.attr("title", tr_labels[t]) + if (l == "hb") { + mc.show() + } else { + mc.hide() + } + } + } + + apply() { + const { tab_views } = Config + + const hlradio = $(".mhradio") + const plradio = $(".mtradio") + const new_tp = P.vs.tp() + const new_tr = P.vs.tr() + const newc = $(`#m${new_tp}`) + const newp = $(`#m${new_tr}`) + hlradio.removeClass("ison") + plradio.removeClass("ison") + if (new_tp != "txt_p" && new_tp != "txt_il") { + for (let i = 1; i <= tab_views; i++) { + const mc = $(`#mtxt_tb${i}`) + if (`txt_tb${i}` == new_tp) { + mc.show() + } else { + mc.hide() + } + } + } + newc.show() + newp.show() + newc.addClass("ison") + newp.addClass("ison") + this.content.show() + this.legend.hide() + close_dialog(this.legend) + this.legendc.hide() + P.material.adapt() + } +} + +/* HEBREW DATA (which fields to show if interlinear text is displayed) + * + */ + +class HebrewSettings { + constructor() { + for (const fld in P.vs.ddata()) { + this[fld] = new HebrewSetting(fld) + } + } + + apply() { + const { versions } = Config + + for (const fld in P.vs.ddata()) { + this[fld].apply() + } + for (const v of versions) { + P.set_csv(v, P.vs.mr(), P.vs.qw(), P.vs.iid()) + } + } +} + +class HebrewSetting { + constructor(fld) { + const { versions } = Config + + this.name = fld + this.hid = `#${this.name}` + $(this.hid).click(e => { + const elem = $(e.target) + const vals = {} + vals[fld] = elem.prop("checked") ? "v" : "x" + P.vs.dstatesv(vals) + P.vs.addHist() + this.applysetting() + for (const v of versions) { + P.set_csv(v, P.vs.mr(), P.vs.qw(), P.vs.iid()) + } + }) + } + + apply() { + const val = P.vs.ddata()[this.name] + $(this.hid).prop("checked", val == "v") + this.applysetting() + } + + applysetting() { + if (P.vs.ddata()[this.name] == "v") { + $(`.${this.name}`).each((i, el) => { + const elem = $(el) + elem.show() + }) + } else { + $(`.${this.name}`).each((i, el) => { + const elem = $(el) + elem.hide() + }) + } + } +} + +export class LSettings { + /* the view controls belonging to a side bar with a list of items + */ + constructor(qw) { + this.qw = qw + + const { sidebars: { side_fetched, sidebar } } = P + + if (qw != "n") { + this.picker2 = new Colorpicker2(this.qw, false) + const hlradio = $(`.${qw}hradio`) + hlradio.click(e => { + e.preventDefault() + const elem = $(e.target) + P.vs.hstatesv(this.qw, { active: elem.attr("id").substring(1) }) + P.vs.addHist() + P.highlight2({ code: "3", qw: this.qw }) + }) + } + if (qw == "q" || qw == "n") { + const pradio = $(`.${qw}pradio`) + pradio.click(e => { + e.preventDefault() + P.vs.hstatesv(this.qw, { pub: P.vs.pub(this.qw) == "x" ? "v" : "x" }) + side_fetched[`m${this.qw}`] = false + sidebar[`m${this.qw}`].content.apply() + }) + } + } + + apply() { + const { qw } = this + if (P.vs.get(qw) == "v") { + if (qw != "n") { + for (const iid in P.picker1list[qw]) { + P.picker1list[qw][iid].apply(false) + } + P.picker2[qw].apply(true) + } + } + if (qw == "q" || qw == "n") { + const pradio = $(`.${qw}pradio`) + if (P.vs.pub(qw) == "v") { + pradio.addClass("ison") + } else { + pradio.removeClass("ison") + } + } + } +} + diff --git a/static/js/app/msg.js b/static/js/app/msg.js new file mode 100644 index 00000000..a2909678 --- /dev/null +++ b/static/js/app/msg.js @@ -0,0 +1,38 @@ +/* eslint-env jquery */ + +export class Msg { + constructor(destination, on_clear) { + this.destination = $(`#${destination}`) + this.trashc = $(`#trash_${destination}`) + this.on_clear = on_clear + + this.trashc.click(e => { + e.preventDefault() + this.clear() + }) + this.trashc.hide() + } + + clear() { + this.destination.html("") + if (this.on_clear != undefined) { + this.on_clear() + } + this.trashc.hide() + } + hide() { + this.destination.hide() + this.trashc.hide() + } + show() { + this.destination.show() + if (this.destination.html() != "") { + this.trashc.show() + } + } + msg(msgobj) { + const mtext = this.destination.html() + this.destination.html(`${mtext}

${msgobj[1]}

`) + this.trashc.show() + } +} diff --git a/static/js/notes.js b/static/js/app/notes.js similarity index 79% rename from static/js/notes.js rename to static/js/app/notes.js index bf71d37e..1a862663 100644 --- a/static/js/notes.js +++ b/static/js/app/notes.js @@ -1,12 +1,11 @@ /* eslint-env jquery */ -/* eslint-disable camelcase, no-new */ +/* eslint-disable no-new */ -/* globals Config, Msg */ +/* globals L, Config */ + +import { LStorage } from "./page.js" +import { Msg } from "./msg.js" -const ns = $.initNamespaceStorage("muting_n") -const vs = $.initNamespaceStorage("nsview") -const muting = ns.localStorage -const nsview = vs.localStorage let ftree, msgflt, rdata const subtractn = 80 /* the canvas holding the material gets a height equal to @@ -18,6 +17,7 @@ const control_height = 100 class View { constructor() { + const { nsview } = L this.prevstate = false if (!nsview.isSet("simple")) { nsview.set("simple", true) @@ -41,6 +41,7 @@ class View { } adjust_view() { + const { nsview } = L const simple = nsview.get("simple") this.nvradio.removeClass("ison") ;(simple ? this.csimple : this.cadvanced).addClass("ison") @@ -57,6 +58,7 @@ class View { class Level { constructor() { + const { nsview } = L this.levels = { u: 1, n: 2 } $(".nlradio").removeClass("ison") @@ -84,6 +86,7 @@ class Level { } expand_level(level) { + const { nsview } = L const { levels } = this $(".nlradio").removeClass("ison") @@ -99,16 +102,20 @@ class Level { } initlevel() { + const { nsview } = L this.expand_level(nsview.get("level")) } } class Filter { constructor() { + const { nsview } = L this.patc = $("#filter_contents") $("#filter_clear").hide() - $("#filter_contents").val(nsview.isSet("filter_pat") ? nsview.get("filter_pat") : "") + $("#filter_contents").val( + nsview.isSet("filter_pat") ? nsview.get("filter_pat") : "" + ) if (nsview.isSet("filter_mode")) { this.pnsearch(nsview.get("filter_mode")) $("#filter_clear").show() @@ -133,6 +140,7 @@ class Filter { } clear() { + const { nsview } = L ftree.ftw.clearFilter() msgflt.clear() msgflt.msg(["good", "no filter applied"]) @@ -147,6 +155,7 @@ class Filter { } pnsearch(kind) { + const { nsview } = L const pat = this.patc.val() nsview.set("filter_pat", pat) let amatches = 0 @@ -183,14 +192,13 @@ class Filter { const submatch = "span.fancytree-submatch" const match = "span.fancytree-match" const base_u = "#queries>ul>li>ul>li>" - const match_u = $(base_u + match).length - const submatch_u = $(base_u + submatch).length - const base_n = base_n + "ul>li>" - const match_n = $(base_n + match).length + const match_u = $(`${base_u}${match}`).length + const submatch_u = $(`${base_u}${submatch}`).length + const base_n = `${base_n}ul>li>` + const match_n = $(`${base_n}${match}`).length $("#count_u").html(` ${match_u} - ${submatch_u}` - ) + ${submatch_u}`) $("#count_n").html(`${match_n}`) if (ftree.view.simple) { $(".brn").hide() @@ -201,6 +209,7 @@ class Filter { class Tree { constructor() { const { pn_url } = Config + const { muting_n: muting } = L this.tps = { u: "user", n: "note" } @@ -268,14 +277,49 @@ class Tree { const form_height = standard_height - control_height const canvas_left = $(".left-sidebar") const canvas_right = $(".right-sidebar") - canvas_left.css("height", standard_height + "px") - $("#notes").css("height", standard_height + "px") - $("#opqforms").css("height", form_height + "px") - $("#opqctrl").css("height", control_height + "px") - canvas_right.css("height", standard_height + "px") + canvas_left.css("height", `${standard_height}px`) + $("#notes").css("height", `${standard_height}px`) + $("#opqforms").css("height", `${form_height}px`) + $("#opqctrl").css("height", `${control_height}px`) + canvas_right.css("height", `${standard_height}px`) + + const detailcontrols = `` + + $('dt.cps').each((i, e) => { + const elem = $(e.target) + const orig = elem.html() + elem.html(`${detailcontrols} ${orig}`) + }) + $('dd.cps').hide() + $('.hidec').hide() + $('.showc').show() + $('.hidec').click(e => { + e.preventDefault() + const elem = $(e.target) + const refo = elem.closest('dt') + refo.next().hide() + refo.find('.hidec').hide() + refo.find('.showc').show() + }) + $('.showc').click(e => { + e.preventDefault() + const elem = $(e.target) + const refo = elem.closest('dt') + refo.next().show() + refo.find('.hidec').show() + refo.find('.showc').hide() + }) } store_select(node) { + const { muting_n: muting } = L const { folder, key: iid, selected } = node if (!folder) { if (selected) { @@ -304,14 +348,10 @@ class Tree { $("#notes a[nkid]").each((i, el) => { const elem = $(el) const vr = elem.attr("v") - const extra = vr == undefined ? "" : "&version=" + vr + const extra = vr == undefined ? "" : `&version=${vr}` elem.attr( "href", - n_url + - "?iid=" + - elem.attr("nkid") + - extra + - "&page=1&mr=r&qw=n&tp=txt_tb1&nget=v" + `${n_url}?iid=${elem.attr("nkid")}${extra}&page=1&mr=r&qw=n&tp=txt_tb1&nget=v` ) }) $("#notes a.md").click(e => { @@ -330,13 +370,11 @@ class Tree { gotonote(nkid) { if (nkid != undefined && nkid != "0") { - const nnode = this.ftw.getNodeByKey("n" + nkid) + const nnode = this.ftw.getNodeByKey(`n${nkid}`) if (nnode != null) { nnode.makeVisible({ noAnimation: true }) $(".treehl").removeClass("treehl") - $(`a[nkid="${nkid}"]`) - .closest("span") - .addClass("treehl") + $(`a[nkid="${nkid}"]`).closest("span").addClass("treehl") $(nnode.li)[0].scrollIntoView() } } @@ -364,20 +402,14 @@ class Upload { if (file.name.length > 0) { const msize = (file.size / 1024).toFixed(1) if (file.type != this.ftype) { - msgs.msg([ - "error", - `File has type ${file.type}; should be ${ftype}`, - ]) + msgs.msg(["error", `File has type ${file.type}; should be ${ftype}`]) } else if (file.size >= limit) { msgs.msg([ "error", `File has size ${msize}Kb; should be less than ${limit / 1024}Kb`, ]) } else { - msgs.msg([ - "good", - `File has type ${file.type} and size ${msize}Kb`, - ]) + msgs.msg(["good", `File has type ${file.type} and size ${msize}Kb`]) ctrl.show() } } @@ -411,6 +443,7 @@ class Upload { } $(() => { + window.L = new LStorage() ftree = new Tree() new Upload() }) diff --git a/static/js/app/notesm.js b/static/js/app/notesm.js new file mode 100644 index 00000000..b4d4a0f8 --- /dev/null +++ b/static/js/app/notesm.js @@ -0,0 +1,506 @@ +/* eslint-env jquery */ +/* globals Config, P, L, markdown */ + +import { escHT, special_links, markdownEscape } from "./helpers.js" +import { Msg } from "./msg.js" + +export class Notes { + constructor(newcontent) { + this.show = false + this.verselist = {} + this.version = P.version + this.sav_controls = $("span.nt_main_sav") + this.sav_c = this.sav_controls.find('a[tp="s"]') + this.rev_c = this.sav_controls.find('a[tp="r"]') + this.logged_in = false + this.cctrl = $("a.nt_main_ctrl") + + newcontent.find(".vradio").each((i, el) => { + const elem = $(el) + const bk = elem.attr("b") + const ch = elem.attr("c") + const vs = elem.attr("v") + const topl = elem.closest("div") + this.verselist[`${bk} ${ch}:${vs}`] = new Notev( + this.version, + bk, + ch, + vs, + topl.find("span.nt_ctrl"), + topl.find("table.t1_table") + ) + }) + const { verselist } = this + this.msgn = new Msg("nt_main_msg", () => { + for (const notev of Object.values(verselist)) { + notev.clear_msg() + } + }) + this.cctrl.click(e => { + e.preventDefault() + P.vs.hstatesv("n", { get: P.vs.get("n") == "v" ? "x" : "v" }) + this.apply() + }) + this.rev_c.click(e => { + e.preventDefault() + for (const notev of Object.values(verselist)) { + notev.revert() + } + }) + this.sav_c.click(e => { + e.preventDefault() + for (const notev of Object.values(verselist)) { + notev.save() + } + this.msgn.msg(["special", "Done"]) + }) + this.msgn.clear() + $("span.nt_main_sav").hide() + this.apply() + } + + apply() { + const { verselist } = this + + if (P.vs.get("n") == "v") { + this.cctrl.addClass("nt_loaded") + for (const notev of Object.values(verselist)) { + notev.show_notes(false) + this.logged_in = notev.logged_in + } + if (this.logged_in) { + this.sav_controls.show() + } + } else { + this.cctrl.removeClass("nt_loaded") + this.sav_controls.hide() + for (const notev of Object.values(verselist)) { + notev.hide_notes() + } + } + } +} + +class Notev { + constructor(vr, bk, ch, vs, ctrl, dest) { + this.loaded = false + this.nnotes = 0 + this.mnotes = 0 + this.show = false + this.edt = false + this.dirty = false + this.version = vr + this.book = bk + this.chapter = ch + this.verse = vs + this.ctrl = ctrl + this.dest = dest + + const { book, chapter, verse } = this + this.msgn = new Msg(`nt_msg_${book}_${chapter}_${verse}`) + this.cctrl = ctrl.find("a.nt_ctrl") + this.sav_controls = ctrl.find("span.nt_sav") + + const { sav_controls } = this + this.sav_c = sav_controls.find('a[tp="s"]') + this.edt_c = sav_controls.find('a[tp="e"]') + this.rev_c = sav_controls.find('a[tp="r"]') + + const { sav_c, edt_c, rev_c, cctrl } = this + + sav_c.click(e => { + e.preventDefault() + this.save() + }) + edt_c.click(e => { + e.preventDefault() + this.edit() + }) + rev_c.click(e => { + e.preventDefault() + this.revert() + }) + cctrl.click(e => { + e.preventDefault() + this.is_dirty() + if (this.show) { + this.hide_notes() + } else { + this.show_notes(true) + } + }) + + dest.find("tr.nt_cmt").hide() + $("span.nt_main_sav").hide() + sav_controls.hide() + } + + fetch(adjust_verse) { + const { cnotes_url } = Config + + const { version, book, chapter, verse, edt, msgn } = this + const senddata = { version, book, chapter, verse, edit: edt } + msgn.msg(["info", "fetching notes ..."]) + $.post(cnotes_url, senddata, data => { + this.loaded = true + msgn.clear() + for (const m of data.msgs) { + msgn.msg(m) + } + const { good, users, notes, nkey_index, changed, logged_in } = data + if (good) { + this.process(users, notes, nkey_index, changed, logged_in) + if (adjust_verse) { + if (P.mr == "m") { + P.vs.mstatesv({ verse }) + P.material.goto_verse() + } + } + } + }) + } + + process(users, notes, nkey_index, changed, logged_in) { + const { + sidebars: { side_fetched, sidebar }, + } = P + if (changed) { + if (P.mr == "m") { + side_fetched["mn"] = false + sidebar["mn"].content.apply() + } + } + this.orig_users = users + this.orig_notes = notes + this.orig_nkey_index = nkey_index + this.orig_edit = [] + this.logged_in = logged_in + this.gen_html(true) + this.dirty = false + this.apply_dirty() + this.decorate() + } + + decorate() { + const { nt_statclass, nt_statsym, nt_statnext } = Config + const { dest, logged_in, sav_controls, edt, sav_c, edt_c } = this + dest + .find("td.nt_stat") + .find("a") + .click(e => { + e.preventDefault() + const elem = $(e.target) + const statcode = elem.attr("code") + const nextcode = nt_statnext[statcode] + const nextsym = nt_statsym[nextcode] + const row = elem.closest("tr") + for (const c in nt_statclass) { + row.removeClass(nt_statclass[c]) + } + for (const c in nt_statsym) { + elem.removeClass(`fa-${nt_statsym[c]}`) + } + row.addClass(nt_statclass[nextcode]) + elem.attr("code", nextcode) + elem.addClass(`fa-${nextsym}`) + }) + dest + .find("td.nt_pub") + .find("a") + .click(e => { + e.preventDefault() + const elem = $(e.target) + if (elem.hasClass("ison")) { + elem.removeClass("ison") + } else { + elem.addClass("ison") + } + }) + dest.find("tr.nt_cmt").show() + if (logged_in) { + $("span.nt_main_sav").show() + sav_controls.show() + if (edt) { + sav_c.show() + edt_c.hide() + } else { + sav_c.hide() + edt_c.show() + } + } + P.decorate_crossrefs(dest) + } + + gen_html_ca(canr) { + const { nt_statclass, nt_statsym } = Config + const { muting_n } = L + const vr = this.version + const notes = this.orig_notes[canr] + const nkey_index = this.orig_nkey_index + let html = "" + this.nnotes += notes.length + for (let n = 0; n < notes.length; n++) { + const nline = notes[n] + const { uid, nid, pub, shared, ro } = nline + const kwtrim = $.trim(nline.kw) + const kws = kwtrim.split(/\s+/) + let mute = false + for (const kw of kws) { + const nkid = nkey_index[`${uid} ${kw}`] + if (muting_n.isSet(`n${nkid}`)) { + mute = true + break + } + } + if (mute) { + this.mnotes += 1 + continue + } + const user = this.orig_users[uid] + const pubc = pub ? "ison" : "" + const sharedc = shared ? "ison" : "" + const statc = nt_statclass[nline.stat] + const statsym = nt_statsym[nline.stat] + const edit_att = ro ? "" : ' edit="1"' + const edit_class = ro ? "" : " edit" + html += `` + if (ro) { + html += ` + + ` + html += `${escHT(nline.kw)}` + const ntxt = special_links(markdown.toHTML(markdownEscape(nline.ntxt))) + html += `${ntxt}` + html += `${escHT(user)}` + html += '' + html += `` + html += `` + } else { + this.orig_edit.push({ canr, note: nline }) + html += ` + ` + html += `` + html += `` + html += `${escHT(user)}` + html += '' + html += `` + html += `${vr}` + html += `` + } + html += "" + } + return html + } + + gen_html(replace) { + this.mnotes = 0 + if (replace) { + this.dest.find("tr[ncanr]").remove() + } + for (const canr in this.orig_notes) { + const target = this.dest.find(`tr[canr="${canr}"]`) + const html = this.gen_html_ca(canr) + target.after(html) + } + if (this.nnotes == 0) { + this.cctrl.addClass("nt_empty") + } else { + this.cctrl.removeClass("nt_empty") + } + if (this.mnotes == 0) { + this.cctrl.removeClass("nt_muted") + } else { + this.cctrl.addClass("nt_muted") + this.msgn.msg(["special", `muted notes: ${this.mnotes}`]) + } + } + + sendnotes(senddata) { + const { cnotes_url } = Config + + $.post( + cnotes_url, + senddata, + data => { + const { good, users, notes, nkey_index, changed, logged_in } = data + this.msgn.clear() + for (const m of data.msgs) { + this.msgn.msg(m) + } + if (good) { + this.dest.find("tr[ncanr]").remove() + this.process(users, notes, nkey_index, changed, logged_in) + } + }, + "json" + ) + } + + is_dirty() { + let dirty = false + const { orig_edit } = this + if (orig_edit == undefined) { + this.dirty = false + return + } + for (let n = 0; n < orig_edit.length; n++) { + const { canr, note: o_note } = orig_edit[n] + const { nid } = o_note + const n_note = + nid == 0 + ? this.dest.find(`tr[nid="0"][ncanr="${canr}"]`) + : this.dest.find(`tr[nid="${nid}"]`) + const o_stat = o_note.stat + const n_stat = n_note.find("td.nt_stat a").attr("code") + const o_kw = $.trim(o_note.kw) + const n_kw = $.trim(n_note.find("td.nt_kw textarea").val()) + const o_ntxt = o_note.ntxt + const n_ntxt = $.trim(n_note.find("td.nt_cmt textarea").val()) + const o_shared = o_note.shared + const n_shared = n_note.find("td.nt_pub a").first().hasClass("ison") + const o_pub = o_note.pub + const n_pub = n_note.find("td.nt_pub a").last().hasClass("ison") + if ( + o_stat != n_stat || + o_kw != n_kw || + o_ntxt != n_ntxt || + o_shared != n_shared || + o_pub != n_pub + ) { + dirty = true + break + } + } + this.dirty = dirty + this.apply_dirty() + } + + apply_dirty() { + if (this.dirty) { + this.cctrl.addClass("dirty") + } else if (this.cctrl.hasClass("dirty")) { + this.cctrl.removeClass("dirty") + } + } + save() { + this.edt = false + const { version, book, chapter, verse, edt, orig_edit } = this + const data = { + version, + book, + chapter, + verse, + save: true, + edit: edt, + } + const notelines = [] + if (orig_edit == undefined) { + return + } + for (let n = 0; n < orig_edit.length; n++) { + const { canr, note: o_note } = orig_edit[n] + const { nid, uid } = o_note + const n_note = + nid == 0 + ? this.dest.find(`tr[nid="0"][ncanr="${canr}"]`) + : this.dest.find(`tr[nid="${nid}"]`) + const o_stat = o_note.stat + const n_stat = n_note.find("td.nt_stat a").attr("code") + const o_kw = $.trim(o_note.kw) + const n_kw = $.trim(n_note.find("td.nt_kw textarea").val()) + const o_ntxt = o_note.ntxt + const n_ntxt = $.trim(n_note.find("td.nt_cmt textarea").val()) + const o_shared = o_note.shared + const n_shared = n_note.find("td.nt_pub a").first().hasClass("ison") + const o_pub = o_note.pub + const n_pub = n_note.find("td.nt_pub a").last().hasClass("ison") + if ( + o_stat != n_stat || + o_kw != n_kw || + o_ntxt != n_ntxt || + o_shared != n_shared || + o_pub != n_pub + ) { + notelines.push({ + nid, + canr, + stat: n_stat, + kw: n_kw, + ntxt: n_ntxt, + uid, + shared: n_shared, + pub: n_pub, + }) + } + } + if (notelines.length > 0) { + data["notes"] = JSON.stringify(notelines) + } else { + this.msgn.msg(["warning", "No changes"]) + } + this.sendnotes(data) + } + + edit() { + this.edt = true + this.fetch(true) + } + + revert() { + this.edt = false + const { version, book, chapter, verse, edt } = this + const data = { + version, + book, + chapter, + verse, + save: true, + edit: edt, + } + data["notes"] = JSON.stringify([]) + this.sendnotes(data) + } + + hide_notes() { + this.show = false + this.cctrl.removeClass("nt_loaded") + this.sav_controls.hide() + this.dest.find("tr.nt_cmt").hide() + this.msgn.hide() + } + + show_notes(adjust_verse) { + this.show = true + this.cctrl.addClass("nt_loaded") + this.msgn.show() + if (!this.loaded) { + this.fetch(adjust_verse) + } else { + this.dest.find("tr.nt_cmt").show() + if (this.logged_in) { + this.sav_controls.show() + if (this.edt) { + this.sav_c.show() + this.edt_c.hide() + } else { + this.sav_c.hide() + this.edt_c.show() + } + } + if (P.mr == "m") { + P.vs.mstatesv({ verse: this.verse }) + P.material.goto_verse() + } + } + } + + clear_msg() { + this.msgn.clear() + } +} diff --git a/static/js/app/page.js b/static/js/app/page.js new file mode 100644 index 00000000..5c08bec5 --- /dev/null +++ b/static/js/app/page.js @@ -0,0 +1,431 @@ +/* eslint-env jquery */ +/* globals Config, P, L */ + +import { defcolor } from "./helpers.js" +import { Material } from "./material.js" +import { Sidebars } from "./sidebars.js" +import { LSettings } from "./materiallib.js" + +export const set_heightw = () => { + /* the heights of the sidebars are set, depending on the height of the window + */ + const subtractw = 80 + const standard_heightw = window.innerHeight - subtractw + $("#words").css("height", `${standard_heightw}px`) + $("#letters").css("height", `${standard_heightw}px`) +} + +export class LStorage { + constructor() { + const vws = $.initNamespaceStorage("nsview") + this.nsview = vws.localStorage + const nsq = $.initNamespaceStorage("muting_q") + this.muting_q = nsq.localStorage + const nsn = $.initNamespaceStorage("muting_n") + this.muting_n = nsn.localStorage + /* on the Queries page the user can "mute" queries. Which queries are muted, + * is stored as key value pairs in this local storage bucket. + * When shebanq shows relevant queries next to a page, muting is taken into account. + */ + } +} + +export class Page { + /* the one and only page object + */ + constructor(vs) { + + this.mql_small_height = "10em" + /* height of mql query body in sidebar + */ + this.mql_small_width = "97%" + /* height of mql query body in sidebar and in dialog + */ + this.mql_big_width_dia = "60%" + /* width of query info in dialog mode + */ + this.mql_big_width = "100%" + /* width of mql query body in sidebar and in dialog + */ + this.edit_side_width = "55%" + /* the desired width of the sidebar when editing a query body + */ + this.edit_main_width = "40%" + /* the desired width of the main area when editing a query body + */ + this.chart_width = "400px" + /* dialog width for charts + */ + this.vs = vs + /* the viewstate + */ + History.Adapter.bind(window, "statechange", this.vs.goback.bind(this.vs)) + this.picker2 = {} + this.picker1 = { q: {}, w: {} } + /* will collect the two Colorpicker1 objects, indexed as q w + */ + this.picker1list = { q: {}, w: {} } + /* will collect the two lists of Colorpicker1 objects, + * index as q w and then by iid + */ + } + + set_height() { + /* the heights of the sidebars are set, depending on the height of the window + */ + const subtractm = 150 + const { tab_views } = Config + const window_height = window.innerHeight + this.window_height = window_height + const standard_height = window_height - subtractm + this.standard_height = standard_height + this.half_standard_height = `${0.4 * standard_height}px` + + $("#material_txt_p").css("height", `${standard_height}px`) + for (let i = 1; i <= tab_views; i++) { + $(`#material_txt_tb${i}`).css("height", `${standard_height}px`) + } + $("#side_material_mq").css("max-height", `${0.6 * standard_height}px`) + $("#side_material_mw").css("max-height", `${0.35 * standard_height}px`) + $("#words").css("height", `${standard_height}px`) + $("#letters").css("height", `${standard_height}px`) + } + + get_width() { + /* save the orginal widths of sidebar and main area + */ + this.orig_side_width = $(".span3").css("width") + this.orig_main_width = $(".span9").css("width") + } + + reset_main_width() { + /* restore the orginal widths of sidebar and main area + */ + const { orig_main_width, orig_side_width } = this + if (orig_side_width != $(".span3").css("width")) { + $(".span3").css("width", orig_side_width) + $(".span3").css("max-width", orig_side_width) + $(".span9").css("width", orig_main_width) + $(".span9").css("max-width", orig_main_width) + } + } + + set_edit_width() { + /* switch to increased sidebar width + */ + this.get_width() + const { edit_main_width, edit_side_width } = this + $(".span3").css("width", edit_side_width) + $(".span9").css("width", edit_main_width) + } + + reset_material_status() { + const { tab_views } = Config + + this.material_fetched = { txt_p: false } + this.material_kind = { txt_p: "" } + for (let i = 1; i <= tab_views; i++) { + this.material_fetched[`txt_tb${i}`] = false + this.material_kind[`txt_tb${i}`] = "" + } + } + + decorate_crossrefs(dest) { + const crossrefs = dest.find("a[b]") + crossrefs.click(e => { + e.preventDefault() + const elem = $(e.target) + const vals = {} + vals["book"] = elem.attr("b") + vals["chapter"] = elem.attr("c") + vals["verse"] = elem.attr("v") + vals["mr"] = "m" + this.vs.mstatesv(vals) + this.vs.addHist() + this.go() + }) + crossrefs.addClass("crossref") + } + + set_csv(vr, mr, qw, iid, extraGiven) { + const { tp_labels } = Config + + if (mr == "r") { + const tasks = { t: "txt_p", d: "txt_il" } + if (qw != "n") { + tasks["b"] = P.vs.tp() + } + + for (const task in tasks) { + const tp = tasks[task] + const csvctrl = $(`#csv${task}_lnk_${vr}_${qw}`) + if (task != "b" || (tp != "txt_p" && tp != "txt_il")) { + const ctit = csvctrl.attr("ftitle") + let extra + if (extraGiven == undefined) { + extra = csvctrl.attr("extra") + } else { + csvctrl.attr("extra", extraGiven) + extra = extraGiven + } + csvctrl.attr("href", P.vs.csv_url(vr, mr, qw, iid, tp, extra)) + csvctrl.attr( + "title", + `${vr}_$[style[qw]["t"]}_${iid}_${extra}_${tp_labels[tp]}.csv${ctit}` + ) + csvctrl.show() + } else { + csvctrl.hide() + } + } + } + } + + init() { + /* dress up the skeleton, initialize state variables + */ + const { vs, picker2 } = this + + this.material = new Material() + this.sidebars = new Sidebars() + this.set_height() + this.get_width() + const listsettings = {} + this.listsettings = listsettings + + for (const qw of ["q", "w", "n"]) { + listsettings[qw] = new LSettings(qw) + if (qw != "n") { + picker2[qw] = listsettings[qw].picker2 + } + } + const prev = {} + this.prev = prev + for (const x in vs.mstate()) { + prev[x] = null + } + this.reset_material_status() + } + + apply() { + /* apply the viewstate: hide and show material as prescribed by the viewstate + */ + this.material.apply() + this.sidebars.apply() + } + go() { + /* go to another page view, check whether initial content has to be loaded + */ + this.reset_main_width() + this.apply() + } + go_material() { + /* load other material, whilst keeping the sidebars the same + */ + this.material.apply() + } + + /* + * the origin must be an object which has a member indicating + * the type of origin and the kind of page. + + * 1: a color picker 1 from an item in a list + * 1a: the color picker 1 on an item page + * 2: a color picker 2 on a list page + * 3: a button of the list view settings + * 4: a click on a word in the text + * 5: when the data or text representation is loaded + */ + highlight2(origin) { + /* all highlighting goes through this function + highlighting is holistic: when the user changes a view settings, + all highlights have to be reevaluated. + The only reduction is that word highlighting is completely orthogonal + to query result highlighting. + */ + const { style } = Config + + const { qw, iid, code } = origin + const { muting_q } = L + const { vs, listsettings } = this + const active = P.vs.active(qw) + if (active == "hlreset") { + /* all viewsettings for either queries or words are restored to 'factory' settings + */ + vs.cstatexx(qw) + vs.hstatesv(qw, { active: "hlcustom", sel_one: defcolor(qw, null) }) + listsettings[qw].apply() + return + } + const hlradio = $(`.${qw}hradio`) + const activeo = $(`#${qw}${active}`) + hlradio.removeClass("ison") + activeo.addClass("ison") + const cmap = vs.colormap(qw) + + const paintings = {} + + /* first we are going to compute what to paint, + * resulting in a list of paint instructions. + Then we apply the paint instructions in one batch. + */ + + /* computing the paint instructions */ + + if (code == "1a") { + /* highlights on an r-page (with a single query or word), + * coming from the associated ColorPicker1 + * This is simple coloring, using a single color. + */ + const paint = cmap[iid] || defcolor(qw == "q", iid) + if (qw == "q") { + $($.parseJSON($("#themonads").val())).each((i, m) => { + paintings[m] = paint + }) + } else if (qw == "w") { + paintings[iid] = paint + } + this.paint(qw, paintings) + return + } + + /* all other cases: highlights on an m-page, responding to a user action + * This is complex coloring, using multiple colors. + * First we determine which monads need to be highlighted. + */ + const selclr = P.vs.sel_one(qw) + const custitems = {} + const plainitems = {} + + if (qw == "q") { + /* Queries: highlight customised items with priority over uncustomised items + * If a word belongs to several query results, + * the last-applied coloring determines the color that the user sees. + * We want to promote the customised colors over the non-customized ones, + * so we compute customized coloring after + * uncustomized coloring. + * Skip the muted queries + */ + $(`#side_list_${qw} li`).each((i, el) => { + const elem = $(el) + const iid = elem.attr("iid") + if (!muting_q.isSet(`${iid}`)) { + const monads = $.parseJSON($(`#${qw}${iid}`).attr("monads")) + if (P.vs.iscolor(qw, iid)) { + custitems[iid] = monads + } else { + plainitems[iid] = monads + } + } + }) + } else if (qw == "w") { + /* Words: they are disjoint, no priority needed + */ + $(`#side_list_${qw} li`).each((i, el) => { + const elem = $(el) + const iid = elem.attr("iid") + if (P.vs.iscolor(qw, iid)) { + custitems[iid] = 1 + } else { + plainitems[iid] = 1 + } + const all = $(`#${qw}${iid}`) + if (active == "hlmany" || P.vs.iscolor(qw, iid)) { + all.show() + } else { + all.hide() + } + }) + } + const chunks = [custitems, plainitems] + + const clselect = iid => { + /* assigns a color to an individual monad, based on the viewsettings + */ + let paint = "" + if (active == "hloff") { + paint = style[qw]["off"] + } /* + viewsetting says: do not color any item */ else if ( + active == "hlone" + ) { + paint = selclr + } else if (active == "hlmany") { + /* viewsetting says: color every applicable item with the same color */ + paint = cmap[iid] || defcolor(qw == "q", iid) + } else if (active == "hlcustom") { + /* viewsetting says: + * color every item with customized color (if customized) + * else with query/word-dependent default color + */ + paint = cmap[iid] || selclr + } else { + /* viewsetting says: + * color every item with customized color (if customized) + * else with a single chosen color + */ + paint = selclr + } /* + but this should not occur */ + return paint + } + + if (qw == "q") { + /* Queries: compute the monads to be painted and the colors needed for it + */ + for (let c = 0; c < 2; c++) { + const chunk = chunks[c] + for (const iid in chunk) { + const color = clselect(iid) + const monads = chunk[iid] + for (const m in monads) { + const monad = monads[m] + if (!(monad in paintings)) { + paintings[monad] = color + } + } + } + } + } else if (qw == "w") { + /* Words: gather the lexeme_ids to be painted and the colors needed for it + */ + for (let c = 0; c < 2; c++) { + const chunk = chunks[c] + for (const iid in chunk) { + let color = style[qw]["off"] + if (c == 0) { + /* do not color the plain items when dealing with words + * (as opposed to queries) + */ + color = clselect(iid) + } + paintings[iid] = color + } + } + } + /* maybe the selection of words of queries has changed for the same material, + * so wipe previous coloring + */ + const monads = $("#material span[m]") + const stl = style[qw]["prop"] + const clr_off = style[qw]["off"] + monads.css(stl, clr_off) + + /* finally, the computed colors are applied */ + this.paint(qw, paintings) + } + + paint(qw, paintings) { + /* Execute a series of computed paint instructions + */ + const { style, vcolors } = Config + + const stl = style[qw]["prop"] + const container = `#material_${P.vs.tp()}` + const att = qw == "q" ? "m" : "l" + for (const item in paintings) { + const color = paintings[item] + $(`${container} span[${att}="${item}"]`).css(stl, vcolors[color][qw]) + } + } +} diff --git a/static/js/app/queries.js b/static/js/app/queries.js new file mode 100644 index 00000000..775dcd8e --- /dev/null +++ b/static/js/app/queries.js @@ -0,0 +1,1022 @@ +/* eslint-env jquery */ +/* eslint-disable no-new */ + +/* globals Config, L */ + +import { escHT } from "./helpers.js" +import { Msg } from "./msg.js" +import { LStorage } from "./page.js" + +const vs = $.initNamespaceStorage("qsview") +const qsview = vs.localStorage +let ftree, msgflt, msgopq, rdata +const subtractq = 80 +/* the canvas holding the material gets a height equal to + * the window height minus this amount + */ +const control_height = 100 +/* height for messages and controls + */ + +class Recent { + constructor() { + this.loaded = false + this.queries = [] + this.msgqr = new Msg("msg_qr") + this.refreshctl = $("#reload_recentq") + + this.msgqr.clear() + this.refreshctl.click(e => { + e.preventDefault() + this.fetch() + }) + this.apply() + } + + apply() { + this.fetch() + } + + fetch() { + const { queriesr_url } = Config + + this.msgqr.msg(["info", "fetching recent queries ..."]) + $.post(queriesr_url, {}, json => { + this.loaded = true + this.msgqr.clear() + const { msgs, good, queries } = json + for (const m of msgs) { + this.msgqr.msg(m) + } + if (good) { + this.queries = queries + this.process() + } + }) + } + process() { + this.gen_html() + this.dress_queriesr() + } + gen_html() { + const target = $("#recentqi") + const { queries } = this + let html = "" + for (let n = 0; n < queries.length; n++) { + const { id, text, title, version } = queries[n] + html += `${text}
+ ` + } + target.html(html) + } + + dress_queriesr() { + $("#recentqi a[qid]").each((i, el) => { + const elem = $(el) + elem.click(e => { + e.preventDefault() + ftree.filter.clear() + ftree.gotoquery(elem.attr("qid")) + }) + }) + } +} + +class View { + constructor() { + this.prevstate = false + if (!qsview.isSet("simple")) { + qsview.set("simple", true) + } + this.qvradio = $(".qvradio") + this.csimple = $("#c_view_simple") + this.cadvanced = $("#c_view_advanced") + + this.csimple.click(e => { + e.preventDefault() + qsview.set("simple", true) + this.adjust_view() + }) + this.cadvanced.click(e => { + e.preventDefault() + qsview.set("simple", false) + this.adjust_view() + }) + this.adjust_view() + } + + adjust_view() { + const simple = qsview.get("simple") + this.qvradio.removeClass("ison") + ;(simple ? this.csimple : this.cadvanced).addClass("ison") + if (this.prevstate != simple) { + if (simple) { + $(".brq").hide() + } else { + $(".brq").show() + } + this.prevstate = simple + } + } +} + +class Level { + constructor() { + this.levels = { o: 1, p: 2, u: 3, q: 4 } + + $(".qlradio").removeClass("ison") + if (!qsview.isSet("level")) { + qsview.set("level", "o") + } + $("#level_o").click(e => { + e.preventDefault() + this.expand_level("o") + }) + $("#level_p").click(e => { + e.preventDefault() + this.expand_level("p") + }) + $("#level_u").click(e => { + e.preventDefault() + this.expand_level("u") + }) + $("#level_q").click(e => { + e.preventDefault() + this.expand_level("q") + }) + $("#level_").click(e => { + e.preventDefault() + this.expand_level("") + }) + } + + expand_all() { + ftree.ftw.visit(n => { + n.setExpanded(true, { noAnimation: true, noEvents: false }) + }, true) + } + + expand_level(level) { + const { levels } = this + + $(".qlradio").removeClass("ison") + $(`#level_${level}`).addClass("ison") + qsview.set("level", level) + if (level in levels) { + const numlevel = levels[level] + ftree.ftw.visit(n => { + const nlevel = n.getLevel() + n.setExpanded(nlevel <= numlevel, { noAnimation: true, noEvents: false }) + }, true) + } + } + + initlevel() { + this.expand_level(qsview.get("level")) + } +} + +class Filter { + constructor() { + this.patc = $("#filter_contents") + + const re_is_my = new RegExp('class="[^"]*qmy', "") + const re_is_private = new RegExp('class="[^"]*qpriv', "") + + this.in_my = pat => node => + re_is_my.test(node.title) && + (pat == "" || node.title.toLowerCase().indexOf(pat.toLowerCase()) >= 0) + + this.in_private = pat => node => + re_is_private.test(node.title) && + (pat == "" || node.title.toLowerCase().indexOf(pat.toLowerCase()) >= 0) + + $("#filter_clear").hide() + $("#filter_contents").val( + qsview.isSet("filter_pat") ? qsview.get("filter_pat") : "" + ) + if (qsview.isSet("filter_mode")) { + this.pqsearch(qsview.get("filter_mode")) + $("#filter_clear").show() + } + + $("#filter_control_a").click(e => { + e.preventDefault() + this.pqsearch("a") + }) + $("#filter_control_c").click(e => { + e.preventDefault() + this.pqsearch("c") + }) + $("#filter_control_q").click(e => { + e.preventDefault() + this.pqsearch("q") + }) + $("#filter_control_m").click(e => { + e.preventDefault() + this.pqsearch("m") + }) + $("#filter_control_r").click(e => { + e.preventDefault() + this.pqsearch("r") + }) + + $("#filter_clear").click(e => { + e.preventDefault() + this.clear() + }) + } + + clear() { + ftree.ftw.clearFilter() + msgflt.clear() + msgflt.msg(["good", "no filter applied"]) + $(".qfradio").removeClass("ison") + qsview.remove("filter_mode") + $("#filter_clear").hide() + $("#amatches").html("") + $("#cmatches").html("") + $("#qmatches").html("") + $("#mmatches").html("") + $("#rmatches").html("") + $("#count_o").html(rdata.o) + $("#count_p").html(rdata.p) + $("#count_u").html(rdata.u) + $("#count_q").html(rdata.q) + } + + pqsearch(kind) { + const { in_my, in_private, patc } = this + const pat = patc.val() + qsview.set("filter_pat", pat) + let amatches = 0 + let cmatches = 0 + let qmatches = 0 + let mmatches = 0 + let rmatches = 0 + if (pat == "") { + amatches = -1 + cmatches = -1 + qmatches = -1 + mmatches = -1 + rmatches = -1 + } + $(".qfradio").removeClass("ison") + if (kind == "m") { + msgflt.clear() + msgflt.msg(["warning", "my queries"]) + } else if (kind == "r") { + msgflt.clear() + msgflt.msg(["warning", "private queries"]) + } else if (pat == "") { + this.clear() + return + } else { + msgflt.clear() + msgflt.msg(["special", "filter applied"]) + } + $(`#filter_control_${kind}`).addClass("ison") + qsview.set("filter_mode", kind) + + ftree.level.expand_all() + if (kind == "a") { + amatches = ftree.ftw.filterNodes(pat, false) + $("#amatches").html(amatches >= 0 ? `(${amatches})` : "") + } else if (kind == "c") { + cmatches = ftree.ftw.filterBranches(pat) + $("#cmatches").html(cmatches >= 0 ? `(${cmatches})` : "") + } else if (kind == "q") { + qmatches = ftree.ftw.filterNodes(pat, true) + $("#qmatches").html(qmatches >= 0 ? `(${qmatches})` : "") + } else if (kind == "m") { + mmatches = ftree.ftw.filterNodes(in_my(pat), true) + $("#mmatches").html(mmatches >= 0 ? `(${mmatches})` : "") + } else if (kind == "r") { + rmatches = ftree.ftw.filterNodes(in_private(pat), true) + $("#rmatches").html(rmatches >= 0 ? `(${rmatches})` : "") + } + $("#filter_clear").show() + const submatch = "span.fancytree-submatch" + const match = "span.fancytree-match" + const base_o = "#queries>ul>li>ul>li>" + const match_o = $(`${base_o}${match}`).length + const submatch_o = $(`${base_o}${submatch}`).length + const base_p = `${base_o}ul>li>` + const match_p = $(`${base_p}${match}`).length + const submatch_p = $(`${base_p}${submatch}`).length + const base_u = `${base_p}ul>li>` + const match_u = $(`${base_u}${match}`).length + const submatch_u = $(`${base_u}${submatch}`).length + const base_q = `${base_u}ul>li>` + const match_q = $(`${base_q}${match}`).length + $("#count_o").html(` + ${match_o} + ${submatch_o}`) + $("#count_p").html(` + ${match_p} + ${submatch_p}`) + $("#count_u").html(` + '${match_u} + ${submatch_u}`) + $("#count_q").html(`${match_q}`) + if (ftree.view.simple) { + $(".brq").hide() + } + } +} + +class Tree { + constructor() { + const { pq_url } = Config + const { muting_q: muting } = L + + this.tps = { o: "organization", p: "project", q: "query" } + + const tree = this + + this.do_new = {} + + $("#queries").fancytree({ + extensions: ["persist", "filter"], + checkbox: true, + selectMode: 3, + activeVisible: true, + toggleEffect: false, + clickFolderMode: 2, + focusOnSelect: false, + quicksearch: true, + icons: false, + idPrefix: "q_", + persist: { + cookiePrefix: "ft-q-", + store: "local", + types: "expanded selected", + }, + source: { + url: pq_url, + dataType: "json", + }, + filter: { + mode: "hide", + }, + init: () => { + muting.removeAll() + tree.ftw = $("#queries").fancytree("getTree") + const s = tree.ftw.getSelectedNodes(true) + for (const i in s) { + tree.store_select_deep(s[i]) + } + tree.ftw.render(true, true) + tree.dress_queries() + rdata = tree.ftw.rootNode.children[0].data + $("#count_o").html(rdata.o) + $("#count_p").html(rdata.p) + $("#count_u").html(rdata.u) + $("#count_q").html(rdata.q) + msgopq = new Msg("opqmsgs") + msgflt = new Msg("filter_msg") + tree.view = new View() + tree.level = new Level() + tree.filter = new Filter() + tree.level.initlevel() + if (rdata.uid) { + tree.editinit() + } else { + tree.viewinit() + } + tree.bothinit() + tree.gotoquery($("#qid").val()) + $("#reload_tree").hide() + }, + expand: () => { + if (tree.level != undefined) { + tree.level.expand_level("") + } + }, + collapse: () => { + if (tree.level != undefined) { + tree.level.expand_level("") + } + }, + select: (e, data) => { + tree.store_select_deep(data.node) + }, + }) + + const standard_height = window.innerHeight - subtractq + const form_height = standard_height - control_height + const canvas_left = $(".left-sidebar") + const canvas_right = $(".right-sidebar") + canvas_left.css("height", `${standard_height}px`) + $("#queries").css("height", `${standard_height}px`) + $("#opqforms").css("height", `${form_height}px`) + $("#opqctrl").css("height", `${control_height}px`) + canvas_right.css("height", `${standard_height}px`) + } + + store_select(node) { + const { muting_q: muting } = L + const { folder, key: iid, selected } = node + if (!folder) { + if (selected) { + muting.set(iid, 1) + } else { + if (muting.isSet(iid)) { + muting.remove(iid) + } + } + } + } + + store_select_deep(node) { + const { children } = node + this.store_select(node) + if (children != null) { + for (const n in children) { + this.store_select_deep(children[n]) + } + } + } + + dress_queries() { + const { q_url } = Config + $("#queries a.md").addClass("fa fa-level-down") + $("#queries a[qid]").each((i, el) => { + const elem = $(el) + const vr = elem.attr("v") + const extra = vr == undefined ? "" : `&version=${vr}` + elem.attr("href", `${q_url}?iid=${elem.attr("qid")}${extra}&page=1&mr=r&qw=q`) + }) + $("#queries a.md").click(e => { + e.preventDefault() + const elem = $(e.target) + const uname = elem.closest("ul").closest("li").find("span[n]").html() + const tit = elem.prev() + const lnk = tit.attr("href") + const qname = tit.html() + window.prompt( + "Press and then to copy link on clipboard", + `[${uname}: ${qname}](${lnk})` + ) + }) + } + + record(tp, o, update, view) { + const { record_url, q_url } = Config + + const lid = $(`#id_${tp}`).val() + if (!update && lid == "0" && tp != "q") { + return + } + const senddata = { + tp, + upd: update, + lid, + name: $(`#name_${tp}`).val(), + } + if (tp == "q") { + senddata["oid"] = $("#fo_q").attr("oid") + senddata["oname"] = $("#nameq_o").val() + senddata["owebsite"] = $("#websiteq_o").val() + senddata["pid"] = $("#fp_q").attr("pid") + senddata["pname"] = $("#nameq_p").val() + senddata["pwebsite"] = $("#websiteq_p").val() + senddata["do_new_o"] = this.do_new["o"] + senddata["do_new_p"] = this.do_new["p"] + } else { + senddata["website"] = $(`#website_${tp}`).val() + } + + $.post( + record_url, + senddata, + json => { + const { + msgs, + good, + ogood, + pgood, + record: rec, + orecord: orec, + precord: prec, + } = json + msgopq.clear() + for (const m of msgs) { + msgopq.msg(m) + } + if (update && tp == "q") { + if (good) { + this.selectid("o", rec.oid, null) + this.selectid("p", rec.pid, o) + } else { + if (ogood) { + this.selectid("o", orec.id, null) + } + if (pgood) { + this.selectid("p", prec.id, o) + } + } + } + if (!update) { + const name = $(`#name_${tp}`) + name.prop("readonly", view) + name.val(rec.name) + if (tp == "q") { + const oname = rec.oname == undefined ? "" : escHT(rec.oname) + const pname = rec.pname == undefined ? "" : escHT(rec.pname) + $(`#fo_${tp}`).attr("href", rec.owebsite) + $(`#fo_${tp}`).html(escHT(oname)) + $(`#fp_${tp}`).attr("href", rec.pwebsite) + $(`#fp_${tp}`).html(escHT(pname)) + $(`#fo_${tp}`).attr("oid", rec.oid) + $(`#fp_${tp}`).attr("pid", rec.pid) + } else { + $(`#website_${tp}`).val(rec.website) + $(`#f${tp}_v`).attr("href", rec.owebsite) + $(`#f${tp}_v`).html(escHT(rec.name)) + } + } else if (update && senddata.lid != "0") { + if (tp == "q") { + if (good) { + const oname = rec.oname == undefined ? "" : escHT(rec.oname) + const pname = rec.pname == undefined ? "" : escHT(rec.pname) + this.hide_new_q(rec.id, "o") + this.hide_new_q(rec.id, "p") + $(`#fo_${tp}`).attr("href", rec.owebsite) + $(`#fo_${tp}`).html(escHT(oname)) + $(`#fp_${tp}`).attr("href", rec.pwebsite) + $(`#fp_${tp}`).html(escHT(pname)) + $(`#fo_${tp}`).attr("oid", rec.oid) + $(`#fp_${tp}`).attr("pid", rec.pid) + $("#title_q").html("Modify") + } else { + if (ogood) { + const oname = orec.name == undefined ? "" : escHT(orec.name) + this.hide_new_q(orec.id, "o") + $(`#fo_${tp}`).attr("href", orec.website) + $(`#fo_${tp}`).html(escHT(oname)) + $(`#fo_${tp}`).attr("oid", orec.id) + } + if (pgood) { + const pname = prec.name == undefined ? "" : escHT(prec.name) + this.hide_new_q(prec.id, "p") + $(`#fp_${tp}`).attr("href", prec.website) + $(`#fp_${tp}`).html(escHT(pname)) + $(`#fp_${tp}`).attr("pid", prec.id) + } + } + } else { + $(`#website_${tp}`).val(rec.website) + $(`#f${tp}_v`).attr("href", rec.owebsite) + $(`#f${tp}_v`).html(escHT(rec.name)) + } + const elm = tp == "q" ? "a" : "span" + const moditem = this.moditem.find(`${elm}[n=1]`) + if (moditem != undefined) { + moditem.html(escHT(rec.name)) + } + } else if (update && senddata.lid == "0") { + if (good) { + $(`#id_${tp}`).val(rec.id) + } + if (tp == "q") { + if (good) { + const oname = rec.oname == undefined ? "" : escHT(rec.oname) + const pname = rec.pname == undefined ? "" : escHT(rec.pname) + this.hide_new_q(rec.id, "o") + this.hide_new_q(rec.id, "p") + $(`#fo_${tp}`).attr("href", rec.owebsite) + $(`#fo_${tp}`).html(escHT(oname)) + $(`#fp_${tp}`).attr("href", rec.pwebsite) + $(`#fp_${tp}`).html(escHT(pname)) + $(`#fo_${tp}`).attr("oid", rec.oid) + $(`#fp_${tp}`).attr("pid", rec.pid) + $("#title_q").html("Modify") + } else { + if (ogood) { + const oname = orec.name == undefined ? "" : escHT(orec.name) + this.hide_new_q(orec.id, "o") + $(`#fo_${tp}`).attr("href", orec.website) + $(`#fo_${tp}`).html(escHT(oname)) + $(`#fo_${tp}`).attr("oid", orec.id) + } + if (pgood) { + const pname = prec.name == undefined ? "" : escHT(prec.name) + this.hide_new_q(prec.id, "p") + $(`#fp_${tp}`).attr("href", prec.website) + $(`#fp_${tp}`).html(escHT(pname)) + $(`#fp_${tp}`).attr("pid", prec.id) + } + } + } + } + const orig = $(".treehl") + const origp = orig.closest("ul").closest("li").closest("ul").closest("li") + const origo = origp.closest("ul").closest("li") + const origoid = origo.find("a[lid]").attr("lid") + const origpid = origp.find("a[lid]").attr("lid") + if ( + update && + good && + (senddata.lid == "0" || origoid != rec.oid || origpid != rec.pid) + ) { + $("#reload_tree").show() + } else { + $("#reload_tree").hide() + } + if (update && good && tp == "q") { + $("#continue_q").attr( + "href", + `${q_url}?iid=${$("#id_q").val()}&page=1&mr=r&qw=q` + ) + $("#continue_q").show() + } else { + $("#continue_q").hide() + } + }, + "json" + ) + } + + do_edit_controls_q() { + const ctlo = $("#new_ctrl_o") + const ctlp = $("#new_ctrl_p") + const ctlxo = $("#newx_ctrl_o") + const ctlxp = $("#newx_ctrl_p") + const detailo = $(".detail_o") + const detailp = $(".detail_p") + const existo = $("#fo_q") + const existp = $("#fp_q") + detailo.hide() + detailp.hide() + ctlxo.hide() + ctlxp.hide() + ctlo.click(e => { + e.preventDefault() + detailo.show() + ctlxo.show() + ctlo.hide() + existo.hide() + this.do_new["o"] = true + }) + ctlxo.click(e => { + e.preventDefault() + detailo.hide() + ctlxo.hide() + ctlo.show() + existo.show() + this.do_new["o"] = false + }) + ctlp.click(e => { + e.preventDefault() + detailp.show() + ctlxp.show() + ctlp.hide() + existp.hide() + this.do_new["p"] = true + this.select_clear("p", true) + }) + ctlxp.click(e => { + e.preventDefault() + detailp.hide() + ctlxp.hide() + ctlp.show() + existp.show() + }) + } + + hide_new_q(lid, tp) { + $(`#new_ctrl_${tp}`).show() + $(`#newx_ctrl_${tp}`).hide() + $(`.detail_${tp}`).hide() + $(`#f${tp}_q`).show() + this.do_new[tp] = false + } + + do_view_controls_q() { + const ctlo = $("#new_ctrl_o") + const ctlp = $("#new_ctrl_p") + const ctlxo = $("#newx_ctrl_o") + const ctlxp = $("#newx_ctrl_p") + const detailo = $(".detail_o") + const detailp = $(".detail_p") + detailo.hide() + detailp.hide() + ctlo.hide() + ctlxo.hide() + ctlp.hide() + ctlxp.hide() + } + + do_create(tp, obj) { + msgopq.clear() + $(".form_l").hide() + $(".ctrl_l").hide() + $(`#title_${tp}`).html("New") + $(`#name_${tp}`).val("") + let o = null + if (tp == "q") { + this.do_new["o"] = false + this.do_new["p"] = false + $("#fo_q").attr("oid", 0) + $("#fp_q").attr("pid", 0) + this.do_edit_controls_q() + } else { + $(`#website_${tp}`).val("") + if (tp == "p") { + o = obj.closest("li") + } + } + $(`#id_${tp}`).val(0) + this.record(tp, o, false, false) + $("#opqforms").show() + $("#opqctrl").show() + $(`#form_${tp}`).show() + $(`#ctrl_${tp}`).show() + $(".old").hide() + } + + do_update(tp, obj, lid) { + let o = null + if (tp == "q") { + this.do_new["o"] = false + this.do_new["p"] = false + o = obj + .closest("ul") + .closest("li") + .closest("ul") + .closest("li") + .closest("ul") + .closest("li") + this.do_edit_controls_q() + } else if (tp == "p") { + o = obj.closest("ul").closest("li") + } + this.moditem = obj.closest("span") + msgopq.clear() + msgopq.msg(["info", "loading ..."]) + $(".form_l").hide() + $(".ctrl_l").hide() + $(`#title_${tp}`).html("Modify") + $(`#id_${tp}`).val(lid) + this.record(tp, o, false, false) + $("#opqforms").show() + $("#opqctrl").show() + $(`#form_${tp}`).show() + $(`#ctrl_${tp}`).show() + $(".old").show() + } + + do_view(tp, obj, lid) { + let o = null + if (tp == "q") { + o = obj + .closest("ul") + .closest("li") + .closest("ul") + .closest("li") + .closest("ul") + .closest("li") + this.do_view_controls_q() + } else if (tp == "p") { + o = obj.closest("ul").closest("li") + } + msgopq.clear() + msgopq.msg(["info", "loading ..."]) + $(".form_l").hide() + $(".ctrl_l").hide() + $(`#title_${tp}`).html("View") + $(`#id_${tp}`).val(lid) + this.record(tp, o, false, true) + $("#opqforms").show() + $("#opqctrl").show() + $(`#form_${tp}`).show() + if (tp == "o" || tp == "p") { + $(`#nameline_${tp}`).hide() + $(`#website_${tp}`).hide() + $(`#f${tp}_v`).show() + } + this.select_hide() + } + + op_selection(tp) { + if (tp == "q") { + this.select_clear("o", true) + this.select_clear("p", true) + } else { + this.select_hide() + } + } + + select_hide() { + for (const tp of ["o", "p"]) { + this.select_clear(tp, false) + } + } + + select_clear(tp, show) { + const objs = $(`.selecthl${tp}`) + const icons = $(`.s_${tp}`) + objs.removeClass(`selecthl${tp}`) + icons.removeClass("fa-check-circle") + icons.addClass("fa-circle-o") + if (show) { + icons.show() + } else { + icons.hide() + } + } + + selectid(tp, lid, pr) { + const jpr = `.s_${tp}[lid=${lid}]` + const icon = pr == null ? $(jpr) : pr.find(jpr) + const i = icon.closest("li") + const is = i.children("span") + this.selectone(tp, icon, is) + } + + selectone(tp, icon, obj) { + const sclass = `selecthl${tp}` + const objs = $(`.${sclass}`) + const iconsr = $(`.s_${tp}`) + objs.removeClass(sclass) + obj.addClass(sclass) + iconsr.removeClass("fa-check-circle") + iconsr.addClass("fa-circle-o") + icon.removeClass("fa-circle-o") + icon.addClass("fa-check-circle") + } + + viewinit() { + $("#lmsg").show() + $(".form_l").hide() + $(".ctrl_l").hide() + $(".treehl").removeClass("treehl") + this.select_hide() + } + + bothinit() { + const canvas_left = $(".left-sidebar") + const canvas_middle = $(".span6") + const canvas_right = $(".right-sidebar") + canvas_left.css("width", "23%") + canvas_middle.css("width", "40%") + canvas_right.css("width", "30%") + const view = $(".v_o, .v_p, .v_q") + view.addClass("fa fa-info") + + const viewtp = tp => { + const objs = $(`.v_${tp}`) + objs.click(e => { + e.preventDefault() + const elem = $(e.target) + $(".treehl").removeClass("treehl") + this.op_selection(tp) + elem.closest("span").addClass("treehl") + const lid = $(this).attr("lid") + this.do_view(tp, $(this), lid) + return false + }) + } + + const select_init = tp => { + const objs = $(`.s_${tp}`) + objs.click(e => { + e.preventDefault() + const elem = $(e.target) + if (tp == "o") { + const o = elem.closest("li") + const oid = o.find("a[lid]").attr("lid") + const oname = o.find("span[n=1]").html() + $("#fo_q").html(oname) + $("#fo_q").attr("oid", oid) + this.selectid("o", oid, null) + } else if (tp == "p") { + const o = elem.closest("ul").closest("li") + const oid = o.find("a[lid]").attr("lid") + const oname = o.find("span[n=1]").html() + const p = elem.closest("li") + const pid = p.find("a[lid]").attr("lid") + const pname = p.find("span[n=1]").html() + $("#fo_q").html(oname) + $("#fp_q").html(pname) + $("#fo_q").attr("oid", oid) + $("#fp_q").attr("pid", pid) + this.selectid("o", oid, null) + this.selectid("p", pid, o) + } + return false + }) + } + for (const t in this.tps) { + $(`#form_${t}`).hide() + $(`#ctrl_${t}`).hide() + viewtp(t) + if (t == "q") { + select_init("o") + select_init("p") + } + } + } + + editinit() { + $(".treehl").removeClass("treehl") + $("#lmsg").hide() + const select = $(".s_o,.s_p") + select.addClass("fa-circle-o") + select.hide() + const create = $(".n_q") + create.addClass("fa fa-plus") + + const createtp = tp => { + const objs = $(`.n_${tp}`) + objs.click(e => { + e.preventDefault() + const elem = $(e.target) + $(".treehl").removeClass("treehl") + this.op_selection(tp) + if (tp == "q") { + $("#id_q").val(0) + } + elem.closest("span").addClass("treehl") + this.do_create(tp, elem) + return false + }) + } + const update = $(".r_o, .r_p, .r_q") + update.addClass("fa fa-pencil") + + const updatetp = tp => { + const objs = $(`.r_${tp}`) + objs.click(e => { + e.preventDefault() + const elem = $(e.target) + $(".treehl").removeClass("treehl") + this.op_selection(tp) + elem.closest("span").addClass("treehl") + const lid = elem.attr("lid") + if (tp == "q") { + $("#id_q").val(lid) + } + this.do_update(tp, elem, lid) + return false + }) + } + const formtp = tp => { + $(`#save_${tp}`).click(e => { + e.preventDefault() + this.op_selection(tp) + this.record(tp, null, true, false) + }) + $(`#cancel_${tp}`).click(e => { + e.preventDefault() + $(".treehl").removeClass("treehl") + this.select_hide() + $(`#form_${tp}`).hide() + $(`#ctrl_${tp}`).hide() + }) + $(`#done_${tp}`).click(e => { + e.preventDefault() + this.op_selection(tp) + this.record(tp, null, true, false) + this.select_hide() + $(`#form_${tp}`).hide() + $(`#ctrl_${tp}`).hide() + }) + $("#reload_tree").click(e => { + e.preventDefault() + window.location.reload(true) + }) + } + for (const t in this.tps) { + $(`#form_${t}`).hide() + $(`#ctrl_${t}`).hide() + createtp(t) + updatetp(t) + formtp(t) + } + } + + gotoquery(qid) { + if (qid != undefined && qid != "0") { + const qnode = this.ftw.getNodeByKey(`q${qid}`) + if (qnode != undefined) { + qnode.makeVisible({ noAnimation: true }) + $(".treehl").removeClass("treehl") + $(`a[qid=${qid}]`).closest("span").addClass("treehl") + $(qnode.li)[0].scrollIntoView({ + behavior: "smooth", + }) + $("#queries").scrollTop -= 20 + } + } + } +} + +$(() => { + window.L = new LStorage() + new Recent() + ftree = new Tree() +}) diff --git a/static/js/app/select.js b/static/js/app/select.js new file mode 100644 index 00000000..dedd83f3 --- /dev/null +++ b/static/js/app/select.js @@ -0,0 +1,623 @@ +/* eslint-env jquery */ +/* globals Config, P */ + +import { close_dialog, defcolor } from "./helpers.js" + +const docName = "0_home" + +const chart_cols = 30 +/* number of chapters in a row in a chart + */ + +export class MSelect { + /* for book and chapter selection + */ + + constructor() { + const { versions } = Config + this.name = "select_passage" + this.hid = `#${this.name}` + this.book = new SelectBook() + this.select = new SelectItems("chapter") + for (const v of versions) { + this.set_vselect(v) + } + $("#self_link").hide() + } + + apply() { + /* apply material viewsettings to current material + */ + const { featurehost, bol_url, pbl_url } = Config + + const thisFeaturehost = `${featurehost}/${docName}` + $(".source").attr("href", thisFeaturehost) + $(".source").attr("title", "BHSA feature documentation") + $(".mvradio").removeClass("ison") + $(`#version_${P.version}`).addClass("ison") + const bol = $("#bol_lnk") + const pbl = $("#pbl_lnk") + + if (P.mr == "m") { + this.book.apply() + this.select.apply() + $(this.hid).show() + const book = P.vs.book() + const chapter = P.vs.chapter() + if (book != "x" && chapter > 0) { + bol.attr("href", `${bol_url}/ETCBC4/${book}/${chapter}`) + bol.show() + pbl.attr("href", `${pbl_url}/${book}/${chapter}`) + pbl.show() + } else { + bol.hide() + pbl.hide() + } + } else { + $(this.hid).hide() + bol.hide() + pbl.hide() + } + } + + set_vselect(v) { + const { sidebars } = P + + $(`#version_${v}`).click(e => { + e.preventDefault() + sidebars.side_fetched["mw"] = false + sidebars.side_fetched["mq"] = false + sidebars.side_fetched["mn"] = false + P.vs.mstatesv({ version: v }) + P.go() + }) + } +} + +export class PSelect { + /* for result page selection + */ + constructor() { + this.name = "select_pages" + this.hid = `#${this.name}` + this.select = new SelectItems("page") + } + + apply() { + /* apply result page selection: fill in headings on the page + */ + if (P.mr == "r") { + this.select.apply() + $(this.hid).show() + } else { + $(this.hid).hide() + } + } + + add(response) { + /* add the content portion of the response to the content portion of the page + */ + const select = "#select_contents_page" + if (P.mr == "r") { + $(select).html(response.find(select).html()) + } + } +} + +export class LSelect { + /* language selection + */ + constructor() { + this.name = "select_contents_lang" + this.hid = `#${this.name}` + this.control = "#select_control_lang" + $(this.control).click(e => { + e.preventDefault() + $(this.hid).dialog("open") + }) + } + + present() { + $(this.hid).dialog({ + autoOpen: false, + dialogClass: "items", + closeOnEscape: true, + modal: false, + title: "choose language", + width: "250px", + }) + } + + gen_html() { + /* generate a new lang selector + */ + const { booklangs } = Config + + const thelang = P.vs.lang() + const nitems = booklangs.length + this.lastitem = nitems + let ht = "" + ht += '' + const langs = Object.keys(booklangs).sort() + for (const item of langs) { + const langinfo = booklangs[item] + const name_en = langinfo[0] + const name_own = langinfo[1] + const clactive = thelang == item ? ' class="active"' : "" + ht += ` + + + ${name_own} + + + ${name_en} + ` + } + ht += "
" + $(this.hid).html(ht) + return nitems + } + + add_item(item) { + item.click(e => { + e.preventDefault() + const elem = $(e.target) + const newobj = elem.closest("li") + const isloaded = newobj.hasClass("active") + if (!isloaded) { + const vals = {} + vals["lang"] = elem.attr("item") + P.vs.mstatesv(vals) + this.update_vlabels() + P.vs.addHist() + P.material.apply() + } + }) + } + + update_vlabels() { + const { booktrans } = Config + + $("span[book]").each((i, el) => { + const elem = $(el) + elem.html(booktrans[P.vs.lang()][elem.attr("book")]) + }) + } + + apply() { + this.gen_html() + $("#select_contents_lang .itemnav").each((i, el) => { + const elem = $(el) + this.add_item(elem) + }) + $(this.control).show() + this.present() + } +} + +class SelectBook { + /* book selection + */ + constructor() { + this.name = "select_contents_book" + this.hid = `#${this.name}` + this.control = "#select_control_book" + $(this.control).click(e => { + e.preventDefault() + $(this.hid).dialog("open") + }) + } + + present() { + $(this.hid).dialog({ + autoOpen: false, + dialogClass: "items", + closeOnEscape: true, + modal: false, + title: "choose book", + width: "110px", + }) + } + + gen_html() { + /* generate a new book selector + */ + const { booktrans, booksorder } = Config + + const thebook = P.vs.book() + const lang = P.vs.lang() + const thisbooksorder = booksorder[P.version] + const nitems = thisbooksorder.length + + this.lastitem = nitems + + let ht = "" + ht += '" + $(this.hid).html(ht) + return nitems + } + + add_item(item) { + item.click(e => { + e.preventDefault() + const elem = $(e.target) + const newobj = elem.closest("li") + const isloaded = newobj.hasClass("active") + if (!isloaded) { + const vals = {} + vals["book"] = elem.attr("item") + vals["chapter"] = "1" + vals["verse"] = "1" + P.vs.mstatesv(vals) + P.vs.addHist() + P.go() + } + }) + } + + apply() { + this.gen_html() + $("#select_contents_book .itemnav").each((i, el) => { + const elem = $(el) + this.add_item(elem) + }) + $(this.control).show() + this.present() + } +} + +class SelectItems { + /* both for chapters and for result pages + */ + constructor(key) { + this.key = key + this.other_key = key == "chapter" ? "page" : "chapter" + this.name = `select_contents_${this.key}` + this.other_name = `select_contents_${this.other_key}` + this.hid = `#${this.name}` + this.other_hid = `#${this.other_name}` + this.control = `#select_control_${this.key}` + this.prev = $(`#prev_${this.key}`) + this.next = $(`#next_${this.key}`) + + this.prev.click(e => { + e.preventDefault() + const elem = $(e.target) + const vals = {} + vals[this.key] = elem.attr("contents") + vals["verse"] = "1" + P.vs.mstatesv(vals) + P.vs.addHist() + this.go() + }) + this.next.click(e => { + e.preventDefault() + const elem = $(e.target) + const vals = {} + vals[this.key] = elem.attr("contents") + vals["verse"] = "1" + P.vs.mstatesv(vals) + P.vs.addHist() + this.go() + }) + $(this.control).click(e => { + e.preventDefault() + $(this.hid).dialog("open") + }) + } + + go() { + if (this.key == "chapter") { + P.go() + } else { + P.go_material() + } + } + + present() { + close_dialog($(this.other_hid)) + $(this.hid).dialog({ + autoOpen: false, + dialogClass: "items", + closeOnEscape: true, + modal: false, + title: `choose ${this.key}`, + width: "200px", + }) + } + + gen_html() { + /* generate a new page selector + */ + const { books } = Config + + let theitem + let itemlist + let nitems + + if (this.key == "chapter") { + const thebook = P.vs.book() + theitem = P.vs.chapter() + nitems = thebook != "x" ? books[P.version][thebook] : 0 + this.lastitem = nitems + itemlist = new Array(nitems) + for (let i = 0; i < nitems; i++) { + itemlist[i] = i + 1 + } + } else { + /* 'page' + */ + theitem = P.vs.page() + nitems = $("#rp_pages").val() + this.lastitem = nitems + itemlist = [] + if (nitems) { + itemlist = $.parseJSON($("#rp_pagelist").val()) + } + } + + let ht = "" + if (nitems != undefined) { + if (nitems != 0) { + ht = '" + } + $(this.hid).html(ht) + } + return nitems + } + + add_item(item) { + item.click(e => { + e.preventDefault() + const elem = $(e.target) + const newobj = elem.closest("li") + const isloaded = newobj.hasClass("active") + if (!isloaded) { + const vals = {} + vals[this.key] = elem.attr("item") + vals["verse"] = "1" + P.vs.mstatesv(vals) + P.vs.addHist() + this.go() + } + }) + } + + apply() { + const showit = this.gen_html() > 0 + if (!showit) { + $(this.control).hide() + } else { + $(`#select_contents_${this.key} .itemnav`).each((i, el) => { + const elem = $(el) + this.add_item(elem) + }) + $(this.control).show() + const thisitem = parseInt(this.key == "page" ? P.vs.page() : P.vs.chapter()) + if (thisitem == undefined || thisitem == 1) { + this.prev.hide() + } else { + this.prev.attr("contents", `${thisitem - 1}`) + this.prev.show() + } + if (thisitem == undefined || thisitem == this.lastitem) { + this.next.hide() + } else { + this.next.attr("contents", `${thisitem + 1}`) + this.next.show() + } + } + this.present() + } +} + +export class CSelect { + /* for chart selection + */ + constructor(vr, qw) { + this.vr = vr + this.qw = qw + this.control = `#select_control_chart_${vr}_${qw}` + this.select = `#select_contents_chart_${vr}_${qw}` + this.loaded = {} + } + + init() { + $(this.control).click(e => { + e.preventDefault() + this.apply() + }) + } + + apply() { + if (!this.loaded[`${this.qw}_${P.iid}`]) { + if ($(`#select_contents_chart_${this.vr}_${this.qw}_${P.iid}`).length == 0) { + $(this.select).append( + `` + ) + } + this.fetch(P.iid) + } else { + this.show() + } + } + + fetch(iid) { + const { chart_url } = Config + + const vars = `?version=${this.vr}&qw=${this.qw}&iid=${iid}` + $(`${this.select}_${iid}`).load( + `${chart_url}${vars}`, + () => { + this.loaded[`${this.qw}_${iid}`] = true + this.process(iid) + }, + "html" + ) + } + + process(iid) { + this.gen_html(iid) + $(`${this.select}_${iid} .cnav`).each((i, el) => { + const elem = $(el) + this.add_item(elem, iid) + }) + $("#theitemc").click(e => { + e.preventDefault() + const vals = {} + vals["iid"] = iid + vals["mr"] = "r" + vals["version"] = this.vr + vals["qw"] = this.qw + P.vs.mstatesv(vals) + P.vs.addHist() + P.go() + }) + $("#theitemc").html(`Back to ${$("#theitem").html()} (version ${this.vr})`) + /* fill in the Back to query/word line in a chart + */ + this.present(iid) + this.show(iid) + } + + present(iid) { + const { style } = Config + const { chart_width } = P + + $(`${this.select}_${iid}`).dialog({ + autoOpen: false, + dialogClass: "items", + closeOnEscape: true, + close: () => { + this.loaded[`${this.qw}_${iid}`] = false + $(`${this.select}_${iid}`).html("") + }, + modal: false, + title: `chart for ${style[this.qw]["tag"]} (version ${this.vr})`, + width: chart_width, + position: { my: "left top", at: "left top", of: window }, + }) + } + + show(iid) { + $(`${this.select}_${iid}`).dialog("open") + } + + gen_html(iid) { + /* generate a new chart + */ + const { style, ccolors } = Config + + let nbooks = 0 + let booklist = $(`#r_chartorder${this.qw}`).val() + let bookdata = $(`#r_chart${this.qw}`).val() + if (booklist) { + booklist = $.parseJSON(booklist) + bookdata = $.parseJSON(bookdata) + nbooks = booklist.length + } else { + booklist = [] + bookdata = {} + } + let ht = "" + ht += ` +

+ back +

+ ` + + const ccl = ccolors.length + for (const book of booklist) { + const blocks = bookdata[book] + ht += ` + + + " + } + ht += "
${book} + ` + let l = 0 + for (let i = 0; i < blocks.length; i++) { + if (l == chart_cols) { + ht += "" + l = 0 + } + const block_info = blocks[i] + const chnum = block_info[0] + const ch_range = `${block_info[1]}-${block_info[2]}` + const blres = block_info[3] + const blsize = block_info[4] + const blres_select = blres >= ccl ? ccl - 1 : blres + const z = ccolors[blres_select] + let s = " " + let sz = "" + let sc = "" + if (blsize < 25) { + s = "=" + sc = "s1" + } else if (blsize < 75) { + s = "-" + sc = "s5" + } + if (blsize < 100) { + sz = ` (${blsize}%)` + } + ht += ` + ` + l++ + } + ht += "
+ ${s} +
" + $(`${this.select}_${iid}`).html(ht) + return nbooks + } + + add_item(item, iid) { + item.click(e => { + e.preventDefault() + const elem = $(e.target) + let vals = {} + vals["book"] = elem.attr("b") + vals["chapter"] = elem.attr("ch") + vals["mr"] = "m" + vals["version"] = this.vr + P.vs.mstatesv(vals) + P.vs.hstatesv("q", { sel_one: "white", active: "hlcustom" }) + P.vs.hstatesv("w", { sel_one: "black", active: "hlcustom" }) + P.vs.cstatexx("q") + P.vs.cstatexx("w") + if (this.qw != "n") { + vals = {} + vals[iid] = P.vs.colormap(this.qw)[iid] || defcolor(this.qw == "q", iid) + P.vs.cstatesv(this.qw, vals) + } + P.vs.addHist() + P.go() + }) + } +} diff --git a/static/js/app/share.js b/static/js/app/share.js new file mode 100755 index 00000000..dbddb421 --- /dev/null +++ b/static/js/app/share.js @@ -0,0 +1,365 @@ +/* eslint-env jquery */ +/* eslint-disable camelcase */ + +/* globals Config, P */ + +import { escHT, toggle_detail } from "./helpers.js" + +const deselectText = () => { + if (document.selection) { + document.selection.empty() + } else if (window.getSelection) { + window.getSelection().removeAllRanges() + } +} +const selectText = containerid => { + deselectText() + if (document.selection) { + const range = document.body.createTextRange() + range.moveToElementText(document.getElementById(containerid)) + range.select() + } else if (window.getSelection) { + const range = document.createRange() + range.selectNode(document.getElementById(containerid)) + window.getSelection().addRange(range) + } +} + +$(() => { + const { query_url, word_url, note_url, page_view_url } = Config + + const qmsg = { + good: + "The results of this query have been obtained after the query body has been last modified", + warning: "This query has never been executed in SHEBANQ", + error: + "The body of this query has been changed after its current results have been obtained.", + } + + const tbar = ` +
+

Cite

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
query vquerywordnotepage view
+ + cite query with its results on + this data version + + + share link to query page + + + cite word with its occs on + this data version + + + cite note set with its members + + + share link to this page + with or without view settings, or as internal note link, + or copy page contents to paste in mail, Evernote, etc. +
+

+

+
+` + /* Add the share tool bar. + */ + $("body").append(tbar) + const st = $("#socialdrawer") + st.css({ + opacity: ".7", + "z-index": "3000", + background: "#FFF", + border: "solid 1px #666", + "border-width": " 1px 0 0 1px", + height: "20px", + width: "40px", + position: "fixed", + bottom: "0", + right: "0", + padding: "2px 5px", + overflow: "hidden", + "-webkit-border-top-left-radius": " 12px", + "-moz-border-radius-topleft": " 12px", + "border-top-left-radius": " 12px", + "-moz-box-shadow": " -3px -3px 3px rgba(0,0,0,0.5)", + "-webkit-box-shadow": " -3px -3px 3px rgba(0,0,0,0.5)", + "box-shadow": " -3px -3px 3px rgba(0,0,0,0.5)", + }) + $("#citeh").css({ + margin: "2px 3px", + "text-shadow": " 1px 1px 1px #FFF", + color: "#444", + "font-size": "12px", + "line-height": "1em", + }) + $("#socialdrawer td,#socialdrawer th").css({ + width: "120px", + "text-align": "center", + "border-left": "2px solid #888888", + "border-right": "2px solid #888888", + }) + $("#socialdrawer .detail").hide() + // hover + $( + "#clip_qx_md,#clip_qx_ht,#clip_q_md,#clip_q_ht,#clip_w_md,#clip_w_ht,#clip_n_md,#clip_n_ht,#clip_pv_md,#clip_pv_ht,#clip_pv_htc,#clip_pv_nl" + ).click(e => { + e.preventDefault() + const elem = $(e.target) + window.prompt( + "Press and then to copy link on clipboard", + elem.attr("lnk") + ) + }) + $("#clip_pv_cn").click(e => { + e.preventDefault() + const shebanq_url_raw = `${page_view_url}${P.vs.getvars()}&pref=alt` + const slink = $("#self_link") + slink.show() + slink.attr("href", shebanq_url_raw) + selectText("material") + }) + $("#xc_qx").click(e => { + e.preventDefault() + const elem = $(e.target) + toggle_detail(elem, $("#x_qx")) + }) + $("#xc_q").click(e => { + e.preventDefault() + const elem = $(e.target) + toggle_detail(elem, $("#x_q")) + }) + $("#xc_w").click(e => { + e.preventDefault() + const elem = $(e.target) + toggle_detail(elem, $("#x_w")) + }) + $("#xc_n").click(e => { + e.preventDefault() + const elem = $(e.target) + toggle_detail(elem, $("#x_n")) + }) + $("#xc_pv").click(e => { + e.preventDefault() + const elem = $(e.target) + toggle_detail(elem, $("#x_pv")) + }) + st.click(e => { + e.preventDefault() + const elem = $(e.target) + const shebanq_url_raw = `${page_view_url}${P.vs.getvars()}&pref=alt` + let shebanq_url_note + let shebanq_url_rawc + const shebanq_url_note_pref = "shebanq:" + + const { version: vr, mr, qw, vs, iid } = P + const tp = vs.tp() + const tr = vs.tr() + const w = vs.get("w") + const q = vs.get("q") + const n = vs.get("n") + const thebook = $("#thebook").html() + const thechapter = $("#thechapter").html() + const book = vs.book() + const chapter = vs.chapter() + const verse = vs.verse() + const page = vs.page() + const shebanq_url_show_vars = `"&version=${vr}&mr=${mr}&qw=${qw}&tp=${tp}&tr=${tr}` + const shebanq_url_side_vars = `&wget=${w}&qget=${q}&nget=${n}` + const sv = `${shebanq_url_show_vars}${shebanq_url_side_vars}` + + $("#citeh").hide() + $("#cdiagpub").html("") + $("#cdiagsts").html("") + $( + ".clip_qx.clr,.clip_q.clr,.clip_w.clr,.clip_n.clr,.clip_pv.clr,#cdiagpub,#cdiagsts" + ).removeClass("error warning good special") + + let pvtitle + + if (mr == "m") { + pvtitle = `bhsa${vr} ${thebook} ${thechapter}:${verse}` + shebanq_url_note = `${shebanq_url_note_pref}?book=${book}&chapter=${chapter}&verse=${verse}${sv}` + shebanq_url_rawc = `${page_view_url}?book=${book}&chapter=${chapter}&verse=${verse}${sv}` + + $(".clip_qx").hide() + $(".clip_q").hide() + $(".clip_w").hide() + $(".clip_n").hide() + } else if (P.mr == "r") { + shebanq_url_note = `${shebanq_url_note_pref}?id=${iid}&page=${page}${shebanq_url_show_vars}` + shebanq_url_rawc = `${page_view_url}?id=${iid}&page=${page}${shebanq_url_show_vars}` + + const iinfo = P.sidebars.sidebar[`r${qw}`].content.info + if (qw == "q") { + const { + ufname, + ulname, + name, + is_shared, + is_published: is_pub, + versions, + } = iinfo + pvtitle = `${ufname} ${ulname}: ${name}` + const qstatus = versions[vr].status + if (is_shared) { + if (!is_pub) { + $(".clip_qx.clr").addClass("warning") + $("#cdiagpub").addClass("warning") + $("#cdiagpub").html( + "Beware of citing this query. It has not been published. It may be changed later." + ) + } else { + $(".clip_qx.clr").addClass("special") + $("#cdiagpub").addClass("special") + $("#cdiagpub").html( + "This query has been published. If that happened more than a week ago, it can be safely cited. It will not be changed anymore." + ) + } + $(".clip_q.clr").addClass(qstatus) + $("#cdiagsts").addClass(qstatus) + $("#cdiagsts").html(qmsg[qstatus]) + } else { + $(".clip_qx.clr").addClass("error") + $(".clip_q.clr").addClass("error") + $(".clip_pv.clr").addClass("error") + $("#cdiagpub").addClass("error") + $("#cdiagpub").html( + "This query is not accessible to others because it is not shared." + ) + } + const quote_url = `${query_url}?id=${iid}` + const quotev_url = `${query_url}?version=${vr}&id=${iid}` + $("#clip_qx_md").attr("lnk", `[${pvtitle}](${quotev_url})`) + $("#clip_qx_ht").attr("lnk", quotev_url) + $("#clip_q_md").attr("lnk", `[${pvtitle}](${quote_url})`) + $("#clip_q_ht").attr("lnk", quote_url) + $(".clip_qx").show() + $(".clip_q").show() + $(".clip_w").hide() + $(".clip_n").hide() + } else if (qw == "w") { + const vinfo = iinfo.versions[vr] + const { entryid, entryid_heb } = vinfo + pvtitle = `${entryid_heb} (${entryid})` + const quotev_url = `${word_url}?version=${vr}&id=${iid}` + $("#clip_w_md").attr("lnk", `[${pvtitle}](${quotev_url})`) + $("#clip_w_ht").attr("lnk", quotev_url) + $(".clip_w.clr").addClass("special") + $(".clip_qx").hide() + $(".clip_q").hide() + $(".clip_w").show() + $(".clip_n").hide() + } else if (qw == "n") { + const { ufname, ulname, kw } = iinfo + const ufnamex = escHT(ufname) + const ulnamex = escHT(ulname) + const kwx = escHT(kw) + pvtitle = `${ufnamex} ${ulnamex} - ${kwx}` + const quotev_url = `${note_url}?version=${vr}&id=${iid}&tp=txt_tb1&nget=v` + $("#clip_n_md").attr("lnk", `[${pvtitle}](${quotev_url})`) + $("#clip_n_ht").attr("lnk", quotev_url) + $(".clip_n.clr").addClass("special") + $(".clip_qx").hide() + $(".clip_q").hide() + $(".clip_w").hide() + $(".clip_n").show() + } + } + $("#clip_pv_md").attr("lnk", `[${pvtitle}](${shebanq_url_raw})`) + $("#clip_pv_ht").attr("lnk", shebanq_url_raw) + $("#clip_pv_htc").attr("lnk", shebanq_url_rawc) + $("#clip_pv_nl").attr("lnk", shebanq_url_note) + $("#clip_pv_cn").attr("lnk", shebanq_url_raw) + $("#clip_pv_cn").attr("tit", pvtitle) + st.animate({ height: "260px", width: "570px", opacity: 0.95 }, 300) + }) + + st.mouseleave(() => { + $("#self_link").hide() + deselectText() + $("#citeh").show() + st.animate({ height: "20px", width: "40px", opacity: 0.7 }, 300) + return false + }) +}) diff --git a/static/js/app/sidebars.js b/static/js/app/sidebars.js new file mode 100644 index 00000000..c7bc2c81 --- /dev/null +++ b/static/js/app/sidebars.js @@ -0,0 +1,722 @@ +/* eslint-env jquery */ +/* globals Config, P, L, State */ + +import { + toggle_detail, + escHT, + special_links, + close_dialog, + put_markdown, +} from "./helpers.js" +import { Msg } from "./msg.js" +import { CSelect } from "./select.js" +import { Colorpicker1 } from "./colorpicker.js" + +export class Sidebars { + /* TOP LEVEL: all four kinds of sidebars + */ + constructor() { + this.sidebar = {} + for (const mr of ["m", "r"]) { + for (const qw of ["q", "w", "n"]) { + this.sidebar[`${mr}${qw}`] = new Sidebar(mr, qw) + } + } + this.side_fetched = {} + } + + apply() { + for (const mr of ["m", "r"]) { + for (const qw of ["q", "w", "n"]) { + this.sidebar[`${mr}${qw}`].apply() + } + } + } + + after_material_fetch() { + for (const qw of ["q", "w", "n"]) { + this.side_fetched[`m${qw}`] = false + } + } + + after_item_fetch() { + for (const qw of ["q", "w", "n"]) { + this.side_fetched[`r${qw}`] = false + } + } +} + +/* SPECIFIC sidebars, the [mr][qw] type is frozen into the object + * + */ + +class Sidebar { + /* the individual sidebar, parametrized with qr and mw + * to specify one of the four kinds + */ + constructor(mr, qw) { + const { versions } = Config + + this.mr = mr + this.qw = qw + this.name = `side_bar_${mr}${qw}` + this.hid = `#${this.name}` + this.hide = $(`#side_hide_${mr}${qw}`) + this.show = $(`#side_show_${mr}${qw}`) + this.content = new SContent(mr, qw) + + if (mr == "r") { + this.cselect = {} + for (const v of versions) { + this.add_version(v) + } + } + this.show.click(e => { + e.preventDefault() + P.vs.hstatesv(this.qw, { get: "v" }) + P.vs.addHist() + this.apply() + }) + + this.hide.click(e => { + e.preventDefault() + P.vs.hstatesv(this.qw, { get: "x" }) + P.vs.addHist() + this.apply() + }) + } + + add_version(v) { + const { qw } = this + this.cselect[v] = new CSelect(v, qw) + } + + apply() { + const { mr, qw, hide, show } = this + const thebar = $(this.hid) + const thelist = $(`#side_material_${mr}${qw}`) + const theset = $(`#side_settings_${mr}${qw}`) + if (this.mr != P.mr || (this.mr == "r" && this.qw != P.qw)) { + thebar.hide() + } else { + thebar.show() + theset.show() + if (this.mr == "m") { + if (P.vs.get(this.qw) == "x") { + thelist.hide() + theset.hide() + hide.hide() + show.show() + } else { + thelist.show() + theset.show() + hide.show() + show.hide() + } + } else { + hide.hide() + show.hide() + } + this.content.apply() + } + } +} + +/* SIDELIST MATERIAL + * + */ + +class SContent { + /* the contents of an individual sidebar + */ + constructor(mr, qw) { + this.mr = mr + this.qw = qw + this.other_mr = this.mr == "m" ? "r" : "m" + this.name = `side_material_${mr}${qw}` + this.hid = `#${this.name}` + + if (mr == "r") { + if (qw != "n") { + P.picker1[qw] = new Colorpicker1(qw, null, true, false) + } + } + } + + msg(m) { + $(this.hid).html(m) + } + + set_vselect(v) { + $(`#version_s_${v}`).click(e => { + e.preventDefault() + P.vs.mstatesv({ version: v }) + P.go() + }) + } + + process() { + const { versions, words_url, notes_url } = Config + + const { mr, qw } = this + + P.sidebars.after_item_fetch() + this.sidelistitems() + if (this.mr == "m") { + P.listsettings[this.qw].apply() + } else { + for (const v of versions) { + P.sidebars.sidebar[`r${this.qw}`].cselect[v].init() + } + + const vr = P.version + const iid = P.vs.iid() + + $(".moredetail").click(e => { + e.preventDefault() + const elem = $(e.target) + toggle_detail(elem) + }) + $(".detail").hide() + $(`div[version="${vr}"]`).find(".detail").show() + + this.msgo = new Msg(`dbmsg_${qw}`) + + let ufname, ulname + + if (qw == "q") { + const { q } = State + this.info = q + $("#theqid").html(q.id) + ufname = escHT(q.ufname || "") + ulname = escHT(q.ulname || "") + const qname = escHT(q.name || "") + $("#itemtag").val(`${ufname} ${ulname}: ${qname}`) + this.msgov = new Msg("dbmsg_qv") + $("#is_pub_c").show() + $("#is_pub_ro").hide() + } else if (qw == "w") { + const { w } = State + this.info = w + if ("versions" in w) { + const wvr = w.versions[vr] + const wentryh = escHT(wvr.entry_heb) + const wentryid = escHT(wvr.entryid) + $("#itemtag").val(`${wentryh}: ${wentryid}`) + $("#gobackw").attr( + "href", + `${words_url}?lan=${wvr.lan}&` + + `letter=${wvr.entry_heb.charCodeAt(0)}&goto=${w.id}` + ) + } + } else if (qw == "n") { + const { n } = State + this.info = n + if ("versions" in n) { + ufname = escHT(n.ufname) + ulname = escHT(n.ulname) + const kw = escHT(n.kw) + $("#itemtag").val(`${ufname} ${ulname}: ${kw}`) + $("#gobackn").attr("href", `${notes_url}?goto=${n.id}`) + } + } + if ("versions" in this.info) { + for (const v in this.info.versions) { + const extra = qw == "w" ? "" : `${ufname}_${ulname}` + this.set_vselect(v) + P.set_csv(v, mr, qw, iid, extra) + } + } + if (qw) { + const { msgs } = State + for (const m of msgs) { + this.msgo.msg(m) + } + } + } + + let thistitle + if (this.mr == "m") { + thistitle = `[${P.vs.version()}] ${P.vs.book()} ${P.vs.chapter()}:${P.vs.verse()}` + } else { + thistitle = $("#itemtag").val() + $("#theitem").html(`${thistitle} `) + /* fill in the title of the query/word/note above the verse material + * and put it in the page title as well + */ + } + document.title = thistitle + + if (this.qw == "q") { + const { mql_small_height, mql_small_width, mql_big_width, mql_big_width_dia } = P + if (this.mr == "m") { + /* in the sidebar list of queries: + * the mql query body can be popped up as a dialog for viewing it + * in a larger canvas + */ + $(".fullc").click(e => { + e.preventDefault() + const elem = $(e.target) + const { window_height } = P + const thisiid = elem.attr("iid") + const mqlq = $(`#area_${thisiid}`) + const dia = $(`#bigq_${thisiid}`).dialog({ + dialogClass: "mql_dialog", + closeOnEscape: true, + close: () => { + dia.dialog("destroy") + const mqlq = $(`#area_${thisiid}`) + mqlq.css("height", mql_small_height) + mqlq.css("width", mql_small_width) + }, + modal: false, + title: "mql query body", + position: { my: "left top", at: "left top", of: window }, + width: mql_big_width_dia, + height: window_height, + }) + mqlq.css("height", P.standard_height) + mqlq.css("width", mql_big_width) + }) + } else { + /* in the sidebar item view of a single query: + * the mql query body can be popped up as a dialog for viewing it + * in a larger canvas + */ + const { q } = State + const vr = P.version + const fullc = $(".fullc") + const editq = $("#editq") + const execq = $("#execq") + const saveq = $("#saveq") + const cancq = $("#cancq") + const doneq = $("#doneq") + const nameq = $("#nameq") + const descm = $("#descm") + const descq = $("#descq") + const mqlq = $("#mqlq") + const pube = $("#is_pub_c") + const pubr = $("#is_pub_ro") + const is_pub = + "versions" in q && vr in q.versions && q.versions[vr].is_published + + fullc.click(e => { + e.preventDefault() + const { window_height, half_standard_height } = P + fullc.hide() + const dia = $("#bigger") + .closest("div") + .dialog({ + dialogClass: "mql_dialog", + closeOnEscape: true, + close: () => { + dia.dialog("destroy") + mqlq.css("height", mql_small_height) + descm.removeClass("desc_dia") + descm.addClass("desc") + descm.css("height", mql_small_height) + fullc.show() + }, + modal: false, + title: "description and mql query body", + position: { my: "left top", at: "left top", of: window }, + width: mql_big_width_dia, + height: window_height, + }) + mqlq.css("height", half_standard_height) + descm.removeClass("desc") + descm.addClass("desc_dia") + descm.css("height", half_standard_height) + }) + + $("#is_pub_c").click(e => { + const elem = $(e.target) + const val = elem.prop("checked") + this.sendval( + q.versions[vr], + elem, + val, + vr, + elem.attr("qid"), + "is_published", + val ? "T" : "" + ) + }) + + $("#is_shared_c").click(e => { + const elem = $(e.target) + const val = elem.prop("checked") + this.sendval(q, elem, val, vr, elem.attr("qid"), "is_shared", val ? "T" : "") + }) + + nameq.hide() + descq.hide() + descm.show() + editq.show() + if (is_pub) { + execq.hide() + } else { + execq.show() + } + saveq.hide() + cancq.hide() + doneq.hide() + pube.show() + pubr.hide() + + editq.click(e => { + e.preventDefault() + const is_pub = q.versions[vr].is_published + this.saved_name = nameq.val() + this.saved_desc = descq.val() + this.saved_mql = mqlq.val() + P.set_edit_width() + if (!is_pub) { + nameq.show() + } + descq.show() + descm.hide() + editq.hide() + saveq.show() + cancq.show() + doneq.show() + pubr.show() + pube.hide() + mqlq.prop("readonly", is_pub) + mqlq.css("height", "20em") + }) + + cancq.click(e => { + e.preventDefault() + nameq.val(this.saved_name) + descq.val(this.saved_desc) + mqlq.val(this.saved_mql) + P.reset_main_width() + nameq.hide() + descq.hide() + descm.show() + editq.show() + saveq.hide() + cancq.hide() + doneq.hide() + pube.show() + pubr.hide() + mqlq.prop("readonly", true) + mqlq.css("height", "10em") + }) + + doneq.click(e => { + e.preventDefault() + P.reset_main_width() + nameq.hide() + descq.hide() + descm.show() + editq.show() + saveq.hide() + cancq.hide() + doneq.hide() + pube.show() + pubr.hide() + mqlq.prop("readonly", true) + mqlq.css("height", "10em") + const data = { + version: P.version, + qid: $("#qid").val(), + name: $("#nameq").val(), + description: $("#descq").val(), + mql: $("#mqlq").val(), + execute: false, + } + this.sendvals(data) + }) + + saveq.click(e => { + e.preventDefault() + const data = { + version: P.version, + qid: $("#qid").val(), + name: $("#nameq").val(), + description: $("#descq").val(), + mql: $("#mqlq").val(), + execute: false, + } + this.sendvals(data) + }) + execq.click(e => { + e.preventDefault() + execq.addClass("fa-spin") + const msg = this.msgov + msg.clear() + msg.msg(["special", "executing query ..."]) + const data = { + version: P.version, + qid: $("#qid").val(), + name: $("#nameq").val(), + description: $("#descq").val(), + mql: $("#mqlq").val(), + execute: true, + } + this.sendvals(data) + }) + } + } + } + + setstatus(vr, cls) { + const statq = cls != null ? cls : $(`#statq${vr}`).attr("class") + const statm = + statq == "good" + ? "results up to date" + : statq == "error" + ? "results outdated" + : "never executed" + $("#statm").html(statm) + } + + sendval(q, checkbx, newval, vr, iid, fname, val) { + const { field_url } = Config + + const senddata = {} + senddata.version = vr + senddata.qid = iid + senddata.fname = fname + senddata.val = val + + $.post( + field_url, + senddata, + data => { + const { good, mod_dates, mod_cls, extra, msgs } = data + + if (good) { + for (const mod_date_fld in mod_dates) { + $(`#${mod_date_fld}`).html(mod_dates[mod_date_fld]) + } + for (const mod_cl in mod_cls) { + const cl = mod_cls[mod_cl] + const dest = $(mod_cl) + dest.removeClass("fa-check fa-close published") + dest.addClass(cl) + } + q[fname] = newval + } else { + checkbx.prop("checked", !newval) + } + + for (const fld in extra) { + const instr = extra[fld] + const prop = instr[0] + const val = instr[1] + + if (prop == "check") { + const dest = $(`#${fld}_c`) + dest.prop("checked", val) + } else if (prop == "show") { + const dest = $(`#${fld}`) + if (val) { + dest.show() + } else { + dest.hide() + } + } + } + const msg = fname == "is_shared" ? this.msgo : this.msgov + msg.clear() + for (const m of msgs) { + msg.msg(m) + } + }, + "json" + ) + } + + sendvals(senddata) { + const { fields_url } = Config + + const { execute, version: vr } = senddata + + $.post( + fields_url, + senddata, + data => { + const { good, q, msgs } = data + + const msg = this.msgov + msg.clear() + + for (const m of msgs) { + msg.msg(m) + } + + if (good) { + const { oldeversions } = data + const qx = q.versions[vr] + $("#nameqm").html(escHT(q.name || "")) + $("#nameq").val(q.name) + const d_md = special_links(q.description_md) + const descm = $("#descm") + descm.html(d_md) + P.decorate_crossrefs(descm) + $("#descq").val(q.description) + $("#mqlq").val(qx.mql) + const ev = $("#eversion") + const evtd = ev.closest("td") + ev.html(qx.eversion) + if (qx.eversion in oldeversions) { + evtd.addClass("oldexeversion") + evtd.attr("title", "this is not the newest version") + } else { + evtd.removeClass("oldexeversion") + evtd.attr("title", "this is the newest version") + } + $("#executed_on").html(qx.executed_on) + $("#xmodified_on").html(qx.xmodified_on) + $("#qresults").html(qx.results) + $("#qresultmonads").html(qx.resultmonads) + $("#statq").removeClass("error warning good").addClass(qx.status) + this.setstatus("", qx.status) + P.sidebars.sidebar["rq"].content.info = q + } + if (execute) { + P.reset_material_status() + P.material.adapt() + const show_chart = close_dialog($(`#select_contents_chart_${vr}_q_${q.id}`)) + if (show_chart) { + P.sidebars.sidebar["rq"].cselect[vr].apply() + } + $("#execq").removeClass("fa-spin") + } + }, + "json" + ) + } + + apply() { + if (P.mr == this.mr && (this.mr == "r" || P.vs.get(this.qw) == "v")) { + this.fetch() + } + } + + fetch() { + const { style, side_url } = Config + const { + version, + iid, + sidebars: { side_fetched }, + } = P + + const { mr, qw } = this + const thelist = $(`#side_material_${mr}${qw}`) + + let vars = `?version=${version}&mr={mr}&qw=${qw}` + + let do_fetch = false + let extra = "" + + if (mr == "m") { + vars += `&book=${P.vs.book()}&chapter=${P.vs.chapter()}` + if (qw == "q" || qw == "n") { + vars += `&${qw}pub=${P.vs.pub(qw)}` + } + do_fetch = P.vs.book() != "x" && P.vs.chapter() > 0 + extra = "m" + } else { + vars += `&iid=${iid}` + do_fetch = P.qw == "q" ? iid >= 0 : iid != "-1" + extra = `${qw}m` + } + if (do_fetch && !side_fetched[`${mr}${qw}`]) { + const tag = `tag${mr == "m" ? "s" : ""}` + this.msg(`fetching ${style[qw][tag]} ...`) + if (mr == "m") { + thelist.load( + `${side_url}${extra}${vars}`, + () => { + side_fetched[`${mr}${qw}`] = true + this.process() + }, + "html" + ) + } else { + $.get( + `${side_url}${extra}${vars}`, + html => { + thelist.html(html) + side_fetched[`${mr}${qw}`] = true + this.process() + }, + "html" + ) + } + } + } + + sidelistitems() { + /* the list of items in an m-sidebar + */ + const { mr, qw } = this + + if (mr == "m") { + if (qw != "n") { + P.picker1list[qw] = {} + } + const qwlist = $(`#side_list_${qw} li`) + qwlist.each((i, el) => { + const elem = $(el) + const iid = elem.attr("iid") + this.sidelistitem(iid) + if (qw != "n") { + P.picker1list[qw][iid] = new Colorpicker1(qw, iid, false, false) + } + }) + } + } + + sidelistitem(iid) { + /* individual item in an m-sidebar + */ + const { muting_n, muting_q } = L + const { qw } = this + + const itop = $(`#${qw}${iid}`) + const more = $(`#m_${qw}${iid}`) + const desc = $(`#d_${qw}${iid}`) + const item = $(`#item_${qw}${iid}`) + const all = $(`#${qw}${iid}`) + + desc.hide() + + more.click(e => { + e.preventDefault() + const elem = $(e.target) + toggle_detail(elem, desc, qw == "q" ? put_markdown : undefined) + }) + + item.click(e => { + e.preventDefault() + const elem = $(e.target) + const { qw } = this + P.vs.mstatesv({ mr: this.other_mr, qw, iid: elem.attr("iid"), page: 1 }) + P.vs.addHist() + P.go() + }) + + if (qw == "w") { + if (!P.vs.iscolor(qw, iid)) { + all.hide() + } + } else if (qw == "q") { + if (muting_q.isSet(`q${iid}`)) { + itop.hide() + } else { + itop.show() + } + } else if (qw == "n") { + if (muting_n.isSet(`n${iid}`)) { + itop.hide() + } else { + itop.show() + } + } + } +} diff --git a/static/js/app/viewstate.js b/static/js/app/viewstate.js new file mode 100644 index 00000000..7c9ae8e0 --- /dev/null +++ b/static/js/app/viewstate.js @@ -0,0 +1,166 @@ +/* eslint-env jquery */ +/* globals Config, P */ + +export class ViewState { + constructor(init, pref) { + this.data = init + this.pref = pref + this.from_push = false + + this.addHist() + } + + getvars() { + const { data } = this + let vars = "" + let sep = "?" + for (const group in data) { + const extra = group == "colormap" ? "c_" : "" + for (const qw in data[group]) { + for (const name in data[group][qw]) { + vars += `${sep}${extra}${qw}${name}=${data[group][qw][name]}` + sep = "&" + } + } + } + return vars + } + + csv_url(vr, mr, qw, iid, tp, extra) { + const { item_url } = Config + + let vars = `?version=${vr}&mr=${mr}&qw=${qw}&iid=${iid}&tp=${tp}&extra=${extra}` + const data = P.vs.ddata() + for (const name in data) { + vars += `&${name}=${data[name]}` + } + return `${item_url}${vars}` + } + + goback() { + const state = History.getState() + if (!this.from_push && state && state.data) { + this.apply(state) + } + } + + addHist() { + const { style, view_url } = Config + + let title + if (this.mr() == "m") { + title = `[${this.version()}] ${this.book()} ${this.chapter()}:${this.verse()}` + } else { + title = `${style[this.qw()]["Tag"]} ${this.iid()} p${this.page()}` + } + this.from_push = true + History.pushState(this.data, title, view_url) + this.from_push = false + } + + apply(state) { + if (state.data != undefined) { + this.data = state.data + } + P.go() + } + + delsv(group, qw, name) { + delete this.data[group][qw][name] + $.cookie(this.pref + group + qw, this.data[group][qw]) + } + + setsv(group, qw, values) { + for (const mb in values) { + this.data[group][qw][mb] = values[mb] + } + $.cookie(this.pref + group + qw, this.data[group][qw]) + } + + resetsv(group, qw) { + for (const mb in this.data[group][qw]) { + delete this.data[group][qw][mb] + } + $.cookie(this.pref + group + qw, this.data[group][qw]) + } + + mstatesv(values) { + this.setsv("material", "", values) + } + dstatesv(values) { + this.setsv("hebrewdata", "", values) + } + hstatesv(qw, values) { + this.setsv("highlights", qw, values) + } + cstatesv(qw, values) { + this.setsv("colormap", qw, values) + } + cstatex(qw, name) { + this.delsv("colormap", qw, name) + } + cstatexx(qw) { + this.resetsv("colormap", qw) + } + + mstate() { + return this.data["material"][""] + } + ddata() { + return this.data["hebrewdata"][""] + } + mr() { + return this.data["material"][""]["mr"] + } + qw() { + return this.data["material"][""]["qw"] + } + tp() { + return this.data["material"][""]["tp"] + } + tr() { + return this.data["material"][""]["tr"] + } + lang() { + return this.data["material"][""]["lang"] + } + iid() { + return this.data["material"][""]["iid"] + } + version() { + return this.data["material"][""]["version"] + } + book() { + return this.data["material"][""]["book"] + } + chapter() { + return this.data["material"][""]["chapter"] + } + verse() { + return this.data["material"][""]["verse"] + } + page() { + return this.data["material"][""]["page"] + } + get(qw) { + return this.data["highlights"][qw]["get"] + } + active(qw) { + return this.data["highlights"][qw]["active"] + } + sel_one(qw) { + return this.data["highlights"][qw]["sel_one"] + } + pub(qw) { + return this.data["highlights"][qw]["pub"] + } + colormap(qw) { + return this.data["colormap"][qw] + } + color(qw, id) { + return this.data["colormap"][qw][id] + } + iscolor(qw, cl) { + return cl in this.data["colormap"][qw] + } +} diff --git a/static/js/app/words.js b/static/js/app/words.js new file mode 100644 index 00000000..db63cf7d --- /dev/null +++ b/static/js/app/words.js @@ -0,0 +1,80 @@ +/* eslint-env jquery */ +/* eslint-disable no-new */ +/* globals Config, ConfigW */ + +import { set_heightw } from "./page.js" + +const RequestInfo = { + parameter(name) { + return this.parameters()[name] + }, + parameters(uriGiven) { + const uri = uriGiven || window.location.search + if (uri.indexOf("?") === -1) { + return {} + } + const query = uri.slice(1) + const params = query.split("&") + const result = {} + let i = 0 + while (i < params.length) { + const parameter = params[i].split("=") + result[parameter[0]] = parameter[1] + i++ + } + return result + }, +} + +class View { + constructor(version) { + this.version = version + this.init() + } + + set_vselect(v, gotoword) { + const { words_url } = Config + const { lan, letter } = ConfigW + + $(`#version_${v}`).click(e => { + e.preventDefault() + this.version = v + window.location.href = `${words_url}?version=${v}&lan=${lan}&letter=${letter}&goto=${gotoword}` + }) + } + + init() { + $(".mvradio").removeClass("ison") + const gotoword = RequestInfo.parameter("goto") + const { versions } = Config + const { version } = this + + for (const v of versions) { + this.set_vselect(v, gotoword) + } + + $(`#version_${version}`).addClass("ison") + set_heightw() + $("[wii]").hide() + $("[gi]").click(e => { + e.preventDefault() + const elem = $(e.target) + const i = elem.attr("gi") + $(`[wi="${i}"]`).toggle() + $(`[wii="${i}"]`).toggle() + }) + $("[gi]").closest("td").removeClass("selecthlw") + const wtarget = $(`[gi=${gotoword}]`).closest("td") + if (wtarget != undefined) { + wtarget.addClass("selecthlw") + if (wtarget[0] != undefined) { + wtarget[0].scrollIntoView() + } + } + } +} + +$(() => { + const { version } = ConfigW + new View(version) +}) diff --git a/static/js/hebrew2021-06-22.js b/static/js/hebrew2021-06-22.js deleted file mode 100644 index 63f142d0..00000000 --- a/static/js/hebrew2021-06-22.js +++ /dev/null @@ -1,3452 +0,0 @@ -/* eslint-env jquery */ -/* eslint-disable camelcase */ - -$.cookie.raw = false -$.cookie.json = true -$.cookie.defaults.expires = 30 -$.cookie.defaults.path = "/" - -const docName = "0_home" -const nsq = $.initNamespaceStorage("muting_q") -const muting_q = nsq.localStorage -const nsn = $.initNamespaceStorage("muting_n") -const muting_n = nsn.localStorage -/* on the Queries page the user can "mute" queries. Which queries are muted, - * is stored as key value pairs in this local storage bucket. - * When shebanq shows relevant queries next to a page, muting is taken into account. - */ - -/* the one and only page object - */ -/* globals P */ - -/* config settings dumped by the server - */ -/* globals Config */ - -/* result data - */ -/* globals State */ - -/* the markdown object - */ -/* globals markdown */ - -let side_fetched, material_fetched, material_kind -/* transitory flags indicating whether kinds of material and sidebars - * have loaded content - */ - -/* fixed dimensions, measures, heights, widths, etc */ - -const subtractm = 150 -/* the canvas holding the material gets a height - * equal to the window height minus this amount - */ -const subtractw = 80 -/* the canvas holding the material gets a height - * equal to the window height minus this amount - */ -let window_height -let standard_height -let half_standard_height -/* height of canvas - */ -let standard_heightw -/* height of canvas - */ -const mql_small_height = "10em" -/* height of mql query body in sidebar - */ -const mql_small_width = "97%" -/* height of mql query body in sidebar and in dialog - */ -const mql_big_width_dia = "60%" -/* width of query info in dialog mode - */ -const mql_big_width = "100%" -/* width of mql query body in sidebar and in dialog - */ -let orig_side_width, orig_main_width -/* the widths of sidebar and main area just after loading the initial page - */ -const edit_side_width = "55%" -/* the desired width of the sidebar when editing a query body - */ -const edit_main_width = "40%" -/* the desired width of the main area when editing a query body - */ -const chart_width = "400px" -/* dialog width for charts - */ -const chart_cols = 30 -/* number of chapters in a row in a chart - */ - -/* TOP LEVEL: DYNAMICS, PAGE, WINDOW, SKELETON - */ - -/* exported setup, dynamics */ - -function setup() { - /* top level function, called when the page has loaded - * P is the handle to manipulate the whole page - */ - const { viewinit, pref } = Config - const P = new Page(new ViewState(viewinit, pref)) - return P -} - -function dynamics(P) { - /* top level function, called when the page has loaded - * P is the handle to manipulate the whole page - */ - P.init() - P.go() -} - -const set_height = () => { - /* the heights of the sidebars are set, depending on the height of the window - */ - const { tab_views } = Config - window_height = window.innerHeight - standard_height = window_height - subtractm - half_standard_height = 0.4 * standard_height + "px" - $("#material_txt_p").css("height", standard_height + "px") - for (let i = 1; i <= tab_views; i++) { - $("#material_txt_tb" + i).css("height", standard_height + "px") - } - $("#side_material_mq").css("max-height", 0.6 * standard_height + "px") - $("#side_material_mw").css("max-height", 0.35 * standard_height + "px") - $("#words").css("height", standard_height + "px") - $("#letters").css("height", standard_height + "px") -} - -/* exported set_heightw */ - -function set_heightw() { - /* the heights of the sidebars are set, depending on the height of the window - */ - standard_heightw = window.innerHeight - subtractw - $("#words").css("height", standard_heightw + "px") - $("#letters").css("height", standard_heightw + "px") -} - -const get_width = () => { - /* save the orginal widths of sidebar and main area - */ - orig_side_width = $(".span3").css("width") - orig_main_width = $(".span9").css("width") -} - -const reset_main_width = () => { - /* restore the orginal widths of sidebar and main area - */ - if (orig_side_width != $(".span3").css("width")) { - $(".span3").css("width", orig_side_width) - $(".span3").css("max-width", orig_side_width) - $(".span9").css("width", orig_main_width) - $(".span9").css("max-width", orig_main_width) - } -} - -const set_edit_width = () => { - /* switch to increased sidebar width - */ - get_width() - $(".span3").css("width", edit_side_width) - $(".span9").css("width", edit_main_width) -} - -class Page { - /* the one and only page object - */ - constructor(vs) { - this.vs = vs - /* the viewstate - */ - History.Adapter.bind(window, "statechange", this.vs.goback.bind(this.vs)) - this.picker2 = {} - this.picker1 = { q: {}, w: {} } - /* will collect the two Colorpicker1 objects, indexed as q w - */ - this.picker1list = { q: {}, w: {} } - /* will collect the two lists of Colorpicker1 objects, - * index as q w and then by iid - */ - } - - init() { - /* dress up the skeleton, initialize state variables - */ - const { vs, picker2 } = this - - this.material = new Material() - this.sidebars = new Sidebars() - set_height() - get_width() - const listsettings = {} - this.listsettings = listsettings - - for (const qw of ["q", "w", "n"]) { - listsettings[qw] = new ListSettings(qw) - if (qw != "n") { - picker2[qw] = listsettings[qw].picker2 - } - } - const prev = {} - this.prev = prev - for (const x in vs.mstate()) { - prev[x] = null - } - reset_material_status() - } - - apply() { - /* apply the viewstate: hide and show material as prescribed by the viewstate - */ - this.material.apply() - this.sidebars.apply() - } - go() { - /* go to another page view, check whether initial content has to be loaded - */ - reset_main_width() - this.apply() - } - go_material() { - /* load other material, whilst keeping the sidebars the same - */ - this.material.apply() - } - - /* - * the origin must be an object which has a member indicating - * the type of origin and the kind of page. - - * 1: a color picker 1 from an item in a list - * 1a: the color picker 1 on an item page - * 2: a color picker 2 on a list page - * 3: a button of the list view settings - * 4: a click on a word in the text - * 5: when the data or text representation is loaded - */ - highlight2(origin) { - /* all highlighting goes through this function - highlighting is holistic: when the user changes a view settings, - all highlights have to be reevaluated. - The only reduction is that word highlighting is completely orthogonal - to query result highlighting. - */ - const { style } = Config - - const { qw, iid, code } = origin - const { vs, listsettings } = this - const active = P.vs.active(qw) - if (active == "hlreset") { - /* all viewsettings for either queries or words are restored to 'factory' settings - */ - vs.cstatexx(qw) - vs.hstatesv(qw, { active: "hlcustom", sel_one: defcolor(qw, null) }) - listsettings[qw].apply() - return - } - const hlradio = $("." + qw + "hradio") - const activeo = $("#" + qw + active) - hlradio.removeClass("ison") - activeo.addClass("ison") - const cmap = vs.colormap(qw) - - const paintings = {} - - /* first we are going to compute what to paint, - * resulting in a list of paint instructions. - Then we apply the paint instructions in one batch. - */ - - /* computing the paint instructions */ - - if (code == "1a") { - /* highlights on an r-page (with a single query or word), - * coming from the associated ColorPicker1 - * This is simple coloring, using a single color. - */ - const paint = cmap[iid] || defcolor(qw == "q", iid) - if (qw == "q") { - $($.parseJSON($("#themonads").val())).each((i, m) => { - paintings[m] = paint - }) - } else if (qw == "w") { - paintings[iid] = paint - } - this.paint(qw, paintings) - return - } - - /* all other cases: highlights on an m-page, responding to a user action - * This is complex coloring, using multiple colors. - * First we determine which monads need to be highlighted. - */ - const selclr = P.vs.sel_one(qw) - const custitems = {} - const plainitems = {} - - if (qw == "q") { - /* Queries: highlight customised items with priority over uncustomised items - * If a word belongs to several query results, - * the last-applied coloring determines the color that the user sees. - * We want to promote the customised colors over the non-customized ones, - * so we compute customized coloring after - * uncustomized coloring. - * Skip the muted queries - */ - $("#side_list_" + qw + " li").each((i, el) => { - const elem = $(el) - const iid = elem.attr("iid") - if (!muting_q.isSet(iid + "")) { - const monads = $.parseJSON($("#" + qw + iid).attr("monads")) - if (P.vs.iscolor(qw, iid)) { - custitems[iid] = monads - } else { - plainitems[iid] = monads - } - } - }) - } else if (qw == "w") { - /* Words: they are disjoint, no priority needed - */ - $("#side_list_" + qw + " li").each((i, el) => { - const elem = $(el) - const iid = elem.attr("iid") - if (P.vs.iscolor(qw, iid)) { - custitems[iid] = 1 - } else { - plainitems[iid] = 1 - } - const all = $("#" + qw + iid) - if (active == "hlmany" || P.vs.iscolor(qw, iid)) { - all.show() - } else { - all.hide() - } - }) - } - const chunks = [custitems, plainitems] - - const clselect = iid => { - /* assigns a color to an individual monad, based on the viewsettings - */ - let paint = "" - if (active == "hloff") { - paint = style[qw]["off"] - } /* - viewsetting says: do not color any item */ else if ( - active == "hlone" - ) { - paint = selclr - } else if (active == "hlmany") { - /* viewsetting says: color every applicable item with the same color */ - paint = cmap[iid] || defcolor(qw == "q", iid) - } else if (active == "hlcustom") { - /* viewsetting says: - * color every item with customized color (if customized) - * else with query/word-dependent default color - */ - paint = cmap[iid] || selclr - } else { - /* viewsetting says: - * color every item with customized color (if customized) - * else with a single chosen color - */ - paint = selclr - } /* - but this should not occur */ - return paint - } - - if (qw == "q") { - /* Queries: compute the monads to be painted and the colors needed for it - */ - for (let c = 0; c < 2; c++) { - const chunk = chunks[c] - for (const iid in chunk) { - const color = clselect(iid) - const monads = chunk[iid] - for (const m in monads) { - const monad = monads[m] - if (!(monad in paintings)) { - paintings[monad] = color - } - } - } - } - } else if (qw == "w") { - /* Words: gather the lexeme_ids to be painted and the colors needed for it - */ - for (let c = 0; c < 2; c++) { - const chunk = chunks[c] - for (const iid in chunk) { - let color = style[qw]["off"] - if (c == 0) { - /* do not color the plain items when dealing with words - * (as opposed to queries) - */ - color = clselect(iid) - } - paintings[iid] = color - } - } - } - /* maybe the selection of words of queries has changed for the same material, - * so wipe previous coloring - */ - const monads = $("#material span[m]") - const stl = style[qw]["prop"] - const clr_off = style[qw]["off"] - monads.css(stl, clr_off) - - /* finally, the computed colors are applied */ - this.paint(qw, paintings) - } - - paint(qw, paintings) { - /* Execute a series of computed paint instructions - */ - const { style, vcolors } = Config - - const stl = style[qw]["prop"] - const container = "#material_" + P.vs.tp() - const att = qw == "q" ? "m" : "l" - for (const item in paintings) { - const color = paintings[item] - $(container + " span[" + att + '="' + item + '"]').css(stl, vcolors[color][qw]) - } - } -} - -/* MATERIAL - */ - -class Material { - /* Object corresponding to everything that controls the material in the main part - * (not in the side bars) - */ - constructor() { - this.name = "material" - this.hid = "#" + this.name - this.lselect = new LSelect() - this.mselect = new MSelect() - this.pselect = new PSelect() - this.message = new MMessage() - this.content = new MContent() - this.msettings = new MSettings(this.content) - this.message.msg("choose a passage or a query or a word") - } - - adapt() { - this.fetch() - } - - apply() { - /* apply viewsettings to current material - */ - const { booklangs, booktrans } = Config - P.version = P.vs.version() - P.mr = P.vs.mr() - P.qw = P.vs.qw() - P.iid = P.vs.iid() - if ( - P.mr != P.prev["mr"] || - P.qw != P.prev["qw"] || - P.version != P.prev["version"] || - (P.mr == "m" && - (P.vs.book() != P.prev["book"] || - P.vs.chapter() != P.prev["chapter"] || - P.vs.verse() != P.prev["verse"])) || - (P.mr == "r" && (P.iid != P.prev["iid"] || P.vs.page() != P.prev["page"])) - ) { - reset_material_status() - const p_mr = P.prev["mr"] - const p_qw = P.prev["qw"] - const p_iid = P.prev["iid"] - if (p_mr == "r" && P.mr == "m") { - const vals = {} - if (p_qw != "n") { - vals[p_iid] = P.vs.colormap(p_qw)[p_iid] || defcolor(p_qw == "q", p_iid) - P.vs.cstatesv(p_qw, vals) - } - } - } - this.lselect.apply() - this.mselect.apply() - this.pselect.apply() - this.msettings.apply() - const book = P.vs.book() - const chapter = P.vs.chapter() - const page = P.vs.page() - $("#thelang").html(booklangs[P.vs.lang()][1]) - $("#thebook").html(book != "x" ? booktrans[P.vs.lang()][book] : "book") - $("#thechapter").html(chapter > 0 ? chapter : "chapter") - $("#thepage").html(page > 0 ? "" + page : "") - for (const x in P.vs.mstate()) { - P.prev[x] = P.vs.mstate()[x] - } - } - - fetch() { - /* get the material by AJAX if needed, and process the material afterward - */ - const { material_url } = Config - - let vars = - `?version=${P.version}&mr=${P.mr}&tp=${P.vs.tp()}&tr=${P.vs.tr()}` + - `&qw=${P.qw}&lang=${P.vs.lang()}` - let do_fetch = false - if (P.mr == "m") { - vars += `&book=${P.vs.book()}&chapter=${P.vs.chapter()}` - do_fetch = P.vs.book() != "x" && P.vs.chapter() > 0 - } else { - vars += `&iid=${P.iid}&page=${P.vs.page()}` - do_fetch = P.qw == "q" ? P.iid >= 0 : P.iid != "-1" - } - const tp = P.vs.tp() - const tr = P.vs.tr() - if ( - do_fetch && - (!material_fetched[tp] || !(tp in material_kind) || material_kind[tp] != tr) - ) { - this.message.msg("fetching data ...") - P.sidebars.after_material_fetch() - $.get( - `${material_url}${vars}`, - html => { - const response = $(html) - this.pselect.add(response) - this.message.add(response) - this.content.add(response) - material_fetched[tp] = true - material_kind[tp] = tr - this.process() - this.goto_verse() - }, - "html" - ) - } else { - P.highlight2({ code: "5", qw: "w" }) - P.highlight2({ code: "5", qw: "q" }) - this.msettings.hebrewsettings.apply() - this.goto_verse() - } - } - goto_verse() { - /* go to the selected verse - */ - $(".vhl").removeClass("vhl") - const xxx = P.mr == "r" ? "div[tvid]" : `div[tvid="${P.vs.verse()}"]` - const vtarget = $(`#material_${P.vs.tp()}>${xxx}`).filter(":first") - if (vtarget != undefined && vtarget[0] != undefined) { - vtarget[0].scrollIntoView() - $("#navbar")[0].scrollIntoView() - vtarget.addClass("vhl") - } - } - process() { - /* process new material obtained by an AJAX call - */ - let mf = 0 - const tp = P.vs.tp() - const tr = P.vs.tr() - for (const x in material_fetched) { - if (material_fetched[x]) { - mf += 1 - } - } - /* count how many versions of this material already have been fetched - */ - if (material_kind[tp] != "" && material_kind != tr) { - /* and also whether the material has already been fetched - * in another transcription - */ - mf += 1 - } - const newcontent = $("#material_" + tp) - const textcontent = $(".txt_p,.txt_tb1,.txt_tb2,.txt_tb3") - const ttextcontent = $(".t1_txt,.lv2") - if (P.vs.tr() == "hb") { - textcontent.removeClass("pho") - textcontent.removeClass("phox") - ttextcontent.removeClass("pho") - textcontent.addClass("heb") - textcontent.addClass("hebx") - ttextcontent.addClass("heb") - } else if (P.vs.tr() == "ph") { - textcontent.removeClass("heb") - textcontent.removeClass("hebx") - ttextcontent.removeClass("heb") - textcontent.addClass("pho") - textcontent.addClass("phox") - ttextcontent.addClass("pho") - } - /* because some processes like highlighting and assignment of verse number clicks - * must be suppressed on first time or on subsequent times - */ - if (P.mr == "r") { - this.pselect.apply() - if (P.qw != "n") { - P.picker1[P.qw].adapt(P.iid, true) - } - $("a.cref").click(e => { - e.preventDefault() - const elem = $(e.target) - P.vs.mstatesv({ - book: elem.attr("book"), - chapter: elem.attr("chapter"), - verse: elem.attr("verse"), - mr: "m", - }) - P.vs.addHist() - P.go() - }) - } else { - this.add_word_actions(newcontent, mf) - } - this.add_vrefs(newcontent, mf) - if (P.vs.tp() == "txt_il") { - this.msettings.hebrewsettings.apply() - } else if (P.vs.tp() == "txt_tb1") { - this.add_cmt(newcontent) - } - } - - add_cmt(newcontent) { - /* add actions for the tab1 view - */ - this.notes = new Notes(newcontent) - } - - add_vrefs(newcontent, mf) { - const { data_url } = Config - - const vrefs = newcontent.find(".vradio") - vrefs.each((i, el) => { - const elem = $(el) - elem.attr("title", "interlinear data") - }) - vrefs.click(e => { - e.preventDefault() - const elem = $(e.target) - const bk = elem.attr("b") - const ch = elem.attr("c") - const vs = elem.attr("v") - const base_tp = P.vs.tp() - const dat = $("#" + base_tp + "_txt_il_" + bk + "_" + ch + "_" + vs) - const txt = $("#" + base_tp + "_" + bk + "_" + ch + "_" + vs) - const legend = $("#datalegend") - const legendc = $("#datalegend_control") - if (elem.hasClass("ison")) { - elem.removeClass("ison") - elem.attr("title", "interlinear data") - legend.hide() - close_dialog(legend) - legendc.hide() - dat.hide() - txt.show() - } else { - elem.addClass("ison") - elem.attr("title", "text/tab") - legendc.show() - dat.show() - txt.hide() - if (dat.attr("lf") == "x") { - dat.html("fetching data for " + bk + " " + ch + ":" + vs + " ...") - dat.load( - `${data_url}?version=${P.version}&book=${bk}&chapter=${ch}&verse=${vs}`, - () => { - dat.attr("lf", "v") - this.msettings.hebrewsettings.apply() - if (P.mr == "r") { - if (P.qw != "n") { - P.picker1[P.qw].adapt(P.iid, true) - } - } else { - P.highlight2({ code: "5", qw: "w" }) - P.highlight2({ code: "5", qw: "q" }) - this.add_word_actions(dat, mf) - } - }, - "html" - ) - } - } - }) - } - - add_word_actions(newcontent, mf) { - /* Make words clickable, so that they show up in the sidebar - */ - newcontent.find("span[l]").click(e => { - e.preventDefault() - const elem = $(e.target) - const iid = elem.attr("l") - const qw = "w" - const all = $(`#${qw}${iid}`) - if (P.vs.iscolor(qw, iid)) { - P.vs.cstatex(qw, iid) - all.hide() - } else { - const vals = {} - vals[iid] = defcolor(false, iid) - P.vs.cstatesv(qw, vals) - all.show() - } - const active = P.vs.active(qw) - if (active != "hlcustom" && active != "hlmany") { - P.vs.hstatesv(qw, { active: "hlcustom" }) - } - if (P.vs.get("w") == "v") { - if (iid in P.picker1list["w"]) { - /* should not happen but it happens when changing data versions - */ - P.picker1list["w"][iid].apply(false) - } - } - P.highlight2({ code: "4", qw }) - P.vs.addHist() - }) - if (mf > 1) { - /* Initially, material gets highlighted once the sidebars have been loaded. - * But when we load a different representation of material (data, tab), - * the sidebars are still there, - * and after loading the material, highlighs have to be applied. - */ - P.highlight2({ code: "5", qw: "q" }) - P.highlight2({ code: "5", qw: "w" }) - } - } -} - -/* MATERIAL: Notes - */ - -class Notes { - constructor(newcontent) { - this.show = false - this.verselist = {} - this.version = P.version - this.sav_controls = $("span.nt_main_sav") - this.sav_c = this.sav_controls.find('a[tp="s"]') - this.rev_c = this.sav_controls.find('a[tp="r"]') - this.logged_in = false - this.cctrl = $("a.nt_main_ctrl") - - newcontent.find(".vradio").each((i, el) => { - const elem = $(el) - const bk = elem.attr("b") - const ch = elem.attr("c") - const vs = elem.attr("v") - const topl = elem.closest("div") - this.verselist[`${bk} ${ch}:${vs}`] = new Notev( - this.version, - bk, - ch, - vs, - topl.find("span.nt_ctrl"), - topl.find("table.t1_table") - ) - }) - const { verselist } = this - this.msgn = new Msg("nt_main_msg", () => { - for (const notev of Object.values(verselist)) { - notev.clear_msg() - } - }) - this.cctrl.click(e => { - e.preventDefault() - P.vs.hstatesv("n", { get: P.vs.get("n") == "v" ? "x" : "v" }) - this.apply() - }) - this.rev_c.click(e => { - e.preventDefault() - for (const notev of Object.values(verselist)) { - notev.revert() - } - }) - this.sav_c.click(e => { - e.preventDefault() - for (const notev of Object.values(verselist)) { - notev.save() - } - this.msgn.msg(["special", "Done"]) - }) - this.msgn.clear() - $("span.nt_main_sav").hide() - this.apply() - } - - apply() { - const { verselist } = this - - if (P.vs.get("n") == "v") { - this.cctrl.addClass("nt_loaded") - for (const notev of Object.values(verselist)) { - notev.show_notes(false) - this.logged_in = notev.logged_in - } - if (this.logged_in) { - this.sav_controls.show() - } - } else { - this.cctrl.removeClass("nt_loaded") - this.sav_controls.hide() - for (const notev of Object.values(verselist)) { - notev.hide_notes() - } - } - } -} - -class Notev { - constructor(vr, bk, ch, vs, ctrl, dest) { - this.loaded = false - this.nnotes = 0 - this.mnotes = 0 - this.show = false - this.edt = false - this.dirty = false - this.version = vr - this.book = bk - this.chapter = ch - this.verse = vs - this.ctrl = ctrl - this.dest = dest - - const { book, chapter, verse } = this - this.msgn = new Msg(`nt_msg_${book}_${chapter}_${verse}`) - this.cctrl = ctrl.find("a.nt_ctrl") - this.sav_controls = ctrl.find("span.nt_sav") - - const { sav_controls } = this - this.sav_c = sav_controls.find('a[tp="s"]') - this.edt_c = sav_controls.find('a[tp="e"]') - this.rev_c = sav_controls.find('a[tp="r"]') - - const { sav_c, edt_c, rev_c, cctrl } = this - - sav_c.click(e => { - e.preventDefault() - this.save() - }) - edt_c.click(e => { - e.preventDefault() - this.edit() - }) - rev_c.click(e => { - e.preventDefault() - this.revert() - }) - cctrl.click(e => { - e.preventDefault() - this.is_dirty() - if (this.show) { - this.hide_notes() - } else { - this.show_notes(true) - } - }) - - dest.find("tr.nt_cmt").hide() - $("span.nt_main_sav").hide() - sav_controls.hide() - } - - fetch(adjust_verse) { - const { cnotes_url } = Config - - const { version, book, chapter, verse, edt, msgn } = this - const senddata = { version, book, chapter, verse, edit: edt } - msgn.msg(["info", "fetching notes ..."]) - $.post(cnotes_url, senddata, data => { - this.loaded = true - msgn.clear() - for (const m of data.msgs) { - msgn.msg(m) - } - const { good, users, notes, nkey_index, changed, logged_in } = data - if (good) { - this.process(users, notes, nkey_index, changed, logged_in) - if (adjust_verse) { - if (P.mr == "m") { - P.vs.mstatesv({ verse }) - P.material.goto_verse() - } - } - } - }) - } - - process(users, notes, nkey_index, changed, logged_in) { - if (changed) { - if (P.mr == "m") { - side_fetched["mn"] = false - P.sidebars.sidebar["mn"].content.apply() - } - } - this.orig_users = users - this.orig_notes = notes - this.orig_nkey_index = nkey_index - this.orig_edit = [] - this.logged_in = logged_in - this.gen_html(true) - this.dirty = false - this.apply_dirty() - this.decorate() - } - - decorate() { - const { nt_statclass, nt_statsym, nt_statnext } = Config - const { dest, logged_in, sav_controls, edt, sav_c, edt_c } = this - dest - .find("td.nt_stat") - .find("a") - .click(e => { - e.preventDefault() - const elem = $(e.target) - const statcode = elem.attr("code") - const nextcode = nt_statnext[statcode] - const nextsym = nt_statsym[nextcode] - const row = elem.closest("tr") - for (const c in nt_statclass) { - row.removeClass(nt_statclass[c]) - } - for (const c in nt_statsym) { - elem.removeClass(`fa-${nt_statsym[c]}`) - } - row.addClass(nt_statclass[nextcode]) - elem.attr("code", nextcode) - elem.addClass(`fa-${nextsym}`) - }) - dest - .find("td.nt_pub") - .find("a") - .click(e => { - e.preventDefault() - const elem = $(e.target) - if (elem.hasClass("ison")) { - elem.removeClass("ison") - } else { - elem.addClass("ison") - } - }) - dest.find("tr.nt_cmt").show() - if (logged_in) { - $("span.nt_main_sav").show() - sav_controls.show() - if (edt) { - sav_c.show() - edt_c.hide() - } else { - sav_c.hide() - edt_c.show() - } - } - decorate_crossrefs(dest) - } - - gen_html_ca(canr) { - const { nt_statclass, nt_statsym } = Config - const vr = this.version - const notes = this.orig_notes[canr] - const nkey_index = this.orig_nkey_index - let html = "" - this.nnotes += notes.length - for (let n = 0; n < notes.length; n++) { - const nline = notes[n] - const { uid, nid, pub, shared, ro } = nline - const kwtrim = $.trim(nline.kw) - const kws = kwtrim.split(/\s+/) - let mute = false - for (const kw of kws) { - const nkid = nkey_index[`${uid} ${kw}`] - if (muting_n.isSet(`n${nkid}`)) { - mute = true - break - } - } - if (mute) { - this.mnotes += 1 - continue - } - const user = this.orig_users[uid] - const pubc = pub ? "ison" : "" - const sharedc = shared ? "ison" : "" - const statc = nt_statclass[nline.stat] - const statsym = nt_statsym[nline.stat] - const edit_att = ro ? "" : ' edit="1"' - const edit_class = ro ? "" : " edit" - html += `` - if (ro) { - html += ` - - ` - html += `${escHT(nline.kw)}` - const ntxt = special_links(markdown.toHTML(markdownEscape(nline.ntxt))) - html += `${ntxt}` - html += `${escHT(user)}` - html += '' - html += `` - html += `` - } else { - this.orig_edit.push({ canr, note: nline }) - html += ` - ` - html += `` - html += `` - html += `${escHT(user)}` - html += '' - html += `` - html += `${vr}` - html += `` - } - html += "" - } - return html - } - - gen_html(replace) { - this.mnotes = 0 - if (replace) { - this.dest.find("tr[ncanr]").remove() - } - for (const canr in this.orig_notes) { - const target = this.dest.find(`tr[canr="${canr}"]`) - const html = this.gen_html_ca(canr) - target.after(html) - } - if (this.nnotes == 0) { - this.cctrl.addClass("nt_empty") - } else { - this.cctrl.removeClass("nt_empty") - } - if (this.mnotes == 0) { - this.cctrl.removeClass("nt_muted") - } else { - this.cctrl.addClass("nt_muted") - this.msgn.msg(["special", `muted notes: ${this.mnotes}`]) - } - } - - sendnotes(senddata) { - const { cnotes_url } = Config - - $.post( - cnotes_url, - senddata, - data => { - const { good, users, notes, nkey_index, changed, logged_in } = data - this.msgn.clear() - for (const m of data.msgs) { - this.msgn.msg(m) - } - if (good) { - this.dest.find("tr[ncanr]").remove() - this.process(users, notes, nkey_index, changed, logged_in) - } - }, - "json" - ) - } - - is_dirty() { - let dirty = false - const { orig_edit } = this - if (orig_edit == undefined) { - this.dirty = false - return - } - for (let n = 0; n < orig_edit.length; n++) { - const { canr, note: o_note } = orig_edit[n] - const { nid } = o_note - const n_note = - nid == 0 - ? this.dest.find(`tr[nid="0"][ncanr="${canr}"]`) - : this.dest.find(`tr[nid="${nid}"]`) - const o_stat = o_note.stat - const n_stat = n_note.find("td.nt_stat a").attr("code") - const o_kw = $.trim(o_note.kw) - const n_kw = $.trim(n_note.find("td.nt_kw textarea").val()) - const o_ntxt = o_note.ntxt - const n_ntxt = $.trim(n_note.find("td.nt_cmt textarea").val()) - const o_shared = o_note.shared - const n_shared = n_note.find("td.nt_pub a").first().hasClass("ison") - const o_pub = o_note.pub - const n_pub = n_note.find("td.nt_pub a").last().hasClass("ison") - if ( - o_stat != n_stat || - o_kw != n_kw || - o_ntxt != n_ntxt || - o_shared != n_shared || - o_pub != n_pub - ) { - dirty = true - break - } - } - this.dirty = dirty - this.apply_dirty() - } - - apply_dirty() { - if (this.dirty) { - this.cctrl.addClass("dirty") - } else if (this.cctrl.hasClass("dirty")) { - this.cctrl.removeClass("dirty") - } - } - save() { - this.edt = false - const { version, book, chapter, verse, edt, orig_edit } = this - const data = { - version, - book, - chapter, - verse, - save: true, - edit: edt, - } - const notelines = [] - if (orig_edit == undefined) { - return - } - for (let n = 0; n < orig_edit.length; n++) { - const { canr, note: o_note } = orig_edit[n] - const { nid, uid } = o_note - const n_note = - nid == 0 - ? this.dest.find(`tr[nid="0"][ncanr="${canr}"]`) - : this.dest.find(`tr[nid="${nid}"]`) - const o_stat = o_note.stat - const n_stat = n_note.find("td.nt_stat a").attr("code") - const o_kw = $.trim(o_note.kw) - const n_kw = $.trim(n_note.find("td.nt_kw textarea").val()) - const o_ntxt = o_note.ntxt - const n_ntxt = $.trim(n_note.find("td.nt_cmt textarea").val()) - const o_shared = o_note.shared - const n_shared = n_note.find("td.nt_pub a").first().hasClass("ison") - const o_pub = o_note.pub - const n_pub = n_note.find("td.nt_pub a").last().hasClass("ison") - if ( - o_stat != n_stat || - o_kw != n_kw || - o_ntxt != n_ntxt || - o_shared != n_shared || - o_pub != n_pub - ) { - notelines.push({ - nid, - canr, - stat: n_stat, - kw: n_kw, - ntxt: n_ntxt, - uid, - shared: n_shared, - pub: n_pub, - }) - } - } - if (notelines.length > 0) { - data["notes"] = JSON.stringify(notelines) - } else { - this.msgn.msg(["warning", "No changes"]) - } - this.sendnotes(data) - } - - edit() { - this.edt = true - this.fetch(true) - } - - revert() { - this.edt = false - const { version, book, chapter, verse, edt } = this - const data = { - version, - book, - chapter, - verse, - save: true, - edit: edt, - } - data["notes"] = JSON.stringify([]) - this.sendnotes(data) - } - - hide_notes() { - this.show = false - this.cctrl.removeClass("nt_loaded") - this.sav_controls.hide() - this.dest.find("tr.nt_cmt").hide() - this.msgn.hide() - } - - show_notes(adjust_verse) { - this.show = true - this.cctrl.addClass("nt_loaded") - this.msgn.show() - if (!this.loaded) { - this.fetch(adjust_verse) - } else { - this.dest.find("tr.nt_cmt").show() - if (this.logged_in) { - this.sav_controls.show() - if (this.edt) { - this.sav_c.show() - this.edt_c.hide() - } else { - this.sav_c.hide() - this.edt_c.show() - } - } - if (P.mr == "m") { - P.vs.mstatesv({ verse: this.verse }) - P.material.goto_verse() - } - } - } - - clear_msg() { - this.msgn.clear() - } -} - -/* MATERIAL: SELECTION - * - */ - -class MSelect { - /* for book and chapter selection - */ - - constructor() { - const { versions } = Config - this.name = "select_passage" - this.hid = "#" + this.name - this.book = new SelectBook() - this.select = new SelectItems("chapter") - for (const v in versions) { - this.set_vselect(v) - } - $("#self_link").hide() - } - - apply() { - /* apply material viewsettings to current material - */ - const { featurehost, bol_url, pbl_url } = Config - - const thisFeaturehost = `${featurehost}/${docName}` - $(".source").attr("href", thisFeaturehost) - $(".source").attr("title", "BHSA feature documentation") - $(".mvradio").removeClass("ison") - $("#version_" + P.version).addClass("ison") - const bol = $("#bol_lnk") - const pbl = $("#pbl_lnk") - - if (P.mr == "m") { - this.book.apply() - this.select.apply() - $(this.hid).show() - const book = P.vs.book() - const chapter = P.vs.chapter() - if (book != "x" && chapter > 0) { - bol.attr("href", `${bol_url}/ETCBC4/${book}/${chapter}`) - bol.show() - pbl.attr("href", `${pbl_url}/${book}/${chapter}`) - pbl.show() - } else { - bol.hide() - pbl.hide() - } - } else { - $(this.hid).hide() - bol.hide() - pbl.hide() - } - } - - set_vselect(v) { - const { versions } = Config - - if (versions[v]) { - $(`#version_${v}`).click(e => { - e.preventDefault() - side_fetched["mw"] = false - side_fetched["mq"] = false - side_fetched["mn"] = false - P.vs.mstatesv({ version: v }) - P.go() - }) - } - } -} - -class PSelect { - /* for result page selection - */ - constructor() { - this.name = "select_pages" - this.hid = `#${this.name}` - this.select = new SelectItems("page") - } - - apply() { - /* apply result page selection: fill in headings on the page - */ - if (P.mr == "r") { - this.select.apply() - $(this.hid).show() - } else { - $(this.hid).hide() - } - } - - add(response) { - /* add the content portion of the response to the content portion of the page - */ - const select = "#select_contents_page" - if (P.mr == "r") { - $(select).html(response.find(select).html()) - } - } -} - -class LSelect { - /* language selection - */ - constructor() { - this.name = "select_contents_lang" - this.hid = "#" + this.name - this.control = "#select_control_lang" - $(this.control).click(e => { - e.preventDefault() - $(this.hid).dialog("open") - }) - } - - present() { - $(this.hid).dialog({ - autoOpen: false, - dialogClass: "items", - closeOnEscape: true, - modal: false, - title: "choose language", - width: "250px", - }) - } - - gen_html() { - /* generate a new lang selector - */ - const { booklangs } = Config - - const thelang = P.vs.lang() - const nitems = booklangs.length - this.lastitem = nitems - let ht = "" - ht += '' - const langs = Object.keys(booklangs).sort() - for (const item of langs) { - const langinfo = booklangs[item] - const name_en = langinfo[0] - const name_own = langinfo[1] - const clactive = thelang == item ? ' class="active"' : "" - ht += ` - - - ${name_own} - - - ${name_en} - ` - } - ht += "
" - $(this.hid).html(ht) - return nitems - } - - add_item(item) { - item.click(e => { - e.preventDefault() - const elem = $(e.target) - const newobj = elem.closest("li") - const isloaded = newobj.hasClass("active") - if (!isloaded) { - const vals = {} - vals["lang"] = elem.attr("item") - P.vs.mstatesv(vals) - this.update_vlabels() - P.vs.addHist() - P.material.apply() - } - }) - } - - update_vlabels() { - const { booktrans } = Config - - $("span[book]").each((i, el) => { - const elem = $(el) - elem.html(booktrans[P.vs.lang()][elem.attr("book")]) - }) - } - - apply() { - this.gen_html() - $("#select_contents_lang .itemnav").each((i, el) => { - const elem = $(el) - this.add_item(elem) - }) - $(this.control).show() - this.present() - } -} - -class SelectBook { - /* book selection - */ - constructor() { - this.name = "select_contents_book" - this.hid = "#" + this.name - this.control = "#select_control_book" - $(this.control).click(e => { - e.preventDefault() - $(this.hid).dialog("open") - }) - } - - present() { - $(this.hid).dialog({ - autoOpen: false, - dialogClass: "items", - closeOnEscape: true, - modal: false, - title: "choose book", - width: "110px", - }) - } - - gen_html() { - /* generate a new book selector - */ - const { booktrans, booksorder } = Config - - const thebook = P.vs.book() - const lang = P.vs.lang() - const thisbooksorder = booksorder[P.version] - const nitems = thisbooksorder.length - - this.lastitem = nitems - - let ht = "" - ht += '" - $(this.hid).html(ht) - return nitems - } - - add_item(item) { - item.click(e => { - e.preventDefault() - const elem = $(e.target) - const newobj = elem.closest("li") - const isloaded = newobj.hasClass("active") - if (!isloaded) { - const vals = {} - vals["book"] = elem.attr("item") - vals["chapter"] = "1" - vals["verse"] = "1" - P.vs.mstatesv(vals) - P.vs.addHist() - P.go() - } - }) - } - - apply() { - this.gen_html() - $("#select_contents_book .itemnav").each((i, el) => { - const elem = $(el) - this.add_item(elem) - }) - $(this.control).show() - this.present() - } -} - -class SelectItems { - /* both for chapters and for result pages - */ - constructor(key) { - this.key = key - this.other_key = key == "chapter" ? "page" : "chapter" - this.name = "select_contents_" + this.key - this.other_name = "select_contents_" + this.other_key - this.hid = "#" + this.name - this.other_hid = "#" + this.other_name - this.control = "#select_control_" + this.key - this.prev = $("#prev_" + this.key) - this.next = $("#next_" + this.key) - - this.prev.click(e => { - e.preventDefault() - const elem = $(e.target) - const vals = {} - vals[this.key] = elem.attr("contents") - vals["verse"] = "1" - P.vs.mstatesv(vals) - P.vs.addHist() - this.go() - }) - this.next.click(e => { - e.preventDefault() - const elem = $(e.target) - const vals = {} - vals[this.key] = elem.attr("contents") - vals["verse"] = "1" - P.vs.mstatesv(vals) - P.vs.addHist() - this.go() - }) - $(this.control).click(e => { - e.preventDefault() - $(this.hid).dialog("open") - }) - } - - go() { - if (this.key == "chapter") { - P.go() - } else { - P.go_material() - } - } - - present() { - close_dialog($(this.other_hid)) - $(this.hid).dialog({ - autoOpen: false, - dialogClass: "items", - closeOnEscape: true, - modal: false, - title: "choose " + this.key, - width: "200px", - }) - } - - gen_html() { - /* generate a new page selector - */ - const { books } = Config - - let theitem - let itemlist - let nitems - - if (this.key == "chapter") { - const thebook = P.vs.book() - theitem = P.vs.chapter() - nitems = thebook != "x" ? books[P.version][thebook] : 0 - this.lastitem = nitems - itemlist = new Array(nitems) - for (let i = 0; i < nitems; i++) { - itemlist[i] = i + 1 - } - } else { - /* 'page' - */ - theitem = P.vs.page() - nitems = $("#rp_pages").val() - this.lastitem = nitems - itemlist = [] - if (nitems) { - itemlist = $.parseJSON($("#rp_pagelist").val()) - } - } - - let ht = "" - if (nitems != undefined) { - if (nitems != 0) { - ht = '" - } - $(this.hid).html(ht) - } - return nitems - } - - add_item(item) { - item.click(e => { - e.preventDefault() - const elem = $(e.target) - const newobj = elem.closest("li") - const isloaded = newobj.hasClass("active") - if (!isloaded) { - const vals = {} - vals[this.key] = elem.attr("item") - vals["verse"] = "1" - P.vs.mstatesv(vals) - P.vs.addHist() - this.go() - } - }) - } - - apply() { - const showit = this.gen_html() > 0 - if (!showit) { - $(this.control).hide() - } else { - $(`#select_contents_${this.key} .itemnav`).each((i, el) => { - const elem = $(el) - this.add_item(elem) - }) - $(this.control).show() - const thisitem = parseInt(this.key == "page" ? P.vs.page() : P.vs.chapter()) - if (thisitem == undefined || thisitem == 1) { - this.prev.hide() - } else { - this.prev.attr("contents", "" + (thisitem - 1)) - this.prev.show() - } - if (thisitem == undefined || thisitem == this.lastitem) { - this.next.hide() - } else { - this.next.attr("contents", "" + (thisitem + 1)) - this.next.show() - } - } - this.present() - } -} - -class CSelect { - /* for chart selection - */ - constructor(vr, qw) { - this.vr = vr - this.qw = qw - this.control = `#select_control_chart_${vr}_${qw}` - this.select = `#select_contents_chart_${vr}_${qw}` - this.loaded = {} - } - - init() { - $(this.control).click(e => { - e.preventDefault() - this.apply() - }) - } - - apply() { - if (!this.loaded[`${this.qw}_${P.iid}`]) { - if ($(`#select_contents_chart_${this.vr}_${this.qw}_${P.iid}`).length == 0) { - $(this.select).append( - `` - ) - } - this.fetch(P.iid) - } else { - this.show() - } - } - - fetch(iid) { - const { chart_url } = Config - - const vars = `?version=${this.vr}&qw=${this.qw}&iid=${iid}` - $(`${this.select}_${iid}`).load( - `${chart_url}${vars}`, - () => { - this.loaded[`${this.qw}_${iid}`] = true - this.process(iid) - }, - "html" - ) - } - - process(iid) { - this.gen_html(iid) - $(`${this.select}_${iid} .cnav`).each((i, el) => { - const elem = $(el) - this.add_item(elem, iid) - }) - $("#theitemc").click(e => { - e.preventDefault() - const vals = {} - vals["iid"] = iid - vals["mr"] = "r" - vals["version"] = this.vr - vals["qw"] = this.qw - P.vs.mstatesv(vals) - P.vs.addHist() - P.go() - }) - $("#theitemc").html(`Back to ${$("#theitem").html()} (version ${this.vr})`) - /* fill in the Back to query/word line in a chart - */ - this.present(iid) - this.show(iid) - } - - present(iid) { - const { style } = Config - - $(`${this.select}_${iid}`).dialog({ - autoOpen: false, - dialogClass: "items", - closeOnEscape: true, - close: () => { - this.loaded[`${this.qw}_${iid}`] = false - $(`${this.select}_${iid}`).html("") - }, - modal: false, - title: `chart for ${style[this.qw]["tag"]} (version ${this.vr})`, - width: chart_width, - position: { my: "left top", at: "left top", of: window }, - }) - } - - show(iid) { - $(`${this.select}_${iid}`).dialog("open") - } - - gen_html(iid) { - /* generate a new chart - */ - const { style, ccolors } = Config - - let nbooks = 0 - let booklist = $(`#r_chartorder${this.qw}`).val() - let bookdata = $(`#r_chart${this.qw}`).val() - if (booklist) { - booklist = $.parseJSON(booklist) - bookdata = $.parseJSON(bookdata) - nbooks = booklist.length - } else { - booklist = [] - bookdata = {} - } - let ht = "" - ht += ` -

- back -

- ` - - const ccl = ccolors.length - for (const book of booklist) { - const blocks = bookdata[book] - ht += ` - - - " - } - ht += "
${book} - ` - let l = 0 - for (let i = 0; i < blocks.length; i++) { - if (l == chart_cols) { - ht += "" - l = 0 - } - const block_info = blocks[i] - const chnum = block_info[0] - const ch_range = block_info[1] + "-" + block_info[2] - const blres = block_info[3] - const blsize = block_info[4] - const blres_select = blres >= ccl ? ccl - 1 : blres - const z = ccolors[blres_select] - let s = " " - let sz = "" - let sc = "" - if (blsize < 25) { - s = "=" - sc = "s1" - } else if (blsize < 75) { - s = "-" - sc = "s5" - } - if (blsize < 100) { - sz = " (" + blsize + "%)" - } - ht += ` - ` - l++ - } - ht += "
- ${s} -
" - $(`${this.select}_${iid}`).html(ht) - return nbooks - } - - add_item(item, iid) { - item.click(e => { - e.preventDefault() - const elem = $(e.target) - let vals = {} - vals["book"] = elem.attr("b") - vals["chapter"] = elem.attr("ch") - vals["mr"] = "m" - vals["version"] = this.vr - P.vs.mstatesv(vals) - P.vs.hstatesv("q", { sel_one: "white", active: "hlcustom" }) - P.vs.hstatesv("w", { sel_one: "black", active: "hlcustom" }) - P.vs.cstatexx("q") - P.vs.cstatexx("w") - if (this.qw != "n") { - vals = {} - vals[iid] = P.vs.colormap(this.qw)[iid] || defcolor(this.qw == "q", iid) - P.vs.cstatesv(this.qw, vals) - } - P.vs.addHist() - P.go() - }) - } -} - -/* MATERIAL (messages when retrieving, storing the contents) - * - */ - -class MMessage { - /* diagnostic output - */ - constructor() { - this.name = "material_message" - this.hid = "#" + this.name - } - - add(response) { - $(this.hid).html(response.children(this.hid).html()) - } - - msg(m) { - $(this.hid).html(m) - } -} - -class MContent { - /* the actual Hebrew content, either plain text or tabbed data - */ - constructor() { - this.name_content = "#material_content" - this.select = () => {} - } - - add(response) { - $(`#material_${P.vs.tp()}`).html(response.children(this.name_content).html()) - } - - show() { - const { next_tp } = Config - - const this_tp = P.vs.tp() - for (const tp in next_tp) { - const this_material = $(`#material_${tp}`) - if (this_tp == tp) { - this_material.show() - } else { - this_material.hide() - } - } - } -} - -/* MATERIAL SETTINGS (for choosing between plain text and tabbed data) - * - */ - -class MSettings { - constructor(content) { - const { featurehost } = Config - - const { next_tp, next_tr, tab_info, tab_views, tr_info, tr_labels } = Config - - const hltext = $("#mtxt_p") - const hltabbed = $("#mtxt_tb1") - this.legend = $("#datalegend") - this.legendc = $("#datalegend_control") - this.name = "material_settings" - this.hid = `#${this.name}` - this.content = content - this.hebrewsettings = new HebrewSettings() - - hltext.show() - hltabbed.show() - - this.legendc.click(e => { - e.preventDefault() - $("#datalegend") - .find("a[fname]") - .each((i, el) => { - const elem = $(el) - const url = `${featurehost}/${elem.attr("fname")}` - elem.attr("href", url) - }) - this.legend.dialog({ - autoOpen: true, - dialogClass: "legend", - closeOnEscape: true, - modal: false, - title: "legend", - width: "600px", - }) - }) - - $(".mhradio").click(e => { - e.preventDefault() - const elem = $(e.target) - const old_tp = P.vs.tp() - let new_tp = elem.attr("id").substring(1) - if (old_tp == "txt_p") { - if (old_tp == new_tp) { - return - } - } else if (old_tp == new_tp) { - new_tp = next_tp[old_tp] - if (new_tp == "txt_p") { - new_tp = next_tp[new_tp] - } - } - P.vs.mstatesv({ tp: new_tp }) - P.vs.addHist() - this.apply() - }) - - $(".mtradio").click(e => { - e.preventDefault() - const elem = $(e.target) - const old_tr = P.vs.tr() - let new_tr = elem.attr("id").substring(1) - if (old_tr == new_tr) { - new_tr = next_tr[old_tr] - } - - P.vs.mstatesv({ tr: new_tr }) - P.vs.addHist() - this.apply() - }) - - for (let i = 1; i <= tab_views; i++) { - const mc = $(`#mtxt_tb${i}`) - mc.attr("title", tab_info[`txt_tb${i}`]) - if (i == 1) { - mc.show() - } else { - mc.hide() - } - } - - for (const l in tr_labels) { - const t = tr_info[l] - const mc = $(`#m${t}`) - mc.attr("title", tr_labels[t]) - if (l == "hb") { - mc.show() - } else { - mc.hide() - } - } - } - - apply() { - const { tab_views } = Config - - const hlradio = $(".mhradio") - const plradio = $(".mtradio") - const new_tp = P.vs.tp() - const new_tr = P.vs.tr() - const newc = $(`#m${new_tp}`) - const newp = $(`#m${new_tr}`) - hlradio.removeClass("ison") - plradio.removeClass("ison") - if (new_tp != "txt_p" && new_tp != "txt_il") { - for (let i = 1; i <= tab_views; i++) { - const mc = $(`#mtxt_tb${i}`) - if (`txt_tb${i}` == new_tp) { - mc.show() - } else { - mc.hide() - } - } - } - newc.show() - newp.show() - newc.addClass("ison") - newp.addClass("ison") - this.content.show() - this.legend.hide() - close_dialog(this.legend) - this.legendc.hide() - P.material.adapt() - } -} - -/* HEBREW DATA (which fields to show if interlinear text is displayed) - * - */ - -class HebrewSettings { - constructor() { - for (const fld in P.vs.ddata()) { - this[fld] = new HebrewSetting(fld) - } - } - - apply() { - const { versions } = Config - - for (const fld in P.vs.ddata()) { - this[fld].apply() - } - for (const v in versions) { - set_csv(v, P.vs.mr(), P.vs.qw(), P.vs.iid()) - } - } -} - -class HebrewSetting { - constructor(fld) { - const { versions } = Config - - this.name = fld - this.hid = `#${this.name}` - $(this.hid).click(e => { - const elem = $(e.target) - const vals = {} - vals[fld] = elem.prop("checked") ? "v" : "x" - P.vs.dstatesv(vals) - P.vs.addHist() - this.applysetting() - for (const v in versions) { - set_csv(v, P.vs.mr(), P.vs.qw(), P.vs.iid()) - } - }) - } - - apply() { - const val = P.vs.ddata()[this.name] - $(this.hid).prop("checked", val == "v") - this.applysetting() - } - - applysetting() { - if (P.vs.ddata()[this.name] == "v") { - $(`.${this.name}`).each((i, el) => { - const elem = $(el) - elem.show() - }) - } else { - $(`.${this.name}`).each((i, el) => { - const elem = $(el) - elem.hide() - }) - } - } -} - -/* SIDEBARS - * - */ - -class Sidebars { - /* TOP LEVEL: all four kinds of sidebars - */ - constructor() { - this.sidebar = {} - for (const mr of ["m", "r"]) { - for (const qw of ["q", "w", "n"]) { - this.sidebar[`${mr}${qw}`] = new Sidebar(mr, qw) - } - } - side_fetched = {} - } - - apply() { - for (const mr of ["m", "r"]) { - for (const qw of ["q", "w", "n"]) { - this.sidebar[`${mr}${qw}`].apply() - } - } - } - - after_material_fetch() { - for (const qw of ["q", "w", "n"]) { - side_fetched[`m${qw}`] = false - } - } - - after_item_fetch() { - for (const qw of ["q", "w", "n"]) { - side_fetched[`r${qw}`] = false - } - } -} - -/* SPECIFIC sidebars, the [mr][qw] type is frozen into the object - * - */ - -class Sidebar { - /* the individual sidebar, parametrized with qr and mw - * to specify one of the four kinds - */ - constructor(mr, qw) { - const { versions } = Config - - this.mr = mr - this.qw = qw - this.name = `side_bar_${mr}${qw}` - this.hid = `#${this.name}` - this.hide = $(`#side_hide_${mr}${qw}`) - this.show = $(`#side_show_${mr}${qw}`) - this.content = new SContent(mr, qw) - - if (mr == "r") { - this.cselect = {} - for (const v in versions) { - if (versions[v]) { - this.add_version(v) - } - } - } - this.show.click(e => { - e.preventDefault() - P.vs.hstatesv(this.qw, { get: "v" }) - P.vs.addHist() - this.apply() - }) - - this.hide.click(e => { - e.preventDefault() - P.vs.hstatesv(this.qw, { get: "x" }) - P.vs.addHist() - this.apply() - }) - } - - add_version(v) { - const { qw } = this - this.cselect[v] = new CSelect(v, qw) - } - - apply() { - const { mr, qw, hide, show } = this - const thebar = $(this.hid) - const thelist = $(`#side_material_${mr}${qw}`) - const theset = $(`#side_settings_${mr}${qw}`) - if (this.mr != P.mr || (this.mr == "r" && this.qw != P.qw)) { - thebar.hide() - } else { - thebar.show() - theset.show() - if (this.mr == "m") { - if (P.vs.get(this.qw) == "x") { - thelist.hide() - theset.hide() - hide.hide() - show.show() - } else { - thelist.show() - theset.show() - hide.show() - show.hide() - } - } else { - hide.hide() - show.hide() - } - this.content.apply() - } - } -} - -/* SIDELIST MATERIAL - * - */ - -class SContent { - /* the contents of an individual sidebar - */ - constructor(mr, qw) { - this.mr = mr - this.qw = qw - this.other_mr = this.mr == "m" ? "r" : "m" - this.name = "side_material_" + mr + qw - this.hid = "#" + this.name - - if (mr == "r") { - if (qw != "n") { - P.picker1[qw] = new Colorpicker1(qw, null, true, false) - } - } - } - - msg(m) { - $(this.hid).html(m) - } - - set_vselect(v) { - const { versions } = Config - - if (versions[v]) { - $(`#version_s_${v}`).click(e => { - e.preventDefault() - P.vs.mstatesv({ version: v }) - P.go() - }) - } - } - - process() { - const { versions, words_url, notes_url } = Config - - const { mr, qw } = this - - P.sidebars.after_item_fetch() - this.sidelistitems() - if (this.mr == "m") { - P.listsettings[this.qw].apply() - } else { - for (const v in versions) { - if (versions[v]) { - P.sidebars.sidebar[`r${this.qw}`].cselect[v].init() - } - } - - const vr = P.version - const iid = P.vs.iid() - - $(".moredetail").click(e => { - e.preventDefault() - const elem = $(e.target) - toggle_detail(elem) - }) - $(".detail").hide() - $(`div[version="${vr}"]`).find(".detail").show() - - this.msgo = new Msg(`dbmsg_${qw}`) - - let ufname, ulname - - if (qw == "q") { - const { q } = State - this.info = q - $("#theqid").html(q.id) - ufname = escHT(q.ufname || "") - ulname = escHT(q.ulname || "") - const qname = escHT(q.name || "") - $("#itemtag").val(`${ufname} ${ulname}: ${qname}`) - this.msgov = new Msg("dbmsg_qv") - $("#is_pub_c").show() - $("#is_pub_ro").hide() - } else if (qw == "w") { - const { w } = State - this.info = w - if ("versions" in w) { - const wvr = w.versions[vr] - const wentryh = escHT(wvr.entry_heb) - const wentryid = escHT(wvr.entryid) - $("#itemtag").val(`${wentryh}: ${wentryid}`) - $("#gobackw").attr( - "href", - `${words_url}?lan=${wvr.lan}&` + - `letter=${wvr.entry_heb.charCodeAt(0)}&goto=${w.id}` - ) - } - } else if (qw == "n") { - const { n } = State - this.info = n - if ("versions" in n) { - ufname = escHT(n.ufname) - ulname = escHT(n.ulname) - const kw = escHT(n.kw) - $("#itemtag").val(`${ufname} ${ulname}: ${kw}`) - $("#gobackn").attr("href", `${notes_url}?goto=${n.id}`) - } - } - if ("versions" in this.info) { - for (const v in this.info.versions) { - const extra = qw == "w" ? "" : `${ufname}_${ulname}` - this.set_vselect(v) - set_csv(v, mr, qw, iid, extra) - } - } - if (qw) { - const { msgs } = State - for (const m of msgs) { - this.msgo.msg(m) - } - } - } - - let thistitle - if (this.mr == "m") { - thistitle = `[${P.vs.version()}] ${P.vs.book()} ${P.vs.chapter()}:${P.vs.verse()}` - } else { - thistitle = $("#itemtag").val() - $("#theitem").html(`${thistitle} `) - /* fill in the title of the query/word/note above the verse material - * and put it in the page title as well - */ - } - document.title = thistitle - - if (this.qw == "q") { - if (this.mr == "m") { - /* in the sidebar list of queries: - * the mql query body can be popped up as a dialog for viewing it - * in a larger canvas - */ - $(".fullc").click(e => { - e.preventDefault() - const elem = $(e.target) - const thisiid = elem.attr("iid") - const mqlq = $(`#area_${thisiid}`) - const dia = $(`#bigq_${thisiid}`).dialog({ - dialogClass: "mql_dialog", - closeOnEscape: true, - close: () => { - dia.dialog("destroy") - const mqlq = $(`#area_${thisiid}`) - mqlq.css("height", mql_small_height) - mqlq.css("width", mql_small_width) - }, - modal: false, - title: "mql query body", - position: { my: "left top", at: "left top", of: window }, - width: mql_big_width_dia, - height: window_height, - }) - mqlq.css("height", standard_height) - mqlq.css("width", mql_big_width) - }) - } else { - /* in the sidebar item view of a single query: - * the mql query body can be popped up as a dialog for viewing it - * in a larger canvas - */ - const { q } = State - const vr = P.version - const fullc = $(".fullc") - const editq = $("#editq") - const execq = $("#execq") - const saveq = $("#saveq") - const cancq = $("#cancq") - const doneq = $("#doneq") - const nameq = $("#nameq") - const descm = $("#descm") - const descq = $("#descq") - const mqlq = $("#mqlq") - const pube = $("#is_pub_c") - const pubr = $("#is_pub_ro") - const is_pub = - "versions" in q && vr in q.versions && q.versions[vr].is_published - - fullc.click(e => { - e.preventDefault() - fullc.hide() - const dia = $("#bigger") - .closest("div") - .dialog({ - dialogClass: "mql_dialog", - closeOnEscape: true, - close: () => { - dia.dialog("destroy") - mqlq.css("height", mql_small_height) - descm.removeClass("desc_dia") - descm.addClass("desc") - descm.css("height", mql_small_height) - fullc.show() - }, - modal: false, - title: "description and mql query body", - position: { my: "left top", at: "left top", of: window }, - width: mql_big_width_dia, - height: window_height, - }) - mqlq.css("height", half_standard_height) - descm.removeClass("desc") - descm.addClass("desc_dia") - descm.css("height", half_standard_height) - }) - - $("#is_pub_c").click(e => { - const elem = $(e.target) - const val = elem.prop("checked") - this.sendval( - q.versions[vr], - elem, - val, - vr, - elem.attr("qid"), - "is_published", - val ? "T" : "" - ) - }) - - $("#is_shared_c").click(e => { - const elem = $(e.target) - const val = elem.prop("checked") - this.sendval(q, elem, val, vr, elem.attr("qid"), "is_shared", val ? "T" : "") - }) - - nameq.hide() - descq.hide() - descm.show() - editq.show() - if (is_pub) { - execq.hide() - } else { - execq.show() - } - saveq.hide() - cancq.hide() - doneq.hide() - pube.show() - pubr.hide() - - editq.click(e => { - e.preventDefault() - const is_pub = q.versions[vr].is_published - this.saved_name = nameq.val() - this.saved_desc = descq.val() - this.saved_mql = mqlq.val() - set_edit_width() - if (!is_pub) { - nameq.show() - } - descq.show() - descm.hide() - editq.hide() - saveq.show() - cancq.show() - doneq.show() - pubr.show() - pube.hide() - mqlq.prop("readonly", is_pub) - mqlq.css("height", "20em") - }) - - cancq.click(e => { - e.preventDefault() - nameq.val(this.saved_name) - descq.val(this.saved_desc) - mqlq.val(this.saved_mql) - reset_main_width() - nameq.hide() - descq.hide() - descm.show() - editq.show() - saveq.hide() - cancq.hide() - doneq.hide() - pube.show() - pubr.hide() - mqlq.prop("readonly", true) - mqlq.css("height", "10em") - }) - - doneq.click(e => { - e.preventDefault() - reset_main_width() - nameq.hide() - descq.hide() - descm.show() - editq.show() - saveq.hide() - cancq.hide() - doneq.hide() - pube.show() - pubr.hide() - mqlq.prop("readonly", true) - mqlq.css("height", "10em") - const data = { - version: P.version, - qid: $("#qid").val(), - name: $("#nameq").val(), - description: $("#descq").val(), - mql: $("#mqlq").val(), - execute: false, - } - this.sendvals(data) - }) - - saveq.click(e => { - e.preventDefault() - const data = { - version: P.version, - qid: $("#qid").val(), - name: $("#nameq").val(), - description: $("#descq").val(), - mql: $("#mqlq").val(), - execute: false, - } - this.sendvals(data) - }) - execq.click(e => { - e.preventDefault() - execq.addClass("fa-spin") - const msg = this.msgov - msg.clear() - msg.msg(["special", "executing query ..."]) - const data = { - version: P.version, - qid: $("#qid").val(), - name: $("#nameq").val(), - description: $("#descq").val(), - mql: $("#mqlq").val(), - execute: true, - } - this.sendvals(data) - }) - } - } - } - - setstatus(vr, cls) { - const statq = cls != null ? cls : $(`#statq${vr}`).attr("class") - const statm = - statq == "good" - ? "results up to date" - : statq == "error" - ? "results outdated" - : "never executed" - $("#statm").html(statm) - } - - sendval(q, checkbx, newval, vr, iid, fname, val) { - const { field_url } = Config - - const senddata = {} - senddata.version = vr - senddata.qid = iid - senddata.fname = fname - senddata.val = val - - $.post( - field_url, - senddata, - data => { - const { good, mod_dates, mod_cls, extra, msgs } = data - - if (good) { - for (const mod_date_fld in mod_dates) { - $(`#${mod_date_fld}`).html(mod_dates[mod_date_fld]) - } - for (const mod_cl in mod_cls) { - const cl = mod_cls[mod_cl] - const dest = $(mod_cl) - dest.removeClass("fa-check fa-close published") - dest.addClass(cl) - } - q[fname] = newval - } else { - checkbx.prop("checked", !newval) - } - - for (const fld in extra) { - const instr = extra[fld] - const prop = instr[0] - const val = instr[1] - - if (prop == "check") { - const dest = $(`#${fld}_c`) - dest.prop("checked", val) - } else if (prop == "show") { - const dest = $(`#${fld}`) - if (val) { - dest.show() - } else { - dest.hide() - } - } - } - const msg = fname == "is_shared" ? this.msgo : this.msgov - msg.clear() - for (const m of msgs) { - msg.msg(m) - } - }, - "json" - ) - } - - sendvals(senddata) { - const { fields_url } = Config - - const { execute, version: vr } = senddata - - $.post( - fields_url, - senddata, - data => { - const { good, q, msgs } = data - - const msg = this.msgov - msg.clear() - - for (const m of msgs) { - msg.msg(m) - } - - if (good) { - const { oldeversions } = data - const qx = q.versions[vr] - $("#nameqm").html(escHT(q.name || "")) - $("#nameq").val(q.name) - const d_md = special_links(q.description_md) - const descm = $("#descm") - descm.html(d_md) - decorate_crossrefs(descm) - $("#descq").val(q.description) - $("#mqlq").val(qx.mql) - const ev = $("#eversion") - const evtd = ev.closest("td") - ev.html(qx.eversion) - if (qx.eversion in oldeversions) { - evtd.addClass("oldexeversion") - evtd.attr("title", "this is not the newest version") - } else { - evtd.removeClass("oldexeversion") - evtd.attr("title", "this is the newest version") - } - $("#executed_on").html(qx.executed_on) - $("#xmodified_on").html(qx.xmodified_on) - $("#qresults").html(qx.results) - $("#qresultmonads").html(qx.resultmonads) - $("#statq").removeClass("error warning good").addClass(qx.status) - this.setstatus("", qx.status) - P.sidebars.sidebar["rq"].content.info = q - } - if (execute) { - reset_material_status() - P.material.adapt() - const show_chart = close_dialog($(`#select_contents_chart_${vr}_q_${q.id}`)) - if (show_chart) { - P.sidebars.sidebar["rq"].cselect[vr].apply() - } - $("#execq").removeClass("fa-spin") - } - }, - "json" - ) - } - - apply() { - if (P.mr == this.mr && (this.mr == "r" || P.vs.get(this.qw) == "v")) { - this.fetch() - } - } - - fetch() { - const { style, side_url } = Config - const { version, iid } = P - - const { mr, qw } = this - const thelist = $("#side_material_" + mr + qw) - - let vars = `?version=${version}&mr={mr}&qw=${qw}` - - let do_fetch = false - let extra = "" - - if (mr == "m") { - vars += `&book=${P.vs.book()}&chapter=${P.vs.chapter()}` - if (qw == "q" || qw == "n") { - vars += `&${qw}pub=${P.vs.pub(qw)}` - } - do_fetch = P.vs.book() != "x" && P.vs.chapter() > 0 - extra = "m" - } else { - vars += `&iid=${iid}` - do_fetch = P.qw == "q" ? iid >= 0 : iid != "-1" - extra = `${qw}m` - } - if (do_fetch && !side_fetched[`${mr}${qw}`]) { - const tag = `tag${mr == "m" ? "s" : ""}` - this.msg(`fetching ${style[qw][tag]} ...`) - if (mr == "m") { - thelist.load( - `${side_url}${extra}${vars}`, - () => { - side_fetched[`${mr}${qw}`] = true - this.process() - }, - "html" - ) - } else { - $.get( - `${side_url}${extra}${vars}`, - html => { - thelist.html(html) - side_fetched[`${mr}${qw}`] = true - this.process() - }, - "html" - ) - } - } - } - - sidelistitems() { - /* the list of items in an m-sidebar - */ - const { mr, qw } = this - - if (mr == "m") { - if (qw != "n") { - P.picker1list[qw] = {} - } - const qwlist = $(`#side_list_${qw} li`) - qwlist.each((i, el) => { - const elem = $(el) - const iid = elem.attr("iid") - this.sidelistitem(iid) - if (qw != "n") { - P.picker1list[qw][iid] = new Colorpicker1(qw, iid, false, false) - } - }) - } - } - - sidelistitem(iid) { - /* individual item in an m-sidebar - */ - const { qw } = this - - const itop = $(`#${qw}${iid}`) - const more = $(`#m_${qw}${iid}`) - const desc = $(`#d_${qw}${iid}`) - const item = $(`#item_${qw}${iid}`) - const all = $(`#${qw}${iid}`) - - desc.hide() - - more.click(e => { - e.preventDefault() - const elem = $(e.target) - toggle_detail(elem, desc, qw == "q" ? put_markdown : undefined) - }) - - item.click(e => { - e.preventDefault() - const elem = $(e.target) - const { qw } = this - P.vs.mstatesv({ mr: this.other_mr, qw, iid: elem.attr("iid"), page: 1 }) - P.vs.addHist() - P.go() - }) - - if (qw == "w") { - if (!P.vs.iscolor(qw, iid)) { - all.hide() - } - } else if (qw == "q") { - if (muting_q.isSet(`q${iid}`)) { - itop.hide() - } else { - itop.show() - } - } else if (qw == "n") { - if (muting_n.isSet(`n${iid}`)) { - itop.hide() - } else { - itop.show() - } - } - } -} - -/* SIDELIST VIEW SETTINGS - * - */ - -class ListSettings { - /* the view controls belonging to a side bar with a list of items - */ - constructor(qw) { - this.qw = qw - - if (qw != "n") { - this.picker2 = new Colorpicker2(this.qw, false) - const hlradio = $(`.${qw}hradio`) - hlradio.click(e => { - e.preventDefault() - const elem = $(e.target) - P.vs.hstatesv(this.qw, { active: elem.attr("id").substring(1) }) - P.vs.addHist() - P.highlight2({ code: "3", qw: this.qw }) - }) - } - if (qw == "q" || qw == "n") { - const pradio = $(`.${qw}pradio`) - pradio.click(e => { - e.preventDefault() - P.vs.hstatesv(this.qw, { pub: P.vs.pub(this.qw) == "x" ? "v" : "x" }) - side_fetched[`m${this.qw}`] = false - P.sidebars.sidebar[`m${this.qw}`].content.apply() - }) - } - } - - apply() { - const { qw } = this - if (P.vs.get(qw) == "v") { - if (qw != "n") { - for (const iid in P.picker1list[qw]) { - P.picker1list[qw][iid].apply(false) - } - P.picker2[qw].apply(true) - } - } - if (qw == "q" || qw == "n") { - const pradio = $(`.${qw}pradio`) - if (P.vs.pub(qw) == "v") { - pradio.addClass("ison") - } else { - pradio.removeClass("ison") - } - } - } -} - -const set_csv = (vr, mr, qw, iid, extraGiven) => { - const { tp_labels } = Config - - if (mr == "r") { - const tasks = { t: "txt_p", d: "txt_il" } - if (qw != "n") { - tasks["b"] = P.vs.tp() - } - - for (const task in tasks) { - const tp = tasks[task] - const csvctrl = $(`#csv${task}_lnk_${vr}_${qw}`) - if (task != "b" || (tp != "txt_p" && tp != "txt_il")) { - const ctit = csvctrl.attr("ftitle") - let extra - if (extraGiven == undefined) { - extra = csvctrl.attr("extra") - } else { - csvctrl.attr("extra", extraGiven) - extra = extraGiven - } - csvctrl.attr("href", P.vs.csv_url(vr, mr, qw, iid, tp, extra)) - csvctrl.attr( - "title", - `${vr}_$[style[qw]["t"]}_${iid}_${extra}_${tp_labels[tp]}.csv${ctit}` - ) - csvctrl.show() - } else { - csvctrl.hide() - } - } - } -} - -class Colorpicker1 { - /* the colorpicker associated with individual items - * - * These pickers show up in lists of items (in mq and mw sidebars) and - * near individual items (in rq and rw sidebars). - * They also have a checkbox, stating whether the color counts as customized. - * Customized colors are held in a global colormap, - * which is saved in a cookie upon every picking action. - * - * All actions are processed by the highlight2 (!) method - * of the associated Settings object. - */ - constructor(qw, iid, is_item, do_highlight) { - const { style, vcolors } = Config - - const pointer = is_item ? "me" : iid - this.code = is_item ? "1a" : "1" - this.qw = qw - this.iid = iid - this.picker = $(`#picker_${qw}${pointer}`) - this.stl = style[qw]["prop"] - this.sel = $(`#sel_${qw}${pointer}`) - this.selw = $(`#sel_${qw}${pointer}>a`) - this.selc = $(`#selc_${qw}${pointer}`) - - this.sel.click(e => { - e.preventDefault() - this.picker.dialog({ - dialogClass: "picker_dialog", - closeOnEscape: true, - modal: true, - title: "choose a color", - position: { my: "right top", at: "left top", of: this.selc }, - width: "200px", - }) - }) - - this.selc.click(() => { - /* process a click on the selectbox of the picker - */ - const { qw, iid, picker } = this - const was_cust = P.vs.iscolor(qw, iid) - close_dialog(picker) - if (was_cust) { - P.vs.cstatex(qw, iid) - } else { - const vals = {} - vals[iid] = defcolor(qw == "q", iid) - P.vs.cstatesv(qw, vals) - const active = P.vs.active(qw) - if (active != "hlcustom" && active != "hlmany") { - P.vs.hstatesv(qw, { active: "hlcustom" }) - } - } - P.vs.addHist() - this.apply(true) - }) - - $(`.c${qw}.${qw}${pointer}>a`).click(e => { - /* process a click on a colored cell of the picker - */ - e.preventDefault() - const elem = $(e.target) - const { picker } = this - close_dialog(picker) - - const { qw, iid } = this - const vals = {} - vals[iid] = elem.html() - P.vs.cstatesv(qw, vals) - P.vs.hstatesv(qw, { active: "hlcustom" }) - P.vs.addHist() - this.apply(true) - }) - - this.picker.hide() - $(`.c${qw}.${qw}${pointer}>a`).each((i, el) => { - /* initialize the individual color cells in the picker - */ - const elem = $(el) - const { qw } = this - const target = qw == "q" ? elem.closest("td") : elem - target.css(this.stl, vcolors[elem.html()][qw]) - }) - this.apply(do_highlight) - } - - adapt(iid, do_highlight) { - this.iid = iid - this.apply(do_highlight) - } - - apply(do_highlight) { - const { vcolors } = Config - - const { qw, iid, stl, sel, selc, selw } = this - const color = P.vs.color(qw, iid) || defcolor(qw == "q", iid) - const target = qw == "q" ? sel : selw - if (color) { - target.css(stl, vcolors[color][qw]) - /* apply state to the selected cell - */ - } - selc.prop("checked", P.vs.iscolor(qw, iid)) - /* apply state to the checkbox - */ - if (do_highlight) { - P.highlight2(this) - } - } -} - -class Colorpicker2 { - /* the colorpicker associated with the view settings in a sidebar - * - * These pickers show up at the top of the individual sidebars, - * only on mq and mw sidebars. - * They are used to control the uniform color with which - * the results are to be painted. - * They can be configured for dealing with background or foreground painting. - * The paint actions depend on the mode of coloring - * that the user has selected in settings. - * So the paint logic is more involved. - * But there is no associated checkbox. - * The selected color is stored in the highlight settings, - * which are synchronized in a cookie. - * All actions are processed by the highlight2 method - * of the associated Settings object. - */ - constructor(qw, do_highlight) { - const { style, vcolors } = Config - - this.code = "2" - this.qw = qw - this.picker = $(`#picker_${qw}one`) - this.stl = style[this.qw]["prop"] - this.sel = $(`#sel_${qw}one`) - this.selw = $(`#sel_${qw}one>a`) - - this.sel.click(e => { - e.preventDefault() - this.picker.dialog({ - dialogClass: "picker_dialog", - closeOnEscape: true, - modal: true, - title: "choose a color", - position: { my: "right top", at: "left top", of: this.sel }, - width: "200px", - }) - }) - - $(`.c${qw}.${qw}one>a`).click(e => { - /* process a click on a colored cell of the picker - */ - e.preventDefault() - const elem = $(e.target) - const { picker } = this - close_dialog(picker) - const { qw } = this - const current_active = P.vs.active(qw) - if (current_active != "hlone" && current_active != "hlcustom") { - P.vs.hstatesv(qw, { active: "hlcustom", sel_one: elem.html() }) - } else { - P.vs.hstatesv(qw, { sel_one: elem.html() }) - } - P.vs.addHist() - this.apply(true) - }) - - this.picker.hide() - - $(`.c${qw}.${qw}one>a`).each((i, el) => { - /* initialize the individual color cells in the picker - */ - const elem = $(el) - const { qw, stl } = this - const target = qw == "q" ? elem.closest("td") : elem - target.css(stl, vcolors[elem.html()][qw]) - }) - this.apply(do_highlight) - } - - apply(do_highlight) { - const { vcolors } = Config - - const { qw, stl, sel, selw } = this - const color = P.vs.sel_one(qw) || defcolor(qw, null) - const target = qw == "q" ? sel : selw - target.css(stl, vcolors[color][qw]) - /* apply state to the selected cell - */ - if (do_highlight) { - P.highlight2(this) - } - } -} - -const defcolor = (qw, iid) => { - /* compute the default color - * - * The data for the computation comes from the server - * and is stored in the javascript global variable Config - * vdefaultcolors, dncols, dnrows - */ - const { style, vdefaultcolors, dncols, dnrows } = Config - - let result - if (qw in style) { - result = style[qw]["default"] - } else if (qw) { - const mod = iid % vdefaultcolors.length - result = vdefaultcolors[dncols * (mod % dnrows) + Math.floor(mod / dnrows)] - } else { - const iidstr = iid == null ? "" : iid - let sumiid = 0 - for (let i = 0; i < iidstr.length; i++) { - sumiid += iidstr.charCodeAt(i) - } - const mod = sumiid % vdefaultcolors.length - result = vdefaultcolors[dncols * (mod % dnrows) + Math.floor(mod / dnrows)] - } - return result -} - -/* VIEW STATE - * - */ - -class ViewState { - constructor(init, pref) { - this.data = init - this.pref = pref - this.from_push = false - - this.addHist() - } - - getvars() { - const { data } = this - let vars = "" - let sep = "?" - for (const group in data) { - const extra = group == "colormap" ? "c_" : "" - for (const qw in data[group]) { - for (const name in data[group][qw]) { - vars += `${sep}${extra}${qw}${name}=${data[group][qw][name]}` - sep = "&" - } - } - } - return vars - } - - csv_url(vr, mr, qw, iid, tp, extra) { - const { item_url } = Config - - let vars = `?version=${vr}&mr=${mr}&qw=${qw}&iid=${iid}&tp=${tp}&extra=${extra}` - const data = P.vs.ddata() - for (const name in data) { - vars += `&${name}=${data[name]}` - } - return `${item_url}${vars}` - } - - goback() { - const state = History.getState() - if (!this.from_push && state && state.data) { - this.apply(state) - } - } - - addHist() { - const { style, view_url } = Config - - let title - if (this.mr() == "m") { - title = `[${this.version()}] ${this.book()} ${this.chapter()}:${this.verse()}` - } else { - title = `${style[this.qw()]["Tag"]} ${this.iid()} p${this.page()}` - } - this.from_push = true - History.pushState(this.data, title, view_url) - this.from_push = false - } - - apply(state) { - if (state.data != undefined) { - this.data = state.data - } - P.go() - } - - delsv(group, qw, name) { - delete this.data[group][qw][name] - $.cookie(this.pref + group + qw, this.data[group][qw]) - } - - setsv(group, qw, values) { - for (const mb in values) { - this.data[group][qw][mb] = values[mb] - } - $.cookie(this.pref + group + qw, this.data[group][qw]) - } - - resetsv(group, qw) { - for (const mb in this.data[group][qw]) { - delete this.data[group][qw][mb] - } - $.cookie(this.pref + group + qw, this.data[group][qw]) - } - - mstatesv(values) { - this.setsv("material", "", values) - } - dstatesv(values) { - this.setsv("hebrewdata", "", values) - } - hstatesv(qw, values) { - this.setsv("highlights", qw, values) - } - cstatesv(qw, values) { - this.setsv("colormap", qw, values) - } - cstatex(qw, name) { - this.delsv("colormap", qw, name) - } - cstatexx(qw) { - this.resetsv("colormap", qw) - } - - mstate() { - return this.data["material"][""] - } - ddata() { - return this.data["hebrewdata"][""] - } - mr() { - return this.data["material"][""]["mr"] - } - qw() { - return this.data["material"][""]["qw"] - } - tp() { - return this.data["material"][""]["tp"] - } - tr() { - return this.data["material"][""]["tr"] - } - lang() { - return this.data["material"][""]["lang"] - } - iid() { - return this.data["material"][""]["iid"] - } - version() { - return this.data["material"][""]["version"] - } - book() { - return this.data["material"][""]["book"] - } - chapter() { - return this.data["material"][""]["chapter"] - } - verse() { - return this.data["material"][""]["verse"] - } - page() { - return this.data["material"][""]["page"] - } - get(qw) { - return this.data["highlights"][qw]["get"] - } - active(qw) { - return this.data["highlights"][qw]["active"] - } - sel_one(qw) { - return this.data["highlights"][qw]["sel_one"] - } - pub(qw) { - return this.data["highlights"][qw]["pub"] - } - colormap(qw) { - return this.data["colormap"][qw] - } - color(qw, id) { - return this.data["colormap"][qw][id] - } - iscolor(qw, cl) { - return cl in this.data["colormap"][qw] - } -} - -const close_dialog = dia => { - const was_open = Boolean( - dia && dia.length && dia.dialog("instance") && dia.dialog("isOpen") - ) - if (was_open) { - dia.dialog("close") - } - return was_open -} - -const reset_material_status = () => { - const { tab_views } = Config - - material_fetched = { txt_p: false } - material_kind = { txt_p: "" } - for (let i = 1; i <= tab_views; i++) { - material_fetched["txt_tb" + i] = false - material_kind["txt_tb" + i] = "" - } -} - -/* GENERIC */ - -const escHT = text => { - const chr = { - "&": "&", - "<": "<", - ">": ">", - } - return text.replace(/[&<>]/g, a => chr[a]) -} - -function toggle_detail(wdg, detail, extra) { - const thedetail = detail == undefined ? wdg.closest("div").find(".detail") : detail - thedetail.toggle() - if (extra != undefined) { - extra(wdg) - } - let thiscl, othercl - if (wdg.hasClass("fa-chevron-right")) { - thiscl = "fa-chevron-right" - othercl = "fa-chevron-down" - } else { - thiscl = "fa-chevron-down" - othercl = "fa-chevron-right" - } - wdg.removeClass(thiscl) - wdg.addClass(othercl) -} - -/* MARKDOWN and CROSSREFS */ - -function decorate_crossrefs(dest) { - const crossrefs = dest.find("a[b]") - crossrefs.click(e => { - e.preventDefault() - const elem = $(e.target) - const vals = {} - vals["book"] = elem.attr("b") - vals["chapter"] = elem.attr("c") - vals["verse"] = elem.attr("v") - vals["mr"] = "m" - P.vs.mstatesv(vals) - P.vs.addHist() - P.go() - }) - crossrefs.addClass("crossref") -} - -function special_links(d_mdGiven) { - const { featurehost, host } = Config - - let d_md = d_mdGiven - d_md = d_md.replace( - /]*href=['"]image[\n\t ]+([^)\n\t '"]+)['"][^>]*>(.*?)(<\/a>)/g, - '

$2
' - ) - d_md = d_md.replace( - /(
]*)href=['"]([^)\n\t '"]+)[\n\t ]+([^:)\n\t '"]+):([^)\n\t '"]+)['"]([^>]*)>(.*?)(<\/a>)/g, - '$1b="$2" c="$3" v="$4" href="#" class="fa fw" $5>$6$7' - ) - d_md = d_md.replace( - /(]*)href=['"]([^)\n\t '"]+)[\n\t ]+([^)\n\t '"]+)['"]([^>]*)>(.*?)(<\/a>)/g, - '$1b="$2" c="$3" v="1" href="#" class="fa fw" $4>$5$6' - ) - d_md = d_md.replace( - /(href=['"])shebanq:([^)\n\t '"]+)(['"])/g, - `$1${host}$2$3 class="fa fw fa-bookmark" ` - ) - d_md = d_md.replace( - /(href=['"])feature:([^)\n\t '"]+)(['"])/g, - `$1${featurehost}/$2$3 target="_blank" class="fa fw fa-file-text" ` - ) - return special_links_m(d_md) -} - -function special_links_m(ntxtGiven) { - const { featurehost, host } = Config - - let ntxt = ntxtGiven - ntxt = ntxt.replace( - /\[([^\]\n\t]+)\]\(image[\n\t ]+([^)\n\t '"]+)\)/g, - '

$1
' - ) - ntxt = ntxt.replace( - /\[([^\]\n\t]+)\]\(([^)\n\t '"]+)[\n\t ]+([^:)\n\t '"]+):([^)\n\t '"]+)\)/g, - '
$1' - ) - ntxt = ntxt.replace( - /\[([^\]\n\t]+)\]\(([^)\n\t '"]+)[\n\t ]+([^)\n\t '"]+)\)/g, - '$1' - ) - ntxt = ntxt.replace( - /\[([^\]\n\t]+)\]\(shebanq:([^)\n\t '"]+)\)/g, - `$1` - ) - ntxt = ntxt.replace( - /\[([^\]\n\t]+)\]\(feature:([^)\n\t '"]+)\)/g, - `$1` - ) - ntxt = ntxt.replace( - /\[([^\]\n\t]+)\]\(([^)\n\t '"]+)\)/g, - '$1' - ) - return ntxt -} - -const mEscape = ns => ns.replace(/_/g, "\\_") - -const markdownEscape = ntxt => ntxt.replace(/\[[^\]\n\t]+\]\([^)]*\)/g, mEscape) - -const put_markdown = wdg => { - const did = wdg.attr("did") - const src = $(`#dv_${did}`) - const mdw = $(`#dm_${did}`) - mdw.html(special_links(markdown.toHTML(src.val()))) -} - -class Msg { - constructor(destination, on_clear) { - this.destination = $(`#${destination}`) - this.trashc = $(`#trash_${destination}`) - this.on_clear = on_clear - - this.trashc.click(e => { - e.preventDefault() - this.clear() - }) - this.trashc.hide() - } - - clear() { - this.destination.html("") - if (this.on_clear != undefined) { - this.on_clear() - } - this.trashc.hide() - } - hide() { - this.destination.hide() - this.trashc.hide() - } - show() { - this.destination.show() - if (this.destination.html() != "") { - this.trashc.show() - } - } - msg(msgobj) { - const mtext = this.destination.html() - this.destination.html(`${mtext}

${msgobj[1]}

`) - this.trashc.show() - } -} diff --git a/static/js/queries.js b/static/js/queries.js deleted file mode 100644 index 3ad15954..00000000 --- a/static/js/queries.js +++ /dev/null @@ -1,941 +0,0 @@ -/* eslint-env jquery */ -/* eslint-disable camelcase */ - -const ns = $.initNamespaceStorage('muting_q') -const vs = $.initNamespaceStorage('qsview') -const muting = ns.localStorage -const qsview = vs.localStorage -let ftree, msgflt, msgopq, rdata -const subtractq = 80 -/* the canvas holding the material gets a height equal to - * the window height minus this amount - */ -const control_height = 100 -/* height for messages and controls - */ - -function Recent() { - var that = this - this.loaded = false - this.queries = [] - this.msgqr = new Msg('msg_qr') - this.refreshctl = $('#reload_recentq') - var target = $('#recentqi') - - this.fetch = function() { - this.msgqr.msg(['info', 'fetching recent queries ...']) - $.post(queriesr_url, {}, function(json) { - that.loaded = true - that.msgqr.clear() - json.msgs.forEach(function(m) { - that.msgqr.msg(m) - }) - if (json.good) { - that.queries = json.queries - that.process() - } - }) - } - this.process = function() { - this.gen_html() - this.dress_queriesr() - } - this.gen_html = function() { - var queries = this.queries - var html = '' - for (var n = 0; n < queries.length; n++) { - var query = queries[n] - var qid = query['id'] - var qtxt = query['text'] - var qtit = query['title'] - var qver = query['version'] - html += ''+qtxt+'
\n' - } - target.html(html) - } - this.dress_queriesr = function() { - $('#recentqi a[qid]').each(function() { - $(this).click(function(e) {e.preventDefault(); - ftree.filter.clear() - ftree.gotoquery($(this).attr('qid')) - }) - }) - } - this.msgqr.clear() - this.refreshctl.click(function(e) {e.preventDefault();that.fetch()}) - this.apply = function() { - this.fetch() - } - this.apply() -} - -function View() { - var that = this - this.prevstate = false - if (!qsview.isSet('simple')) { - qsview.set('simple', true) - } - this.qvradio = $('.qvradio') - this.csimple = $('#c_view_simple') - this.cadvanced = $('#c_view_advanced') - - this.adjust_view = function() { - var simple = qsview.get('simple'); - this.qvradio.removeClass('ison'); - (simple?this.csimple:this.cadvanced).addClass('ison') - if (this.prevstate != simple) { - if (simple) { - $('.brq').hide() - } - else { - $('.brq').show() - } - this.prevstate = simple - } - } - - this.csimple.click(function(e) {e.preventDefault(); - qsview.set('simple', true) - that.adjust_view() - }) - this.cadvanced.click(function(e) {e.preventDefault(); - qsview.set('simple', false) - that.adjust_view() - }) - this.adjust_view() -} - -function Level() { - var that = this - var levels = {o: 1, p: 2, u: 3, q: 4} - this.expand_all = function() { - ftree.ftw.visit(function(n) { - n.setExpanded(true, {noAnimation: true, noEvents: false}) - }, true) - } - - this.expand_level = function(level) { - $('.qlradio').removeClass('ison') - $('#level_'+level).addClass('ison') - qsview.set('level', level) - if (level in levels) { - var numlevel = levels[level] - ftree.ftw.visit(function(n) { - var nlevel = n.getLevel() - n.setExpanded(nlevel <= numlevel , {noAnimation: true, noEvents: false}) - }, true) - } - } - - this.initlevel = function() { - this.expand_level(qsview.get('level')) - } - $('.qlradio').removeClass('ison') - if (!qsview.isSet('level')) {qsview.set('level', 'o')} - $('#level_o').click(function(e) {e.preventDefault();that.expand_level('o')}) - $('#level_p').click(function(e) {e.preventDefault();that.expand_level('p')}) - $('#level_u').click(function(e) {e.preventDefault();that.expand_level('u')}) - $('#level_q').click(function(e) {e.preventDefault();that.expand_level('q')}) - $('#level_').click(function(e) {e.preventDefault();that.expand_level('')}) -} - -function Filter() { - var that = this - this.patc = $('#filter_contents') - - var re_is_my = new RegExp('class="[^"]*qmy', ""); - var re_is_private = new RegExp('class="[^"]*qpriv', ""); - - function is_my(node) {return re_is_my.test(node.title)} - function is_private(node) {return re_is_private.test(node.title)} - - function in_my(pat) { - var lpat = pat.toLowerCase() - return function(node) { - return re_is_my.test(node.title) && (pat == '' || node.title.toLowerCase().indexOf(lpat) >= 0) - } - } - function in_private(pat) { - var lpat = pat.toLowerCase() - return function(node) {return re_is_private.test(node.title) && (pat == '' || node.title.toLowerCase().indexOf(lpat) >= 0)} - } - - this.clear = function() { - ftree.ftw.clearFilter() - msgflt.clear() - msgflt.msg(['good', 'no filter applied']) - $('.qfradio').removeClass('ison') - qsview.remove('filter_mode') - $('#filter_clear').hide() - $('#amatches').html('') - $('#cmatches').html('') - $('#qmatches').html('') - $('#mmatches').html('') - $('#rmatches').html('') - $('#count_o').html(rdata.o) - $('#count_p').html(rdata.p) - $('#count_u').html(rdata.u) - $('#count_q').html(rdata.q) - } - this.pqsearch = function(kind) { - var pat = this.patc.val() - qsview.set('filter_pat', pat); - var amatches = 0 - var cmatches = 0 - var qmatches = 0 - var mmatches = 0 - var rmatches = 0 - if (pat == '') { - amatches = -1 - cmatches = -1 - qmatches = -1 - mmatches = -1 - rmatches = -1 - } - $('.qfradio').removeClass('ison') - if (kind == 'm') { - msgflt.clear() - msgflt.msg(['warning', 'my queries']) - } - else if (kind == 'r') { - msgflt.clear() - msgflt.msg(['warning', 'private queries']) - } - else if (pat == '') { - this.clear() - return - } - else { - msgflt.clear() - msgflt.msg(['special', 'filter applied']) - } - $('#filter_control_'+kind).addClass('ison') - qsview.set('filter_mode', kind) - - ftree.level.expand_all() - if (kind == 'a') { - amatches = ftree.ftw.filterNodes(pat, false) - $('#amatches').html(amatches>=0?('('+amatches+')'):'') - } - else if (kind == 'c') { - cmatches = ftree.ftw.filterBranches(pat) - $('#cmatches').html(cmatches>=0?('('+cmatches+')'):'') - } - else if (kind == 'q') { - qmatches = ftree.ftw.filterNodes(pat, true) - $('#qmatches').html(qmatches>=0?('('+qmatches+')'):'') - } - else if (kind == 'm') { - mmatches = ftree.ftw.filterNodes(in_my(pat), true) - $('#mmatches').html(mmatches>=0?('('+mmatches+')'):'') - } - else if (kind == 'r') { - rmatches = ftree.ftw.filterNodes(in_private(pat), true) - $('#rmatches').html((rmatches >= 0)?('('+rmatches+')'):'') - } - $('#filter_clear').show() - var submatch = 'span.fancytree-submatch' - var match = 'span.fancytree-match' - var base_o = '#queries>ul>li>ul>li>' - var match_o = $(base_o+match).length - var submatch_o = $(base_o+submatch).length - var base_p = base_o+'ul>li>' - var match_p = $(base_p+match).length - var submatch_p = $(base_p+submatch).length - var base_u = base_p+'ul>li>' - var match_u = $(base_u+match).length - var submatch_u = $(base_u+submatch).length - var base_q = base_u+'ul>li>' - var match_q = $(base_q+match).length - $('#count_o').html(''+match_o+' '+submatch_o+'') - $('#count_p').html(''+match_p+' '+submatch_p+'') - $('#count_u').html(''+match_u+' '+submatch_u+'') - $('#count_q').html(''+match_q+'') - if (ftree.view.simple) { - $('.brq').hide() - } - } - - $('#filter_clear').hide() - $('#filter_contents').val(qsview.isSet('filter_pat')?qsview.get('filter_pat'):'') - if (qsview.isSet('filter_mode')) { - this.pqsearch(qsview.get('filter_mode')) - $('#filter_clear').show() - } - - - $('#filter_control_a').click(function(e) {e.preventDefault();that.pqsearch('a')}) - $('#filter_control_c').click(function(e) {e.preventDefault();that.pqsearch('c')}) - $('#filter_control_q').click(function(e) {e.preventDefault();that.pqsearch('q')}) - $('#filter_control_m').click(function(e) {e.preventDefault();that.pqsearch('m')}) - $('#filter_control_r').click(function(e) {e.preventDefault();that.pqsearch('r')}) - - $('#filter_clear').click(function(e) {e.preventDefault();that.clear()}) -} - -function Msg(destination) { - var that = this - this.destination = $('#'+destination) - this.trashc = $('#trash_'+destination) - this.clear = function() { - this.destination.html('') - this.trashc.hide() - } - this.trashc.click(function(e) {e.preventDefault(); - that.clear() - }) - this.msg = function(msgobj) { - var mtext = this.destination.html() - this.destination.html(mtext+'

'+msgobj[1]+'

') - this.trashc.show() - } - this.trashc.hide() -} - -function Tree() { - var that = this - this.tps = {o: 'organization', p:'project', q:'query'} - this.do_new = {} - - this.store_select = function(node) { - if (!node.folder) { - var iid = node.key - if (node.selected) { - muting.set(iid, 1) - } - else { - if (muting.isSet(iid)) { - muting.remove(iid) - } - } - } - } - this.store_select_deep = function(node) { - this.store_select(node) - var children = node.children - if (children != null) { - for (n in children) { - this.store_select_deep(children[n]) - } - } - } - this.dress_queries = function() { - $('#queries a.md').addClass('fa fa-level-down') - $('#queries a[qid]').each(function() { - var vr = $(this).attr('v') - var extra = (vr == undefined)?'':'&version='+vr; - $(this).attr('href', q_url+'?iid='+$(this).attr('qid')+extra+'&page=1&mr=r&qw=q') - }) - $('#queries a.md').click(function(e) {e.preventDefault(); - var uname = $(this).closest('ul').closest('li').find('span[n]').html() - var tit = $(this).prev() - var lnk = tit.attr('href') - var qname = tit.html() - window.prompt('Press and then to copy link on clipboard', '['+uname+': '+qname+']('+lnk+')') - }) - } - this.record = function(tp, o, update, view) { - var lid = $('#id_'+tp).val() - if (!update && lid == '0' && tp != 'q') { - return - } - var senddata = { - tp: tp, - upd: update, - lid: lid, - name: $('#name_'+tp).val(), - } - if (tp == 'q') { - senddata['oid'] = $('#fo_q').attr('oid') - senddata['oname'] = $('#nameq_o').val() - senddata['owebsite'] = $('#websiteq_o').val() - senddata['pid'] = $('#fp_q').attr('pid') - senddata['pname'] = $('#nameq_p').val() - senddata['pwebsite'] = $('#websiteq_p').val() - senddata['do_new_o'] = this.do_new['o'] - senddata['do_new_p'] = this.do_new['p'] - } - else { - senddata['website'] = $('#website_'+tp).val() - } - var good = false - $.post(record_url, senddata, function(json) { - var rec = json.record - var orec = json.orecord - var prec = json.precord - good = json.good - ogood = json.ogood - pgood = json.pgood - msgopq.clear() - json.msgs.forEach(function(m) { - msgopq.msg(m) - }) - if (update && tp == 'q') { - if (good) { - that.selectid('o', rec.oid, null) - that.selectid('p', rec.pid, o) - } - else { - if (ogood) { - that.selectid('o', orec.id, null) - } - if (pgood) { - that.selectid('p', prec.id, o) - } - } - } - if (!update) { - var name = $('#name_'+tp) - name.prop('readonly', view) - name.val(rec.name) - if (tp == 'q') { - var oname = (rec.oname == undefined)?'':escHT(rec.oname); - var pname = (rec.pname == undefined)?'':escHT(rec.pname); - $('#fo_'+tp).attr('href', rec.owebsite) - $('#fo_'+tp).html(escHT(oname)) - $('#fp_'+tp).attr('href', rec.pwebsite) - $('#fp_'+tp).html(escHT(pname)) - $('#fo_'+tp).attr('oid', rec.oid) - $('#fp_'+tp).attr('pid', rec.pid) - } - else { - $('#website_'+tp).val(rec.website) - $('#f'+tp+'_v').attr('href', rec.owebsite) - $('#f'+tp+'_v').html(escHT(rec.name)) - } - } - else if (update && senddata.lid != '0') { - if (tp == 'q') { - if (good) { - var oname = (rec.oname == undefined)?'':escHT(rec.oname); - var pname = (rec.pname == undefined)?'':escHT(rec.pname); - that.hide_new_q(rec.id, 'o') - that.hide_new_q(rec.id, 'p') - $('#fo_'+tp).attr('href', rec.owebsite) - $('#fo_'+tp).html(escHT(oname)) - $('#fp_'+tp).attr('href', rec.pwebsite) - $('#fp_'+tp).html(escHT(pname)) - $('#fo_'+tp).attr('oid', rec.oid) - $('#fp_'+tp).attr('pid', rec.pid) - $('#title_q').html('Modify') - } - else { - if (ogood) { - var oname = (orec.name == undefined)?'':escHT(orec.name); - that.hide_new_q(orec.id, 'o') - $('#fo_'+tp).attr('href', orec.website) - $('#fo_'+tp).html(escHT(oname)) - $('#fo_'+tp).attr('oid', orec.id) - } - if (pgood) { - var pname = (prec.name == undefined)?'':escHT(prec.name); - that.hide_new_q(prec.id, 'p') - $('#fp_'+tp).attr('href', prec.website) - $('#fp_'+tp).html(escHT(pname)) - $('#fp_'+tp).attr('pid', prec.id) - } - } - } - else { - $('#website_'+tp).val(rec.website) - $('#f'+tp+'_v').attr('href', rec.owebsite) - $('#f'+tp+'_v').html(escHT(rec.name)) - } - var elem = (tp == 'q')?'a':'span' - var moditem = that.moditem.find(elem+'[n=1]') - if (moditem != undefined) { - moditem.html(escHT(rec.name)) - } - } - else if (update && senddata.lid == '0') { - if (good) { - $('#id_'+tp).val(rec.id) - } - if (tp == 'q') { - if (good) { - var oname = (rec.oname == undefined)?'':escHT(rec.oname); - var pname = (rec.pname == undefined)?'':escHT(rec.pname); - that.hide_new_q(rec.id, 'o') - that.hide_new_q(rec.id, 'p') - $('#fo_'+tp).attr('href', rec.owebsite) - $('#fo_'+tp).html(escHT(oname)) - $('#fp_'+tp).attr('href', rec.pwebsite) - $('#fp_'+tp).html(escHT(pname)) - $('#fo_'+tp).attr('oid', rec.oid) - $('#fp_'+tp).attr('pid', rec.pid) - $('#title_q').html('Modify') - } - else { - if (ogood) { - var oname = (orec.name == undefined)?'':escHT(orec.name); - that.hide_new_q(orec.id, 'o') - $('#fo_'+tp).attr('href', orec.website) - $('#fo_'+tp).html(escHT(oname)) - $('#fo_'+tp).attr('oid', orec.id) - } - if (pgood) { - var pname = (prec.name == undefined)?'':escHT(prec.name); - that.hide_new_q(prec.id, 'p') - $('#fp_'+tp).attr('href', prec.website) - $('#fp_'+tp).html(escHT(pname)) - $('#fp_'+tp).attr('pid', prec.id) - } - } - } - } - var orig = $('.treehl') - var origp = orig.closest('ul').closest('li').closest('ul').closest('li') - var origo = origp.closest('ul').closest('li') - var origoid = origo.find('a[lid]').attr('lid') - var origpid = origp.find('a[lid]').attr('lid') - if (update && good && (senddata.lid == '0' || origoid != rec.oid || origpid != rec.pid)) { - $('#reload_tree').show() - } - else { - $('#reload_tree').hide() - } - if (update && good && tp == 'q') { - $('#continue_q').attr('href', q_url+'?iid='+$('#id_q').val()+'&page=1&mr=r&qw=q') - $('#continue_q').show() - } - else { - $('#continue_q').hide() - } - }, 'json') - } - this.do_edit_controls_q = function() { - var ctlo = $('#new_ctrl_o') - var ctlp = $('#new_ctrl_p') - var ctlxo = $('#newx_ctrl_o') - var ctlxp = $('#newx_ctrl_p') - var detailo = $('.detail_o') - var detailp = $('.detail_p') - var existo = $('#fo_q') - var existp = $('#fp_q') - detailo.hide() - detailp.hide() - ctlxo.hide() - ctlxp.hide() - ctlo.click(function(e) {e.preventDefault(); - detailo.show() - ctlxo.show() - ctlo.hide() - existo.hide() - that.do_new['o'] = true - }) - ctlxo.click(function(e) {e.preventDefault(); - detailo.hide() - ctlxo.hide() - ctlo.show() - existo.show() - that.do_new['o'] = false - }) - ctlp.click(function(e) {e.preventDefault(); - detailp.show() - ctlxp.show() - ctlp.hide() - existp.hide() - that.do_new['p'] = true - that.select_clear('p', true) - }) - ctlxp.click(function(e) {e.preventDefault(); - detailp.hide() - ctlxp.hide() - ctlp.show() - existp.show() - }) - } - this.hide_new_q = function(lid, tp) { - $('#new_ctrl_'+tp).show() - $('#newx_ctrl_'+tp).hide() - $('.detail_'+tp).hide() - $('#f'+tp+'_q').show() - that.do_new[tp] = false - } - this.do_view_controls_q = function() { - var ctlo = $('#new_ctrl_o') - var ctlp = $('#new_ctrl_p') - var ctlxo = $('#newx_ctrl_o') - var ctlxp = $('#newx_ctrl_p') - var detailo = $('.detail_o') - var detailp = $('.detail_p') - var existo = $('#fo_q') - var existp = $('#fp_q') - detailo.hide() - detailp.hide() - ctlo.hide() - ctlxo.hide() - ctlp.hide() - ctlxp.hide() - } - this.do_create = function(tp, obj) { - msgopq.clear() - $('.form_l').hide() - $('.ctrl_l').hide() - $('#title_'+tp).html('New') - $('#name_'+tp).val('') - var o = null - if (tp == 'q') { - this.do_new['o'] = false - this.do_new['p'] = false - $('#fo_q').attr('oid', 0) - $('#fp_q').attr('pid', 0) - this.do_edit_controls_q() - } - else { - $('#website_'+tp).val('') - if (tp == 'p') { - o = obj.closest('li') - } - } - $('#id_'+tp).val(0) - this.record(tp, o, false, false) - $('#opqforms').show() - $('#opqctrl').show() - $('#form_'+tp).show() - $('#ctrl_'+tp).show() - $('.old').hide() - } - this.do_update = function(tp, obj, lid) { - var o = null - if (tp == 'q') { - this.do_new['o'] = false - this.do_new['p'] = false - o = obj.closest('ul').closest('li').closest('ul').closest('li').closest('ul').closest('li') - this.do_edit_controls_q() - } - else if (tp == 'p') { - o = obj.closest('ul').closest('li') - } - this.moditem = obj.closest('span') - msgopq.clear() - msgopq.msg(['info', 'loading ...']) - $('.form_l').hide() - $('.ctrl_l').hide() - $('#title_'+tp).html('Modify') - $('#id_'+tp).val(lid) - this.record(tp, o, false, false) - $('#opqforms').show() - $('#opqctrl').show() - $('#form_'+tp).show() - $('#ctrl_'+tp).show() - $('.old').show() - } - this.do_view = function(tp, obj, lid) { - var o = null - if (tp == 'q') { - o = obj.closest('ul').closest('li').closest('ul').closest('li').closest('ul').closest('li') - this.do_view_controls_q() - } - else if (tp == 'p') { - o = obj.closest('ul').closest('li') - } - var mtp = tp+'v' - msgopq.clear() - msgopq.msg(['info', 'loading ...']) - $('.form_l').hide() - $('.ctrl_l').hide() - $('#title_'+tp).html('View') - $('#id_'+tp).val(lid) - this.record(tp, o, false, true) - $('#opqforms').show() - $('#opqctrl').show() - $('#form_'+tp).show() - if (tp == 'o' || tp == 'p') { - $('#nameline_'+tp).hide() - $('#website_'+tp).hide() - $('#f'+tp+'_v').show() - } - this.select_hide() - } - this.op_selection = function(tp) { - if (tp == 'q') { - this.select_clear('o', true) - this.select_clear('p', true) - } - else { - this.select_hide() - } - } - this.select_hide = function() { - for (tp in {o: 1, p: 1}) { - this.select_clear(tp, false) - } - } - this.select_clear = function(tp, show) { - var objs = $('.selecthl'+tp) - var icons = $('.s_'+tp) - objs.removeClass('selecthl'+tp) - icons.removeClass('fa-check-circle') - icons.addClass('fa-circle-o') - if (show) { - icons.show() - } - else { - icons.hide() - } - } - this.selectid = function(tp, lid, pr) { - var jpr = '.s_'+tp+'[lid='+lid+']' - var icon = (pr == null)?$(jpr):pr.find(jpr) - var i = icon.closest('li') - var is = i.children('span') - this.selectone(tp, icon, is) - } - this.selectone = function(tp, icon, obj) { - var sclass= 'selecthl'+tp - var objs = $('.'+sclass) - var icons = (tp == 'o')?$('.s_'+tp):icon.closest('ul').find('.s_'+tp) - var iconsr = $('.s_'+tp) - objs.removeClass(sclass) - obj.addClass(sclass) - iconsr.removeClass('fa-check-circle') - iconsr.addClass('fa-circle-o') - icon.removeClass('fa-circle-o') - icon.addClass('fa-check-circle') - } - this.viewinit = function() { - $('#lmsg').show() - $('.form_l').hide() - $('.ctrl_l').hide() - $('.treehl').removeClass('treehl') - this.select_hide() - } - this.bothinit = function() { - var canvas_left = $('.left-sidebar') - var canvas_middle = $('.span6') - var canvas_right = $('.right-sidebar') - canvas_left.css('width', '23%') - canvas_middle.css('width','40%') - canvas_right.css('width', '30%') - var view = $('.v_o, .v_p, .v_q') - view.addClass('fa fa-info') - this.viewtp = function(tp) { - /*if (tp != 'q') { - return - }*/ - var objs = $('.v_'+tp); - objs.click(function(e) {e.preventDefault(); - $('.treehl').removeClass('treehl') - that.op_selection(tp) - $(this).closest('span').addClass('treehl') - var lid = $(this).attr('lid') - that.do_view(tp, $(this), lid) - return false - }) - } - this.select_init = function(tp) { - var objs = $('.s_'+tp); - objs.click(function(e) {e.preventDefault(); - if (tp == 'o') { - var o = $(this).closest('li') - var oid = o.find('a[lid]').attr('lid') - var oname = o.find('span[n=1]').html() - $('#fo_q').html(oname) - $('#fo_q').attr('oid', oid) - that.selectid('o', oid, null) - } - else if (tp == 'p') { - var o = $(this).closest('ul').closest('li') - var oid = o.find('a[lid]').attr('lid') - var oname = o.find('span[n=1]').html() - var p = $(this).closest('li') - var pid = p.find('a[lid]').attr('lid') - var pname = p.find('span[n=1]').html() - $('#fo_q').html(oname) - $('#fp_q').html(pname) - $('#fo_q').attr('oid', oid) - $('#fp_q').attr('pid', pid) - that.selectid('o', oid, null) - that.selectid('p', pid, o) - } - return false - }) - } - for (var t in this.tps) { - $('#form_'+t).hide() - $('#ctrl_'+t).hide() - this.viewtp(t) - if (t == 'q') { - this.select_init('o') - this.select_init('p') - } - } - } - this.editinit = function() { - $('.treehl').removeClass('treehl') - $('#lmsg').hide() - var select = $('.s_o,.s_p') - select.addClass('fa-circle-o') - select.hide() - var create = $('.n_q'); - create.addClass('fa fa-plus') - this.createtp = function(tp) { - var objs = $('.n_'+tp); - objs.click(function(e) {e.preventDefault(); - $('.treehl').removeClass('treehl') - that.op_selection(tp) - if (tp == 'q') { - $('#id_q').val(0) - } - $(this).closest('span').addClass('treehl') - that.do_create(tp, $(this)) - return false - }) - } - var update = $('.r_o, .r_p, .r_q') - update.addClass('fa fa-pencil') - this.updatetp = function(tp) { - var objs = $('.r_'+tp); - objs.click(function(e) {e.preventDefault(); - $('.treehl').removeClass('treehl') - that.op_selection(tp) - $(this).closest('span').addClass('treehl') - var lid = $(this).attr('lid') - if (tp == 'q') { - $('#id_q').val(lid) - } - that.do_update(tp, $(this), lid) - return false - }) - } - this.formtp = function(tp) { - $('#save_'+tp).click(function(e) {e.preventDefault(); - that.op_selection(tp) - that.record(tp, null, true, false) - }) - $('#cancel_'+tp).click(function(e) {e.preventDefault(); - $('.treehl').removeClass('treehl') - that.select_hide() - $('#form_'+tp).hide() - $('#ctrl_'+tp).hide() - }) - $('#done_'+tp).click(function(e) {e.preventDefault(); - that.op_selection(tp) - that.record(tp, null, true, false) - that.select_hide() - $('#form_'+tp).hide() - $('#ctrl_'+tp).hide() - }) - $('#reload_tree').click(function(e) {e.preventDefault(); - window.location.reload(true) - }) - } - for (var t in this.tps) { - $('#form_'+t).hide() - $('#ctrl_'+t).hide() - this.createtp(t) - this.updatetp(t) - this.formtp(t) - } - } - this.gotoquery = function(qid) { - if (qid != undefined && qid != '0') { - var qnode = this.ftw.getNodeByKey('q'+qid) - if (qnode != undefined) { - qnode.makeVisible({noAnimation: true}) - $('.treehl').removeClass('treehl') - $('a[qid='+qid+']').closest('span').addClass('treehl') - $(qnode.li)[0].scrollIntoView({ - behavior: "smooth", - }) - $('#queries').scrollTop -= 20 - /* - var editable = $(qnode.li).has('.r_q').length > 0 - if (editable) { - var qtitle = $('a[qid='+qid+']').nextAll('.r_q') - this.do_update('q', qtitle, qid) - } - else { - var qtitle = $('a[qid='+qid+']').nextAll('.v_q') - this.do_view('q', qtitle, qid) - } - */ - } - } - } - - $("#queries").fancytree({ - extensions: ["persist", "filter"], - checkbox: true, - selectMode: 3, - activeVisible: true, - toggleEffect: false, - clickFolderMode: 2, - focusOnSelect: false, - quicksearch: true, - icons: false, - idPrefix: 'q_', - persist: { - cookiePrefix: 'ft-q-', - store: 'local', - types: 'expanded selected', - }, - source: { - url: pq_url, - dataType: "json", - }, - filter: { - mode: 'hide', - }, - init: function(e, data) { - muting.removeAll() - that.ftw = $("#queries").fancytree('getTree') - var s = that.ftw.getSelectedNodes(true) - for (i in s) { - that.store_select_deep(s[i]) - } - that.ftw.render(true, true) - that.dress_queries() - rdata = that.ftw.rootNode.children[0].data - $('#count_o').html(rdata.o) - $('#count_p').html(rdata.p) - $('#count_u').html(rdata.u) - $('#count_q').html(rdata.q) - msgopq = new Msg('opqmsgs') - msgflt = new Msg('filter_msg') - that.view = new View() - that.level = new Level() - that.filter = new Filter() - that.level.initlevel() - if (rdata.uid) { - that.editinit() - } - else { - that.viewinit() - } - that.bothinit() - that.gotoquery($('#qid').val()) - $('#reload_tree').hide() - }, - expand: function(e, data) { - if (that.level != undefined) { - that.level.expand_level('') - } - }, - collapse: function(e, data) { - if (that.level != undefined) { - that.level.expand_level('') - } - }, - select: function(e, data) { - that.store_select_deep(data.node) - }, - }) - var standard_height = window.innerHeight - subtractq - var form_height = standard_height - control_height - var standard_width = window.innerWidth - var canvas_left = $('.left-sidebar') - var canvas_middle = $('.span6') - var canvas_right = $('.right-sidebar') - canvas_left.css('height', standard_height+'px') - $('#queries').css('height', standard_height+'px') - //$('#opqforms').css('height', form_height+'px') - $('#opqctrl').css('height', control_height+'px') - canvas_right.css('height', standard_height+'px') -} - -$(function(){ - recent = new Recent() - ftree = new Tree() -}) diff --git a/static/js/share.js b/static/js/share.js deleted file mode 100755 index 6093341b..00000000 --- a/static/js/share.js +++ /dev/null @@ -1,326 +0,0 @@ -/* eslint-env jquery */ -/* eslint-disable camelcase */ - -function deselectText() { - if (document.selection) { - document.selection.empty() - } else if (window.getSelection) { - window.getSelection().removeAllRanges() - } -} -function selectText(containerid) { - deselectText() - if (document.selection) { - var range = document.body.createTextRange() - range.moveToElementText(document.getElementById(containerid)) - range.select() - } else if (window.getSelection) { - var range = document.createRange() - range.selectNode(document.getElementById(containerid)) - window.getSelection().addRange(range) - } -} - -jQuery(function () { - const { query_url, word_url, note_url, page_view_url } = Config - - var script_source = jQuery('script[src*="share.js"]').attr("src") - var params = function (name, default_value) { - var match = RegExp("[?&]" + name + "=([^&]*)").exec(script_source) - return (match && decodeURIComponent(match[1].replace(/\+/g, " "))) || default_value - } - var path = params("static", "social") - var url = encodeURIComponent(window.location.href) - var host = window.location.hostname - var qmsg = { - good: - "The results of this query have been obtained after the query body has been last modified", - warning: "This query has never been executed in SHEBANQ", - error: - "The body of this query has been changed after its current results have been obtained.", - } - - var tbar = - '\ -
\ -

Cite

\ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
\ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -
query vquerywordnotepage view
cite query with its results on this data versionshare link to query pagecite word with its occs on this data versioncite note set with its membersshare link to this page with or without view settings, or as internal note link,\ - or copy page contents to paste in mail, Evernote, etc.
\ -

\ -

\ -
\ -' - // Add the share tool bar. - jQuery("body").append(tbar) - var st = jQuery("#socialdrawer") - st.css({ - opacity: ".7", - "z-index": "3000", - background: "#FFF", - border: "solid 1px #666", - "border-width": " 1px 0 0 1px", - height: "20px", - width: "40px", - position: "fixed", - bottom: "0", - right: "0", - padding: "2px 5px", - overflow: "hidden", - "-webkit-border-top-left-radius": " 12px", - "-moz-border-radius-topleft": " 12px", - "border-top-left-radius": " 12px", - "-moz-box-shadow": " -3px -3px 3px rgba(0,0,0,0.5)", - "-webkit-box-shadow": " -3px -3px 3px rgba(0,0,0,0.5)", - "box-shadow": " -3px -3px 3px rgba(0,0,0,0.5)", - }) - jQuery("#citeh").css({ - margin: "2px 3px", - "text-shadow": " 1px 1px 1px #FFF", - color: "#444", - "font-size": "12px", - "line-height": "1em", - }) - jQuery("#socialdrawer td,#socialdrawer th").css({ - width: "120px", - "text-align": "center", - "border-left": "2px solid #888888", - "border-right": "2px solid #888888", - }) - jQuery("#socialdrawer .detail").hide() - // hover - $( - "#clip_qx_md,#clip_qx_ht,#clip_q_md,#clip_q_ht,#clip_w_md,#clip_w_ht,#clip_n_md,#clip_n_ht,#clip_pv_md,#clip_pv_ht,#clip_pv_htc,#clip_pv_nl" - ).click(function (e) { - e.preventDefault() - window.prompt( - "Press and then to copy link on clipboard", - $(this).attr("lnk") - ) - }) - $("#clip_pv_cn").click(function (e) { - e.preventDefault() - var shebanq_url_raw = page_view_url + P.vs.getvars() + "&pref=alt" - var slink = $("#self_link") - slink.show() - slink.attr("href", shebanq_url_raw) - selectText("material") - }) - $("#xc_qx").click(function (e) { - e.preventDefault() - toggle_detail($(this), $("#x_qx")) - }) - $("#xc_q").click(function (e) { - e.preventDefault() - toggle_detail($(this), $("#x_q")) - }) - $("#xc_w").click(function (e) { - e.preventDefault() - toggle_detail($(this), $("#x_w")) - }) - $("#xc_n").click(function (e) { - e.preventDefault() - toggle_detail($(this), $("#x_n")) - }) - $("#xc_pv").click(function (e) { - e.preventDefault() - toggle_detail($(this), $("#x_pv")) - }) - st.click(function (e) { - e.preventDefault() - var shebanq_url_raw = page_view_url + P.vs.getvars() + "&pref=alt" - var shebanq_url_note - var shebanq_url_rawc - var shebanq_url_note_pref = "shebanq:" - var shebanq_url_show_vars = - "&version=" + - P.version + - "&mr=" + - P.mr + - "&qw=" + - P.qw + - "&tp=" + - P.vs.tp() + - "&tr=" + - P.vs.tr() - var shebanq_url_side_vars = - "&wget=" + P.vs.get("w") + "&qget=" + P.vs.get("q") + "&nget=" + P.vs.get("n") - var shebanq_url = encodeURIComponent(shebanq_url_raw) - var vr = P.version - var pvtitle - $("#citeh").hide() - $("#cdiagpub").html("") - $("#cdiagsts").html("") - $( - ".clip_qx.clr,.clip_q.clr,.clip_w.clr,.clip_n.clr,.clip_pv.clr,#cdiagpub,#cdiagsts" - ).removeClass("error warning good special") - if (P.mr == "m") { - //pvtitle = escape($('title').text()); - pvtitle = - "bhsa" + - vr + - " " + - $("#thebook").html() + - " " + - $("#thechapter").html() + - ":" + - P.vs.verse() - shebanq_url_note = - shebanq_url_note_pref + - "?book=" + - P.vs.book() + - "&chapter=" + - P.vs.chapter() + - "&verse=" + - P.vs.verse() + - shebanq_url_show_vars + - shebanq_url_side_vars - shebanq_url_rawc = - page_view_url + - "?book=" + - P.vs.book() + - "&chapter=" + - P.vs.chapter() + - "&verse=" + - P.vs.verse() + - shebanq_url_show_vars + - shebanq_url_side_vars - $(".clip_qx").hide() - $(".clip_q").hide() - $(".clip_w").hide() - $(".clip_n").hide() - } else if (P.mr == "r") { - shebanq_url_note = - shebanq_url_note_pref + - "?id=" + - P.iid + - "&page=" + - P.vs.page() + - shebanq_url_show_vars - shebanq_url_rawc = - page_view_url + "?id=" + P.iid + "&page=" + P.vs.page() + shebanq_url_show_vars - var iinfo = P.sidebars.sidebar["r" + P.qw].content.info - if (P.qw == "q") { - pvtitle = iinfo.ufname + " " + iinfo.ulname + ": " + iinfo.name - var is_shared = iinfo.is_shared - var is_pub = iinfo.versions[vr].is_published - var qstatus = iinfo.versions[vr].status - if (is_shared) { - if (!is_pub) { - $(".clip_qx.clr").addClass("warning") - $("#cdiagpub").addClass("warning") - $("#cdiagpub").html( - "Beware of citing this query. It has not been published. It may be changed later." - ) - } else { - $(".clip_qx.clr").addClass("special") - $("#cdiagpub").addClass("special") - $("#cdiagpub").html( - "This query has been published. If that happened more than a week ago, it can be safely cited. It will not be changed anymore." - ) - } - $(".clip_q.clr").addClass(qstatus) - $("#cdiagsts").addClass(qstatus) - $("#cdiagsts").html(qmsg[qstatus]) - } else { - $(".clip_qx.clr").addClass("error") - $(".clip_q.clr").addClass("error") - $(".clip_pv.clr").addClass("error") - $("#cdiagpub").addClass("error") - $("#cdiagpub").html( - "This query is not accessible to others because it is not shared." - ) - } - var quote_url = query_url + "?id=" + P.iid - var quotev_url = query_url + "?version=" + vr + "&id=" + P.iid - $("#clip_qx_md").attr("lnk", "[" + pvtitle + "](" + quotev_url + ")") - $("#clip_qx_ht").attr("lnk", quotev_url) - $("#clip_q_md").attr("lnk", "[" + pvtitle + "](" + quote_url + ")") - $("#clip_q_ht").attr("lnk", quote_url) - $(".clip_qx").show() - $(".clip_q").show() - $(".clip_w").hide() - $(".clip_n").hide() - } else if (P.qw == "w") { - var vinfo = iinfo.versions[vr] - pvtitle = vinfo.entryid_heb + " (" + vinfo.entryid + ")" - var quotev_url = word_url + "?version=" + vr + "&id=" + P.iid - $("#clip_w_md").attr("lnk", "[" + pvtitle + "](" + quotev_url + ")") - $("#clip_w_ht").attr("lnk", quotev_url) - $(".clip_w.clr").addClass("special") - $(".clip_qx").hide() - $(".clip_q").hide() - $(".clip_w").show() - $(".clip_n").hide() - } else if (P.qw == "n") { - var ufname = escHT(iinfo.ufname) - var ulname = escHT(iinfo.ulname) - var kw = escHT(iinfo.kw) - pvtitle = ufname + " " + ulname + " - " + kw - var quotev_url = - note_url + "?version=" + vr + "&id=" + P.iid + "&tp=txt_tb1&nget=v" - $("#clip_n_md").attr("lnk", "[" + pvtitle + "](" + quotev_url + ")") - $("#clip_n_ht").attr("lnk", quotev_url) - $(".clip_n.clr").addClass("special") - $(".clip_qx").hide() - $(".clip_q").hide() - $(".clip_w").hide() - $(".clip_n").show() - } - } - $("#clip_pv_md").attr("lnk", "[" + pvtitle + "](" + shebanq_url_raw + ")") - $("#clip_pv_ht").attr("lnk", shebanq_url_raw) - $("#clip_pv_htc").attr("lnk", shebanq_url_rawc) - $("#clip_pv_nl").attr("lnk", shebanq_url_note) - $("#clip_pv_cn").attr("lnk", shebanq_url_raw) - $("#clip_pv_cn").attr("tit", pvtitle) - jQuery(this).animate({ height: "260px", width: "570px", opacity: 0.95 }, 300) - }) - //leave - st.mouseleave(function () { - $("#self_link").hide() - deselectText() - $("#citeh").show() - st.animate({ height: "20px", width: "40px", opacity: 0.7 }, 300) - return false - }) -}) diff --git a/static/js/words.js b/static/js/words.js deleted file mode 100644 index 64797da5..00000000 --- a/static/js/words.js +++ /dev/null @@ -1,69 +0,0 @@ -/* eslint-env jquery */ -/* eslint-disable camelcase */ - -/* globals Config, set_heightw, versions, lan, letter */ - -let version - -class RequestInfo { - parameter(name) { - return this.parameters()[name] - } - parameters(uriGiven) { - const uri = uriGiven || window.location.search - if (uri.indexOf("?") === -1) { - return {} - } - const query = uri.slice(1) - const params = query.split("&") - const result = {} - let i = 0 - while (i < params.length) { - const parameter = params[i].split("=") - result[parameter[0]] = parameter[1] - i++ - } - return result - } -} - -const set_vselect = (v, gotoword) => { - const { words_url } = Config - - if (versions[v]) { - $(`#version_${v}`).click(e => { - e.preventDefault() - version = v - window.location.href = - `${words_url}?version=${v}&lan=${lan}&letter=${letter}&goto=${gotoword}` - }) - } -} - -/* exported words_init */ - -const words_init = () => { - $(".mvradio").removeClass("ison") - const gotoword = RequestInfo.parameter("goto") - for (const v in versions) { - set_vselect(v, gotoword) - } - $(`#version_${version}`).addClass("ison") - set_heightw() - $("[wii]").hide() - $("[gi]").click(e => { - e.preventDefault() - const elem = $(e.target) - const i = elem.attr("gi") - $(`[wi="${i}"]`).toggle() - $(`[wii="${i}"]`).toggle() - }) - $("[gi]").closest("td").removeClass("selecthlw") - const wtarget = $(`[gi=${gotoword}]`).closest("td") - if (wtarget != undefined) { - wtarget.addClass("selecthlw") - if (wtarget[0] != undefined) { - wtarget[0].scrollIntoView() - } - } -} diff --git a/views/hebrew/note.html b/views/hebrew/note.html index 18606e2c..f62507b2 100644 --- a/views/hebrew/note.html +++ b/views/hebrew/note.html @@ -6,5 +6,5 @@ {{ block left_sidebar }} {{ include 'hebrew/text_sidebar.html' }} {{ end }} - - + + diff --git a/views/hebrew/notes.html b/views/hebrew/notes.html index c18a844d..999609f3 100644 --- a/views/hebrew/notes.html +++ b/views/hebrew/notes.html @@ -31,7 +31,8 @@

Level

manual

- + + {{ end }} @@ -57,41 +58,4 @@

Bulk note upload through csv

{{pass}} - - {{ end }} diff --git a/views/hebrew/queries.html b/views/hebrew/queries.html index b2a02a41..3e2d3b22 100644 --- a/views/hebrew/queries.html +++ b/views/hebrew/queries.html @@ -53,12 +53,9 @@

Level

queries ()

manual

+ - - - + {{ end }} diff --git a/views/hebrew/query.html b/views/hebrew/query.html index 18606e2c..f62507b2 100644 --- a/views/hebrew/query.html +++ b/views/hebrew/query.html @@ -6,5 +6,5 @@ {{ block left_sidebar }} {{ include 'hebrew/text_sidebar.html' }} {{ end }} - - + + diff --git a/views/hebrew/sideq.load b/views/hebrew/sideq.load index 50936379..7626039d 100644 --- a/views/hebrew/sideq.load +++ b/views/hebrew/sideq.load @@ -142,6 +142,6 @@ var State = { const descm = $('#descm') d_md = special_links(descm.html()) descm.html(d_md) -decorate_crossrefs(descm) +P.decorate_crossrefs(descm) diff --git a/views/hebrew/text.html b/views/hebrew/text.html index 35f6471d..f62507b2 100644 --- a/views/hebrew/text.html +++ b/views/hebrew/text.html @@ -6,6 +6,5 @@ {{ block left_sidebar }} {{ include 'hebrew/text_sidebar.html' }} {{ end }} - - - + + diff --git a/views/hebrew/text_body.html b/views/hebrew/text_body.html index 08855f28..97930dfa 100644 --- a/views/hebrew/text_body.html +++ b/views/hebrew/text_body.html @@ -44,4 +44,4 @@
{{pass}} - + diff --git a/views/hebrew/versions.html b/views/hebrew/versions.html index da5d4f0c..99bc42ec 100644 --- a/views/hebrew/versions.html +++ b/views/hebrew/versions.html @@ -1,10 +1,10 @@ -
- bhsa - {{for v in version_order: - vinfo = versions[v] - vdate = vinfo['date'] or 'recent' - title = '{}: {} [{}]'.format(vdate, vinfo['desc'], vinfo['notes']) - }}{{if vinfo['present']:}}{{=v}}{{ - else:}}{{=v}}{{pass}} - {{pass}} -
+
+ bhsa + {{for v in version_order: + vinfo = versions[v] + vdate = vinfo['date'] or 'recent' + title = '{}: {} [{}]'.format(vdate, vinfo['desc'], vinfo['notes']) + }}{{if vinfo['present']:}}{{=v}}{{ + else:}}{{=v}}{{pass}} + {{pass}} +
diff --git a/views/hebrew/word.html b/views/hebrew/word.html index 18606e2c..f62507b2 100644 --- a/views/hebrew/word.html +++ b/views/hebrew/word.html @@ -6,5 +6,5 @@ {{ block left_sidebar }} {{ include 'hebrew/text_sidebar.html' }} {{ end }} - - + + diff --git a/views/hebrew/words.html b/views/hebrew/words.html index b7e84252..d5e27896 100644 --- a/views/hebrew/words.html +++ b/views/hebrew/words.html @@ -1,8 +1,6 @@ {{ left_sidebar_enabled = True }} {{ extend 'layout.html' }} - - {{ block head }} {{ other_lan = 'arc' if lan == 'hbo' else 'hbo' @@ -96,12 +94,14 @@

{{=gletter}}

{{ end }} + + + diff --git a/views/layout.html b/views/layout.html index f944e894..93c82ab2 100755 --- a/views/layout.html +++ b/views/layout.html @@ -80,7 +80,6 @@ -