- (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.superagent = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i
- "use strict";
+ !(function (a) {
+ function b(a, d) {
+ if (((a = a ? a : ""), (d = d || {}), a instanceof b)) return a
+ if (!(this instanceof b)) return new b(a, d)
+ var e = c(a)
+ ;(this._originalInput = a),
+ (this._r = e.r),
+ (this._g = e.g),
+ (this._b = e.b),
+ (this._a = e.a),
+ (this._roundA = P(100 * this._a) / 100),
+ (this._format = d.format || e.format),
+ (this._gradientType = d.gradientType),
+ this._r < 1 && (this._r = P(this._r)),
+ this._g < 1 && (this._g = P(this._g)),
+ this._b < 1 && (this._b = P(this._b)),
+ (this._ok = e.ok),
+ (this._tc_id = O++)
+ }
+ function c(a) {
+ var b = { r: 0, g: 0, b: 0 },
+ c = 1,
+ e = null,
+ g = null,
+ i = null,
+ j = !1,
+ k = !1
+ return (
+ "string" == typeof a && (a = K(a)),
+ "object" == typeof a &&
+ (J(a.r) && J(a.g) && J(a.b)
+ ? ((b = d(a.r, a.g, a.b)), (j = !0), (k = "%" === String(a.r).substr(-1) ? "prgb" : "rgb"))
+ : J(a.h) && J(a.s) && J(a.v)
+ ? ((e = G(a.s)), (g = G(a.v)), (b = h(a.h, e, g)), (j = !0), (k = "hsv"))
+ : J(a.h) && J(a.s) && J(a.l) && ((e = G(a.s)), (i = G(a.l)), (b = f(a.h, e, i)), (j = !0), (k = "hsl")),
+ a.hasOwnProperty("a") && (c = a.a)),
+ (c = z(c)),
+ { ok: j, format: a.format || k, r: Q(255, R(b.r, 0)), g: Q(255, R(b.g, 0)), b: Q(255, R(b.b, 0)), a: c }
+ )
+ }
+ function d(a, b, c) {
+ return { r: 255 * A(a, 255), g: 255 * A(b, 255), b: 255 * A(c, 255) }
+ }
+ function e(a, b, c) {
+ ;(a = A(a, 255)), (b = A(b, 255)), (c = A(c, 255))
+ var d,
+ e,
+ f = R(a, b, c),
+ g = Q(a, b, c),
+ h = (f + g) / 2
+ if (f == g) d = e = 0
+ else {
+ var i = f - g
+ switch (((e = h > 0.5 ? i / (2 - f - g) : i / (f + g)), f)) {
+ case a:
+ d = (b - c) / i + (c > b ? 6 : 0)
+ break
+ case b:
+ d = (c - a) / i + 2
+ break
+ case c:
+ d = (a - b) / i + 4
+ }
+ d /= 6
+ }
+ return { h: d, s: e, l: h }
+ }
+ function f(a, b, c) {
+ function d(a, b, c) {
+ return 0 > c && (c += 1), c > 1 && (c -= 1), 1 / 6 > c ? a + 6 * (b - a) * c : 0.5 > c ? b : 2 / 3 > c ? a + 6 * (b - a) * (2 / 3 - c) : a
+ }
+ var e, f, g
+ if (((a = A(a, 360)), (b = A(b, 100)), (c = A(c, 100)), 0 === b)) e = f = g = c
+ else {
+ var h = 0.5 > c ? c * (1 + b) : c + b - c * b,
+ i = 2 * c - h
+ ;(e = d(i, h, a + 1 / 3)), (f = d(i, h, a)), (g = d(i, h, a - 1 / 3))
+ }
+ return { r: 255 * e, g: 255 * f, b: 255 * g }
+ }
+ function g(a, b, c) {
+ ;(a = A(a, 255)), (b = A(b, 255)), (c = A(c, 255))
+ var d,
+ e,
+ f = R(a, b, c),
+ g = Q(a, b, c),
+ h = f,
+ i = f - g
+ if (((e = 0 === f ? 0 : i / f), f == g)) d = 0
+ else {
+ switch (f) {
+ case a:
+ d = (b - c) / i + (c > b ? 6 : 0)
+ break
+ case b:
+ d = (c - a) / i + 2
+ break
+ case c:
+ d = (a - b) / i + 4
+ }
+ d /= 6
+ }
+ return { h: d, s: e, v: h }
+ }
+ function h(b, c, d) {
+ ;(b = 6 * A(b, 360)), (c = A(c, 100)), (d = A(d, 100))
+ var e = a.floor(b),
+ f = b - e,
+ g = d * (1 - c),
+ h = d * (1 - f * c),
+ i = d * (1 - (1 - f) * c),
+ j = e % 6,
+ k = [d, h, g, g, i, d][j],
+ l = [i, d, d, h, g, g][j],
+ m = [g, g, i, d, d, h][j]
+ return { r: 255 * k, g: 255 * l, b: 255 * m }
+ }
+ function i(a, b, c, d) {
+ var e = [F(P(a).toString(16)), F(P(b).toString(16)), F(P(c).toString(16))]
+ return d && e[0].charAt(0) == e[0].charAt(1) && e[1].charAt(0) == e[1].charAt(1) && e[2].charAt(0) == e[2].charAt(1)
+ ? e[0].charAt(0) + e[1].charAt(0) + e[2].charAt(0)
+ : e.join("")
+ }
+ function j(a, b, c, d, e) {
+ var f = [F(P(a).toString(16)), F(P(b).toString(16)), F(P(c).toString(16)), F(H(d))]
+ return e && f[0].charAt(0) == f[0].charAt(1) && f[1].charAt(0) == f[1].charAt(1) && f[2].charAt(0) == f[2].charAt(1) && f[3].charAt(0) == f[3].charAt(1)
+ ? f[0].charAt(0) + f[1].charAt(0) + f[2].charAt(0) + f[3].charAt(0)
+ : f.join("")
+ }
+ function k(a, b, c, d) {
+ var e = [F(H(d)), F(P(a).toString(16)), F(P(b).toString(16)), F(P(c).toString(16))]
+ return e.join("")
+ }
+ function l(a, c) {
+ c = 0 === c ? 0 : c || 10
+ var d = b(a).toHsl()
+ return (d.s -= c / 100), (d.s = B(d.s)), b(d)
+ }
+ function m(a, c) {
+ c = 0 === c ? 0 : c || 10
+ var d = b(a).toHsl()
+ return (d.s += c / 100), (d.s = B(d.s)), b(d)
+ }
+ function n(a) {
+ return b(a).desaturate(100)
+ }
+ function o(a, c) {
+ c = 0 === c ? 0 : c || 10
+ var d = b(a).toHsl()
+ return (d.l += c / 100), (d.l = B(d.l)), b(d)
+ }
+ function p(a, c) {
+ c = 0 === c ? 0 : c || 10
+ var d = b(a).toRgb()
+ return (d.r = R(0, Q(255, d.r - P(255 * -(c / 100))))), (d.g = R(0, Q(255, d.g - P(255 * -(c / 100))))), (d.b = R(0, Q(255, d.b - P(255 * -(c / 100))))), b(d)
+ }
+ function q(a, c) {
+ c = 0 === c ? 0 : c || 10
+ var d = b(a).toHsl()
+ return (d.l -= c / 100), (d.l = B(d.l)), b(d)
+ }
+ function r(a, c) {
+ var d = b(a).toHsl(),
+ e = (d.h + c) % 360
+ return (d.h = 0 > e ? 360 + e : e), b(d)
+ }
+ function s(a) {
+ var c = b(a).toHsl()
+ return (c.h = (c.h + 180) % 360), b(c)
+ }
+ function t(a) {
+ var c = b(a).toHsl(),
+ d = c.h
+ return [b(a), b({ h: (d + 120) % 360, s: c.s, l: c.l }), b({ h: (d + 240) % 360, s: c.s, l: c.l })]
+ }
+ function u(a) {
+ var c = b(a).toHsl(),
+ d = c.h
+ return [b(a), b({ h: (d + 90) % 360, s: c.s, l: c.l }), b({ h: (d + 180) % 360, s: c.s, l: c.l }), b({ h: (d + 270) % 360, s: c.s, l: c.l })]
+ }
+ function v(a) {
+ var c = b(a).toHsl(),
+ d = c.h
+ return [b(a), b({ h: (d + 72) % 360, s: c.s, l: c.l }), b({ h: (d + 216) % 360, s: c.s, l: c.l })]
+ }
+ function w(a, c, d) {
+ ;(c = c || 6), (d = d || 30)
+ var e = b(a).toHsl(),
+ f = 360 / d,
+ g = [b(a)]
+ for (e.h = (e.h - ((f * c) >> 1) + 720) % 360; --c; ) (e.h = (e.h + f) % 360), g.push(b(e))
+ return g
+ }
+ function x(a, c) {
+ c = c || 6
+ for (var d = b(a).toHsv(), e = d.h, f = d.s, g = d.v, h = [], i = 1 / c; c--; ) h.push(b({ h: e, s: f, v: g })), (g = (g + i) % 1)
+ return h
+ }
+ function y(a) {
+ var b = {}
+ for (var c in a) a.hasOwnProperty(c) && (b[a[c]] = c)
+ return b
+ }
+ function z(a) {
+ return (a = parseFloat(a)), (isNaN(a) || 0 > a || a > 1) && (a = 1), a
+ }
+ function A(b, c) {
+ D(b) && (b = "100%")
+ var d = E(b)
+ return (b = Q(c, R(0, parseFloat(b)))), d && (b = parseInt(b * c, 10) / 100), a.abs(b - c) < 1e-6 ? 1 : (b % c) / parseFloat(c)
+ }
+ function B(a) {
+ return Q(1, R(0, a))
+ }
+ function C(a) {
+ return parseInt(a, 16)
+ }
+ function D(a) {
+ return "string" == typeof a && -1 != a.indexOf(".") && 1 === parseFloat(a)
+ }
+ function E(a) {
+ return "string" == typeof a && -1 != a.indexOf("%")
+ }
+ function F(a) {
+ return 1 == a.length ? "0" + a : "" + a
+ }
+ function G(a) {
+ return 1 >= a && (a = 100 * a + "%"), a
+ }
+ function H(b) {
+ return a.round(255 * parseFloat(b)).toString(16)
+ }
+ function I(a) {
+ return C(a) / 255
+ }
+ function J(a) {
+ return !!V.CSS_UNIT.exec(a)
+ }
+ function K(a) {
+ a = a.replace(M, "").replace(N, "").toLowerCase()
+ var b = !1
+ if (T[a]) (a = T[a]), (b = !0)
+ else if ("transparent" == a) return { r: 0, g: 0, b: 0, a: 0, format: "name" }
+ var c
+ return (c = V.rgb.exec(a))
+ ? { r: c[1], g: c[2], b: c[3] }
+ : (c = V.rgba.exec(a))
+ ? { r: c[1], g: c[2], b: c[3], a: c[4] }
+ : (c = V.hsl.exec(a))
+ ? { h: c[1], s: c[2], l: c[3] }
+ : (c = V.hsla.exec(a))
+ ? { h: c[1], s: c[2], l: c[3], a: c[4] }
+ : (c = V.hsv.exec(a))
+ ? { h: c[1], s: c[2], v: c[3] }
+ : (c = V.hsva.exec(a))
+ ? { h: c[1], s: c[2], v: c[3], a: c[4] }
+ : (c = V.hex8.exec(a))
+ ? { r: C(c[1]), g: C(c[2]), b: C(c[3]), a: I(c[4]), format: b ? "name" : "hex8" }
+ : (c = V.hex6.exec(a))
+ ? { r: C(c[1]), g: C(c[2]), b: C(c[3]), format: b ? "name" : "hex" }
+ : (c = V.hex4.exec(a))
+ ? { r: C(c[1] + "" + c[1]), g: C(c[2] + "" + c[2]), b: C(c[3] + "" + c[3]), a: I(c[4] + "" + c[4]), format: b ? "name" : "hex8" }
+ : (c = V.hex3.exec(a))
+ ? { r: C(c[1] + "" + c[1]), g: C(c[2] + "" + c[2]), b: C(c[3] + "" + c[3]), format: b ? "name" : "hex" }
+ : !1
+ }
+ function L(a) {
+ var b, c
+ return (
+ (a = a || { level: "AA", size: "small" }),
+ (b = (a.level || "AA").toUpperCase()),
+ (c = (a.size || "small").toLowerCase()),
+ "AA" !== b && "AAA" !== b && (b = "AA"),
+ "small" !== c && "large" !== c && (c = "small"),
+ { level: b, size: c }
+ )
+ }
+ var M = /^\s+/,
+ N = /\s+$/,
+ O = 0,
+ P = a.round,
+ Q = a.min,
+ R = a.max,
+ S = a.random
+ ;(b.prototype = {
+ isDark: function () {
+ return this.getBrightness() < 128
+ },
+ isLight: function () {
+ return !this.isDark()
+ },
+ isValid: function () {
+ return this._ok
+ },
+ getOriginalInput: function () {
+ return this._originalInput
+ },
+ getFormat: function () {
+ return this._format
+ },
+ getAlpha: function () {
+ return this._a
+ },
+ getBrightness: function () {
+ var a = this.toRgb()
+ return (299 * a.r + 587 * a.g + 114 * a.b) / 1e3
+ },
+ getLuminance: function () {
+ var b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h = this.toRgb()
+ return (
+ (b = h.r / 255),
+ (c = h.g / 255),
+ (d = h.b / 255),
+ (e = 0.03928 >= b ? b / 12.92 : a.pow((b + 0.055) / 1.055, 2.4)),
+ (f = 0.03928 >= c ? c / 12.92 : a.pow((c + 0.055) / 1.055, 2.4)),
+ (g = 0.03928 >= d ? d / 12.92 : a.pow((d + 0.055) / 1.055, 2.4)),
+ 0.2126 * e + 0.7152 * f + 0.0722 * g
+ )
+ },
+ setAlpha: function (a) {
+ return (this._a = z(a)), (this._roundA = P(100 * this._a) / 100), this
+ },
+ toHsv: function () {
+ var a = g(this._r, this._g, this._b)
+ return { h: 360 * a.h, s: a.s, v: a.v, a: this._a }
+ },
+ toHsvString: function () {
+ var a = g(this._r, this._g, this._b),
+ b = P(360 * a.h),
+ c = P(100 * a.s),
+ d = P(100 * a.v)
+ return 1 == this._a ? "hsv(" + b + ", " + c + "%, " + d + "%)" : "hsva(" + b + ", " + c + "%, " + d + "%, " + this._roundA + ")"
+ },
+ toHsl: function () {
+ var a = e(this._r, this._g, this._b)
+ return { h: 360 * a.h, s: a.s, l: a.l, a: this._a }
+ },
+ toHslString: function () {
+ var a = e(this._r, this._g, this._b),
+ b = P(360 * a.h),
+ c = P(100 * a.s),
+ d = P(100 * a.l)
+ return 1 == this._a ? "hsl(" + b + ", " + c + "%, " + d + "%)" : "hsla(" + b + ", " + c + "%, " + d + "%, " + this._roundA + ")"
+ },
+ toHex: function (a) {
+ return i(this._r, this._g, this._b, a)
+ },
+ toHexString: function (a) {
+ return "#" + this.toHex(a)
+ },
+ toHex8: function (a) {
+ return j(this._r, this._g, this._b, this._a, a)
+ },
+ toHex8String: function (a) {
+ return "#" + this.toHex8(a)
+ },
+ toRgb: function () {
+ return { r: P(this._r), g: P(this._g), b: P(this._b), a: this._a }
+ },
+ toRgbString: function () {
+ return 1 == this._a
+ ? "rgb(" + P(this._r) + ", " + P(this._g) + ", " + P(this._b) + ")"
+ : "rgba(" + P(this._r) + ", " + P(this._g) + ", " + P(this._b) + ", " + this._roundA + ")"
+ },
+ toPercentageRgb: function () {
+ return { r: P(100 * A(this._r, 255)) + "%", g: P(100 * A(this._g, 255)) + "%", b: P(100 * A(this._b, 255)) + "%", a: this._a }
+ },
+ toPercentageRgbString: function () {
+ return 1 == this._a
+ ? "rgb(" + P(100 * A(this._r, 255)) + "%, " + P(100 * A(this._g, 255)) + "%, " + P(100 * A(this._b, 255)) + "%)"
+ : "rgba(" + P(100 * A(this._r, 255)) + "%, " + P(100 * A(this._g, 255)) + "%, " + P(100 * A(this._b, 255)) + "%, " + this._roundA + ")"
+ },
+ toName: function () {
+ return 0 === this._a ? "transparent" : this._a < 1 ? !1 : U[i(this._r, this._g, this._b, !0)] || !1
+ },
+ toFilter: function (a) {
+ var c = "#" + k(this._r, this._g, this._b, this._a),
+ d = c,
+ e = this._gradientType ? "GradientType = 1, " : ""
+ if (a) {
+ var f = b(a)
+ d = "#" + k(f._r, f._g, f._b, f._a)
+ }
+ return "progid:DXImageTransform.Microsoft.gradient(" + e + "startColorstr=" + c + ",endColorstr=" + d + ")"
+ },
+ toString: function (a) {
+ var b = !!a
+ a = a || this._format
+ var c = !1,
+ d = this._a < 1 && this._a >= 0,
+ e = !b && d && ("hex" === a || "hex6" === a || "hex3" === a || "hex4" === a || "hex8" === a || "name" === a)
+ return e
+ ? "name" === a && 0 === this._a
+ ? this.toName()
+ : this.toRgbString()
+ : ("rgb" === a && (c = this.toRgbString()),
+ "prgb" === a && (c = this.toPercentageRgbString()),
+ ("hex" === a || "hex6" === a) && (c = this.toHexString()),
+ "hex3" === a && (c = this.toHexString(!0)),
+ "hex4" === a && (c = this.toHex8String(!0)),
+ "hex8" === a && (c = this.toHex8String()),
+ "name" === a && (c = this.toName()),
+ "hsl" === a && (c = this.toHslString()),
+ "hsv" === a && (c = this.toHsvString()),
+ c || this.toHexString())
+ },
+ clone: function () {
+ return b(this.toString())
+ },
+ _applyModification: function (a, b) {
+ var c = a.apply(null, [this].concat([].slice.call(b)))
+ return (this._r = c._r), (this._g = c._g), (this._b = c._b), this.setAlpha(c._a), this
+ },
+ lighten: function () {
+ return this._applyModification(o, arguments)
+ },
+ brighten: function () {
+ return this._applyModification(p, arguments)
+ },
+ darken: function () {
+ return this._applyModification(q, arguments)
+ },
+ desaturate: function () {
+ return this._applyModification(l, arguments)
+ },
+ saturate: function () {
+ return this._applyModification(m, arguments)
+ },
+ greyscale: function () {
+ return this._applyModification(n, arguments)
+ },
+ spin: function () {
+ return this._applyModification(r, arguments)
+ },
+ _applyCombination: function (a, b) {
+ return a.apply(null, [this].concat([].slice.call(b)))
+ },
+ analogous: function () {
+ return this._applyCombination(w, arguments)
+ },
+ complement: function () {
+ return this._applyCombination(s, arguments)
+ },
+ monochromatic: function () {
+ return this._applyCombination(x, arguments)
+ },
+ splitcomplement: function () {
+ return this._applyCombination(v, arguments)
+ },
+ triad: function () {
+ return this._applyCombination(t, arguments)
+ },
+ tetrad: function () {
+ return this._applyCombination(u, arguments)
+ },
+ }),
+ (b.fromRatio = function (a, c) {
+ if ("object" == typeof a) {
+ var d = {}
+ for (var e in a) a.hasOwnProperty(e) && (d[e] = "a" === e ? a[e] : G(a[e]))
+ a = d
+ }
+ return b(a, c)
+ }),
+ (b.equals = function (a, c) {
+ return a && c ? b(a).toRgbString() == b(c).toRgbString() : !1
+ }),
+ (b.random = function () {
+ return b.fromRatio({ r: S(), g: S(), b: S() })
+ }),
+ (b.mix = function (a, c, d) {
+ d = 0 === d ? 0 : d || 50
+ var e = b(a).toRgb(),
+ f = b(c).toRgb(),
+ g = d / 100,
+ h = { r: (f.r - e.r) * g + e.r, g: (f.g - e.g) * g + e.g, b: (f.b - e.b) * g + e.b, a: (f.a - e.a) * g + e.a }
+ return b(h)
+ }),
+ (b.readability = function (c, d) {
+ var e = b(c),
+ f = b(d)
+ return (a.max(e.getLuminance(), f.getLuminance()) + 0.05) / (a.min(e.getLuminance(), f.getLuminance()) + 0.05)
+ }),
+ (b.isReadable = function (a, c, d) {
+ var e,
+ f,
+ g = b.readability(a, c)
+ switch (((f = !1), (e = L(d)), e.level + e.size)) {
+ case "AAsmall":
+ case "AAAlarge":
+ f = g >= 4.5
+ break
+ case "AAlarge":
+ f = g >= 3
+ break
+ case "AAAsmall":
+ f = g >= 7
+ }
+ return f
+ }),
+ (b.mostReadable = function (a, c, d) {
+ var e,
+ f,
+ g,
+ h,
+ i = null,
+ j = 0
+ ;(d = d || {}), (f = d.includeFallbackColors), (g = d.level), (h = d.size)
+ for (var k = 0; k < c.length; k++) (e = b.readability(a, c[k])), e > j && ((j = e), (i = b(c[k])))
+ return b.isReadable(a, i, { level: g, size: h }) || !f ? i : ((d.includeFallbackColors = !1), b.mostReadable(a, ["#fff", "#000"], d))
+ })
+ var T = (b.names = {
+ aliceblue: "f0f8ff",
+ antiquewhite: "faebd7",
+ aqua: "0ff",
+ aquamarine: "7fffd4",
+ azure: "f0ffff",
+ beige: "f5f5dc",
+ bisque: "ffe4c4",
+ black: "000",
+ blanchedalmond: "ffebcd",
+ blue: "00f",
+ blueviolet: "8a2be2",
+ brown: "a52a2a",
+ burlywood: "deb887",
+ burntsienna: "ea7e5d",
+ cadetblue: "5f9ea0",
+ chartreuse: "7fff00",
+ chocolate: "d2691e",
+ coral: "ff7f50",
+ cornflowerblue: "6495ed",
+ cornsilk: "fff8dc",
+ crimson: "dc143c",
+ cyan: "0ff",
+ darkblue: "00008b",
+ darkcyan: "008b8b",
+ darkgoldenrod: "b8860b",
+ darkgray: "a9a9a9",
+ darkgreen: "006400",
+ darkgrey: "a9a9a9",
+ darkkhaki: "bdb76b",
+ darkmagenta: "8b008b",
+ darkolivegreen: "556b2f",
+ darkorange: "ff8c00",
+ darkorchid: "9932cc",
+ darkred: "8b0000",
+ darksalmon: "e9967a",
+ darkseagreen: "8fbc8f",
+ darkslateblue: "483d8b",
+ darkslategray: "2f4f4f",
+ darkslategrey: "2f4f4f",
+ darkturquoise: "00ced1",
+ darkviolet: "9400d3",
+ deeppink: "ff1493",
+ deepskyblue: "00bfff",
+ dimgray: "696969",
+ dimgrey: "696969",
+ dodgerblue: "1e90ff",
+ firebrick: "b22222",
+ floralwhite: "fffaf0",
+ forestgreen: "228b22",
+ fuchsia: "f0f",
+ gainsboro: "dcdcdc",
+ ghostwhite: "f8f8ff",
+ gold: "ffd700",
+ goldenrod: "daa520",
+ gray: "808080",
+ green: "008000",
+ greenyellow: "adff2f",
+ grey: "808080",
+ honeydew: "f0fff0",
+ hotpink: "ff69b4",
+ indianred: "cd5c5c",
+ indigo: "4b0082",
+ ivory: "fffff0",
+ khaki: "f0e68c",
+ lavender: "e6e6fa",
+ lavenderblush: "fff0f5",
+ lawngreen: "7cfc00",
+ lemonchiffon: "fffacd",
+ lightblue: "add8e6",
+ lightcoral: "f08080",
+ lightcyan: "e0ffff",
+ lightgoldenrodyellow: "fafad2",
+ lightgray: "d3d3d3",
+ lightgreen: "90ee90",
+ lightgrey: "d3d3d3",
+ lightpink: "ffb6c1",
+ lightsalmon: "ffa07a",
+ lightseagreen: "20b2aa",
+ lightskyblue: "87cefa",
+ lightslategray: "789",
+ lightslategrey: "789",
+ lightsteelblue: "b0c4de",
+ lightyellow: "ffffe0",
+ lime: "0f0",
+ limegreen: "32cd32",
+ linen: "faf0e6",
+ magenta: "f0f",
+ maroon: "800000",
+ mediumaquamarine: "66cdaa",
+ mediumblue: "0000cd",
+ mediumorchid: "ba55d3",
+ mediumpurple: "9370db",
+ mediumseagreen: "3cb371",
+ mediumslateblue: "7b68ee",
+ mediumspringgreen: "00fa9a",
+ mediumturquoise: "48d1cc",
+ mediumvioletred: "c71585",
+ midnightblue: "191970",
+ mintcream: "f5fffa",
+ mistyrose: "ffe4e1",
+ moccasin: "ffe4b5",
+ navajowhite: "ffdead",
+ navy: "000080",
+ oldlace: "fdf5e6",
+ olive: "808000",
+ olivedrab: "6b8e23",
+ orange: "ffa500",
+ orangered: "ff4500",
+ orchid: "da70d6",
+ palegoldenrod: "eee8aa",
+ palegreen: "98fb98",
+ paleturquoise: "afeeee",
+ palevioletred: "db7093",
+ papayawhip: "ffefd5",
+ peachpuff: "ffdab9",
+ peru: "cd853f",
+ pink: "ffc0cb",
+ plum: "dda0dd",
+ powderblue: "b0e0e6",
+ purple: "800080",
+ rebeccapurple: "663399",
+ red: "f00",
+ rosybrown: "bc8f8f",
+ royalblue: "4169e1",
+ saddlebrown: "8b4513",
+ salmon: "fa8072",
+ sandybrown: "f4a460",
+ seagreen: "2e8b57",
+ seashell: "fff5ee",
+ sienna: "a0522d",
+ silver: "c0c0c0",
+ skyblue: "87ceeb",
+ slateblue: "6a5acd",
+ slategray: "708090",
+ slategrey: "708090",
+ snow: "fffafa",
+ springgreen: "00ff7f",
+ steelblue: "4682b4",
+ tan: "d2b48c",
+ teal: "008080",
+ thistle: "d8bfd8",
+ tomato: "ff6347",
+ turquoise: "40e0d0",
+ violet: "ee82ee",
+ wheat: "f5deb3",
+ white: "fff",
+ whitesmoke: "f5f5f5",
+ yellow: "ff0",
+ yellowgreen: "9acd32",
+ }),
+ U = (b.hexNames = y(T)),
+ V = (function () {
+ var a = "[-\\+]?\\d+%?",
+ b = "[-\\+]?\\d*\\.\\d+%?",
+ c = "(?:" + b + ")|(?:" + a + ")",
+ d = "[\\s|\\(]+(" + c + ")[,|\\s]+(" + c + ")[,|\\s]+(" + c + ")\\s*\\)?",
+ e = "[\\s|\\(]+(" + c + ")[,|\\s]+(" + c + ")[,|\\s]+(" + c + ")[,|\\s]+(" + c + ")\\s*\\)?"
+ return {
+ CSS_UNIT: new RegExp(c),
+ rgb: new RegExp("rgb" + d),
+ rgba: new RegExp("rgba" + e),
+ hsl: new RegExp("hsl" + d),
+ hsla: new RegExp("hsla" + e),
+ hsv: new RegExp("hsv" + d),
+ hsva: new RegExp("hsva" + e),
+ hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
+ hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
+ hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
+ hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
+ }
+ })()
+ "undefined" != typeof module && module.exports
+ ? (module.exports = b)
+ : "function" == typeof define && define.amd
+ ? define(function () {
+ return b
+ })
+ : (window.tinycolor = b)
+ })(Math)
+ ;(function (f) {
+ if (typeof exports === "object" && typeof module !== "undefined") {
+ module.exports = f()
+ } else if (typeof define === "function" && define.amd) {
+ define([], f)
+ } else {
+ var g
+ if (typeof window !== "undefined") {
+ g = window
+ } else if (typeof global !== "undefined") {
+ g = global
+ } else if (typeof self !== "undefined") {
+ g = self
+ } else {
+ g = this
+ }
+ g.superagent = f()
+ }
+ })(function () {
+ var define, module, exports
+ return (function () {
+ function r(e, n, t) {
+ function o(i, f) {
+ if (!n[i]) {
+ if (!e[i]) {
+ var c = "function" == typeof require && require
+ if (!f && c) return c(i, !0)
+ if (u) return u(i, !0)
+ var a = new Error("Cannot find module '" + i + "'")
+ throw ((a.code = "MODULE_NOT_FOUND"), a)
+ }
+ var p = (n[i] = { exports: {} })
+ e[i][0].call(
+ p.exports,
+ function (r) {
+ var n = e[i][1][r]
+ return o(n || r)
+ },
+ p,
+ p.exports,
+ r,
+ e,
+ n,
+ t
+ )
+ }
+ return n[i].exports
+ }
+ for (var u = "function" == typeof require && require, i = 0; i < t.length; i++) o(t[i])
+ return o
+ }
+ return r
+ })()(
+ {
+ 1: [
+ function (require, module, exports) {
+ "use strict"
+
+ /**
+ * Expose `Emitter`.
+ */
+ if (typeof module !== "undefined") {
+ module.exports = Emitter
+ }
+ /**
+ * Initialize a new `Emitter`.
+ *
+ * @api public
+ */
+
+ function Emitter(obj) {
+ if (obj) return mixin(obj)
+ }
+
+ /**
+ * Mixin the emitter properties.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+ function mixin(obj) {
+ for (var key in Emitter.prototype) {
+ obj[key] = Emitter.prototype[key]
+ }
+
+ return obj
+ }
+ /**
+ * Listen on the given `event` with `fn`.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+ Emitter.prototype.on = Emitter.prototype.addEventListener = function (event, fn) {
+ this._callbacks = this._callbacks || {}
+ ;(this._callbacks["$" + event] = this._callbacks["$" + event] || []).push(fn)
+ return this
+ }
+ /**
+ * Adds an `event` listener that will be invoked a single
+ * time then automatically removed.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+ Emitter.prototype.once = function (event, fn) {
+ function on() {
+ this.off(event, on)
+ fn.apply(this, arguments)
+ }
+
+ on.fn = fn
+ this.on(event, on)
+ return this
+ }
+ /**
+ * Remove the given callback for `event` or all
+ * registered callbacks.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+ Emitter.prototype.off =
+ Emitter.prototype.removeListener =
+ Emitter.prototype.removeAllListeners =
+ Emitter.prototype.removeEventListener =
+ function (event, fn) {
+ this._callbacks = this._callbacks || {} // all
+
+ if (0 == arguments.length) {
+ this._callbacks = {}
+ return this
+ } // specific event
+
+ var callbacks = this._callbacks["$" + event]
+ if (!callbacks) return this // remove all handlers
+
+ if (1 == arguments.length) {
+ delete this._callbacks["$" + event]
+ return this
+ } // remove specific handler
+
+ var cb
+
+ for (var i = 0; i < callbacks.length; i++) {
+ cb = callbacks[i]
+
+ if (cb === fn || cb.fn === fn) {
+ callbacks.splice(i, 1)
+ break
+ }
+ } // Remove event specific arrays for event types that no
+ // one is subscribed for to avoid memory leak.
+
+ if (callbacks.length === 0) {
+ delete this._callbacks["$" + event]
+ }
+
+ return this
+ }
+ /**
+ * Emit `event` with the given args.
+ *
+ * @param {String} event
+ * @param {Mixed} ...
+ * @return {Emitter}
+ */
+
+ Emitter.prototype.emit = function (event) {
+ this._callbacks = this._callbacks || {}
+ var args = new Array(arguments.length - 1),
+ callbacks = this._callbacks["$" + event]
+
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i]
+ }
+
+ if (callbacks) {
+ callbacks = callbacks.slice(0)
+
+ for (var i = 0, len = callbacks.length; i < len; ++i) {
+ callbacks[i].apply(this, args)
+ }
+ }
+
+ return this
+ }
+ /**
+ * Return array of callbacks for `event`.
+ *
+ * @param {String} event
+ * @return {Array}
+ * @api public
+ */
+
+ Emitter.prototype.listeners = function (event) {
+ this._callbacks = this._callbacks || {}
+ return this._callbacks["$" + event] || []
+ }
+ /**
+ * Check if this emitter has `event` handlers.
+ *
+ * @param {String} event
+ * @return {Boolean}
+ * @api public
+ */
+
+ Emitter.prototype.hasListeners = function (event) {
+ return !!this.listeners(event).length
+ }
+ },
+ {},
+ ],
+ 2: [
+ function (require, module, exports) {
+ "use strict"
+
+ function _typeof(obj) {
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
+ _typeof = function _typeof(obj) {
+ return typeof obj
+ }
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj
+ }
+ }
+ return _typeof(obj)
+ }
+
+ module.exports = stringify
+ stringify.default = stringify
+ stringify.stable = deterministicStringify
+ stringify.stableStringify = deterministicStringify
+ var arr = [] // Regular stringify
+
+ function stringify(obj, replacer, spacer) {
+ decirc(obj, "", [], undefined)
+ var res = JSON.stringify(obj, replacer, spacer)
+
+ while (arr.length !== 0) {
+ var part = arr.pop()
+ part[0][part[1]] = part[2]
+ }
+
+ return res
+ }
+
+ function decirc(val, k, stack, parent) {
+ var i
+
+ if (_typeof(val) === "object" && val !== null) {
+ for (i = 0; i < stack.length; i++) {
+ if (stack[i] === val) {
+ parent[k] = "[Circular]"
+ arr.push([parent, k, val])
+ return
+ }
+ }
+
+ stack.push(val) // Optimize for Arrays. Big arrays could kill the performance otherwise!
+
+ if (Array.isArray(val)) {
+ for (i = 0; i < val.length; i++) {
+ decirc(val[i], i, stack, val)
+ }
+ } else {
+ var keys = Object.keys(val)
+
+ for (i = 0; i < keys.length; i++) {
+ var key = keys[i]
+ decirc(val[key], key, stack, val)
+ }
+ }
+
+ stack.pop()
+ }
+ } // Stable-stringify
+
+ function compareFunction(a, b) {
+ if (a < b) {
+ return -1
+ }
+
+ if (a > b) {
+ return 1
+ }
+
+ return 0
+ }
+
+ function deterministicStringify(obj, replacer, spacer) {
+ var tmp = deterministicDecirc(obj, "", [], undefined) || obj
+ var res = JSON.stringify(tmp, replacer, spacer)
+
+ while (arr.length !== 0) {
+ var part = arr.pop()
+ part[0][part[1]] = part[2]
+ }
+
+ return res
+ }
+
+ function deterministicDecirc(val, k, stack, parent) {
+ var i
+
+ if (_typeof(val) === "object" && val !== null) {
+ for (i = 0; i < stack.length; i++) {
+ if (stack[i] === val) {
+ parent[k] = "[Circular]"
+ arr.push([parent, k, val])
+ return
+ }
+ }
+
+ if (typeof val.toJSON === "function") {
+ return
+ }
+
+ stack.push(val) // Optimize for Arrays. Big arrays could kill the performance otherwise!
+
+ if (Array.isArray(val)) {
+ for (i = 0; i < val.length; i++) {
+ deterministicDecirc(val[i], i, stack, val)
+ }
+ } else {
+ // Create a temporary object in the required way
+ var tmp = {}
+ var keys = Object.keys(val).sort(compareFunction)
+
+ for (i = 0; i < keys.length; i++) {
+ var key = keys[i]
+ deterministicDecirc(val[key], key, stack, val)
+ tmp[key] = val[key]
+ }
+
+ if (parent !== undefined) {
+ arr.push([parent, k, val])
+ parent[k] = tmp
+ } else {
+ return tmp
+ }
+ }
+
+ stack.pop()
+ }
+ }
+ },
+ {},
+ ],
+ 3: [
+ function (require, module, exports) {
+ "use strict"
+
+ function _toConsumableArray(arr) {
+ return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread()
+ }
+
+ function _nonIterableSpread() {
+ throw new TypeError("Invalid attempt to spread non-iterable instance")
+ }
+
+ function _iterableToArray(iter) {
+ if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter)
+ }
+
+ function _arrayWithoutHoles(arr) {
+ if (Array.isArray(arr)) {
+ for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) {
+ arr2[i] = arr[i]
+ }
+ return arr2
+ }
+ }
+
+ function Agent() {
+ this._defaults = []
+ }
+
+ ;[
+ "use",
+ "on",
+ "once",
+ "set",
+ "query",
+ "type",
+ "accept",
+ "auth",
+ "withCredentials",
+ "sortQuery",
+ "retry",
+ "ok",
+ "redirects",
+ "timeout",
+ "buffer",
+ "serialize",
+ "parse",
+ "ca",
+ "key",
+ "pfx",
+ "cert",
+ ].forEach(function (fn) {
+ // Default setting for all requests from this agent
+ Agent.prototype[fn] = function () {
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key]
+ }
+
+ this._defaults.push({
+ fn: fn,
+ args: args,
+ })
+
+ return this
+ }
+ })
+
+ Agent.prototype._setDefaults = function (req) {
+ this._defaults.forEach(function (def) {
+ req[def.fn].apply(req, _toConsumableArray(def.args))
+ })
+ }
+
+ module.exports = Agent
+ },
+ {},
+ ],
+ 4: [
+ function (require, module, exports) {
+ "use strict"
+
+ function _typeof(obj) {
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
+ _typeof = function _typeof(obj) {
+ return typeof obj
+ }
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj
+ }
+ }
+ return _typeof(obj)
+ }
+
+ /**
+ * Check if `obj` is an object.
+ *
+ * @param {Object} obj
+ * @return {Boolean}
+ * @api private
+ */
+ function isObject(obj) {
+ return obj !== null && _typeof(obj) === "object"
+ }
+
+ module.exports = isObject
+ },
+ {},
+ ],
+ 5: [
+ function (require, module, exports) {
+ "use strict"
+
+ function _typeof(obj) {
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
+ _typeof = function _typeof(obj) {
+ return typeof obj
+ }
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj
+ }
+ }
+ return _typeof(obj)
+ }
+
+ /**
+ * Root reference for iframes.
+ */
+ var root
+
+ if (typeof window !== "undefined") {
+ // Browser window
+ root = window
+ } else if (typeof self === "undefined") {
+ // Other environments
+ console.warn("Using browser-only version of superagent in non-browser environment")
+ root = void 0
+ } else {
+ // Web Worker
+ root = self
+ }
+
+ var Emitter = require("component-emitter")
+
+ var safeStringify = require("fast-safe-stringify")
+
+ var RequestBase = require("./request-base")
+
+ var isObject = require("./is-object")
+
+ var ResponseBase = require("./response-base")
+
+ var Agent = require("./agent-base")
+ /**
+ * Noop.
+ */
+
+ function noop() {}
+ /**
+ * Expose `request`.
+ */
+
+ module.exports = function (method, url) {
+ // callback
+ if (typeof url === "function") {
+ return new exports.Request("GET", method).end(url)
+ } // url first
+
+ if (arguments.length === 1) {
+ return new exports.Request("GET", method)
+ }
+
+ return new exports.Request(method, url)
+ }
+
+ exports = module.exports
+ var request = exports
+ exports.Request = Request
+ /**
+ * Determine XHR.
+ */
+
+ request.getXHR = function () {
+ if (root.XMLHttpRequest && (!root.location || root.location.protocol !== "file:" || !root.ActiveXObject)) {
+ return new XMLHttpRequest()
+ }
+
+ try {
+ return new ActiveXObject("Microsoft.XMLHTTP")
+ } catch (err) {}
+
+ try {
+ return new ActiveXObject("Msxml2.XMLHTTP.6.0")
+ } catch (err) {}
+
+ try {
+ return new ActiveXObject("Msxml2.XMLHTTP.3.0")
+ } catch (err) {}
+
+ try {
+ return new ActiveXObject("Msxml2.XMLHTTP")
+ } catch (err) {}
+
+ throw new Error("Browser-only version of superagent could not find XHR")
+ }
+ /**
+ * Removes leading and trailing whitespace, added to support IE.
+ *
+ * @param {String} s
+ * @return {String}
+ * @api private
+ */
+
+ var trim = "".trim
+ ? function (s) {
+ return s.trim()
+ }
+ : function (s) {
+ return s.replace(/(^\s*|\s*$)/g, "")
+ }
+ /**
+ * Serialize the given `obj`.
+ *
+ * @param {Object} obj
+ * @return {String}
+ * @api private
+ */
+
+ function serialize(obj) {
+ if (!isObject(obj)) return obj
+ var pairs = []
+
+ for (var key in obj) {
+ if (Object.prototype.hasOwnProperty.call(obj, key)) pushEncodedKeyValuePair(pairs, key, obj[key])
+ }
+
+ return pairs.join("&")
+ }
+ /**
+ * Helps 'serialize' with serializing arrays.
+ * Mutates the pairs array.
+ *
+ * @param {Array} pairs
+ * @param {String} key
+ * @param {Mixed} val
+ */
+
+ function pushEncodedKeyValuePair(pairs, key, val) {
+ if (val === undefined) return
+
+ if (val === null) {
+ pairs.push(encodeURIComponent(key))
+ return
+ }
+
+ if (Array.isArray(val)) {
+ val.forEach(function (v) {
+ pushEncodedKeyValuePair(pairs, key, v)
+ })
+ } else if (isObject(val)) {
+ for (var subkey in val) {
+ if (Object.prototype.hasOwnProperty.call(val, subkey)) pushEncodedKeyValuePair(pairs, "".concat(key, "[").concat(subkey, "]"), val[subkey])
+ }
+ } else {
+ pairs.push(encodeURIComponent(key) + "=" + encodeURIComponent(val))
+ }
+ }
+ /**
+ * Expose serialization method.
+ */
+
+ request.serializeObject = serialize
+ /**
+ * Parse the given x-www-form-urlencoded `str`.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+ function parseString(str) {
+ var obj = {}
+ var pairs = str.split("&")
+ var pair
+ var pos
+
+ for (var i = 0, len = pairs.length; i < len; ++i) {
+ pair = pairs[i]
+ pos = pair.indexOf("=")
+
+ if (pos === -1) {
+ obj[decodeURIComponent(pair)] = ""
+ } else {
+ obj[decodeURIComponent(pair.slice(0, pos))] = decodeURIComponent(pair.slice(pos + 1))
+ }
+ }
+
+ return obj
+ }
+ /**
+ * Expose parser.
+ */
+
+ request.parseString = parseString
+ /**
+ * Default MIME type map.
+ *
+ * superagent.types.xml = 'application/xml';
+ *
+ */
+
+ request.types = {
+ html: "text/html",
+ json: "application/json",
+ xml: "text/xml",
+ urlencoded: "application/x-www-form-urlencoded",
+ form: "application/x-www-form-urlencoded",
+ "form-data": "application/x-www-form-urlencoded",
+ }
+ /**
+ * Default serialization map.
+ *
+ * superagent.serialize['application/xml'] = function(obj){
+ * return 'generated xml here';
+ * };
+ *
+ */
+
+ request.serialize = {
+ "application/x-www-form-urlencoded": serialize,
+ "application/json": safeStringify,
+ }
+ /**
+ * Default parsers.
+ *
+ * superagent.parse['application/xml'] = function(str){
+ * return { object parsed from str };
+ * };
+ *
+ */
+
+ request.parse = {
+ "application/x-www-form-urlencoded": parseString,
+ "application/json": JSON.parse,
+ }
+ /**
+ * Parse the given header `str` into
+ * an object containing the mapped fields.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+ function parseHeader(str) {
+ var lines = str.split(/\r?\n/)
+ var fields = {}
+ var index
+ var line
+ var field
+ var val
+
+ for (var i = 0, len = lines.length; i < len; ++i) {
+ line = lines[i]
+ index = line.indexOf(":")
+
+ if (index === -1) {
+ // could be empty line, just skip it
+ continue
+ }
+
+ field = line.slice(0, index).toLowerCase()
+ val = trim(line.slice(index + 1))
+ fields[field] = val
+ }
+
+ return fields
+ }
+ /**
+ * Check if `mime` is json or has +json structured syntax suffix.
+ *
+ * @param {String} mime
+ * @return {Boolean}
+ * @api private
+ */
+
+ function isJSON(mime) {
+ // should match /json or +json
+ // but not /json-seq
+ return /[/+]json($|[^-\w])/.test(mime)
+ }
+ /**
+ * Initialize a new `Response` with the given `xhr`.
+ *
+ * - set flags (.ok, .error, etc)
+ * - parse header
+ *
+ * Examples:
+ *
+ * Aliasing `superagent` as `request` is nice:
+ *
+ * request = superagent;
+ *
+ * We can use the promise-like API, or pass callbacks:
+ *
+ * request.get('/').end(function(res){});
+ * request.get('/', function(res){});
+ *
+ * Sending data can be chained:
+ *
+ * request
+ * .post('/user')
+ * .send({ name: 'tj' })
+ * .end(function(res){});
+ *
+ * Or passed to `.send()`:
+ *
+ * request
+ * .post('/user')
+ * .send({ name: 'tj' }, function(res){});
+ *
+ * Or passed to `.post()`:
+ *
+ * request
+ * .post('/user', { name: 'tj' })
+ * .end(function(res){});
+ *
+ * Or further reduced to a single call for simple cases:
+ *
+ * request
+ * .post('/user', { name: 'tj' }, function(res){});
+ *
+ * @param {XMLHTTPRequest} xhr
+ * @param {Object} options
+ * @api private
+ */
+
+ function Response(req) {
+ this.req = req
+ this.xhr = this.req.xhr // responseText is accessible only if responseType is '' or 'text' and on older browsers
+
+ this.text =
+ (this.req.method !== "HEAD" && (this.xhr.responseType === "" || this.xhr.responseType === "text")) || typeof this.xhr.responseType === "undefined"
+ ? this.xhr.responseText
+ : null
+ this.statusText = this.req.xhr.statusText
+ var status = this.xhr.status // handle IE9 bug: http://stackoverflow.com/questions/10046972/msie-returns-status-code-of-1223-for-ajax-request
+
+ if (status === 1223) {
+ status = 204
+ }
+
+ this._setStatusProperties(status)
+
+ this.headers = parseHeader(this.xhr.getAllResponseHeaders())
+ this.header = this.headers // getAllResponseHeaders sometimes falsely returns "" for CORS requests, but
+ // getResponseHeader still works. so we get content-type even if getting
+ // other headers fails.
+
+ this.header["content-type"] = this.xhr.getResponseHeader("content-type")
+
+ this._setHeaderProperties(this.header)
+
+ if (this.text === null && req._responseType) {
+ this.body = this.xhr.response
+ } else {
+ this.body = this.req.method === "HEAD" ? null : this._parseBody(this.text ? this.text : this.xhr.response)
+ }
+ } // eslint-disable-next-line new-cap
+
+ ResponseBase(Response.prototype)
+ /**
+ * Parse the given body `str`.
+ *
+ * Used for auto-parsing of bodies. Parsers
+ * are defined on the `superagent.parse` object.
+ *
+ * @param {String} str
+ * @return {Mixed}
+ * @api private
+ */
+
+ Response.prototype._parseBody = function (str) {
+ var parse = request.parse[this.type]
+
+ if (this.req._parser) {
+ return this.req._parser(this, str)
+ }
+
+ if (!parse && isJSON(this.type)) {
+ parse = request.parse["application/json"]
+ }
+
+ return parse && str && (str.length > 0 || str instanceof Object) ? parse(str) : null
+ }
+ /**
+ * Return an `Error` representative of this response.
+ *
+ * @return {Error}
+ * @api public
+ */
+
+ Response.prototype.toError = function () {
+ var req = this.req
+ var method = req.method
+ var url = req.url
+ var msg = "cannot ".concat(method, " ").concat(url, " (").concat(this.status, ")")
+ var err = new Error(msg)
+ err.status = this.status
+ err.method = method
+ err.url = url
+ return err
+ }
+ /**
+ * Expose `Response`.
+ */
+
+ request.Response = Response
+ /**
+ * Initialize a new `Request` with the given `method` and `url`.
+ *
+ * @param {String} method
+ * @param {String} url
+ * @api public
+ */
+
+ function Request(method, url) {
+ var self = this
+ this._query = this._query || []
+ this.method = method
+ this.url = url
+ this.header = {} // preserves header name case
+
+ this._header = {} // coerces header names to lowercase
+
+ this.on("end", function () {
+ var err = null
+ var res = null
+
+ try {
+ res = new Response(self)
+ } catch (err2) {
+ err = new Error("Parser is unable to parse the response")
+ err.parse = true
+ err.original = err2 // issue #675: return the raw response if the response parsing fails
+
+ if (self.xhr) {
+ // ie9 doesn't have 'response' property
+ err.rawResponse = typeof self.xhr.responseType === "undefined" ? self.xhr.responseText : self.xhr.response // issue #876: return the http status code if the response parsing fails
+
+ err.status = self.xhr.status ? self.xhr.status : null
+ err.statusCode = err.status // backwards-compat only
+ } else {
+ err.rawResponse = null
+ err.status = null
+ }
+
+ return self.callback(err)
+ }
+
+ self.emit("response", res)
+ var new_err
+
+ try {
+ if (!self._isResponseOK(res)) {
+ new_err = new Error(res.statusText || "Unsuccessful HTTP response")
+ }
+ } catch (err2) {
+ new_err = err2 // ok() callback can throw
+ } // #1000 don't catch errors from the callback to avoid double calling it
+
+ if (new_err) {
+ new_err.original = err
+ new_err.response = res
+ new_err.status = res.status
+ self.callback(new_err, res)
+ } else {
+ self.callback(null, res)
+ }
+ })
+ }
+ /**
+ * Mixin `Emitter` and `RequestBase`.
+ */
+ // eslint-disable-next-line new-cap
+
+ Emitter(Request.prototype) // eslint-disable-next-line new-cap
+
+ RequestBase(Request.prototype)
+ /**
+ * Set Content-Type to `type`, mapping values from `request.types`.
+ *
+ * Examples:
+ *
+ * superagent.types.xml = 'application/xml';
+ *
+ * request.post('/')
+ * .type('xml')
+ * .send(xmlstring)
+ * .end(callback);
+ *
+ * request.post('/')
+ * .type('application/xml')
+ * .send(xmlstring)
+ * .end(callback);
+ *
+ * @param {String} type
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ Request.prototype.type = function (type) {
+ this.set("Content-Type", request.types[type] || type)
+ return this
+ }
+ /**
+ * Set Accept to `type`, mapping values from `request.types`.
+ *
+ * Examples:
+ *
+ * superagent.types.json = 'application/json';
+ *
+ * request.get('/agent')
+ * .accept('json')
+ * .end(callback);
+ *
+ * request.get('/agent')
+ * .accept('application/json')
+ * .end(callback);
+ *
+ * @param {String} accept
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ Request.prototype.accept = function (type) {
+ this.set("Accept", request.types[type] || type)
+ return this
+ }
+ /**
+ * Set Authorization field value with `user` and `pass`.
+ *
+ * @param {String} user
+ * @param {String} [pass] optional in case of using 'bearer' as type
+ * @param {Object} options with 'type' property 'auto', 'basic' or 'bearer' (default 'basic')
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ Request.prototype.auth = function (user, pass, options) {
+ if (arguments.length === 1) pass = ""
+
+ if (_typeof(pass) === "object" && pass !== null) {
+ // pass is optional and can be replaced with options
+ options = pass
+ pass = ""
+ }
+
+ if (!options) {
+ options = {
+ type: typeof btoa === "function" ? "basic" : "auto",
+ }
+ }
+
+ var encoder = function encoder(string) {
+ if (typeof btoa === "function") {
+ return btoa(string)
+ }
+
+ throw new Error("Cannot use basic auth, btoa is not a function")
+ }
+
+ return this._auth(user, pass, options, encoder)
+ }
+ /**
+ * Add query-string `val`.
+ *
+ * Examples:
+ *
+ * request.get('/shoes')
+ * .query('size=10')
+ * .query({ color: 'blue' })
+ *
+ * @param {Object|String} val
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ Request.prototype.query = function (val) {
+ if (typeof val !== "string") val = serialize(val)
+ if (val) this._query.push(val)
+ return this
+ }
+ /**
+ * Queue the given `file` as an attachment to the specified `field`,
+ * with optional `options` (or filename).
+ *
+ * ``` js
+ * request.post('/upload')
+ * .attach('content', new Blob(['
hey! '], { type: "text/html"}))
+ * .end(callback);
+ * ```
+ *
+ * @param {String} field
+ * @param {Blob|File} file
+ * @param {String|Object} options
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ Request.prototype.attach = function (field, file, options) {
+ if (file) {
+ if (this._data) {
+ throw new Error("superagent can't mix .send() and .attach()")
+ }
+
+ this._getFormData().append(field, file, options || file.name)
+ }
+
+ return this
+ }
+
+ Request.prototype._getFormData = function () {
+ if (!this._formData) {
+ this._formData = new root.FormData()
+ }
+
+ return this._formData
+ }
+ /**
+ * Invoke the callback with `err` and `res`
+ * and handle arity check.
+ *
+ * @param {Error} err
+ * @param {Response} res
+ * @api private
+ */
+
+ Request.prototype.callback = function (err, res) {
+ if (this._shouldRetry(err, res)) {
+ return this._retry()
+ }
+
+ var fn = this._callback
+ this.clearTimeout()
+
+ if (err) {
+ if (this._maxRetries) err.retries = this._retries - 1
+ this.emit("error", err)
+ }
+
+ fn(err, res)
+ }
+ /**
+ * Invoke callback with x-domain error.
+ *
+ * @api private
+ */
+
+ Request.prototype.crossDomainError = function () {
+ var err = new Error(
+ "Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc."
+ )
+ err.crossDomain = true
+ err.status = this.status
+ err.method = this.method
+ err.url = this.url
+ this.callback(err)
+ } // This only warns, because the request is still likely to work
+
+ Request.prototype.agent = function () {
+ console.warn("This is not supported in browser version of superagent")
+ return this
+ }
+
+ Request.prototype.buffer = Request.prototype.ca
+ Request.prototype.ca = Request.prototype.agent // This throws, because it can't send/receive data as expected
+
+ Request.prototype.write = function () {
+ throw new Error("Streaming is not supported in browser version of superagent")
+ }
+
+ Request.prototype.pipe = Request.prototype.write
+ /**
+ * Check if `obj` is a host object,
+ * we don't want to serialize these :)
+ *
+ * @param {Object} obj host object
+ * @return {Boolean} is a host object
+ * @api private
+ */
+
+ Request.prototype._isHost = function (obj) {
+ // Native objects stringify to [object File], [object Blob], [object FormData], etc.
+ return obj && _typeof(obj) === "object" && !Array.isArray(obj) && Object.prototype.toString.call(obj) !== "[object Object]"
+ }
+ /**
+ * Initiate request, invoking callback `fn(res)`
+ * with an instanceof `Response`.
+ *
+ * @param {Function} fn
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ Request.prototype.end = function (fn) {
+ if (this._endCalled) {
+ console.warn("Warning: .end() was called twice. This is not supported in superagent")
+ }
+
+ this._endCalled = true // store callback
+
+ this._callback = fn || noop // querystring
+
+ this._finalizeQueryString()
+
+ this._end()
+ }
+
+ Request.prototype._setUploadTimeout = function () {
+ var self = this // upload timeout it's wokrs only if deadline timeout is off
+
+ if (this._uploadTimeout && !this._uploadTimeoutTimer) {
+ this._uploadTimeoutTimer = setTimeout(function () {
+ self._timeoutError("Upload timeout of ", self._uploadTimeout, "ETIMEDOUT")
+ }, this._uploadTimeout)
+ }
+ } // eslint-disable-next-line complexity
+
+ Request.prototype._end = function () {
+ if (this._aborted) return this.callback(new Error("The request has been aborted even before .end() was called"))
+ var self = this
+ this.xhr = request.getXHR()
+ var xhr = this.xhr
+ var data = this._formData || this._data
+
+ this._setTimeouts() // state change
+
+ xhr.onreadystatechange = function () {
+ var readyState = xhr.readyState
+
+ if (readyState >= 2 && self._responseTimeoutTimer) {
+ clearTimeout(self._responseTimeoutTimer)
+ }
+
+ if (readyState !== 4) {
+ return
+ } // In IE9, reads to any property (e.g. status) off of an aborted XHR will
+ // result in the error "Could not complete the operation due to error c00c023f"
+
+ var status
+
+ try {
+ status = xhr.status
+ } catch (err) {
+ status = 0
+ }
+
+ if (!status) {
+ if (self.timedout || self._aborted) return
+ return self.crossDomainError()
+ }
+
+ self.emit("end")
+ } // progress
+
+ var handleProgress = function handleProgress(direction, e) {
+ if (e.total > 0) {
+ e.percent = (e.loaded / e.total) * 100
+
+ if (e.percent === 100) {
+ clearTimeout(self._uploadTimeoutTimer)
+ }
+ }
+
+ e.direction = direction
+ self.emit("progress", e)
+ }
+
+ if (this.hasListeners("progress")) {
+ try {
+ xhr.addEventListener("progress", handleProgress.bind(null, "download"))
+
+ if (xhr.upload) {
+ xhr.upload.addEventListener("progress", handleProgress.bind(null, "upload"))
+ }
+ } catch (err) {
+ // Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist.
+ // Reported here:
+ // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context
+ }
+ }
+
+ if (xhr.upload) {
+ this._setUploadTimeout()
+ } // initiate request
+
+ try {
+ if (this.username && this.password) {
+ xhr.open(this.method, this.url, true, this.username, this.password)
+ } else {
+ xhr.open(this.method, this.url, true)
+ }
+ } catch (err) {
+ // see #1149
+ return this.callback(err)
+ } // CORS
+
+ if (this._withCredentials) xhr.withCredentials = true // body
+
+ if (!this._formData && this.method !== "GET" && this.method !== "HEAD" && typeof data !== "string" && !this._isHost(data)) {
+ // serialize stuff
+ var contentType = this._header["content-type"]
+
+ var _serialize = this._serializer || request.serialize[contentType ? contentType.split(";")[0] : ""]
+
+ if (!_serialize && isJSON(contentType)) {
+ _serialize = request.serialize["application/json"]
+ }
+
+ if (_serialize) data = _serialize(data)
+ } // set header fields
+
+ for (var field in this.header) {
+ if (this.header[field] === null) continue
+ if (Object.prototype.hasOwnProperty.call(this.header, field)) xhr.setRequestHeader(field, this.header[field])
+ }
+
+ if (this._responseType) {
+ xhr.responseType = this._responseType
+ } // send stuff
+
+ this.emit("request", this) // IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing)
+ // We need null here if data is undefined
+
+ xhr.send(typeof data === "undefined" ? null : data)
+ }
+
+ request.agent = function () {
+ return new Agent()
+ }
+ ;["GET", "POST", "OPTIONS", "PATCH", "PUT", "DELETE"].forEach(function (method) {
+ Agent.prototypeethod.toLowerCase()] = function (url, fn) {
+ var req = new request.Request(method, url)
+
+ this._setDefaults(req)
+
+ if (fn) {
+ req.end(fn)
+ }
+
+ return req
+ }
+ })
+ Agent.prototype.del = Agent.prototype.delete
+ /**
+ * GET `url` with optional callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} [data] or fn
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+ request.get = function (url, data, fn) {
+ var req = request("GET", url)
+
+ if (typeof data === "function") {
+ fn = data
+ data = null
+ }
+
+ if (data) req.query(data)
+ if (fn) req.end(fn)
+ return req
+ }
+ /**
+ * HEAD `url` with optional callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} [data] or fn
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+ request.head = function (url, data, fn) {
+ var req = request("HEAD", url)
+
+ if (typeof data === "function") {
+ fn = data
+ data = null
+ }
+
+ if (data) req.query(data)
+ if (fn) req.end(fn)
+ return req
+ }
+ /**
+ * OPTIONS query to `url` with optional callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} [data] or fn
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+ request.options = function (url, data, fn) {
+ var req = request("OPTIONS", url)
+
+ if (typeof data === "function") {
+ fn = data
+ data = null
+ }
+
+ if (data) req.send(data)
+ if (fn) req.end(fn)
+ return req
+ }
+ /**
+ * DELETE `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed} [data]
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+ function del(url, data, fn) {
+ var req = request("DELETE", url)
+
+ if (typeof data === "function") {
+ fn = data
+ data = null
+ }
+
+ if (data) req.send(data)
+ if (fn) req.end(fn)
+ return req
+ }
+
+ request.del = del
+ request.delete = del
+ /**
+ * PATCH `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed} [data]
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+ request.patch = function (url, data, fn) {
+ var req = request("PATCH", url)
+
+ if (typeof data === "function") {
+ fn = data
+ data = null
+ }
+
+ if (data) req.send(data)
+ if (fn) req.end(fn)
+ return req
+ }
+ /**
+ * POST `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed} [data]
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+ request.post = function (url, data, fn) {
+ var req = request("POST", url)
+
+ if (typeof data === "function") {
+ fn = data
+ data = null
+ }
+
+ if (data) req.send(data)
+ if (fn) req.end(fn)
+ return req
+ }
+ /**
+ * PUT `url` with optional `data` and callback `fn(res)`.
+ *
+ * @param {String} url
+ * @param {Mixed|Function} [data] or fn
+ * @param {Function} [fn]
+ * @return {Request}
+ * @api public
+ */
+
+ request.put = function (url, data, fn) {
+ var req = request("PUT", url)
+
+ if (typeof data === "function") {
+ fn = data
+ data = null
+ }
+
+ if (data) req.send(data)
+ if (fn) req.end(fn)
+ return req
+ }
+ },
+ { "./agent-base": 3, "./is-object": 4, "./request-base": 6, "./response-base": 7, "component-emitter": 1, "fast-safe-stringify": 2 },
+ ],
+ 6: [
+ function (require, module, exports) {
+ "use strict"
+
+ function _typeof(obj) {
+ if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
+ _typeof = function _typeof(obj) {
+ return typeof obj
+ }
+ } else {
+ _typeof = function _typeof(obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj
+ }
+ }
+ return _typeof(obj)
+ }
+
+ /**
+ * Module of mixed-in functions shared between node and client code
+ */
+ var isObject = require("./is-object")
+ /**
+ * Expose `RequestBase`.
+ */
+
+ module.exports = RequestBase
+ /**
+ * Initialize a new `RequestBase`.
+ *
+ * @api public
+ */
+
+ function RequestBase(obj) {
+ if (obj) return mixin(obj)
+ }
+ /**
+ * Mixin the prototype properties.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+ function mixin(obj) {
+ for (var key in RequestBase.prototype) {
+ if (Object.prototype.hasOwnProperty.call(RequestBase.prototype, key)) obj[key] = RequestBase.prototype[key]
+ }
+
+ return obj
+ }
+ /**
+ * Clear previous timeout.
+ *
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ RequestBase.prototype.clearTimeout = function () {
+ clearTimeout(this._timer)
+ clearTimeout(this._responseTimeoutTimer)
+ clearTimeout(this._uploadTimeoutTimer)
+ delete this._timer
+ delete this._responseTimeoutTimer
+ delete this._uploadTimeoutTimer
+ return this
+ }
+ /**
+ * Override default response body parser
+ *
+ * This function will be called to convert incoming data into request.body
+ *
+ * @param {Function}
+ * @api public
+ */
+
+ RequestBase.prototype.parse = function (fn) {
+ this._parser = fn
+ return this
+ }
+ /**
+ * Set format of binary response body.
+ * In browser valid formats are 'blob' and 'arraybuffer',
+ * which return Blob and ArrayBuffer, respectively.
+ *
+ * In Node all values result in Buffer.
+ *
+ * Examples:
+ *
+ * req.get('/')
+ * .responseType('blob')
+ * .end(callback);
+ *
+ * @param {String} val
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ RequestBase.prototype.responseType = function (val) {
+ this._responseType = val
+ return this
+ }
+ /**
+ * Override default request body serializer
+ *
+ * This function will be called to convert data set via .send or .attach into payload to send
+ *
+ * @param {Function}
+ * @api public
+ */
+
+ RequestBase.prototype.serialize = function (fn) {
+ this._serializer = fn
+ return this
+ }
+ /**
+ * Set timeouts.
+ *
+ * - response timeout is time between sending request and receiving the first byte of the response. Includes DNS and connection time.
+ * - deadline is the time from start of the request to receiving response body in full. If the deadline is too short large files may not load at all on slow connections.
+ * - upload is the time since last bit of data was sent or received. This timeout works only if deadline timeout is off
+ *
+ * Value of 0 or false means no timeout.
+ *
+ * @param {Number|Object} ms or {response, deadline}
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ RequestBase.prototype.timeout = function (options) {
+ if (!options || _typeof(options) !== "object") {
+ this._timeout = options
+ this._responseTimeout = 0
+ this._uploadTimeout = 0
+ return this
+ }
+
+ for (var option in options) {
+ if (Object.prototype.hasOwnProperty.call(options, option)) {
+ switch (option) {
+ case "deadline":
+ this._timeout = options.deadline
+ break
+
+ case "response":
+ this._responseTimeout = options.response
+ break
+
+ case "upload":
+ this._uploadTimeout = options.upload
+ break
+
+ default:
+ console.warn("Unknown timeout option", option)
+ }
+ }
+ }
+
+ return this
+ }
+ /**
+ * Set number of retry attempts on error.
+ *
+ * Failed requests will be retried 'count' times if timeout or err.code >= 500.
+ *
+ * @param {Number} count
+ * @param {Function} [fn]
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ RequestBase.prototype.retry = function (count, fn) {
+ // Default to 1 if no count passed or true
+ if (arguments.length === 0 || count === true) count = 1
+ if (count <= 0) count = 0
+ this._maxRetries = count
+ this._retries = 0
+ this._retryCallback = fn
+ return this
+ }
+
+ var ERROR_CODES = ["ECONNRESET", "ETIMEDOUT", "EADDRINFO", "ESOCKETTIMEDOUT"]
+ /**
+ * Determine if a request should be retried.
+ * (Borrowed from segmentio/superagent-retry)
+ *
+ * @param {Error} err an error
+ * @param {Response} [res] response
+ * @returns {Boolean} if segment should be retried
+ */
+
+ RequestBase.prototype._shouldRetry = function (err, res) {
+ if (!this._maxRetries || this._retries++ >= this._maxRetries) {
+ return false
+ }
+
+ if (this._retryCallback) {
+ try {
+ var override = this._retryCallback(err, res)
+
+ if (override === true) return true
+ if (override === false) return false // undefined falls back to defaults
+ } catch (err2) {
+ console.error(err2)
+ }
+ }
+
+ if (res && res.status && res.status >= 500 && res.status !== 501) return true
+
+ if (err) {
+ if (err.code && ERROR_CODES.indexOf(err.code) !== -1) return true // Superagent timeout
+
+ if (err.timeout && err.code === "ECONNABORTED") return true
+ if (err.crossDomain) return true
+ }
+
+ return false
+ }
+ /**
+ * Retry request
+ *
+ * @return {Request} for chaining
+ * @api private
+ */
+
+ RequestBase.prototype._retry = function () {
+ this.clearTimeout() // node
+
+ if (this.req) {
+ this.req = null
+ this.req = this.request()
+ }
+
+ this._aborted = false
+ this.timedout = false
+ return this._end()
+ }
+ /**
+ * Promise support
+ *
+ * @param {Function} resolve
+ * @param {Function} [reject]
+ * @return {Request}
+ */
+
+ RequestBase.prototype.then = function (resolve, reject) {
+ var _this = this
+
+ if (!this._fullfilledPromise) {
+ var self = this
+
+ if (this._endCalled) {
+ console.warn("Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises")
+ }
+
+ this._fullfilledPromise = new Promise(function (resolve, reject) {
+ self.on("abort", function () {
+ var err = new Error("Aborted")
+ err.code = "ABORTED"
+ err.status = _this.status
+ err.method = _this.method
+ err.url = _this.url
+ reject(err)
+ })
+ self.end(function (err, res) {
+ if (err) reject(err)
+ else resolve(res)
+ })
+ })
+ }
+
+ return this._fullfilledPromise.then(resolve, reject)
+ }
+
+ RequestBase.prototype.catch = function (cb) {
+ return this.then(undefined, cb)
+ }
+ /**
+ * Allow for extension
+ */
+
+ RequestBase.prototype.use = function (fn) {
+ fn(this)
+ return this
+ }
+
+ RequestBase.prototype.ok = function (cb) {
+ if (typeof cb !== "function") throw new Error("Callback required")
+ this._okCallback = cb
+ return this
+ }
+
+ RequestBase.prototype._isResponseOK = function (res) {
+ if (!res) {
+ return false
+ }
+
+ if (this._okCallback) {
+ return this._okCallback(res)
+ }
+
+ return res.status >= 200 && res.status < 300
+ }
+ /**
+ * Get request header `field`.
+ * Case-insensitive.
+ *
+ * @param {String} field
+ * @return {String}
+ * @api public
+ */
+
+ RequestBase.prototype.get = function (field) {
+ return this._header[field.toLowerCase()]
+ }
+ /**
+ * Get case-insensitive header `field` value.
+ * This is a deprecated internal API. Use `.get(field)` instead.
+ *
+ * (getHeader is no longer used internally by the superagent code base)
+ *
+ * @param {String} field
+ * @return {String}
+ * @api private
+ * @deprecated
+ */
+
+ RequestBase.prototype.getHeader = RequestBase.prototype.get
+ /**
+ * Set header `field` to `val`, or multiple fields with one object.
+ * Case-insensitive.
+ *
+ * Examples:
+ *
+ * req.get('/')
+ * .set('Accept', 'application/json')
+ * .set('X-API-Key', 'foobar')
+ * .end(callback);
+ *
+ * req.get('/')
+ * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' })
+ * .end(callback);
+ *
+ * @param {String|Object} field
+ * @param {String} val
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ RequestBase.prototype.set = function (field, val) {
+ if (isObject(field)) {
+ for (var key in field) {
+ if (Object.prototype.hasOwnProperty.call(field, key)) this.set(key, field[key])
+ }
+
+ return this
+ }
+
+ this._header[field.toLowerCase()] = val
+ this.header[field] = val
+ return this
+ } // eslint-disable-next-line valid-jsdoc
+
+ /**
+ * Remove header `field`.
+ * Case-insensitive.
+ *
+ * Example:
+ *
+ * req.get('/')
+ * .unset('User-Agent')
+ * .end(callback);
+ *
+ * @param {String} field field name
+ */
+
+ RequestBase.prototype.unset = function (field) {
+ delete this._header[field.toLowerCase()]
+ delete this.header[field]
+ return this
+ }
+ /**
+ * Write the field `name` and `val`, or multiple fields with one object
+ * for "multipart/form-data" request bodies.
+ *
+ * ``` js
+ * request.post('/upload')
+ * .field('foo', 'bar')
+ * .end(callback);
+ *
+ * request.post('/upload')
+ * .field({ foo: 'bar', baz: 'qux' })
+ * .end(callback);
+ * ```
+ *
+ * @param {String|Object} name name of field
+ * @param {String|Blob|File|Buffer|fs.ReadStream} val value of field
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ RequestBase.prototype.field = function (name, val) {
+ // name should be either a string or an object.
+ if (name === null || undefined === name) {
+ throw new Error(".field(name, val) name can not be empty")
+ }
+
+ if (this._data) {
+ throw new Error(".field() can't be used if .send() is used. Please use only .send() or only .field() & .attach()")
+ }
+
+ if (isObject(name)) {
+ for (var key in name) {
+ if (Object.prototype.hasOwnProperty.call(name, key)) this.field(key, name[key])
+ }
+
+ return this
+ }
+
+ if (Array.isArray(val)) {
+ for (var i in val) {
+ if (Object.prototype.hasOwnProperty.call(val, i)) this.field(name, val[i])
+ }
+
+ return this
+ } // val should be defined now
+
+ if (val === null || undefined === val) {
+ throw new Error(".field(name, val) val can not be empty")
+ }
+
+ if (typeof val === "boolean") {
+ val = String(val)
+ }
+
+ this._getFormData().append(name, val)
+
+ return this
+ }
+ /**
+ * Abort the request, and clear potential timeout.
+ *
+ * @return {Request} request
+ * @api public
+ */
+
+ RequestBase.prototype.abort = function () {
+ if (this._aborted) {
+ return this
+ }
+
+ this._aborted = true
+ if (this.xhr) this.xhr.abort() // browser
+
+ if (this.req) this.req.abort() // node
+
+ this.clearTimeout()
+ this.emit("abort")
+ return this
+ }
+
+ RequestBase.prototype._auth = function (user, pass, options, base64Encoder) {
+ switch (options.type) {
+ case "basic":
+ this.set("Authorization", "Basic ".concat(base64Encoder("".concat(user, ":").concat(pass))))
+ break
+
+ case "auto":
+ this.username = user
+ this.password = pass
+ break
+
+ case "bearer":
+ // usage would be .auth(accessToken, { type: 'bearer' })
+ this.set("Authorization", "Bearer ".concat(user))
+ break
+
+ default:
+ break
+ }
+
+ return this
+ }
+ /**
+ * Enable transmission of cookies with x-domain requests.
+ *
+ * Note that for this to work the origin must not be
+ * using "Access-Control-Allow-Origin" with a wildcard,
+ * and also must set "Access-Control-Allow-Credentials"
+ * to "true".
+ *
+ * @api public
+ */
+
+ RequestBase.prototype.withCredentials = function (on) {
+ // This is browser-only functionality. Node side is no-op.
+ if (on === undefined) on = true
+ this._withCredentials = on
+ return this
+ }
+ /**
+ * Set the max redirects to `n`. Does nothing in browser XHR implementation.
+ *
+ * @param {Number} n
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ RequestBase.prototype.redirects = function (n) {
+ this._maxRedirects = n
+ return this
+ }
+ /**
+ * Maximum size of buffered response body, in bytes. Counts uncompressed size.
+ * Default 200MB.
+ *
+ * @param {Number} n number of bytes
+ * @return {Request} for chaining
+ */
+
+ RequestBase.prototype.maxResponseSize = function (n) {
+ if (typeof n !== "number") {
+ throw new TypeError("Invalid argument")
+ }
+
+ this._maxResponseSize = n
+ return this
+ }
+ /**
+ * Convert to a plain javascript object (not JSON string) of scalar properties.
+ * Note as this method is designed to return a useful non-this value,
+ * it cannot be chained.
+ *
+ * @return {Object} describing method, url, and data of this request
+ * @api public
+ */
+
+ RequestBase.prototype.toJSON = function () {
+ return {
+ method: this.method,
+ url: this.url,
+ data: this._data,
+ headers: this._header,
+ }
+ }
+ /**
+ * Send `data` as the request body, defaulting the `.type()` to "json" when
+ * an object is given.
+ *
+ * Examples:
+ *
+ * // manual json
+ * request.post('/user')
+ * .type('json')
+ * .send('{"name":"tj"}')
+ * .end(callback)
+ *
+ * // auto json
+ * request.post('/user')
+ * .send({ name: 'tj' })
+ * .end(callback)
+ *
+ * // manual x-www-form-urlencoded
+ * request.post('/user')
+ * .type('form')
+ * .send('name=tj')
+ * .end(callback)
+ *
+ * // auto x-www-form-urlencoded
+ * request.post('/user')
+ * .type('form')
+ * .send({ name: 'tj' })
+ * .end(callback)
+ *
+ * // defaults to x-www-form-urlencoded
+ * request.post('/user')
+ * .send('name=tobi')
+ * .send('species=ferret')
+ * .end(callback)
+ *
+ * @param {String|Object} data
+ * @return {Request} for chaining
+ * @api public
+ */
+ // eslint-disable-next-line complexity
+
+ RequestBase.prototype.send = function (data) {
+ var isObj = isObject(data)
+ var type = this._header["content-type"]
+
+ if (this._formData) {
+ throw new Error(".send() can't be used if .attach() or .field() is used. Please use only .send() or only .field() & .attach()")
+ }
+
+ if (isObj && !this._data) {
+ if (Array.isArray(data)) {
+ this._data = []
+ } else if (!this._isHost(data)) {
+ this._data = {}
+ }
+ } else if (data && this._data && this._isHost(this._data)) {
+ throw new Error("Can't merge these send calls")
+ } // merge
+
+ if (isObj && isObject(this._data)) {
+ for (var key in data) {
+ if (Object.prototype.hasOwnProperty.call(data, key)) this._data[key] = data[key]
+ }
+ } else if (typeof data === "string") {
+ // default to x-www-form-urlencoded
+ if (!type) this.type("form")
+ type = this._header["content-type"]
+
+ if (type === "application/x-www-form-urlencoded") {
+ this._data = this._data ? "".concat(this._data, "&").concat(data) : data
+ } else {
+ this._data = (this._data || "") + data
+ }
+ } else {
+ this._data = data
+ }
+
+ if (!isObj || this._isHost(data)) {
+ return this
+ } // default to json
+
+ if (!type) this.type("json")
+ return this
+ }
+ /**
+ * Sort `querystring` by the sort function
+ *
+ *
+ * Examples:
+ *
+ * // default order
+ * request.get('/user')
+ * .query('name=Nick')
+ * .query('search=Manny')
+ * .sortQuery()
+ * .end(callback)
+ *
+ * // customized sort function
+ * request.get('/user')
+ * .query('name=Nick')
+ * .query('search=Manny')
+ * .sortQuery(function(a, b){
+ * return a.length - b.length;
+ * })
+ * .end(callback)
+ *
+ *
+ * @param {Function} sort
+ * @return {Request} for chaining
+ * @api public
+ */
+
+ RequestBase.prototype.sortQuery = function (sort) {
+ // _sort default to true but otherwise can be a function or boolean
+ this._sort = typeof sort === "undefined" ? true : sort
+ return this
+ }
+ /**
+ * Compose querystring to append to req.url
+ *
+ * @api private
+ */
+
+ RequestBase.prototype._finalizeQueryString = function () {
+ var query = this._query.join("&")
+
+ if (query) {
+ this.url += (this.url.indexOf("?") >= 0 ? "&" : "?") + query
+ }
+
+ this._query.length = 0 // Makes the call idempotent
+
+ if (this._sort) {
+ var index = this.url.indexOf("?")
+
+ if (index >= 0) {
+ var queryArr = this.url.substring(index + 1).split("&")
+
+ if (typeof this._sort === "function") {
+ queryArr.sort(this._sort)
+ } else {
+ queryArr.sort()
+ }
+
+ this.url = this.url.substring(0, index) + "?" + queryArr.join("&")
+ }
+ }
+ } // For backwards compat only
+
+ RequestBase.prototype._appendQueryString = function () {
+ console.warn("Unsupported")
+ }
+ /**
+ * Invoke callback with timeout error.
+ *
+ * @api private
+ */
+
+ RequestBase.prototype._timeoutError = function (reason, timeout, errno) {
+ if (this._aborted) {
+ return
+ }
+
+ var err = new Error("".concat(reason + timeout, "ms exceeded"))
+ err.timeout = timeout
+ err.code = "ECONNABORTED"
+ err.errno = errno
+ this.timedout = true
+ this.abort()
+ this.callback(err)
+ }
+
+ RequestBase.prototype._setTimeouts = function () {
+ var self = this // deadline
+
+ if (this._timeout && !this._timer) {
+ this._timer = setTimeout(function () {
+ self._timeoutError("Timeout of ", self._timeout, "ETIME")
+ }, this._timeout)
+ } // response timeout
+
+ if (this._responseTimeout && !this._responseTimeoutTimer) {
+ this._responseTimeoutTimer = setTimeout(function () {
+ self._timeoutError("Response timeout of ", self._responseTimeout, "ETIMEDOUT")
+ }, this._responseTimeout)
+ }
+ }
+ },
+ { "./is-object": 4 },
+ ],
+ 7: [
+ function (require, module, exports) {
+ "use strict"
+
+ /**
+ * Module dependencies.
+ */
+ var utils = require("./utils")
+ /**
+ * Expose `ResponseBase`.
+ */
+
+ module.exports = ResponseBase
+ /**
+ * Initialize a new `ResponseBase`.
+ *
+ * @api public
+ */
+
+ function ResponseBase(obj) {
+ if (obj) return mixin(obj)
+ }
+ /**
+ * Mixin the prototype properties.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+ function mixin(obj) {
+ for (var key in ResponseBase.prototype) {
+ if (Object.prototype.hasOwnProperty.call(ResponseBase.prototype, key)) obj[key] = ResponseBase.prototype[key]
+ }
+
+ return obj
+ }
+ /**
+ * Get case-insensitive `field` value.
+ *
+ * @param {String} field
+ * @return {String}
+ * @api public
+ */
+
+ ResponseBase.prototype.get = function (field) {
+ return this.header[field.toLowerCase()]
+ }
+ /**
+ * Set header related properties:
+ *
+ * - `.type` the content type without params
+ *
+ * A response of "Content-Type: text/plain; charset=utf-8"
+ * will provide you with a `.type` of "text/plain".
+ *
+ * @param {Object} header
+ * @api private
+ */
+
+ ResponseBase.prototype._setHeaderProperties = function (header) {
+ // TODO: moar!
+ // TODO: make this a util
+ // content-type
+ var ct = header["content-type"] || ""
+ this.type = utils.type(ct) // params
+
+ var params = utils.params(ct)
+
+ for (var key in params) {
+ if (Object.prototype.hasOwnProperty.call(params, key)) this[key] = params[key]
+ }
+
+ this.links = {} // links
+
+ try {
+ if (header.link) {
+ this.links = utils.parseLinks(header.link)
+ }
+ } catch (err) {
+ // ignore
+ }
+ }
+ /**
+ * Set flags such as `.ok` based on `status`.
+ *
+ * For example a 2xx response will give you a `.ok` of __true__
+ * whereas 5xx will be __false__ and `.error` will be __true__. The
+ * `.clientError` and `.serverError` are also available to be more
+ * specific, and `.statusType` is the class of error ranging from 1..5
+ * sometimes useful for mapping respond colors etc.
+ *
+ * "sugar" properties are also defined for common cases. Currently providing:
+ *
+ * - .noContent
+ * - .badRequest
+ * - .unauthorized
+ * - .notAcceptable
+ * - .notFound
+ *
+ * @param {Number} status
+ * @api private
+ */
+
+ ResponseBase.prototype._setStatusProperties = function (status) {
+ var type = (status / 100) | 0 // status / class
+
+ this.statusCode = status
+ this.status = this.statusCode
+ this.statusType = type // basics
+
+ this.info = type === 1
+ this.ok = type === 2
+ this.redirect = type === 3
+ this.clientError = type === 4
+ this.serverError = type === 5
+ this.error = type === 4 || type === 5 ? this.toError() : false // sugar
+
+ this.created = status === 201
+ this.accepted = status === 202
+ this.noContent = status === 204
+ this.badRequest = status === 400
+ this.unauthorized = status === 401
+ this.notAcceptable = status === 406
+ this.forbidden = status === 403
+ this.notFound = status === 404
+ this.unprocessableEntity = status === 422
+ }
+ },
+ { "./utils": 8 },
+ ],
+ 8: [
+ function (require, module, exports) {
+ "use strict"
+
+ /**
+ * Return the mime type for the given `str`.
+ *
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+ exports.type = function (str) {
+ return str.split(/ *; */).shift()
+ }
+ /**
+ * Return header field parameters.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+ exports.params = function (str) {
+ return str.split(/ *; */).reduce(function (obj, str) {
+ var parts = str.split(/ *= */)
+ var key = parts.shift()
+ var val = parts.shift()
+ if (key && val) obj[key] = val
+ return obj
+ }, {})
+ }
+ /**
+ * Parse Link header fields.
+ *
+ * @param {String} str
+ * @return {Object}
+ * @api private
+ */
+
+ exports.parseLinks = function (str) {
+ return str.split(/ *, */).reduce(function (obj, str) {
+ var parts = str.split(/ *; */)
+ var url = parts[0].slice(1, -1)
+ var rel = parts[1].split(/ *= */)[1].slice(1, -1)
+ obj[rel] = url
+ return obj
+ }, {})
+ }
+ /**
+ * Strip content related fields from `header`.
+ *
+ * @param {Object} header
+ * @return {Object} header
+ * @api private
+ */
+
+ exports.cleanHeader = function (header, changesOrigin) {
+ delete header["content-type"]
+ delete header["content-length"]
+ delete header["transfer-encoding"]
+ delete header.host // secuirty
+
+ if (changesOrigin) {
+ delete header.authorization
+ delete header.cookie
+ }
+
+ return header
+ }
+ },
+ {},
+ ],
+ },
+ {},
+ [5]
+ )(5)
+ })
+ /* mousetrap v1.6.3 craig.is/killing/mice */
+ ;(function (q, u, c) {
+ function v(a, b, g) {
+ a.addEventListener ? a.addEventListener(b, g, !1) : a.attachEvent("on" + b, g)
+ }
+ function z(a) {
+ if ("keypress" == a.type) {
+ var b = String.fromCharCode(a.which)
+ a.shiftKey || (b = b.toLowerCase())
+ return b
+ }
+ return n[a.which] ? n[a.which] : r[a.which] ? r[a.which] : String.fromCharCode(a.which).toLowerCase()
+ }
+ function F(a) {
+ var b = []
+ a.shiftKey && b.push("shift")
+ a.altKey && b.push("alt")
+ a.ctrlKey && b.push("ctrl")
+ a.metaKey && b.push("meta")
+ return b
+ }
+ function w(a) {
+ return "shift" == a || "ctrl" == a || "alt" == a || "meta" == a
+ }
+ function A(a, b) {
+ var g,
+ d = []
+ var e = a
+ "+" === e ? (e = ["+"]) : ((e = e.replace(/\+{2}/g, "+plus")), (e = e.split("+")))
+ for (g = 0; g < e.length; ++g) {
+ var m = e[g]
+ B] && (m = B])
+ b && "keypress" != b && C] && ((m = C]), d.push("shift"))
+ w(m) && d.push(m)
+ }
+ e = m
+ g = b
+ if (!g) {
+ if (!p) {
+ p = {}
+ for (var c in n) (95 < c && 112 > c) || (n.hasOwnProperty(c) && (p[n[c]] = c))
+ }
+ g = p[e] ? "keydown" : "keypress"
+ }
+ "keypress" == g && d.length && (g = "keydown")
+ return { key: m, modifiers: d, action: g }
+ }
+ function D(a, b) {
+ return null === a || a === u ? !1 : a === b ? !0 : D(a.parentNode, b)
+ }
+ function d(a) {
+ function b(a) {
+ a = a || {}
+ var b = !1,
+ l
+ for (l in p) a[l] ? (b = !0) : (p[l] = 0)
+ b || (x = !1)
+ }
+ function g(a, b, t, f, g, d) {
+ var l,
+ E = [],
+ h = t.type
+ if (!k._callbacks[a]) return []
+ "keyup" == h && w(a) && (b = [a])
+ for (l = 0; l < k._callbacks[a].length; ++l) {
+ var c = k._callbacks[a][l]
+ if ((f || !c.seq || p[c.seq] == c.level) && h == c.action) {
+ var e
+ ;(e = "keypress" == h && !t.metaKey && !t.ctrlKey) || ((e = c.modifiers), (e = b.sort().join(",") === e.sort().join(",")))
+ e && ((e = f && c.seq == f && c.level == d), ((!f && c.combo == g) || e) && k._callbacks[a].splice(l, 1), E.push(c))
+ }
+ }
+ return E
+ }
+ function c(a, b, c, f) {
+ k.stopCallback(b, b.target || b.srcElement, c, f) ||
+ !1 !== a(b, c) ||
+ (b.preventDefault ? b.preventDefault() : (b.returnValue = !1), b.stopPropagation ? b.stopPropagation() : (b.cancelBubble = !0))
+ }
+ function e(a) {
+ "number" !== typeof a.which && (a.which = a.keyCode)
+ var b = z(a)
+ b && ("keyup" == a.type && y === b ? (y = !1) : k.handleKey(b, F(a), a))
+ }
+ function m(a, g, t, f) {
+ function h(c) {
+ return function () {
+ x = c
+ ++p[a]
+ clearTimeout(q)
+ q = setTimeout(b, 1e3)
+ }
+ }
+ function l(g) {
+ c(t, g, a)
+ "keyup" !== f && (y = z(g))
+ setTimeout(b, 10)
+ }
+ for (var d = (p[a] = 0); d < g.length; ++d) {
+ var e = d + 1 === g.length ? l : h(f || A(g[d + 1]).action)
+ n(g[d], e, f, a, d)
+ }
+ }
+ function n(a, b, c, f, d) {
+ k._directMap[a + ":" + c] = b
+ a = a.replace(/\s+/g, " ")
+ var e = a.split(" ")
+ 1 < e.length
+ ? m(a, e, b, c)
+ : ((c = A(a, c)),
+ (k._callbacks[c.key] = k._callbacks[c.key] || []),
+ g(c.key, c.modifiers, { type: c.action }, f, a, d),
+ k._callbacks[c.key][f ? "unshift" : "push"]({ callback: b, modifiers: c.modifiers, action: c.action, seq: f, level: d, combo: a }))
+ }
+ var k = this
+ a = a || u
+ if (!(k instanceof d)) return new d(a)
+ k.target = a
+ k._callbacks = {}
+ k._directMap = {}
+ var p = {},
+ q,
+ y = !1,
+ r = !1,
+ x = !1
+ k._handleKey = function (a, d, e) {
+ var f = g(a, d, e),
+ h
+ d = {}
+ var k = 0,
+ l = !1
+ for (h = 0; h < f.length; ++h) f[h].seq && (k = Math.max(k, f[h].level))
+ for (h = 0; h < f.length; ++h) f[h].seq ? f[h].level == k && ((l = !0), (d[f[h].seq] = 1), c(f[h].callback, e, f[h].combo, f[h].seq)) : l || c(f[h].callback, e, f[h].combo)
+ f = "keypress" == e.type && r
+ e.type != x || w(a) || f || b(d)
+ r = l && "keydown" == e.type
+ }
+ k._bindMultiple = function (a, b, c) {
+ for (var d = 0; d < a.length; ++d) n(a[d], b, c)
+ }
+ v(a, "keypress", e)
+ v(a, "keydown", e)
+ v(a, "keyup", e)
+ }
+ if (q) {
+ var n = {
+ 8: "backspace",
+ 9: "tab",
+ 13: "enter",
+ 16: "shift",
+ 17: "ctrl",
+ 18: "alt",
+ 20: "capslock",
+ 27: "esc",
+ 32: "space",
+ 33: "pageup",
+ 34: "pagedown",
+ 35: "end",
+ 36: "home",
+ 37: "left",
+ 38: "up",
+ 39: "right",
+ 40: "down",
+ 45: "ins",
+ 46: "del",
+ 91: "meta",
+ 93: "meta",
+ 224: "meta",
+ },
+ r = { 106: "*", 107: "+", 109: "-", 110: ".", 111: "/", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'" },
+ C = {
+ "~": "`",
+ "!": "1",
+ "@": "2",
+ "#": "3",
+ $: "4",
+ "%": "5",
+ "^": "6",
+ "&": "7",
+ "*": "8",
+ "(": "9",
+ ")": "0",
+ _: "-",
+ "+": "=",
+ ":": ";",
+ '"': "'",
+ "<": ",",
+ ">": ".",
+ "?": "/",
+ "|": "\\",
+ },
+ B = { option: "alt", command: "meta", return: "enter", escape: "esc", plus: "+", mod: /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? "meta" : "ctrl" },
+ p
+ for (c = 1; 20 > c; ++c) n[111 + c] = "f" + c
+ for (c = 0; 9 >= c; ++c) n[c + 96] = c.toString()
+ d.prototype.bind = function (a, b, c) {
+ a = a instanceof Array ? a : [a]
+ this._bindMultiple.call(this, a, b, c)
+ return this
+ }
+ d.prototype.unbind = function (a, b) {
+ return this.bind.call(this, a, function () {}, b)
+ }
+ d.prototype.trigger = function (a, b) {
+ if (this._directMap[a + ":" + b]) this._directMap[a + ":" + b]({}, a)
+ return this
+ }
+ d.prototype.reset = function () {
+ this._callbacks = {}
+ this._directMap = {}
+ return this
+ }
+ d.prototype.stopCallback = function (a, b) {
+ if (-1 < (" " + b.className + " ").indexOf(" mousetrap ") || D(b, this.target)) return !1
+ if ("composedPath" in a && "function" === typeof a.composedPath) {
+ var c = a.composedPath()[0]
+ c !== a.target && (b = c)
+ }
+ return "INPUT" == b.tagName || "SELECT" == b.tagName || "TEXTAREA" == b.tagName || b.isContentEditable
+ }
+ d.prototype.handleKey = function () {
+ return this._handleKey.apply(this, arguments)
+ }
+ d.addKeycodes = function (a) {
+ for (var b in a) a.hasOwnProperty(b) && (n[b] = a[b])
+ p = null
+ }
+ d.init = function () {
+ var a = d(u),
+ b
+ for (b in a)
+ "_" !== b.charAt(0) &&
+ (d[b] = (function (b) {
+ return function () {
+ return a[b].apply(a, arguments)
+ }
+ })(b))
+ }
+ d.init()
+ q.Mousetrap = d
+ "undefined" !== typeof module && module.exports && (module.exports = d)
+ "function" === typeof define &&
+ define.amd &&
+ define(function () {
+ return d
+ })
+ }
+ })("undefined" !== typeof window ? window : null, "undefined" !== typeof window ? document : null)
+ /*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */
+ !(function (e, t) {
+ "use strict"
+ "object" == typeof module && "object" == typeof module.exports
+ ? (module.exports = e.document
+ ? t(e, !0)
+ : function (e) {
+ if (!e.document) throw new Error("jQuery requires a window with a document")
+ return t(e)
+ })
+ : t(e)
+ })("undefined" != typeof window ? window : this, function (C, e) {
+ "use strict"
+ var t = [],
+ E = C.document,
+ r = Object.getPrototypeOf,
+ s = t.slice,
+ g = t.concat,
+ u = t.push,
+ i = t.indexOf,
+ n = {},
+ o = n.toString,
+ v = n.hasOwnProperty,
+ a = v.toString,
+ l = a.call(Object),
+ y = {},
+ m = function (e) {
+ return "function" == typeof e && "number" != typeof e.nodeType
+ },
+ x = function (e) {
+ return null != e && e === e.window
+ },
+ c = { type: !0, src: !0, nonce: !0, noModule: !0 }
+ function b(e, t, n) {
+ var r,
+ i,
+ o = (n = n || E).createElement("script")
+ if (((o.text = e), t)) for (r in c) (i = t[r] || (t.getAttribute && t.getAttribute(r))) && o.setAttribute(r, i)
+ n.head.appendChild(o).parentNode.removeChild(o)
+ }
+ function w(e) {
+ return null == e ? e + "" : "object" == typeof e || "function" == typeof e ? n[o.call(e)] || "object" : typeof e
+ }
+ var f = "3.4.1",
+ k = function (e, t) {
+ return new k.fn.init(e, t)
+ },
+ p = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g
+ function d(e) {
+ var t = !!e && "length" in e && e.length,
+ n = w(e)
+ return !m(e) && !x(e) && ("array" === n || 0 === t || ("number" == typeof t && 0 < t && t - 1 in e))
+ }
+ ;(k.fn = k.prototype =
+ {
+ jquery: f,
+ constructor: k,
+ length: 0,
+ toArray: function () {
+ return s.call(this)
+ },
+ get: function (e) {
+ return null == e ? s.call(this) : e < 0 ? this[e + this.length] : this[e]
+ },
+ pushStack: function (e) {
+ var t = k.merge(this.constructor(), e)
+ return (t.prevObject = this), t
+ },
+ each: function (e) {
+ return k.each(this, e)
+ },
+ map: function (n) {
+ return this.pushStack(
+ k.map(this, function (e, t) {
+ return n.call(e, t, e)
+ })
+ )
+ },
+ slice: function () {
+ return this.pushStack(s.apply(this, arguments))
+ },
+ first: function () {
+ return this.eq(0)
+ },
+ last: function () {
+ return this.eq(-1)
+ },
+ eq: function (e) {
+ var t = this.length,
+ n = +e + (e < 0 ? t : 0)
+ return this.pushStack(0 <= n && n < t ? [this[n]] : [])
+ },
+ end: function () {
+ return this.prevObject || this.constructor()
+ },
+ push: u,
+ sort: t.sort,
+ splice: t.splice,
+ }),
+ (k.extend = k.fn.extend =
+ function () {
+ var e,
+ t,
+ n,
+ r,
+ i,
+ o,
+ a = arguments[0] || {},
+ s = 1,
+ u = arguments.length,
+ l = !1
+ for ("boolean" == typeof a && ((l = a), (a = arguments[s] || {}), s++), "object" == typeof a || m(a) || (a = {}), s === u && ((a = this), s--); s < u; s++)
+ if (null != (e = arguments[s]))
+ for (t in e)
+ (r = e[t]),
+ "__proto__" !== t &&
+ a !== r &&
+ (l && r && (k.isPlainObject(r) || (i = Array.isArray(r)))
+ ? ((n = a[t]), (o = i && !Array.isArray(n) ? [] : i || k.isPlainObject(n) ? n : {}), (i = !1), (a[t] = k.extend(l, o, r)))
+ : void 0 !== r && (a[t] = r))
+ return a
+ }),
+ k.extend({
+ expando: "jQuery" + (f + Math.random()).replace(/\D/g, ""),
+ isReady: !0,
+ error: function (e) {
+ throw new Error(e)
+ },
+ noop: function () {},
+ isPlainObject: function (e) {
+ var t, n
+ return !(!e || "[object Object]" !== o.call(e)) && (!(t = r(e)) || ("function" == typeof (n = v.call(t, "constructor") && t.constructor) && a.call(n) === l))
+ },
+ isEmptyObject: function (e) {
+ var t
+ for (t in e) return !1
+ return !0
+ },
+ globalEval: function (e, t) {
+ b(e, { nonce: t && t.nonce })
+ },
+ each: function (e, t) {
+ var n,
+ r = 0
+ if (d(e)) {
+ for (n = e.length; r < n; r++) if (!1 === t.call(e[r], r, e[r])) break
+ } else for (r in e) if (!1 === t.call(e[r], r, e[r])) break
+ return e
+ },
+ trim: function (e) {
+ return null == e ? "" : (e + "").replace(p, "")
+ },
+ makeArray: function (e, t) {
+ var n = t || []
+ return null != e && (d(Object(e)) ? k.merge(n, "string" == typeof e ? [e] : e) : u.call(n, e)), n
+ },
+ inArray: function (e, t, n) {
+ return null == t ? -1 : i.call(t, e, n)
+ },
+ merge: function (e, t) {
+ for (var n = +t.length, r = 0, i = e.length; r < n; r++) e[i++] = t[r]
+ return (e.length = i), e
+ },
+ grep: function (e, t, n) {
+ for (var r = [], i = 0, o = e.length, a = !n; i < o; i++) !t(e[i], i) !== a && r.push(e[i])
+ return r
+ },
+ map: function (e, t, n) {
+ var r,
+ i,
+ o = 0,
+ a = []
+ if (d(e)) for (r = e.length; o < r; o++) null != (i = t(e[o], o, n)) && a.push(i)
+ else for (o in e) null != (i = t(e[o], o, n)) && a.push(i)
+ return g.apply([], a)
+ },
+ guid: 1,
+ support: y,
+ }),
+ "function" == typeof Symbol && (k.fn[Symbol.iterator] = t[Symbol.iterator]),
+ k.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "), function (e, t) {
+ n["[object " + t + "]"] = t.toLowerCase()
+ })
+ var h = (function (n) {
+ var e,
+ d,
+ b,
+ o,
+ i,
+ h,
+ f,
+ g,
+ w,
+ u,
+ l,
+ T,
+ C,
+ a,
+ E,
+ v,
+ s,
+ c,
+ y,
+ k = "sizzle" + 1 * new Date(),
+ m = n.document,
+ S = 0,
+ r = 0,
+ p = ue(),
+ x = ue(),
+ N = ue(),
+ A = ue(),
+ D = function (e, t) {
+ return e === t && (l = !0), 0
+ },
+ j = {}.hasOwnProperty,
+ t = [],
+ q = t.pop,
+ L = t.push,
+ H = t.push,
+ O = t.slice,
+ P = function (e, t) {
+ for (var n = 0, r = e.length; n < r; n++) if (e[n] === t) return n
+ return -1
+ },
+ R = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+ M = "[\\x20\\t\\r\\n\\f]",
+ I = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+",
+ W = "\\[" + M + "*(" + I + ")(?:" + M + "*([*^$|!~]?=)" + M + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + I + "))|)" + M + "*\\]",
+ $ = ":(" + I + ")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|" + W + ")*)|.*)\\)|)",
+ F = new RegExp(M + "+", "g"),
+ B = new RegExp("^" + M + "+|((?:^|[^\\\\])(?:\\\\.)*)" + M + "+$", "g"),
+ _ = new RegExp("^" + M + "*," + M + "*"),
+ z = new RegExp("^" + M + "*([>+~]|" + M + ")" + M + "*"),
+ U = new RegExp(M + "|>"),
+ X = new RegExp($),
+ V = new RegExp("^" + I + "$"),
+ G = {
+ ID: new RegExp("^#(" + I + ")"),
+ CLASS: new RegExp("^\\.(" + I + ")"),
+ TAG: new RegExp("^(" + I + "|[*])"),
+ ATTR: new RegExp("^" + W),
+ PSEUDO: new RegExp("^" + $),
+ CHILD: new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + M + "*(even|odd|(([+-]|)(\\d*)n|)" + M + "*(?:([+-]|)" + M + "*(\\d+)|))" + M + "*\\)|)", "i"),
+ bool: new RegExp("^(?:" + R + ")$", "i"),
+ needsContext: new RegExp("^" + M + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + M + "*((?:-\\d)?\\d*)" + M + "*\\)|)(?=[^-]|$)", "i"),
+ },
+ Y = /HTML$/i,
+ Q = /^(?:input|select|textarea|button)$/i,
+ J = /^h\d$/i,
+ K = /^[^{]+\{\s*\[native \w/,
+ Z = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+ ee = /[+~]/,
+ te = new RegExp("\\\\([\\da-f]{1,6}" + M + "?|(" + M + ")|.)", "ig"),
+ ne = function (e, t, n) {
+ var r = "0x" + t - 65536
+ return r != r || n ? t : r < 0 ? String.fromCharCode(r + 65536) : String.fromCharCode((r >> 10) | 55296, (1023 & r) | 56320)
+ },
+ re = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
+ ie = function (e, t) {
+ return t ? ("\0" === e ? "\ufffd" : e.slice(0, -1) + "\\" + e.charCodeAt(e.length - 1).toString(16) + " ") : "\\" + e
+ },
+ oe = function () {
+ T()
+ },
+ ae = be(
+ function (e) {
+ return !0 === e.disabled && "fieldset" === e.nodeName.toLowerCase()
+ },
+ { dir: "parentNode", next: "legend" }
+ )
+ try {
+ H.apply((t = O.call(m.childNodes)), m.childNodes), t.childNodes.length].nodeType
+ } catch (e) {
+ H = {
+ apply: t.length
+ ? function (e, t) {
+ L.apply(e, O.call(t))
+ }
+ : function (e, t) {
+ var n = e.length,
+ r = 0
+ while ((e[n++] = t[r++]));
+ e.length = n - 1
+ },
+ }
+ }
+ function se(t, e, n, r) {
+ var i,
+ o,
+ a,
+ s,
+ u,
+ l,
+ c,
+ f = e && e.ownerDocument,
+ p = e ? e.nodeType : 9
+ if (((n = n || []), "string" != typeof t || !t || (1 !== p && 9 !== p && 11 !== p))) return n
+ if (!r && ((e ? e.ownerDocument || e : m) !== C && T(e), (e = e || C), E)) {
+ if (11 !== p && (u = Z.exec(t)))
+ if ((i = u[1])) {
+ if (9 === p) {
+ if (!(a = e.getElementById(i))) return n
+ if (a.id === i) return n.push(a), n
+ } else if (f && (a = f.getElementById(i)) && y(e, a) && a.id === i) return n.push(a), n
+ } else {
+ if (u[2]) return H.apply(n, e.getElementsByTagName(t)), n
+ if ((i = u[3]) && d.getElementsByClassName && e.getElementsByClassName) return H.apply(n, e.getElementsByClassName(i)), n
+ }
+ if (d.qsa && !A[t + " "] && (!v || !v.test(t)) && (1 !== p || "object" !== e.nodeName.toLowerCase())) {
+ if (((c = t), (f = e), 1 === p && U.test(t))) {
+ ;(s = e.getAttribute("id")) ? (s = s.replace(re, ie)) : e.setAttribute("id", (s = k)), (o = (l = h(t)).length)
+ while (o--) l[o] = "#" + s + " " + xe(l[o])
+ ;(c = l.join(",")), (f = (ee.test(t) && ye(e.parentNode)) || e)
+ }
+ try {
+ return H.apply(n, f.querySelectorAll(c)), n
+ } catch (e) {
+ A(t, !0)
+ } finally {
+ s === k && e.removeAttribute("id")
+ }
+ }
+ }
+ return g(t.replace(B, "$1"), e, n, r)
+ }
+ function ue() {
+ var r = []
+ return function e(t, n) {
+ return r.push(t + " ") > b.cacheLength && delete e[r.shift()], (e[t + " "] = n)
+ }
+ }
+ function le(e) {
+ return (e[k] = !0), e
+ }
+ function ce(e) {
+ var t = C.createElement("fieldset")
+ try {
+ return !!e(t)
+ } catch (e) {
+ return !1
+ } finally {
+ t.parentNode && t.parentNode.removeChild(t), (t = null)
+ }
+ }
+ function fe(e, t) {
+ var n = e.split("|"),
+ r = n.length
+ while (r--) b.attrHandle[n[r]] = t
+ }
+ function pe(e, t) {
+ var n = t && e,
+ r = n && 1 === e.nodeType && 1 === t.nodeType && e.sourceIndex - t.sourceIndex
+ if (r) return r
+ if (n) while ((n = n.nextSibling)) if (n === t) return -1
+ return e ? 1 : -1
+ }
+ function de(t) {
+ return function (e) {
+ return "input" === e.nodeName.toLowerCase() && e.type === t
+ }
+ }
+ function he(n) {
+ return function (e) {
+ var t = e.nodeName.toLowerCase()
+ return ("input" === t || "button" === t) && e.type === n
+ }
+ }
+ function ge(t) {
+ return function (e) {
+ return "form" in e
+ ? e.parentNode && !1 === e.disabled
+ ? "label" in e
+ ? "label" in e.parentNode
+ ? e.parentNode.disabled === t
+ : e.disabled === t
+ : e.isDisabled === t || (e.isDisabled !== !t && ae(e) === t)
+ : e.disabled === t
+ : "label" in e && e.disabled === t
+ }
+ }
+ function ve(a) {
+ return le(function (o) {
+ return (
+ (o = +o),
+ le(function (e, t) {
+ var n,
+ r = a([], e.length, o),
+ i = r.length
+ while (i--) e[(n = r[i])] && (e[n] = !(t[n] = e[n]))
+ })
+ )
+ })
+ }
+ function ye(e) {
+ return e && "undefined" != typeof e.getElementsByTagName && e
+ }
+ for (e in ((d = se.support = {}),
+ (i = se.isXML =
+ function (e) {
+ var t = e.namespaceURI,
+ n = (e.ownerDocument || e).documentElement
+ return !Y.test(t || (n && n.nodeName) || "HTML")
+ }),
+ (T = se.setDocument =
+ function (e) {
+ var t,
+ n,
+ r = e ? e.ownerDocument || e : m
+ return (
+ r !== C &&
+ 9 === r.nodeType &&
+ r.documentElement &&
+ ((a = (C = r).documentElement),
+ (E = !i(C)),
+ m !== C && (n = C.defaultView) && n.top !== n && (n.addEventListener ? n.addEventListener("unload", oe, !1) : n.attachEvent && n.attachEvent("onunload", oe)),
+ (d.attributes = ce(function (e) {
+ return (e.className = "i"), !e.getAttribute("className")
+ })),
+ (d.getElementsByTagName = ce(function (e) {
+ return e.appendChild(C.createComment("")), !e.getElementsByTagName("*").length
+ })),
+ (d.getElementsByClassName = K.test(C.getElementsByClassName)),
+ (d.getById = ce(function (e) {
+ return (a.appendChild(e).id = k), !C.getElementsByName || !C.getElementsByName(k).length
+ })),
+ d.getById
+ ? ((b.filter.ID = function (e) {
+ var t = e.replace(te, ne)
+ return function (e) {
+ return e.getAttribute("id") === t
+ }
+ }),
+ (b.find.ID = function (e, t) {
+ if ("undefined" != typeof t.getElementById && E) {
+ var n = t.getElementById(e)
+ return n ? [n] : []
+ }
+ }))
+ : ((b.filter.ID = function (e) {
+ var n = e.replace(te, ne)
+ return function (e) {
+ var t = "undefined" != typeof e.getAttributeNode && e.getAttributeNode("id")
+ return t && t.value === n
+ }
+ }),
+ (b.find.ID = function (e, t) {
+ if ("undefined" != typeof t.getElementById && E) {
+ var n,
+ r,
+ i,
+ o = t.getElementById(e)
+ if (o) {
+ if ((n = o.getAttributeNode("id")) && n.value === e) return [o]
+ ;(i = t.getElementsByName(e)), (r = 0)
+ while ((o = i[r++])) if ((n = o.getAttributeNode("id")) && n.value === e) return [o]
+ }
+ return []
+ }
+ })),
+ (b.find.TAG = d.getElementsByTagName
+ ? function (e, t) {
+ return "undefined" != typeof t.getElementsByTagName ? t.getElementsByTagName(e) : d.qsa ? t.querySelectorAll(e) : void 0
+ }
+ : function (e, t) {
+ var n,
+ r = [],
+ i = 0,
+ o = t.getElementsByTagName(e)
+ if ("*" === e) {
+ while ((n = o[i++])) 1 === n.nodeType && r.push(n)
+ return r
+ }
+ return o
+ }),
+ (b.find.CLASS =
+ d.getElementsByClassName &&
+ function (e, t) {
+ if ("undefined" != typeof t.getElementsByClassName && E) return t.getElementsByClassName(e)
+ }),
+ (s = []),
+ (v = []),
+ (d.qsa = K.test(C.querySelectorAll)) &&
+ (ce(function (e) {
+ ;(a.appendChild(e).innerHTML = "
"),
+ e.querySelectorAll("sallowcapture^='']").length && v.push("[*^$]=" + M + "*(?:''|\"\")"),
+ e.querySelectorAll("[selected]").length || v.push("\\[" + M + "*(?:value|" + R + ")"),
+ e.querySelectorAll("[id~=" + k + "-]").length || v.push("~="),
+ e.querySelectorAll(":checked").length || v.push(":checked"),
+ e.querySelectorAll("a#" + k + "+*").length || v.push(".#.+[+~]")
+ }),
+ ce(function (e) {
+ var t = C.createElement("input")
+ t.setAttribute("type", "hidden"),
+ e.appendChild(t).setAttribute("name", "D"),
+ e.querySelectorAll("[name=d]").length && v.push("name" + M + "*[*^$|!~]?="),
+ 2 !== e.querySelectorAll(":enabled").length && v.push(":enabled", ":disabled"),
+ (a.appendChild(e).disabled = !0),
+ 2 !== e.querySelectorAll(":disabled").length && v.push(":enabled", ":disabled"),
+ e.querySelectorAll("*,:x"),
+ v.push(",.*:")
+ })),
+ (d.matchesSelector = K.test((c = a.matches || a.webkitMatchesSelector || a.mozMatchesSelector || a.oMatchesSelector || a.msMatchesSelector))) &&
+ ce(function (e) {
+ ;(d.disconnectedMatch = c.call(e, "*")), c.call(e, "[s!='']:x"), s.push("!=", $)
+ }),
+ (v = v.length && new RegExp(v.join("|"))),
+ (s = s.length && new RegExp(s.join("|"))),
+ (t = K.test(a.compareDocumentPosition)),
+ (y =
+ t || K.test(a.contains)
+ ? function (e, t) {
+ var n = 9 === e.nodeType ? e.documentElement : e,
+ r = t && t.parentNode
+ return e === r || !(!r || 1 !== r.nodeType || !(n.contains ? n.contains(r) : e.compareDocumentPosition && 16 & e.compareDocumentPosition(r)))
+ }
+ : function (e, t) {
+ if (t) while ((t = t.parentNode)) if (t === e) return !0
+ return !1
+ }),
+ (D = t
+ ? function (e, t) {
+ if (e === t) return (l = !0), 0
+ var n = !e.compareDocumentPosition - !t.compareDocumentPosition
+ return (
+ n ||
+ (1 & (n = (e.ownerDocument || e) === (t.ownerDocument || t) ? e.compareDocumentPosition(t) : 1) || (!d.sortDetached && t.compareDocumentPosition(e) === n)
+ ? e === C || (e.ownerDocument === m && y(m, e))
+ ? -1
+ : t === C || (t.ownerDocument === m && y(m, t))
+ ? 1
+ : u
+ ? P(u, e) - P(u, t)
+ : 0
+ : 4 & n
+ ? -1
+ : 1)
+ )
+ }
+ : function (e, t) {
+ if (e === t) return (l = !0), 0
+ var n,
+ r = 0,
+ i = e.parentNode,
+ o = t.parentNode,
+ a = [e],
+ s = [t]
+ if (!i || !o) return e === C ? -1 : t === C ? 1 : i ? -1 : o ? 1 : u ? P(u, e) - P(u, t) : 0
+ if (i === o) return pe(e, t)
+ n = e
+ while ((n = n.parentNode)) a.unshift(n)
+ n = t
+ while ((n = n.parentNode)) s.unshift(n)
+ while (a[r] === s[r]) r++
+ return r ? pe(a[r], s[r]) : a[r] === m ? -1 : s[r] === m ? 1 : 0
+ })),
+ C
+ )
+ }),
+ (se.matches = function (e, t) {
+ return se(e, null, null, t)
+ }),
+ (se.matchesSelector = function (e, t) {
+ if (((e.ownerDocument || e) !== C && T(e), d.matchesSelector && E && !A[t + " "] && (!s || !s.test(t)) && (!v || !v.test(t))))
+ try {
+ var n = c.call(e, t)
+ if (n || d.disconnectedMatch || (e.document && 11 !== e.document.nodeType)) return n
+ } catch (e) {
+ A(t, !0)
+ }
+ return 0 < se(t, C, null, [e]).length
+ }),
+ (se.contains = function (e, t) {
+ return (e.ownerDocument || e) !== C && T(e), y(e, t)
+ }),
+ (se.attr = function (e, t) {
+ ;(e.ownerDocument || e) !== C && T(e)
+ var n = b.attrHandle[t.toLowerCase()],
+ r = n && j.call(b.attrHandle, t.toLowerCase()) ? n(e, t, !E) : void 0
+ return void 0 !== r ? r : d.attributes || !E ? e.getAttribute(t) : (r = e.getAttributeNode(t)) && r.specified ? r.value : null
+ }),
+ (se.escape = function (e) {
+ return (e + "").replace(re, ie)
+ }),
+ (se.error = function (e) {
+ throw new Error("Syntax error, unrecognized expression: " + e)
+ }),
+ (se.uniqueSort = function (e) {
+ var t,
+ n = [],
+ r = 0,
+ i = 0
+ if (((l = !d.detectDuplicates), (u = !d.sortStable && e.slice(0)), e.sort(D), l)) {
+ while ((t = e[i++])) t === e[i] && (r = n.push(i))
+ while (r--) e.splice(n[r], 1)
+ }
+ return (u = null), e
+ }),
+ (o = se.getText =
+ function (e) {
+ var t,
+ n = "",
+ r = 0,
+ i = e.nodeType
+ if (i) {
+ if (1 === i || 9 === i || 11 === i) {
+ if ("string" == typeof e.textContent) return e.textContent
+ for (e = e.firstChild; e; e = e.nextSibling) n += o(e)
+ } else if (3 === i || 4 === i) return e.nodeValue
+ } else while ((t = e[r++])) n += o(t)
+ return n
+ }),
+ ((b = se.selectors =
+ {
+ cacheLength: 50,
+ createPseudo: le,
+ match: G,
+ attrHandle: {},
+ find: {},
+ relative: { ">": { dir: "parentNode", first: !0 }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: !0 }, "~": { dir: "previousSibling" } },
+ preFilter: {
+ ATTR: function (e) {
+ return (e[1] = e[1].replace(te, ne)), (e[3] = (e[3] || e[4] || e[5] || "").replace(te, ne)), "~=" === e[2] && (e[3] = " " + e[3] + " "), e.slice(0, 4)
+ },
+ CHILD: function (e) {
+ return (
+ (e[1] = e[1].toLowerCase()),
+ "nth" === e[1].slice(0, 3)
+ ? (e[3] || se.error(e[0]), (e[4] = +(e[4] ? e[5] + (e[6] || 1) : 2 * ("even" === e[3] || "odd" === e[3]))), (e[5] = +(e[7] + e[8] || "odd" === e[3])))
+ : e[3] && se.error(e[0]),
+ e
+ )
+ },
+ PSEUDO: function (e) {
+ var t,
+ n = !e[6] && e[2]
+ return G.CHILD.test(e[0])
+ ? null
+ : (e[3]
+ ? (e[2] = e[4] || e[5] || "")
+ : n && X.test(n) && (t = h(n, !0)) && (t = n.indexOf(")", n.length - t) - n.length) && ((e[0] = e[0].slice(0, t)), (e[2] = n.slice(0, t))),
+ e.slice(0, 3))
+ },
+ },
+ filter: {
+ TAG: function (e) {
+ var t = e.replace(te, ne).toLowerCase()
+ return "*" === e
+ ? function () {
+ return !0
+ }
+ : function (e) {
+ return e.nodeName && e.nodeName.toLowerCase() === t
+ }
+ },
+ CLASS: function (e) {
+ var t = p[e + " "]
+ return (
+ t ||
+ ((t = new RegExp("(^|" + M + ")" + e + "(" + M + "|$)")) &&
+ p(e, function (e) {
+ return t.test(("string" == typeof e.className && e.className) || ("undefined" != typeof e.getAttribute && e.getAttribute("class")) || "")
+ }))
+ )
+ },
+ ATTR: function (n, r, i) {
+ return function (e) {
+ var t = se.attr(e, n)
+ return null == t
+ ? "!=" === r
+ : !r ||
+ ((t += ""),
+ "=" === r
+ ? t === i
+ : "!=" === r
+ ? t !== i
+ : "^=" === r
+ ? i && 0 === t.indexOf(i)
+ : "*=" === r
+ ? i && -1 < t.indexOf(i)
+ : "$=" === r
+ ? i && t.slice(-i.length) === i
+ : "~=" === r
+ ? -1 < (" " + t.replace(F, " ") + " ").indexOf(i)
+ : "|=" === r && (t === i || t.slice(0, i.length + 1) === i + "-"))
+ }
+ },
+ CHILD: function (h, e, t, g, v) {
+ var y = "nth" !== h.slice(0, 3),
+ m = "last" !== h.slice(-4),
+ x = "of-type" === e
+ return 1 === g && 0 === v
+ ? function (e) {
+ return !!e.parentNode
+ }
+ : function (e, t, n) {
+ var r,
+ i,
+ o,
+ a,
+ s,
+ u,
+ l = y !== m ? "nextSibling" : "previousSibling",
+ c = e.parentNode,
+ f = x && e.nodeName.toLowerCase(),
+ p = !n && !x,
+ d = !1
+ if (c) {
+ if (y) {
+ while (l) {
+ a = e
+ while ((a = a[l])) if (x ? a.nodeName.toLowerCase() === f : 1 === a.nodeType) return !1
+ u = l = "only" === h && !u && "nextSibling"
+ }
+ return !0
+ }
+ if (((u = ? c.firstChild : c.lastChild]), m && p)) {
+ ;(d = (s = (r = (i = (o = (a = c)[k] || (a[k] = {}))[a.uniqueID] || (o[a.uniqueID] = {}))[h] || [])[0] === S && r[1]) && r[2]), (a = s && c.childNodes[s])
+ while ((a = (++s && a && a[l]) || (d = s = 0) || u.pop()))
+ if (1 === a.nodeType && ++d && a === e) {
+ i[h] = [S, s, d]
+ break
+ }
+ } else if ((p && (d = s = (r = (i = (o = (a = e)[k] || (a[k] = {}))[a.uniqueID] || (o[a.uniqueID] = {}))[h] || [])[0] === S && r[1]), !1 === d))
+ while ((a = (++s && a && a[l]) || (d = s = 0) || u.pop()))
+ if (
+ (x ? a.nodeName.toLowerCase() === f : 1 === a.nodeType) &&
+ ++d &&
+ (p && ((i = (o = a[k] || (a[k] = {}))[a.uniqueID] || (o[a.uniqueID] = {}))[h] = [S, d]), a === e)
+ )
+ break
+ return (d -= v) === g || (d % g == 0 && 0 <= d / g)
+ }
+ }
+ },
+ PSEUDO: function (e, o) {
+ var t,
+ a = b.pseudos[e] || b.setFilters[e.toLowerCase()] || se.error("unsupported pseudo: " + e)
+ return a[k]
+ ? a(o)
+ : 1 < a.length
+ ? ((t = [e, e, "", o]),
+ b.setFilters.hasOwnProperty(e.toLowerCase())
+ ? le(function (e, t) {
+ var n,
+ r = a(e, o),
+ i = r.length
+ while (i--) e[(n = P(e, r[i]))] = !(t[n] = r[i])
+ })
+ : function (e) {
+ return a(e, 0, t)
+ })
+ : a
+ },
+ },
+ pseudos: {
+ not: le(function (e) {
+ var r = [],
+ i = [],
+ s = f(e.replace(B, "$1"))
+ return s[k]
+ ? le(function (e, t, n, r) {
+ var i,
+ o = s(e, null, r, []),
+ a = e.length
+ while (a--) (i = o[a]) && (e[a] = !(t[a] = i))
+ })
+ : function (e, t, n) {
+ return (r[0] = e), s(r, null, n, i), (r[0] = null), !i.pop()
+ }
+ }),
+ has: le(function (t) {
+ return function (e) {
+ return 0 < se(t, e).length
+ }
+ }),
+ contains: le(function (t) {
+ return (
+ (t = t.replace(te, ne)),
+ function (e) {
+ return -1 < (e.textContent || o(e)).indexOf(t)
+ }
+ )
+ }),
+ lang: le(function (n) {
+ return (
+ V.test(n || "") || se.error("unsupported lang: " + n),
+ (n = n.replace(te, ne).toLowerCase()),
+ function (e) {
+ var t
+ do {
+ if ((t = E ? e.lang : e.getAttribute("xml:lang") || e.getAttribute("lang"))) return (t = t.toLowerCase()) === n || 0 === t.indexOf(n + "-")
+ } while ((e = e.parentNode) && 1 === e.nodeType)
+ return !1
+ }
+ )
+ }),
+ target: function (e) {
+ var t = n.location && n.location.hash
+ return t && t.slice(1) === e.id
+ },
+ root: function (e) {
+ return e === a
+ },
+ focus: function (e) {
+ return e === C.activeElement && (!C.hasFocus || C.hasFocus()) && !!(e.type || e.href || ~e.tabIndex)
+ },
+ enabled: ge(!1),
+ disabled: ge(!0),
+ checked: function (e) {
+ var t = e.nodeName.toLowerCase()
+ return ("input" === t && !!e.checked) || ("option" === t && !!e.selected)
+ },
+ selected: function (e) {
+ return e.parentNode && e.parentNode.selectedIndex, !0 === e.selected
+ },
+ empty: function (e) {
+ for (e = e.firstChild; e; e = e.nextSibling) if (e.nodeType < 6) return !1
+ return !0
+ },
+ parent: function (e) {
+ return !b.pseudos.empty(e)
+ },
+ header: function (e) {
+ return J.test(e.nodeName)
+ },
+ input: function (e) {
+ return Q.test(e.nodeName)
+ },
+ button: function (e) {
+ var t = e.nodeName.toLowerCase()
+ return ("input" === t && "button" === e.type) || "button" === t
+ },
+ text: function (e) {
+ var t
+ return "input" === e.nodeName.toLowerCase() && "text" === e.type && (null == (t = e.getAttribute("type")) || "text" === t.toLowerCase())
+ },
+ first: ve(function () {
+ return [0]
+ }),
+ last: ve(function (e, t) {
+ return [t - 1]
+ }),
+ eq: ve(function (e, t, n) {
+ return [n < 0 ? n + t : n]
+ }),
+ even: ve(function (e, t) {
+ for (var n = 0; n < t; n += 2) e.push(n)
+ return e
+ }),
+ odd: ve(function (e, t) {
+ for (var n = 1; n < t; n += 2) e.push(n)
+ return e
+ }),
+ lt: ve(function (e, t, n) {
+ for (var r = n < 0 ? n + t : t < n ? t : n; 0 <= --r; ) e.push(r)
+ return e
+ }),
+ gt: ve(function (e, t, n) {
+ for (var r = n < 0 ? n + t : n; ++r < t; ) e.push(r)
+ return e
+ }),
+ },
+ }).pseudos.nth = b.pseudos.eq),
+ { radio: !0, checkbox: !0, file: !0, password: !0, image: !0 }))
+ b.pseudos[e] = de(e)
+ for (e in { submit: !0, reset: !0 }) b.pseudos[e] = he(e)
+ function me() {}
+ function xe(e) {
+ for (var t = 0, n = e.length, r = ""; t < n; t++) r += e[t].value
+ return r
+ }
+ function be(s, e, t) {
+ var u = e.dir,
+ l = e.next,
+ c = l || u,
+ f = t && "parentNode" === c,
+ p = r++
+ return e.first
+ ? function (e, t, n) {
+ while ((e = e[u])) if (1 === e.nodeType || f) return s(e, t, n)
+ return !1
+ }
+ : function (e, t, n) {
+ var r,
+ i,
+ o,
+ a = [S, p]
+ if (n) {
+ while ((e = e[u])) if ((1 === e.nodeType || f) && s(e, t, n)) return !0
+ } else
+ while ((e = e[u]))
+ if (1 === e.nodeType || f)
+ if (((i = (o = e[k] || (e[k] = {}))[e.uniqueID] || (o[e.uniqueID] = {})), l && l === e.nodeName.toLowerCase())) e = e[u] || e
+ else {
+ if ((r = i[c]) && r[0] === S && r[1] === p) return (a[2] = r[2])
+ if (((i[c] = a)[2] = s(e, t, n))) return !0
+ }
+ return !1
+ }
+ }
+ function we(i) {
+ return 1 < i.length
+ ? function (e, t, n) {
+ var r = i.length
+ while (r--) if (!i[r](e, t, n)) return !1
+ return !0
+ }
+ : i[0]
+ }
+ function Te(e, t, n, r, i) {
+ for (var o, a = [], s = 0, u = e.length, l = null != t; s < u; s++) (o = e[s]) && ((n && !n(o, r, i)) || (a.push(o), l && t.push(s)))
+ return a
+ }
+ function Ce(d, h, g, v, y, e) {
+ return (
+ v && !v[k] && (v = Ce(v)),
+ y && !y[k] && (y = Ce(y, e)),
+ le(function (e, t, n, r) {
+ var i,
+ o,
+ a,
+ s = [],
+ u = [],
+ l = t.length,
+ c =
+ e ||
+ (function (e, t, n) {
+ for (var r = 0, i = t.length; r < i; r++) se(e, t[r], n)
+ return n
+ })(h || "*", n.nodeType ? [n] : n, []),
+ f = !d || (!e && h) ? c : Te(c, s, d, n, r),
+ p = g ? (y || (e ? d : l || v) ? [] : t) : f
+ if ((g && g(f, p, n, r), v)) {
+ ;(i = Te(p, u)), v(i, [], n, r), (o = i.length)
+ while (o--) (a = i[o]) && (p[u[o]] = !(f[u[o]] = a))
+ }
+ if (e) {
+ if (y || d) {
+ if (y) {
+ ;(i = []), (o = p.length)
+ while (o--) (a = p[o]) && i.push((f[o] = a))
+ y(null, (p = []), i, r)
+ }
+ o = p.length
+ while (o--) (a = p[o]) && -1 < (i = y ? P(e, a) : s[o]) && (e[i] = !(t[i] = a))
+ }
+ } else (p = Te(p === t ? p.splice(l, p.length) : p)), y ? y(null, t, p, r) : H.apply(t, p)
+ })
+ )
+ }
+ function Ee(e) {
+ for (
+ var i,
+ t,
+ n,
+ r = e.length,
+ o = b.relative[e[0].type],
+ a = o || b.relative[" "],
+ s = o ? 1 : 0,
+ u = be(
+ function (e) {
+ return e === i
+ },
+ a,
+ !0
+ ),
+ l = be(
+ function (e) {
+ return -1 < P(i, e)
+ },
+ a,
+ !0
+ ),
+ c = [
+ function (e, t, n) {
+ var r = (!o && (n || t !== w)) || ((i = t).nodeType ? u(e, t, n) : l(e, t, n))
+ return (i = null), r
+ },
+ ];
+ s < r;
+ s++
+ )
+ if ((t = b.relative[e[s].type])) c = [be(we(c), t)]
+ else {
+ if ((t = b.filter[e[s].type].apply(null, e[s].matches))[k]) {
+ for (n = ++s; n < r; n++) if (b.relative[e[n].type]) break
+ return Ce(
+ 1 < s && we(c),
+ 1 < s && xe(e.slice(0, s - 1).concat({ value: " " === e[s - 2].type ? "*" : "" })).replace(B, "$1"),
+ t,
+ s < n && Ee(e.slice(s, n)),
+ n < r && Ee((e = e.slice(n))),
+ n < r && xe(e)
+ )
+ }
+ c.push(t)
+ }
+ return we(c)
+ }
+ return (
+ (me.prototype = b.filters = b.pseudos),
+ (b.setFilters = new me()),
+ (h = se.tokenize =
+ function (e, t) {
+ var n,
+ r,
+ i,
+ o,
+ a,
+ s,
+ u,
+ l = x[e + " "]
+ if (l) return t ? 0 : l.slice(0)
+ ;(a = e), (s = []), (u = b.preFilter)
+ while (a) {
+ for (o in ((n && !(r = _.exec(a))) || (r && (a = a.slice(r[0].length) || a), s.push((i = []))),
+ (n = !1),
+ (r = z.exec(a)) && ((n = r.shift()), i.push({ value: n, type: r[0].replace(B, " ") }), (a = a.slice(n.length))),
+ b.filter))
+ !(r = G[o].exec(a)) || (u[o] && !(r = u[o](r))) || ((n = r.shift()), i.push({ value: n, type: o, matches: r }), (a = a.slice(n.length)))
+ if (!n) break
+ }
+ return t ? a.length : a ? se.error(e) : x(e, s).slice(0)
+ }),
+ (f = se.compile =
+ function (e, t) {
+ var n,
+ v,
+ y,
+ m,
+ x,
+ r,
+ i = [],
+ o = [],
+ a = N[e + " "]
+ if (!a) {
+ t || (t = h(e)), (n = t.length)
+ while (n--) (a = Ee(t[n]))[k] ? i.push(a) : o.push(a)
+ ;(a = N(
+ e,
+ ((v = o),
+ (m = 0 < (y = i).length),
+ (x = 0 < v.length),
+ (r = function (e, t, n, r, i) {
+ var o,
+ a,
+ s,
+ u = 0,
+ l = "0",
+ c = e && [],
+ f = [],
+ p = w,
+ d = e || (x && b.find.TAG("*", i)),
+ h = (S += null == p ? 1 : Math.random() || 0.1),
+ g = d.length
+ for (i && (w = t === C || t || i); l !== g && null != (o = d[l]); l++) {
+ if (x && o) {
+ ;(a = 0), t || o.ownerDocument === C || (T(o), (n = !E))
+ while ((s = v[a++]))
+ if (s(o, t || C, n)) {
+ r.push(o)
+ break
+ }
+ i && (S = h)
+ }
+ m && ((o = !s && o) && u--, e && c.push(o))
+ }
+ if (((u += l), m && l !== u)) {
+ a = 0
+ while ((s = y[a++])) s(c, f, t, n)
+ if (e) {
+ if (0 < u) while (l--) c[l] || f[l] || (f[l] = q.call(r))
+ f = Te(f)
+ }
+ H.apply(r, f), i && !e && 0 < f.length && 1 < u + y.length && se.uniqueSort(r)
+ }
+ return i && ((S = h), (w = p)), c
+ }),
+ m ? le(r) : r)
+ )).selector = e
+ }
+ return a
+ }),
+ (g = se.select =
+ function (e, t, n, r) {
+ var i,
+ o,
+ a,
+ s,
+ u,
+ l = "function" == typeof e && e,
+ c = !r && h((e = l.selector || e))
+ if (((n = n || []), 1 === c.length)) {
+ if (2 < (o = c[0] = c[0].slice(0)).length && "ID" === (a = o[0]).type && 9 === t.nodeType && E && b.relative[o[1].type]) {
+ if (!(t = (b.find.ID(a.matches[0].replace(te, ne), t) || [])[0])) return n
+ l && (t = t.parentNode), (e = e.slice(o.shift().value.length))
+ }
+ i = G.needsContext.test(e) ? 0 : o.length
+ while (i--) {
+ if (((a = o[i]), b.relative[(s = a.type)])) break
+ if ((u = b.find[s]) && (r = u(a.matches[0].replace(te, ne), (ee.test(o[0].type) && ye(t.parentNode)) || t))) {
+ if ((o.splice(i, 1), !(e = r.length && xe(o)))) return H.apply(n, r), n
+ break
+ }
+ }
+ }
+ return (l || f(e, c))(r, t, !E, n, !t || (ee.test(e) && ye(t.parentNode)) || t), n
+ }),
+ (d.sortStable = k.split("").sort(D).join("") === k),
+ (d.detectDuplicates = !!l),
+ T(),
+ (d.sortDetached = ce(function (e) {
+ return 1 & e.compareDocumentPosition(C.createElement("fieldset"))
+ })),
+ ce(function (e) {
+ return (e.innerHTML = "
"), "#" === e.firstChild.getAttribute("href")
+ }) ||
+ fe("type|href|height|width", function (e, t, n) {
+ if (!n) return e.getAttribute(t, "type" === t.toLowerCase() ? 1 : 2)
+ }),
+ (d.attributes &&
+ ce(function (e) {
+ return (e.innerHTML = " "), e.firstChild.setAttribute("value", ""), "" === e.firstChild.getAttribute("value")
+ })) ||
+ fe("value", function (e, t, n) {
+ if (!n && "input" === e.nodeName.toLowerCase()) return e.defaultValue
+ }),
+ ce(function (e) {
+ return null == e.getAttribute("disabled")
+ }) ||
+ fe(R, function (e, t, n) {
+ var r
+ if (!n) return !0 === e[t] ? t.toLowerCase() : (r = e.getAttributeNode(t)) && r.specified ? r.value : null
+ }),
+ se
+ )
+ })(C)
+ ;(k.find = h),
+ (k.expr = h.selectors),
+ (k.expr[":"] = k.expr.pseudos),
+ (k.uniqueSort = k.unique = h.uniqueSort),
+ (k.text = h.getText),
+ (k.isXMLDoc = h.isXML),
+ (k.contains = h.contains),
+ (k.escapeSelector = h.escape)
+ var T = function (e, t, n) {
+ var r = [],
+ i = void 0 !== n
+ while ((e = e[t]) && 9 !== e.nodeType)
+ if (1 === e.nodeType) {
+ if (i && k(e).is(n)) break
+ r.push(e)
+ }
+ return r
+ },
+ S = function (e, t) {
+ for (var n = []; e; e = e.nextSibling) 1 === e.nodeType && e !== t && n.push(e)
+ return n
+ },
+ N = k.expr.match.needsContext
+ function A(e, t) {
+ return e.nodeName && e.nodeName.toLowerCase() === t.toLowerCase()
+ }
+ var D = /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i
+ function j(e, n, r) {
+ return m(n)
+ ? k.grep(e, function (e, t) {
+ return !!n.call(e, t, e) !== r
+ })
+ : n.nodeType
+ ? k.grep(e, function (e) {
+ return (e === n) !== r
+ })
+ : "string" != typeof n
+ ? k.grep(e, function (e) {
+ return -1 < i.call(n, e) !== r
+ })
+ : k.filter(n, e, r)
+ }
+ ;(k.filter = function (e, t, n) {
+ var r = t[0]
+ return (
+ n && (e = ":not(" + e + ")"),
+ 1 === t.length && 1 === r.nodeType
+ ? k.find.matchesSelector(r, e)
+ ? [r]
+ : []
+ : k.find.matches(
+ e,
+ k.grep(t, function (e) {
+ return 1 === e.nodeType
+ })
+ )
+ )
+ }),
+ k.fn.extend({
+ find: function (e) {
+ var t,
+ n,
+ r = this.length,
+ i = this
+ if ("string" != typeof e)
+ return this.pushStack(
+ k(e).filter(function () {
+ for (t = 0; t < r; t++) if (k.contains(i[t], this)) return !0
+ })
+ )
+ for (n = this.pushStack([]), t = 0; t < r; t++) k.find(e, i[t], n)
+ return 1 < r ? k.uniqueSort(n) : n
+ },
+ filter: function (e) {
+ return this.pushStack(j(this, e || [], !1))
+ },
+ not: function (e) {
+ return this.pushStack(j(this, e || [], !0))
+ },
+ is: function (e) {
+ return !!j(this, "string" == typeof e && N.test(e) ? k(e) : e || [], !1).length
+ },
+ })
+ var q,
+ L = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/
+ ;((k.fn.init = function (e, t, n) {
+ var r, i
+ if (!e) return this
+ if (((n = n || q), "string" == typeof e)) {
+ if (!(r = "<" === e[0] && ">" === e[e.length - 1] && 3 <= e.length ? [null, e, null] : L.exec(e)) || (!r[1] && t))
+ return !t || t.jquery ? (t || n).find(e) : this.constructor(t).find(e)
+ if (r[1]) {
+ if (((t = t instanceof k ? t[0] : t), k.merge(this, k.parseHTML(r[1], t && t.nodeType ? t.ownerDocument || t : E, !0)), D.test(r[1]) && k.isPlainObject(t)))
+ for (r in t) m(this[r]) ? this[r](t[r]) : this.attr(r, t[r])
+ return this
+ }
+ return (i = E.getElementById(r[2])) && ((this[0] = i), (this.length = 1)), this
+ }
+ return e.nodeType ? ((this[0] = e), (this.length = 1), this) : m(e) ? (void 0 !== n.ready ? n.ready(e) : e(k)) : k.makeArray(e, this)
+ }).prototype = k.fn),
+ (q = k(E))
+ var H = /^(?:parents|prev(?:Until|All))/,
+ O = { children: !0, contents: !0, next: !0, prev: !0 }
+ function P(e, t) {
+ while ((e = e[t]) && 1 !== e.nodeType);
+ return e
+ }
+ k.fn.extend({
+ has: function (e) {
+ var t = k(e, this),
+ n = t.length
+ return this.filter(function () {
+ for (var e = 0; e < n; e++) if (k.contains(this, t[e])) return !0
+ })
+ },
+ closest: function (e, t) {
+ var n,
+ r = 0,
+ i = this.length,
+ o = [],
+ a = "string" != typeof e && k(e)
+ if (!N.test(e))
+ for (; r < i; r++)
+ for (n = this[r]; n && n !== t; n = n.parentNode)
+ if (n.nodeType < 11 && (a ? -1 < a.index(n) : 1 === n.nodeType && k.find.matchesSelector(n, e))) {
+ o.push(n)
+ break
+ }
+ return this.pushStack(1 < o.length ? k.uniqueSort(o) : o)
+ },
+ index: function (e) {
+ return e ? ("string" == typeof e ? i.call(k(e), this[0]) : i.call(this, e.jquery ? e[0] : e)) : this[0] && this[0].parentNode ? this.first().prevAll().length : -1
+ },
+ add: function (e, t) {
+ return this.pushStack(k.uniqueSort(k.merge(this.get(), k(e, t))))
+ },
+ addBack: function (e) {
+ return this.add(null == e ? this.prevObject : this.prevObject.filter(e))
+ },
+ }),
+ k.each(
+ {
+ parent: function (e) {
+ var t = e.parentNode
+ return t && 11 !== t.nodeType ? t : null
+ },
+ parents: function (e) {
+ return T(e, "parentNode")
+ },
+ parentsUntil: function (e, t, n) {
+ return T(e, "parentNode", n)
+ },
+ next: function (e) {
+ return P(e, "nextSibling")
+ },
+ prev: function (e) {
+ return P(e, "previousSibling")
+ },
+ nextAll: function (e) {
+ return T(e, "nextSibling")
+ },
+ prevAll: function (e) {
+ return T(e, "previousSibling")
+ },
+ nextUntil: function (e, t, n) {
+ return T(e, "nextSibling", n)
+ },
+ prevUntil: function (e, t, n) {
+ return T(e, "previousSibling", n)
+ },
+ siblings: function (e) {
+ return S((e.parentNode || {}).firstChild, e)
+ },
+ children: function (e) {
+ return S(e.firstChild)
+ },
+ contents: function (e) {
+ return "undefined" != typeof e.contentDocument ? e.contentDocument : (A(e, "template") && (e = e.content || e), k.merge([], e.childNodes))
+ },
+ },
+ function (r, i) {
+ k.fn[r] = function (e, t) {
+ var n = k.map(this, i, e)
+ return (
+ "Until" !== r.slice(-5) && (t = e),
+ t && "string" == typeof t && (n = k.filter(t, n)),
+ 1 < this.length && (O[r] || k.uniqueSort(n), H.test(r) && n.reverse()),
+ this.pushStack(n)
+ )
+ }
+ }
+ )
+ var R = /[^\x20\t\r\n\f]+/g
+ function M(e) {
+ return e
+ }
+ function I(e) {
+ throw e
+ }
+ function W(e, t, n, r) {
+ var i
+ try {
+ e && m((i = e.promise)) ? i.call(e).done(t).fail(n) : e && m((i = e.then)) ? i.call(e, t, n) : t.apply(void 0, [e].slice(r))
+ } catch (e) {
+ n.apply(void 0, [e])
+ }
+ }
+ ;(k.Callbacks = function (r) {
+ var e, n
+ r =
+ "string" == typeof r
+ ? ((e = r),
+ (n = {}),
+ k.each(e.match(R) || [], function (e, t) {
+ n[t] = !0
+ }),
+ n)
+ : k.extend({}, r)
+ var i,
+ t,
+ o,
+ a,
+ s = [],
+ u = [],
+ l = -1,
+ c = function () {
+ for (a = a || r.once, o = i = !0; u.length; l = -1) {
+ t = u.shift()
+ while (++l < s.length) !1 === s[l].apply(t[0], t[1]) && r.stopOnFalse && ((l = s.length), (t = !1))
+ }
+ r.memory || (t = !1), (i = !1), a && (s = t ? [] : "")
+ },
+ f = {
+ add: function () {
+ return (
+ s &&
+ (t && !i && ((l = s.length - 1), u.push(t)),
+ (function n(e) {
+ k.each(e, function (e, t) {
+ m(t) ? (r.unique && f.has(t)) || s.push(t) : t && t.length && "string" !== w(t) && n(t)
+ })
+ })(arguments),
+ t && !i && c()),
+ this
+ )
+ },
+ remove: function () {
+ return (
+ k.each(arguments, function (e, t) {
+ var n
+ while (-1 < (n = k.inArray(t, s, n))) s.splice(n, 1), n <= l && l--
+ }),
+ this
+ )
+ },
+ has: function (e) {
+ return e ? -1 < k.inArray(e, s) : 0 < s.length
+ },
+ empty: function () {
+ return s && (s = []), this
+ },
+ disable: function () {
+ return (a = u = []), (s = t = ""), this
+ },
+ disabled: function () {
+ return !s
+ },
+ lock: function () {
+ return (a = u = []), t || i || (s = t = ""), this
+ },
+ locked: function () {
+ return !!a
+ },
+ fireWith: function (e, t) {
+ return a || ((t = [e, (t = t || []).slice ? t.slice() : t]), u.push(t), i || c()), this
+ },
+ fire: function () {
+ return f.fireWith(this, arguments), this
+ },
+ fired: function () {
+ return !!o
+ },
+ }
+ return f
+ }),
+ k.extend({
+ Deferred: function (e) {
+ var o = [
+ ["notify", "progress", k.Callbacks("memory"), k.Callbacks("memory"), 2],
+ ["resolve", "done", k.Callbacks("once memory"), k.Callbacks("once memory"), 0, "resolved"],
+ ["reject", "fail", k.Callbacks("once memory"), k.Callbacks("once memory"), 1, "rejected"],
+ ],
+ i = "pending",
+ a = {
+ state: function () {
+ return i
+ },
+ always: function () {
+ return s.done(arguments).fail(arguments), this
+ },
+ catch: function (e) {
+ return a.then(null, e)
+ },
+ pipe: function () {
+ var i = arguments
+ return k
+ .Deferred(function (r) {
+ k.each(o, function (e, t) {
+ var n = m(i[t[4]]) && i[t[4]]
+ s[t[1]](function () {
+ var e = n && n.apply(this, arguments)
+ e && m(e.promise) ? e.promise().progress(r.notify).done(r.resolve).fail(r.reject) : r[t[0] + "With"](this, n ? [e] : arguments)
+ })
+ }),
+ (i = null)
+ })
+ .promise()
+ },
+ then: function (t, n, r) {
+ var u = 0
+ function l(i, o, a, s) {
+ return function () {
+ var n = this,
+ r = arguments,
+ e = function () {
+ var e, t
+ if (!(i < u)) {
+ if ((e = a.apply(n, r)) === o.promise()) throw new TypeError("Thenable self-resolution")
+ ;(t = e && ("object" == typeof e || "function" == typeof e) && e.then),
+ m(t)
+ ? s
+ ? t.call(e, l(u, o, M, s), l(u, o, I, s))
+ : (u++, t.call(e, l(u, o, M, s), l(u, o, I, s), l(u, o, M, o.notifyWith)))
+ : (a !== M && ((n = void 0), (r = [e])), (s || o.resolveWith)(n, r))
+ }
+ },
+ t = s
+ ? e
+ : function () {
+ try {
+ e()
+ } catch (e) {
+ k.Deferred.exceptionHook && k.Deferred.exceptionHook(e, t.stackTrace), u <= i + 1 && (a !== I && ((n = void 0), (r = [e])), o.rejectWith(n, r))
+ }
+ }
+ i ? t() : (k.Deferred.getStackHook && (t.stackTrace = k.Deferred.getStackHook()), C.setTimeout(t))
+ }
+ }
+ return k
+ .Deferred(function (e) {
+ o[0][3].add(l(0, e, m(r) ? r : M, e.notifyWith)), o[1][3].add(l(0, e, m(t) ? t : M)), o[2][3].add(l(0, e, m(n) ? n : I))
+ })
+ .promise()
+ },
+ promise: function (e) {
+ return null != e ? k.extend(e, a) : a
+ },
+ },
+ s = {}
+ return (
+ k.each(o, function (e, t) {
+ var n = t[2],
+ r = t[5]
+ ;(a[t[1]] = n.add),
+ r &&
+ n.add(
+ function () {
+ i = r
+ },
+ o[3 - e][2].disable,
+ o[3 - e][3].disable,
+ o[0][2].lock,
+ o[0][3].lock
+ ),
+ n.add(t[3].fire),
+ (s[t[0]] = function () {
+ return s[t[0] + "With"](this === s ? void 0 : this, arguments), this
+ }),
+ (s[t[0] + "With"] = n.fireWith)
+ }),
+ a.promise(s),
+ e && e.call(s, s),
+ s
+ )
+ },
+ when: function (e) {
+ var n = arguments.length,
+ t = n,
+ r = Array(t),
+ i = s.call(arguments),
+ o = k.Deferred(),
+ a = function (t) {
+ return function (e) {
+ ;(r[t] = this), (i[t] = 1 < arguments.length ? s.call(arguments) : e), --n || o.resolveWith(r, i)
+ }
+ }
+ if (n <= 1 && (W(e, o.done(a(t)).resolve, o.reject, !n), "pending" === o.state() || m(i[t] && i[t].then))) return o.then()
+ while (t--) W(i[t], a(t), o.reject)
+ return o.promise()
+ },
+ })
+ var $ = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/
+ ;(k.Deferred.exceptionHook = function (e, t) {
+ C.console && C.console.warn && e && $.test(e.name) && C.console.warn("jQuery.Deferred exception: " + e.message, e.stack, t)
+ }),
+ (k.readyException = function (e) {
+ C.setTimeout(function () {
+ throw e
+ })
+ })
+ var F = k.Deferred()
+ function B() {
+ E.removeEventListener("DOMContentLoaded", B), C.removeEventListener("load", B), k.ready()
+ }
+ ;(k.fn.ready = function (e) {
+ return (
+ F.then(e)["catch"](function (e) {
+ k.readyException(e)
+ }),
+ this
+ )
+ }),
+ k.extend({
+ isReady: !1,
+ readyWait: 1,
+ ready: function (e) {
+ ;(!0 === e ? --k.readyWait : k.isReady) || ((k.isReady = !0) !== e && 0 < --k.readyWait) || F.resolveWith(E, [k])
+ },
+ }),
+ (k.ready.then = F.then),
+ "complete" === E.readyState || ("loading" !== E.readyState && !E.documentElement.doScroll)
+ ? C.setTimeout(k.ready)
+ : (E.addEventListener("DOMContentLoaded", B), C.addEventListener("load", B))
+ var _ = function (e, t, n, r, i, o, a) {
+ var s = 0,
+ u = e.length,
+ l = null == n
+ if ("object" === w(n)) for (s in ((i = !0), n)) _(e, t, s, n[s], !0, o, a)
+ else if (
+ void 0 !== r &&
+ ((i = !0),
+ m(r) || (a = !0),
+ l &&
+ (a
+ ? (t.call(e, r), (t = null))
+ : ((l = t),
+ (t = function (e, t, n) {
+ return l.call(k(e), n)
+ }))),
+ t)
+ )
+ for (; s < u; s++) t(e[s], n, a ? r : r.call(e[s], s, t(e[s], n)))
+ return i ? e : l ? t.call(e) : u ? t(e[0], n) : o
+ },
+ z = /^-ms-/,
+ U = /-([a-z])/g
+ function X(e, t) {
+ return t.toUpperCase()
+ }
+ function V(e) {
+ return e.replace(z, "ms-").replace(U, X)
+ }
+ var G = function (e) {
+ return 1 === e.nodeType || 9 === e.nodeType || !+e.nodeType
+ }
+ function Y() {
+ this.expando = k.expando + Y.uid++
+ }
+ ;(Y.uid = 1),
+ (Y.prototype = {
+ cache: function (e) {
+ var t = e[this.expando]
+ return t || ((t = {}), G(e) && (e.nodeType ? (e[this.expando] = t) : Object.defineProperty(e, this.expando, { value: t, configurable: !0 }))), t
+ },
+ set: function (e, t, n) {
+ var r,
+ i = this.cache(e)
+ if ("string" == typeof t) i[V(t)] = n
+ else for (r in t) i[V(r)] = t[r]
+ return i
+ },
+ get: function (e, t) {
+ return void 0 === t ? this.cache(e) : e[this.expando] && e[this.expando][V(t)]
+ },
+ access: function (e, t, n) {
+ return void 0 === t || (t && "string" == typeof t && void 0 === n) ? this.get(e, t) : (this.set(e, t, n), void 0 !== n ? n : t)
+ },
+ remove: function (e, t) {
+ var n,
+ r = e[this.expando]
+ if (void 0 !== r) {
+ if (void 0 !== t) {
+ n = (t = Array.isArray(t) ? t.map(V) : (t = V(t)) in r ? [t] : t.match(R) || []).length
+ while (n--) delete r[t[n]]
+ }
+ ;(void 0 === t || k.isEmptyObject(r)) && (e.nodeType ? (e[this.expando] = void 0) : delete e[this.expando])
+ }
+ },
+ hasData: function (e) {
+ var t = e[this.expando]
+ return void 0 !== t && !k.isEmptyObject(t)
+ },
+ })
+ var Q = new Y(),
+ J = new Y(),
+ K = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
+ Z = /[A-Z]/g
+ function ee(e, t, n) {
+ var r, i
+ if (void 0 === n && 1 === e.nodeType)
+ if (((r = "data-" + t.replace(Z, "-$&").toLowerCase()), "string" == typeof (n = e.getAttribute(r)))) {
+ try {
+ n = "true" === (i = n) || ("false" !== i && ("null" === i ? null : i === +i + "" ? +i : K.test(i) ? JSON.parse(i) : i))
+ } catch (e) {}
+ J.set(e, t, n)
+ } else n = void 0
+ return n
+ }
+ k.extend({
+ hasData: function (e) {
+ return J.hasData(e) || Q.hasData(e)
+ },
+ data: function (e, t, n) {
+ return J.access(e, t, n)
+ },
+ removeData: function (e, t) {
+ J.remove(e, t)
+ },
+ _data: function (e, t, n) {
+ return Q.access(e, t, n)
+ },
+ _removeData: function (e, t) {
+ Q.remove(e, t)
+ },
+ }),
+ k.fn.extend({
+ data: function (n, e) {
+ var t,
+ r,
+ i,
+ o = this[0],
+ a = o && o.attributes
+ if (void 0 === n) {
+ if (this.length && ((i = J.get(o)), 1 === o.nodeType && !Q.get(o, "hasDataAttrs"))) {
+ t = a.length
+ while (t--) a[t] && 0 === (r = a[t].name).indexOf("data-") && ((r = V(r.slice(5))), ee(o, r, i[r]))
+ Q.set(o, "hasDataAttrs", !0)
+ }
+ return i
+ }
+ return "object" == typeof n
+ ? this.each(function () {
+ J.set(this, n)
+ })
+ : _(
+ this,
+ function (e) {
+ var t
+ if (o && void 0 === e) return void 0 !== (t = J.get(o, n)) ? t : void 0 !== (t = ee(o, n)) ? t : void 0
+ this.each(function () {
+ J.set(this, n, e)
+ })
+ },
+ null,
+ e,
+ 1 < arguments.length,
+ null,
+ !0
+ )
+ },
+ removeData: function (e) {
+ return this.each(function () {
+ J.remove(this, e)
+ })
+ },
+ }),
+ k.extend({
+ queue: function (e, t, n) {
+ var r
+ if (e) return (t = (t || "fx") + "queue"), (r = Q.get(e, t)), n && (!r || Array.isArray(n) ? (r = Q.access(e, t, k.makeArray(n))) : r.push(n)), r || []
+ },
+ dequeue: function (e, t) {
+ t = t || "fx"
+ var n = k.queue(e, t),
+ r = n.length,
+ i = n.shift(),
+ o = k._queueHooks(e, t)
+ "inprogress" === i && ((i = n.shift()), r--),
+ i &&
+ ("fx" === t && n.unshift("inprogress"),
+ delete o.stop,
+ i.call(
+ e,
+ function () {
+ k.dequeue(e, t)
+ },
+ o
+ )),
+ !r && o && o.empty.fire()
+ },
+ _queueHooks: function (e, t) {
+ var n = t + "queueHooks"
+ return (
+ Q.get(e, n) ||
+ Q.access(e, n, {
+ empty: k.Callbacks("once memory").add(function () {
+ Q.remove(e, [t + "queue", n])
+ }),
+ })
+ )
+ },
+ }),
+ k.fn.extend({
+ queue: function (t, n) {
+ var e = 2
+ return (
+ "string" != typeof t && ((n = t), (t = "fx"), e--),
+ arguments.length < e
+ ? k.queue(this[0], t)
+ : void 0 === n
+ ? this
+ : this.each(function () {
+ var e = k.queue(this, t, n)
+ k._queueHooks(this, t), "fx" === t && "inprogress" !== e[0] && k.dequeue(this, t)
+ })
+ )
+ },
+ dequeue: function (e) {
+ return this.each(function () {
+ k.dequeue(this, e)
+ })
+ },
+ clearQueue: function (e) {
+ return this.queue(e || "fx", [])
+ },
+ promise: function (e, t) {
+ var n,
+ r = 1,
+ i = k.Deferred(),
+ o = this,
+ a = this.length,
+ s = function () {
+ --r || i.resolveWith(o, [o])
+ }
+ "string" != typeof e && ((t = e), (e = void 0)), (e = e || "fx")
+ while (a--) (n = Q.get(o[a], e + "queueHooks")) && n.empty && (r++, n.empty.add(s))
+ return s(), i.promise(t)
+ },
+ })
+ var te = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
+ ne = new RegExp("^(?:([+-])=|)(" + te + ")([a-z%]*)$", "i"),
+ re = ["Top", "Right", "Bottom", "Left"],
+ ie = E.documentElement,
+ oe = function (e) {
+ return k.contains(e.ownerDocument, e)
+ },
+ ae = { composed: !0 }
+ ie.getRootNode &&
+ (oe = function (e) {
+ return k.contains(e.ownerDocument, e) || e.getRootNode(ae) === e.ownerDocument
+ })
+ var se = function (e, t) {
+ return "none" === (e = t || e).style.display || ("" === e.style.display && oe(e) && "none" === k.css(e, "display"))
+ },
+ ue = function (e, t, n, r) {
+ var i,
+ o,
+ a = {}
+ for (o in t) (a[o] = e.style[o]), (e.style[o] = t[o])
+ for (o in ((i = n.apply(e, r || [])), t)) e.style[o] = a[o]
+ return i
+ }
+ function le(e, t, n, r) {
+ var i,
+ o,
+ a = 20,
+ s = r
+ ? function () {
+ return r.cur()
+ }
+ : function () {
+ return k.css(e, t, "")
+ },
+ u = s(),
+ l = (n && n[3]) || (k.cssNumber[t] ? "" : "px"),
+ c = e.nodeType && (k.cssNumber[t] || ("px" !== l && +u)) && ne.exec(k.css(e, t))
+ if (c && c[3] !== l) {
+ ;(u /= 2), (l = l || c[3]), (c = +u || 1)
+ while (a--) k.style(e, t, c + l), (1 - o) * (1 - (o = s() / u || 0.5)) <= 0 && (a = 0), (c /= o)
+ ;(c *= 2), k.style(e, t, c + l), (n = n || [])
+ }
+ return n && ((c = +c || +u || 0), (i = n[1] ? c + (n[1] + 1) * n[2] : +n[2]), r && ((r.unit = l), (r.start = c), (r.end = i))), i
+ }
+ var ce = {}
+ function fe(e, t) {
+ for (var n, r, i, o, a, s, u, l = [], c = 0, f = e.length; c < f; c++)
+ (r = e[c]).style &&
+ ((n = r.style.display),
+ t
+ ? ("none" === n && ((l[c] = Q.get(r, "display") || null), l[c] || (r.style.display = "")),
+ "" === r.style.display &&
+ se(r) &&
+ (l[c] =
+ ((u = a = o = void 0),
+ (a = (i = r).ownerDocument),
+ (s = i.nodeName),
+ (u = ce[s]) || ((o = a.body.appendChild(a.createElement(s))), (u = k.css(o, "display")), o.parentNode.removeChild(o), "none" === u && (u = "block"), (ce[s] = u)))))
+ : "none" !== n && ((l[c] = "none"), Q.set(r, "display", n)))
+ for (c = 0; c < f; c++) null != l[c] && (e[c].style.display = l[c])
+ return e
+ }
+ k.fn.extend({
+ show: function () {
+ return fe(this, !0)
+ },
+ hide: function () {
+ return fe(this)
+ },
+ toggle: function (e) {
+ return "boolean" == typeof e
+ ? e
+ ? this.show()
+ : this.hide()
+ : this.each(function () {
+ se(this) ? k(this).show() : k(this).hide()
+ })
+ },
+ })
+ var pe = /^(?:checkbox|radio)$/i,
+ de = /<([a-z][^\/\0>\x20\t\r\n\f]*)/i,
+ he = /^$|^module$|\/(?:java|ecma)script/i,
+ ge = {
+ option: [1, "", " "],
+ _default: [0, "", ""],
+ }
+ function ve(e, t) {
+ var n
+ return (
+ (n = "undefined" != typeof e.getElementsByTagName ? e.getElementsByTagName(t || "*") : "undefined" != typeof e.querySelectorAll ? e.querySelectorAll(t || "*") : []),
+ void 0 === t || (t && A(e, t)) ? k.merge([e], n) : n
+ )
+ }
+ function ye(e, t) {
+ for (var n = 0, r = e.length; n < r; n++) Q.set(e[n], "globalEval", !t || Q.get(t[n], "globalEval"))
+ }
+ ;(ge.optgroup = ge.option), (ge.tbody = ge.tfoot = ge.colgroup = ge.caption = ge.thead), (ge.th = ge.td)
+ var me,
+ xe,
+ be = /<|?\w+;/
+ function we(e, t, n, r, i) {
+ for (var o, a, s, u, l, c, f = t.createDocumentFragment(), p = [], d = 0, h = e.length; d < h; d++)
+ if ((o = e[d]) || 0 === o)
+ if ("object" === w(o)) k.merge(p, o.nodeType ? [o] : o)
+ else if (be.test(o)) {
+ ;(a = a || f.appendChild(t.createElement("div"))),
+ (s = (de.exec(o) || ["", ""])[1].toLowerCase()),
+ (u = ge[s] || ge._default),
+ (a.innerHTML = u[1] + k.htmlPrefilter(o) + u[2]),
+ (c = u[0])
+ while (c--) a = a.lastChild
+ k.merge(p, a.childNodes), ((a = f.firstChild).textContent = "")
+ } else p.push(t.createTextNode(o))
+ ;(f.textContent = ""), (d = 0)
+ while ((o = p[d++]))
+ if (r && -1 < k.inArray(o, r)) i && i.push(o)
+ else if (((l = oe(o)), (a = ve(f.appendChild(o), "script")), l && ye(a), n)) {
+ c = 0
+ while ((o = a[c++])) he.test(o.type || "") && n.push(o)
+ }
+ return f
+ }
+ ;(me = E.createDocumentFragment().appendChild(E.createElement("div"))),
+ (xe = E.createElement("input")).setAttribute("type", "radio"),
+ xe.setAttribute("checked", "checked"),
+ xe.setAttribute("name", "t"),
+ me.appendChild(xe),
+ (y.checkClone = me.cloneNode(!0).cloneNode(!0).lastChild.checked),
+ (me.innerHTML = ""),
+ (y.noCloneChecked = !!me.cloneNode(!0).lastChild.defaultValue)
+ var Te = /^key/,
+ Ce = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
+ Ee = /^([^.]*)(?:\.(.+)|)/
+ function ke() {
+ return !0
+ }
+ function Se() {
+ return !1
+ }
+ function Ne(e, t) {
+ return (
+ (e ===
+ (function () {
+ try {
+ return E.activeElement
+ } catch (e) {}
+ })()) ==
+ ("focus" === t)
+ )
+ }
+ function Ae(e, t, n, r, i, o) {
+ var a, s
+ if ("object" == typeof t) {
+ for (s in ("string" != typeof n && ((r = r || n), (n = void 0)), t)) Ae(e, s, n, r, t[s], o)
+ return e
+ }
+ if ((null == r && null == i ? ((i = n), (r = n = void 0)) : null == i && ("string" == typeof n ? ((i = r), (r = void 0)) : ((i = r), (r = n), (n = void 0))), !1 === i)) i = Se
+ else if (!i) return e
+ return (
+ 1 === o &&
+ ((a = i),
+ ((i = function (e) {
+ return k().off(e), a.apply(this, arguments)
+ }).guid = a.guid || (a.guid = k.guid++))),
+ e.each(function () {
+ k.event.add(this, t, i, r, n)
+ })
+ )
+ }
+ function De(e, i, o) {
+ o
+ ? (Q.set(e, i, !1),
+ k.event.add(e, i, {
+ namespace: !1,
+ handler: function (e) {
+ var t,
+ n,
+ r = Q.get(this, i)
+ if (1 & e.isTrigger && this[i]) {
+ if (r.length) (k.event.special[i] || {}).delegateType && e.stopPropagation()
+ else if (((r = s.call(arguments)), Q.set(this, i, r), (t = o(this, i)), this[i](), r !== (n = Q.get(this, i)) || t ? Q.set(this, i, !1) : (n = {}), r !== n))
+ return e.stopImmediatePropagation(), e.preventDefault(), n.value
+ } else r.length && (Q.set(this, i, { value: k.event.trigger(k.extend(r[0], k.Event.prototype), r.slice(1), this) }), e.stopImmediatePropagation())
+ },
+ }))
+ : void 0 === Q.get(e, i) && k.event.add(e, i, ke)
+ }
+ ;(k.event = {
+ global: {},
+ add: function (t, e, n, r, i) {
+ var o,
+ a,
+ s,
+ u,
+ l,
+ c,
+ f,
+ p,
+ d,
+ h,
+ g,
+ v = Q.get(t)
+ if (v) {
+ n.handler && ((n = (o = n).handler), (i = o.selector)),
+ i && k.find.matchesSelector(ie, i),
+ n.guid || (n.guid = k.guid++),
+ (u = v.events) || (u = v.events = {}),
+ (a = v.handle) ||
+ (a = v.handle =
+ function (e) {
+ return "undefined" != typeof k && k.event.triggered !== e.type ? k.event.dispatch.apply(t, arguments) : void 0
+ }),
+ (l = (e = (e || "").match(R) || [""]).length)
+ while (l--)
+ (d = g = (s = Ee.exec(e[l]) || [])[1]),
+ (h = (s[2] || "").split(".").sort()),
+ d &&
+ ((f = k.event.special[d] || {}),
+ (d = (i ? f.delegateType : f.bindType) || d),
+ (f = k.event.special[d] || {}),
+ (c = k.extend(
+ { type: d, origType: g, data: r, handler: n, guid: n.guid, selector: i, needsContext: i && k.expr.match.needsContext.test(i), namespace: h.join(".") },
+ o
+ )),
+ (p = u[d]) || (((p = u[d] = []).delegateCount = 0), (f.setup && !1 !== f.setup.call(t, r, h, a)) || (t.addEventListener && t.addEventListener(d, a))),
+ f.add && (f.add.call(t, c), c.handler.guid || (c.handler.guid = n.guid)),
+ i ? p.splice(p.delegateCount++, 0, c) : p.push(c),
+ (k.event.global[d] = !0))
+ }
+ },
+ remove: function (e, t, n, r, i) {
+ var o,
+ a,
+ s,
+ u,
+ l,
+ c,
+ f,
+ p,
+ d,
+ h,
+ g,
+ v = Q.hasData(e) && Q.get(e)
+ if (v && (u = v.events)) {
+ l = (t = (t || "").match(R) || [""]).length
+ while (l--)
+ if (((d = g = (s = Ee.exec(t[l]) || [])[1]), (h = (s[2] || "").split(".").sort()), d)) {
+ ;(f = k.event.special[d] || {}),
+ (p = u[(d = (r ? f.delegateType : f.bindType) || d)] || []),
+ (s = s[2] && new RegExp("(^|\\.)" + h.join("\\.(?:.*\\.|)") + "(\\.|$)")),
+ (a = o = p.length)
+ while (o--)
+ (c = p[o]),
+ (!i && g !== c.origType) ||
+ (n && n.guid !== c.guid) ||
+ (s && !s.test(c.namespace)) ||
+ (r && r !== c.selector && ("**" !== r || !c.selector)) ||
+ (p.splice(o, 1), c.selector && p.delegateCount--, f.remove && f.remove.call(e, c))
+ a && !p.length && ((f.teardown && !1 !== f.teardown.call(e, h, v.handle)) || k.removeEvent(e, d, v.handle), delete u[d])
+ } else for (d in u) k.event.remove(e, d + t[l], n, r, !0)
+ k.isEmptyObject(u) && Q.remove(e, "handle events")
+ }
+ },
+ dispatch: function (e) {
+ var t,
+ n,
+ r,
+ i,
+ o,
+ a,
+ s = k.event.fix(e),
+ u = new Array(arguments.length),
+ l = (Q.get(this, "events") || {})[s.type] || [],
+ c = k.event.special[s.type] || {}
+ for (u[0] = s, t = 1; t < arguments.length; t++) u[t] = arguments[t]
+ if (((s.delegateTarget = this), !c.preDispatch || !1 !== c.preDispatch.call(this, s))) {
+ ;(a = k.event.handlers.call(this, s, l)), (t = 0)
+ while ((i = a[t++]) && !s.isPropagationStopped()) {
+ ;(s.currentTarget = i.elem), (n = 0)
+ while ((o = i.handlers[n++]) && !s.isImmediatePropagationStopped())
+ (s.rnamespace && !1 !== o.namespace && !s.rnamespace.test(o.namespace)) ||
+ ((s.handleObj = o),
+ (s.data = o.data),
+ void 0 !== (r = ((k.event.special[o.origType] || {}).handle || o.handler).apply(i.elem, u)) && !1 === (s.result = r) && (s.preventDefault(), s.stopPropagation()))
+ }
+ return c.postDispatch && c.postDispatch.call(this, s), s.result
+ }
+ },
+ handlers: function (e, t) {
+ var n,
+ r,
+ i,
+ o,
+ a,
+ s = [],
+ u = t.delegateCount,
+ l = e.target
+ if (u && l.nodeType && !("click" === e.type && 1 <= e.button))
+ for (; l !== this; l = l.parentNode || this)
+ if (1 === l.nodeType && ("click" !== e.type || !0 !== l.disabled)) {
+ for (o = [], a = {}, n = 0; n < u; n++)
+ void 0 === a[(i = (r = t[n]).selector + " ")] && (a[i] = r.needsContext ? -1 < k(i, this).index(l) : k.find(i, this, null, [l]).length), a[i] && o.push(r)
+ o.length && s.push({ elem: l, handlers: o })
+ }
+ return (l = this), u < t.length && s.push({ elem: l, handlers: t.slice(u) }), s
+ },
+ addProp: function (t, e) {
+ Object.defineProperty(k.Event.prototype, t, {
+ enumerable: !0,
+ configurable: !0,
+ get: m(e)
+ ? function () {
+ if (this.originalEvent) return e(this.originalEvent)
+ }
+ : function () {
+ if (this.originalEvent) return this.originalEvent[t]
+ },
+ set: function (e) {
+ Object.defineProperty(this, t, { enumerable: !0, configurable: !0, writable: !0, value: e })
+ },
+ })
+ },
+ fix: function (e) {
+ return e[k.expando] ? e : new k.Event(e)
+ },
+ special: {
+ load: { noBubble: !0 },
+ click: {
+ setup: function (e) {
+ var t = this || e
+ return pe.test(t.type) && t.click && A(t, "input") && De(t, "click", ke), !1
+ },
+ trigger: function (e) {
+ var t = this || e
+ return pe.test(t.type) && t.click && A(t, "input") && De(t, "click"), !0
+ },
+ _default: function (e) {
+ var t = e.target
+ return (pe.test(t.type) && t.click && A(t, "input") && Q.get(t, "click")) || A(t, "a")
+ },
+ },
+ beforeunload: {
+ postDispatch: function (e) {
+ void 0 !== e.result && e.originalEvent && (e.originalEvent.returnValue = e.result)
+ },
+ },
+ },
+ }),
+ (k.removeEvent = function (e, t, n) {
+ e.removeEventListener && e.removeEventListener(t, n)
+ }),
+ (k.Event = function (e, t) {
+ if (!(this instanceof k.Event)) return new k.Event(e, t)
+ e && e.type
+ ? ((this.originalEvent = e),
+ (this.type = e.type),
+ (this.isDefaultPrevented = e.defaultPrevented || (void 0 === e.defaultPrevented && !1 === e.returnValue) ? ke : Se),
+ (this.target = e.target && 3 === e.target.nodeType ? e.target.parentNode : e.target),
+ (this.currentTarget = e.currentTarget),
+ (this.relatedTarget = e.relatedTarget))
+ : (this.type = e),
+ t && k.extend(this, t),
+ (this.timeStamp = (e && e.timeStamp) || Date.now()),
+ (this[k.expando] = !0)
+ }),
+ (k.Event.prototype = {
+ constructor: k.Event,
+ isDefaultPrevented: Se,
+ isPropagationStopped: Se,
+ isImmediatePropagationStopped: Se,
+ isSimulated: !1,
+ preventDefault: function () {
+ var e = this.originalEvent
+ ;(this.isDefaultPrevented = ke), e && !this.isSimulated && e.preventDefault()
+ },
+ stopPropagation: function () {
+ var e = this.originalEvent
+ ;(this.isPropagationStopped = ke), e && !this.isSimulated && e.stopPropagation()
+ },
+ stopImmediatePropagation: function () {
+ var e = this.originalEvent
+ ;(this.isImmediatePropagationStopped = ke), e && !this.isSimulated && e.stopImmediatePropagation(), this.stopPropagation()
+ },
+ }),
+ k.each(
+ {
+ altKey: !0,
+ bubbles: !0,
+ cancelable: !0,
+ changedTouches: !0,
+ ctrlKey: !0,
+ detail: !0,
+ eventPhase: !0,
+ metaKey: !0,
+ pageX: !0,
+ pageY: !0,
+ shiftKey: !0,
+ view: !0,
+ char: !0,
+ code: !0,
+ charCode: !0,
+ key: !0,
+ keyCode: !0,
+ button: !0,
+ buttons: !0,
+ clientX: !0,
+ clientY: !0,
+ offsetX: !0,
+ offsetY: !0,
+ pointerId: !0,
+ pointerType: !0,
+ screenX: !0,
+ screenY: !0,
+ targetTouches: !0,
+ toElement: !0,
+ touches: !0,
+ which: function (e) {
+ var t = e.button
+ return null == e.which && Te.test(e.type)
+ ? null != e.charCode
+ ? e.charCode
+ : e.keyCode
+ : !e.which && void 0 !== t && Ce.test(e.type)
+ ? 1 & t
+ ? 1
+ : 2 & t
+ ? 3
+ : 4 & t
+ ? 2
+ : 0
+ : e.which
+ },
+ },
+ k.event.addProp
+ ),
+ k.each({ focus: "focusin", blur: "focusout" }, function (e, t) {
+ k.event.special[e] = {
+ setup: function () {
+ return De(this, e, Ne), !1
+ },
+ trigger: function () {
+ return De(this, e), !0
+ },
+ delegateType: t,
+ }
+ }),
+ k.each({ mouseenter: "mouseover", mouseleave: "mouseout", pointerenter: "pointerover", pointerleave: "pointerout" }, function (e, i) {
+ k.event.special[e] = {
+ delegateType: i,
+ bindType: i,
+ handle: function (e) {
+ var t,
+ n = e.relatedTarget,
+ r = e.handleObj
+ return (n && (n === this || k.contains(this, n))) || ((e.type = r.origType), (t = r.handler.apply(this, arguments)), (e.type = i)), t
+ },
+ }
+ }),
+ k.fn.extend({
+ on: function (e, t, n, r) {
+ return Ae(this, e, t, n, r)
+ },
+ one: function (e, t, n, r) {
+ return Ae(this, e, t, n, r, 1)
+ },
+ off: function (e, t, n) {
+ var r, i
+ if (e && e.preventDefault && e.handleObj)
+ return (r = e.handleObj), k(e.delegateTarget).off(r.namespace ? r.origType + "." + r.namespace : r.origType, r.selector, r.handler), this
+ if ("object" == typeof e) {
+ for (i in e) this.off(i, t, e[i])
+ return this
+ }
+ return (
+ (!1 !== t && "function" != typeof t) || ((n = t), (t = void 0)),
+ !1 === n && (n = Se),
+ this.each(function () {
+ k.event.remove(this, e, n, t)
+ })
+ )
+ },
+ })
+ var je = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,
+ qe = /
+
+
+ const sampleCode = \`${sampleCode.toString()}\`
+ ${testCode}
+ `
+ const samplePath = "sample." + this.getExtensionName()
+ files[samplePath] = sampleCode.toString()
+ files[GrammarBundleFiles.testJs] = `const ${languageName} = require("./index.js")
+ /*keep-line*/ const sampleCode = require("fs").readFileSync("${samplePath}", "utf8")
+ ${testCode}`
+ return files
+ }
+ getTargetExtension() {
+ return this.getRootNodeTypeDefinitionNode().get(GrammarConstants.compilesTo)
+ }
+ getCellTypeDefinitions() {
+ if (!this._cache_cellTypes) this._cache_cellTypes = this._getCellTypeDefinitions()
+ return this._cache_cellTypes
+ }
+ getCellTypeDefinitionById(cellTypeId) {
+ // todo: return unknownCellTypeDefinition? or is that handled somewhere else?
+ return this.getCellTypeDefinitions()[cellTypeId]
+ }
+ getNodeTypeFamilyTree() {
+ const tree = new TreeNode()
+ Object.values(this.getValidConcreteAndAbstractNodeTypeDefinitions()).forEach((node) => {
+ const path = node.getAncestorNodeTypeIdsArray().join(" ")
+ tree.touchNode(path)
+ })
+ return tree
+ }
+ _getCellTypeDefinitions() {
+ const types = {}
+ // todo: add built in word types?
+ this.getChildrenByNodeConstructor(cellTypeDefinitionNode).forEach((type) => (types[type.getCellTypeId()] = type))
+ return types
+ }
+ getLanguageDefinitionProgram() {
+ return this
+ }
+ getValidConcreteAndAbstractNodeTypeDefinitions() {
+ return this.getChildrenByNodeConstructor(nodeTypeDefinitionNode).filter((node) => node._hasValidNodeTypeId())
+ }
+ _getLastRootNodeTypeDefinitionNode() {
+ return this.findLast((def) => def instanceof AbstractGrammarDefinitionNode && def.has(GrammarConstants.root) && def._hasValidNodeTypeId())
+ }
+ _initRootNodeTypeDefinitionNode() {
+ if (this._cache_rootNodeTypeNode) return
+ if (!this._cache_rootNodeTypeNode) this._cache_rootNodeTypeNode = this._getLastRootNodeTypeDefinitionNode()
+ // By default, have a very permissive basic root node.
+ // todo: whats the best design pattern to use for this sort of thing?
+ if (!this._cache_rootNodeTypeNode) {
+ this._cache_rootNodeTypeNode = this.concat(`${GrammarConstants.defaultRootNode}
+ ${GrammarConstants.root}
+ ${GrammarConstants.catchAllNodeType} ${GrammarConstants.BlobNode}`)[0]
+ this._addDefaultCatchAllBlobNode()
+ }
+ }
+ getRootNodeTypeDefinitionNode() {
+ this._initRootNodeTypeDefinitionNode()
+ return this._cache_rootNodeTypeNode
+ }
+ // todo: whats the best design pattern to use for this sort of thing?
+ _addDefaultCatchAllBlobNode() {
+ delete this._cache_nodeTypeDefinitions
+ this.concat(`${GrammarConstants.BlobNode}
+ ${GrammarConstants.baseNodeType} ${GrammarConstants.blobNode}`)
+ }
+ getExtensionName() {
+ return this.getGrammarName()
+ }
+ getRootNodeTypeId() {
+ return this.getRootNodeTypeDefinitionNode().getNodeTypeIdFromDefinition()
+ }
+ getGrammarName() {
+ return this.getRootNodeTypeId().replace(HandGrammarProgram.nodeTypeSuffixRegex, "")
+ }
+ _getMyInScopeNodeTypeIds() {
+ const nodeTypesNode = this.getRootNodeTypeDefinitionNode().getNode(GrammarConstants.inScope)
+ return nodeTypesNode ? nodeTypesNode.getWordsFrom(1) : []
+ }
+ _getInScopeNodeTypeIds() {
+ const nodeTypesNode = this.getRootNodeTypeDefinitionNode().getNode(GrammarConstants.inScope)
+ return nodeTypesNode ? nodeTypesNode.getWordsFrom(1) : []
+ }
+ _initProgramNodeTypeDefinitionCache() {
+ if (this._cache_nodeTypeDefinitions) return undefined
+ this._cache_nodeTypeDefinitions = {}
+ this.getChildrenByNodeConstructor(nodeTypeDefinitionNode).forEach((nodeTypeDefinitionNode) => {
+ this._cache_nodeTypeDefinitions[nodeTypeDefinitionNode.getNodeTypeIdFromDefinition()] = nodeTypeDefinitionNode
+ })
+ }
+ _getProgramNodeTypeDefinitionCache() {
+ this._initProgramNodeTypeDefinitionCache()
+ return this._cache_nodeTypeDefinitions
+ }
+ compileAndReturnRootConstructor() {
+ if (!this._cache_rootConstructorClass) {
+ const def = this.getRootNodeTypeDefinitionNode()
+ const rootNodeTypeId = def.getNodeTypeIdFromDefinition()
+ this._cache_rootConstructorClass = def.getLanguageDefinitionProgram()._compileAndReturnNodeTypeMap()[rootNodeTypeId]
+ }
+ return this._cache_rootConstructorClass
+ }
+ _getFileExtensions() {
+ return this.getRootNodeTypeDefinitionNode().get(GrammarConstants.extensions)
+ ? this.getRootNodeTypeDefinitionNode().get(GrammarConstants.extensions).split(" ").join(",")
+ : this.getExtensionName()
+ }
+ toNodeJsJavascript(jtreePath = "jtree") {
+ return this._rootNodeDefToJavascriptClass(jtreePath, true).trim()
+ }
+ toBrowserJavascript() {
+ return this._rootNodeDefToJavascriptClass("", false).trim()
+ }
+ _getProperName() {
+ return TreeUtils.ucfirst(this.getExtensionName())
+ }
+ _rootNodeDefToJavascriptClass(jtreePath, forNodeJs = true) {
+ const defs = this.getValidConcreteAndAbstractNodeTypeDefinitions()
+ // todo: throw if there is no root node defined
+ const nodeTypeClasses = defs.map((def) => def._nodeDefToJavascriptClass()).join("\n\n")
+ const rootDef = this.getRootNodeTypeDefinitionNode()
+ const rootNodeJsHeader = forNodeJs && rootDef._getConcatBlockStringFromExtended(GrammarConstants._rootNodeJsHeader)
+ const rootName = rootDef._getGeneratedClassName()
+ if (!rootName) throw new Error(`Root Node Type Has No Name`)
+ let exportScript = ""
+ if (forNodeJs) {
+ exportScript = `module.exports = ${rootName};
+ ${rootName}`
+ } else {
+ exportScript = `window.${rootName} = ${rootName}`
+ }
+ // todo: we can expose the previous "constants" export, if needed, via the grammar, which we preserve.
+ return `{
+ ${forNodeJs ? `const {jtree} = require("${jtreePath}")` : ""}
+ ${rootNodeJsHeader ? rootNodeJsHeader : ""}
+ ${nodeTypeClasses}
- if (this.req._parser) {
- return this.req._parser(this, str);
- }
+ ${exportScript}
+ }
+ `
+ }
+ toSublimeSyntaxFile() {
+ const cellTypeDefs = this.getCellTypeDefinitions()
+ const variables = Object.keys(cellTypeDefs)
+ .map((name) => ` ${name}: '${cellTypeDefs[name].getRegexString()}'`)
+ .join("\n")
+ const defs = this.getValidConcreteAndAbstractNodeTypeDefinitions().filter((kw) => !kw._isAbstract())
+ const nodeTypeContexts = defs.map((def) => def._toSublimeMatchBlock()).join("\n\n")
+ const includes = defs.map((nodeTypeDef) => ` - include: '${nodeTypeDef.getNodeTypeIdFromDefinition()}'`).join("\n")
+ return `%YAML 1.2
+ ---
+ name: ${this.getExtensionName()}
+ file_extensions: [${this._getFileExtensions()}]
+ scope: source.${this.getExtensionName()}
- if (!parse && isJSON(this.type)) {
- parse = request.parse['application/json'];
- }
+ variables:
+ ${variables}
- return parse && str && (str.length > 0 || str instanceof Object) ? parse(str) : null;
- };
- /**
- * Return an `Error` representative of this response.
- *
- * @return {Error}
- * @api public
- */
+ contexts:
+ main:
+ ${includes}
+ ${nodeTypeContexts}`
+ }
+ }
+ HandGrammarProgram.makeNodeTypeId = (str) =>
+ TreeUtils._replaceNonAlphaNumericCharactersWithCharCodes(str).replace(HandGrammarProgram.nodeTypeSuffixRegex, "") + GrammarConstants.nodeTypeSuffix
+ HandGrammarProgram.makeCellTypeId = (str) =>
+ TreeUtils._replaceNonAlphaNumericCharactersWithCharCodes(str).replace(HandGrammarProgram.cellTypeSuffixRegex, "") + GrammarConstants.cellTypeSuffix
+ HandGrammarProgram.nodeTypeSuffixRegex = new RegExp(GrammarConstants.nodeTypeSuffix + "$")
+ HandGrammarProgram.nodeTypeFullRegex = new RegExp("^[a-zA-Z0-9_]+" + GrammarConstants.nodeTypeSuffix + "$")
+ HandGrammarProgram.cellTypeSuffixRegex = new RegExp(GrammarConstants.cellTypeSuffix + "$")
+ HandGrammarProgram.cellTypeFullRegex = new RegExp("^[a-zA-Z0-9_]+" + GrammarConstants.cellTypeSuffix + "$")
+ HandGrammarProgram._languages = {}
+ HandGrammarProgram._nodeTypes = {}
+ const PreludeKinds = {}
+ PreludeKinds[PreludeCellTypeIds.anyCell] = GrammarAnyCell
+ PreludeKinds[PreludeCellTypeIds.keywordCell] = GrammarKeywordCell
+ PreludeKinds[PreludeCellTypeIds.floatCell] = GrammarFloatCell
+ PreludeKinds[PreludeCellTypeIds.numberCell] = GrammarFloatCell
+ PreludeKinds[PreludeCellTypeIds.bitCell] = GrammarBitCell
+ PreludeKinds[PreludeCellTypeIds.boolCell] = GrammarBoolCell
+ PreludeKinds[PreludeCellTypeIds.intCell] = GrammarIntCell
+ window.GrammarConstants = GrammarConstants
+ window.PreludeCellTypeIds = PreludeCellTypeIds
+ window.HandGrammarProgram = HandGrammarProgram
+ window.GrammarBackedNode = GrammarBackedNode
+ window.UnknownNodeTypeError = UnknownNodeTypeError
+ class Upgrader extends TreeNode {
+ upgradeManyInPlace(globPatterns, fromVersion, toVersion) {
+ this._upgradeMany(globPatterns, fromVersion, toVersion).forEach((file) => file.tree.toDisk(file.path))
+ return this
+ }
+ upgradeManyPreview(globPatterns, fromVersion, toVersion) {
+ return this._upgradeMany(globPatterns, fromVersion, toVersion)
+ }
+ _upgradeMany(globPatterns, fromVersion, toVersion) {
+ const glob = this.require("glob")
+ const files = TreeUtils.flatten(globPatterns.map((pattern) => glob.sync(pattern)))
+ console.log(`${files.length} files to upgrade`)
+ return files.map((path) => {
+ console.log("Upgrading " + path)
+ return {
+ tree: this.upgrade(TreeNode.fromDisk(path), fromVersion, toVersion),
+ path: path,
+ }
+ })
+ }
+ upgrade(code, fromVersion, toVersion) {
+ const updateFromMap = this.getUpgradeFromMap()
+ const semver = this.require("semver")
+ let fromMap
+ while ((fromMap = updateFromMap[fromVersion])) {
+ const toNextVersion = Object.keys(fromMap)[0] // todo: currently we just assume 1 step at a time
+ if (semver.lt(toVersion, toNextVersion)) break
+ const fn = Object.values(fromMap)[0]
+ code = fn(code)
+ fromVersion = toNextVersion
+ }
+ return code
+ }
+ }
+ window.Upgrader = Upgrader
+ class UnknownGrammarProgram extends TreeNode {
+ _inferRootNodeForAPrefixLanguage(grammarName) {
+ grammarName = HandGrammarProgram.makeNodeTypeId(grammarName)
+ const rootNode = new TreeNode(`${grammarName}
+ ${GrammarConstants.root}`)
+ // note: right now we assume 1 global cellTypeMap and nodeTypeMap per grammar. But we may have scopes in the future?
+ const rootNodeNames = this.getFirstWords()
+ .filter((identity) => identity)
+ .map((word) => HandGrammarProgram.makeNodeTypeId(word))
+ rootNode
+ .nodeAt(0)
+ .touchNode(GrammarConstants.inScope)
+ .setWordsFrom(1, Array.from(new Set(rootNodeNames)))
+ return rootNode
+ }
+ _renameIntegerKeywords(clone) {
+ // todo: why are we doing this?
+ for (let node of clone.getTopDownArrayIterator()) {
+ const firstWordIsAnInteger = !!node.getFirstWord().match(/^\d+$/)
+ const parentFirstWord = node.getParent().getFirstWord()
+ if (firstWordIsAnInteger && parentFirstWord) node.setFirstWord(HandGrammarProgram.makeNodeTypeId(parentFirstWord + UnknownGrammarProgram._childSuffix))
+ }
+ }
+ _getKeywordMaps(clone) {
+ const keywordsToChildKeywords = {}
+ const keywordsToNodeInstances = {}
+ for (let node of clone.getTopDownArrayIterator()) {
+ const firstWord = node.getFirstWord()
+ if (!keywordsToChildKeywords[firstWord]) keywordsToChildKeywords[firstWord] = {}
+ if (!keywordsToNodeInstances[firstWord]) keywordsToNodeInstances[firstWord] = []
+ keywordsToNodeInstances[firstWord].push(node)
+ node.forEach((child) => {
+ keywordsToChildKeywords[firstWord][child.getFirstWord()] = true
+ })
+ }
+ return { keywordsToChildKeywords: keywordsToChildKeywords, keywordsToNodeInstances: keywordsToNodeInstances }
+ }
+ _inferNodeTypeDef(firstWord, globalCellTypeMap, childFirstWords, instances) {
+ const edgeSymbol = this.getEdgeSymbol()
+ const nodeTypeId = HandGrammarProgram.makeNodeTypeId(firstWord)
+ const nodeDefNode = new TreeNode(nodeTypeId).nodeAt(0)
+ const childNodeTypeIds = childFirstWords.map((word) => HandGrammarProgram.makeNodeTypeId(word))
+ if (childNodeTypeIds.length) nodeDefNode.touchNode(GrammarConstants.inScope).setWordsFrom(1, childNodeTypeIds)
+ const cellsForAllInstances = instances
+ .map((line) => line.getContent())
+ .filter((identity) => identity)
+ .map((line) => line.split(edgeSymbol))
+ const instanceCellCounts = new Set(cellsForAllInstances.map((cells) => cells.length))
+ const maxCellsOnLine = Math.max(...Array.from(instanceCellCounts))
+ const minCellsOnLine = Math.min(...Array.from(instanceCellCounts))
+ let catchAllCellType
+ let cellTypeIds = []
+ for (let cellIndex = 0; cellIndex < maxCellsOnLine; cellIndex++) {
+ const cellType = this._getBestCellType(
+ firstWord,
+ instances.length,
+ maxCellsOnLine,
+ cellsForAllInstances.map((cells) => cells[cellIndex])
+ )
+ if (!globalCellTypeMap.has(cellType.cellTypeId)) globalCellTypeMap.set(cellType.cellTypeId, cellType.cellTypeDefinition)
+ cellTypeIds.push(cellType.cellTypeId)
+ }
+ if (maxCellsOnLine > minCellsOnLine) {
+ //columns = columns.slice(0, min)
+ catchAllCellType = cellTypeIds.pop()
+ while (cellTypeIds[cellTypeIds.length - 1] === catchAllCellType) {
+ cellTypeIds.pop()
+ }
+ }
+ const needsCruxProperty = !firstWord.endsWith(UnknownGrammarProgram._childSuffix + "Node") // todo: cleanup
+ if (needsCruxProperty) nodeDefNode.set(GrammarConstants.crux, firstWord)
+ if (catchAllCellType) nodeDefNode.set(GrammarConstants.catchAllCellType, catchAllCellType)
+ const cellLine = cellTypeIds.slice()
+ cellLine.unshift(PreludeCellTypeIds.keywordCell)
+ if (cellLine.length > 0) nodeDefNode.set(GrammarConstants.cells, cellLine.join(edgeSymbol))
+ //if (!catchAllCellType && cellTypeIds.length === 1) nodeDefNode.set(GrammarConstants.cells, cellTypeIds[0])
+ // Todo: add conditional frequencies
+ return nodeDefNode.getParent().toString()
+ }
+ // inferGrammarFileForAnSSVLanguage(grammarName: string): string {
+ // grammarName = HandGrammarProgram.makeNodeTypeId(grammarName)
+ // const rootNode = new TreeNode(`${grammarName}
+ // ${GrammarConstants.root}`)
+ // // note: right now we assume 1 global cellTypeMap and nodeTypeMap per grammar. But we may have scopes in the future?
+ // const rootNodeNames = this.getFirstWords().map(word => HandGrammarProgram.makeNodeTypeId(word))
+ // rootNode
+ // .nodeAt(0)
+ // .touchNode(GrammarConstants.inScope)
+ // .setWordsFrom(1, Array.from(new Set(rootNodeNames)))
+ // return rootNode
+ // }
+ inferGrammarFileForAKeywordLanguage(grammarName) {
+ const clone = this.clone()
+ this._renameIntegerKeywords(clone)
+ const { keywordsToChildKeywords, keywordsToNodeInstances } = this._getKeywordMaps(clone)
+ const globalCellTypeMap = new Map()
+ globalCellTypeMap.set(PreludeCellTypeIds.keywordCell, undefined)
+ const nodeTypeDefs = Object.keys(keywordsToChildKeywords)
+ .filter((identity) => identity)
+ .map((firstWord) => this._inferNodeTypeDef(firstWord, globalCellTypeMap, Object.keys(keywordsToChildKeywords[firstWord]), keywordsToNodeInstances[firstWord]))
+ const cellTypeDefs = []
+ globalCellTypeMap.forEach((def, id) => cellTypeDefs.push(def ? def : id))
+ const nodeBreakSymbol = this.getNodeBreakSymbol()
+ return this._formatCode(
+ [this._inferRootNodeForAPrefixLanguage(grammarName).toString(), cellTypeDefs.join(nodeBreakSymbol), nodeTypeDefs.join(nodeBreakSymbol)]
+ .filter((identity) => identity)
+ .join("\n")
+ )
+ }
+ _formatCode(code) {
+ // todo: make this run in browser too
+ if (!this.isNodeJs()) return code
+ const grammarProgram = new HandGrammarProgram(TreeNode.fromDisk(__dirname + "/../langs/grammar/grammar.grammar"))
+ const programConstructor = grammarProgram.compileAndReturnRootConstructor()
+ const program = new programConstructor(code)
+ return program.format().toString()
+ }
+ _getBestCellType(firstWord, instanceCount, maxCellsOnLine, allValues) {
+ const asSet = new Set(allValues)
+ const edgeSymbol = this.getEdgeSymbol()
+ const values = Array.from(asSet).filter((identity) => identity)
+ const every = (fn) => {
+ for (let index = 0; index < values.length; index++) {
+ if (!fn(values[index])) return false
+ }
+ return true
+ }
+ if (every((str) => str === "0" || str === "1")) return { cellTypeId: PreludeCellTypeIds.bitCell }
+ if (
+ every((str) => {
+ const num = parseInt(str)
+ if (isNaN(num)) return false
+ return num.toString() === str
+ })
+ ) {
+ return { cellTypeId: PreludeCellTypeIds.intCell }
+ }
+ if (every((str) => str.match(/^-?\d*.?\d+$/))) return { cellTypeId: PreludeCellTypeIds.floatCell }
+ const bools = new Set(["1", "0", "true", "false", "t", "f", "yes", "no"])
+ if (every((str) => bools.has(str.toLowerCase()))) return { cellTypeId: PreludeCellTypeIds.boolCell }
+ // todo: cleanup
+ const enumLimit = 30
+ if (instanceCount > 1 && maxCellsOnLine === 1 && allValues.length > asSet.size && asSet.size < enumLimit)
+ return {
+ cellTypeId: HandGrammarProgram.makeCellTypeId(firstWord),
+ cellTypeDefinition: `${HandGrammarProgram.makeCellTypeId(firstWord)}
+ enum ${values.join(edgeSymbol)}`,
+ }
+ return { cellTypeId: PreludeCellTypeIds.anyCell }
+ }
+ }
+ UnknownGrammarProgram._childSuffix = "Child"
+ window.UnknownGrammarProgram = UnknownGrammarProgram
+ // Adapted from https://github.com/NeekSandhu/codemirror-textmate/blob/master/src/tmToCm.ts
+ var CmToken
+ ;(function (CmToken) {
+ CmToken["Atom"] = "atom"
+ CmToken["Attribute"] = "attribute"
+ CmToken["Bracket"] = "bracket"
+ CmToken["Builtin"] = "builtin"
+ CmToken["Comment"] = "comment"
+ CmToken["Def"] = "def"
+ CmToken["Error"] = "error"
+ CmToken["Header"] = "header"
+ CmToken["HR"] = "hr"
+ CmToken["Keyword"] = "keyword"
+ CmToken["Link"] = "link"
+ CmToken["Meta"] = "meta"
+ CmToken["Number"] = "number"
+ CmToken["Operator"] = "operator"
+ CmToken["Property"] = "property"
+ CmToken["Qualifier"] = "qualifier"
+ CmToken["Quote"] = "quote"
+ CmToken["String"] = "string"
+ CmToken["String2"] = "string-2"
+ CmToken["Tag"] = "tag"
+ CmToken["Type"] = "type"
+ CmToken["Variable"] = "variable"
+ CmToken["Variable2"] = "variable-2"
+ CmToken["Variable3"] = "variable-3"
+ })(CmToken || (CmToken = {}))
+ const tmToCm = {
+ comment: {
+ $: CmToken.Comment,
+ },
+ constant: {
+ // TODO: Revision
+ $: CmToken.Def,
+ character: {
+ escape: {
+ $: CmToken.String2,
+ },
+ },
+ language: {
+ $: CmToken.Atom,
+ },
+ numeric: {
+ $: CmToken.Number,
+ },
+ other: {
+ email: {
+ link: {
+ $: CmToken.Link,
+ },
+ },
+ symbol: {
+ // TODO: Revision
+ $: CmToken.Def,
+ },
+ },
+ },
+ entity: {
+ name: {
+ class: {
+ $: CmToken.Def,
+ },
+ function: {
+ $: CmToken.Def,
+ },
+ tag: {
+ $: CmToken.Tag,
+ },
+ type: {
+ $: CmToken.Type,
+ class: {
+ $: CmToken.Variable,
+ },
+ },
+ },
+ other: {
+ "attribute-name": {
+ $: CmToken.Attribute,
+ },
+ "inherited-class": {
+ // TODO: Revision
+ $: CmToken.Def,
+ },
+ },
+ support: {
+ function: {
+ // TODO: Revision
+ $: CmToken.Def,
+ },
+ },
+ },
+ invalid: {
+ $: CmToken.Error,
+ illegal: { $: CmToken.Error },
+ deprecated: {
+ $: CmToken.Error,
+ },
+ },
+ keyword: {
+ $: CmToken.Keyword,
+ operator: {
+ $: CmToken.Operator,
+ },
+ other: {
+ "special-method": CmToken.Def,
+ },
+ },
+ punctuation: {
+ $: CmToken.Operator,
+ definition: {
+ comment: {
+ $: CmToken.Comment,
+ },
+ tag: {
+ $: CmToken.Bracket,
+ },
+ // 'template-expression': {
+ // $: CodeMirrorToken.Operator,
+ // },
+ },
+ // terminator: {
+ // $: CodeMirrorToken.Operator,
+ // },
+ },
+ storage: {
+ $: CmToken.Keyword,
+ },
+ string: {
+ $: CmToken.String,
+ regexp: {
+ $: CmToken.String2,
+ },
+ },
+ support: {
+ class: {
+ $: CmToken.Def,
+ },
+ constant: {
+ $: CmToken.Variable2,
+ },
+ function: {
+ $: CmToken.Def,
+ },
+ type: {
+ $: CmToken.Type,
+ },
+ variable: {
+ $: CmToken.Variable2,
+ property: {
+ $: CmToken.Property,
+ },
+ },
+ },
+ variable: {
+ $: CmToken.Def,
+ language: {
+ // TODO: Revision
+ $: CmToken.Variable3,
+ },
+ other: {
+ object: {
+ $: CmToken.Variable,
+ property: {
+ $: CmToken.Property,
+ },
+ },
+ property: {
+ $: CmToken.Property,
+ },
+ },
+ parameter: {
+ $: CmToken.Def,
+ },
+ },
+ }
+ const textMateScopeToCodeMirrorStyle = (scopeSegments, styleTree = tmToCm) => {
+ const matchingBranch = styleTree[scopeSegments.shift()]
+ return matchingBranch ? textMateScopeToCodeMirrorStyle(scopeSegments, matchingBranch) || matchingBranch.$ || null : null
+ }
+ class TreeNotationCodeMirrorMode {
+ constructor(name, getProgramConstructorFn, getProgramCodeFn, codeMirrorLib = undefined) {
+ this._name = name
+ this._getProgramConstructorFn = getProgramConstructorFn
+ this._getProgramCodeFn = getProgramCodeFn || ((instance) => (instance ? instance.getValue() : this._originalValue))
+ this._codeMirrorLib = codeMirrorLib
+ }
+ _getParsedProgram() {
+ const source = this._getProgramCodeFn(this._cmInstance) || ""
+ if (!this._cachedProgram || this._cachedSource !== source) {
+ this._cachedSource = source
+ this._cachedProgram = new (this._getProgramConstructorFn())(source)
+ }
+ return this._cachedProgram
+ }
+ _getExcludedIntelliSenseTriggerKeys() {
+ return {
+ 8: "backspace",
+ 9: "tab",
+ 13: "enter",
+ 16: "shift",
+ 17: "ctrl",
+ 18: "alt",
+ 19: "pause",
+ 20: "capslock",
+ 27: "escape",
+ 33: "pageup",
+ 34: "pagedown",
+ 35: "end",
+ 36: "home",
+ 37: "left",
+ 38: "up",
+ 39: "right",
+ 40: "down",
+ 45: "insert",
+ 46: "delete",
+ 91: "left window key",
+ 92: "right window key",
+ 93: "select",
+ 112: "f1",
+ 113: "f2",
+ 114: "f3",
+ 115: "f4",
+ 116: "f5",
+ 117: "f6",
+ 118: "f7",
+ 119: "f8",
+ 120: "f9",
+ 121: "f10",
+ 122: "f11",
+ 123: "f12",
+ 144: "numlock",
+ 145: "scrolllock",
+ }
+ }
+ token(stream, state) {
+ return this._advanceStreamAndReturnTokenType(stream, state)
+ }
+ fromTextAreaWithAutocomplete(area, options) {
+ this._originalValue = area.value
+ const defaultOptions = {
+ lineNumbers: true,
+ mode: this._name,
+ tabSize: 1,
+ indentUnit: 1,
+ hintOptions: {
+ hint: (cmInstance, options) => this.codeMirrorAutocomplete(cmInstance, options),
+ },
+ }
+ Object.assign(defaultOptions, options)
+ this._cmInstance = this._getCodeMirrorLib().fromTextArea(area, defaultOptions)
+ this._enableAutoComplete(this._cmInstance)
+ return this._cmInstance
+ }
+ _enableAutoComplete(cmInstance) {
+ const excludedKeys = this._getExcludedIntelliSenseTriggerKeys()
+ const codeMirrorLib = this._getCodeMirrorLib()
+ cmInstance.on("keyup", (cm, event) => {
+ // https://stackoverflow.com/questions/13744176/codemirror-autocomplete-after-any-keyup
+ if (!cm.state.completionActive && !excludedKeys[event.keyCode.toString()])
+ // Todo: get typings for CM autocomplete
+ codeMirrorLib.commands.autocomplete(cm, null, { completeSingle: false })
+ })
+ }
+ _getCodeMirrorLib() {
+ return this._codeMirrorLib
+ }
+ async codeMirrorAutocomplete(cmInstance, options) {
+ const cursor = cmInstance.getDoc().getCursor()
+ const codeMirrorLib = this._getCodeMirrorLib()
+ const result = await this._getParsedProgram().getAutocompleteResultsAt(cursor.line, cursor.ch)
+ // It seems to be better UX if there's only 1 result, and its the word the user entered, to close autocomplete
+ if (result.matches.length === 1 && result.matches[0].text === result.word) return null
+ return result.matches.length
+ ? {
+ list: result.matches,
+ from: codeMirrorLib.Pos(cursor.line, result.startCharIndex),
+ to: codeMirrorLib.Pos(cursor.line, result.endCharIndex),
+ }
+ : null
+ }
+ register() {
+ const codeMirrorLib = this._getCodeMirrorLib()
+ codeMirrorLib.defineMode(this._name, () => this)
+ codeMirrorLib.defineMIME("text/" + this._name, this._name)
+ return this
+ }
+ _advanceStreamAndReturnTokenType(stream, state) {
+ let nextCharacter = stream.next()
+ const lineNumber = stream.lineOracle.line + 1 // state.lineIndex
+ const WordBreakSymbol = " "
+ const NodeBreakSymbol = "\n"
+ while (typeof nextCharacter === "string") {
+ const peek = stream.peek()
+ if (nextCharacter === WordBreakSymbol) {
+ if (peek === undefined || peek === NodeBreakSymbol) {
+ stream.skipToEnd() // advance string to end
+ this._incrementLine(state)
+ }
+ if (peek === WordBreakSymbol && state.cellIndex) {
+ // If we are missing a cell.
+ // TODO: this is broken for a blank 1st cell. We need to track WordBreakSymbol level.
+ state.cellIndex++
+ }
+ return "bracket"
+ }
+ if (peek === WordBreakSymbol) {
+ state.cellIndex++
+ return this._getCellStyle(lineNumber, state.cellIndex)
+ }
+ nextCharacter = stream.next()
+ }
+ state.cellIndex++
+ const style = this._getCellStyle(lineNumber, state.cellIndex)
+ this._incrementLine(state)
+ return style
+ }
+ _getCellStyle(lineIndex, cellIndex) {
+ const program = this._getParsedProgram()
+ // todo: if the current word is an error, don't show red?
+ if (!program.getCellHighlightScopeAtPosition) console.log(program)
+ const highlightScope = program.getCellHighlightScopeAtPosition(lineIndex, cellIndex)
+ const style = highlightScope ? textMateScopeToCodeMirrorStyle(highlightScope.split(".")) : undefined
+ return style || "noHighlightScopeDefinedInGrammar"
+ }
+ // todo: remove.
+ startState() {
+ return {
+ cellIndex: 0,
+ }
+ }
+ _incrementLine(state) {
+ state.cellIndex = 0
+ }
+ }
+ window.TreeNotationCodeMirrorMode = TreeNotationCodeMirrorMode
+ class jtree {}
+ jtree.GrammarBackedNode = GrammarBackedNode
+ jtree.GrammarConstants = GrammarConstants
+ jtree.Utils = TreeUtils
+ jtree.UnknownNodeTypeError = UnknownNodeTypeError
+ jtree.TestRacer = TestRacer
+ jtree.TreeEvents = TreeEvents
+ jtree.TreeNode = TreeNode
+ jtree.ExtendibleTreeNode = ExtendibleTreeNode
+ jtree.HandGrammarProgram = HandGrammarProgram
+ jtree.UnknownGrammarProgram = UnknownGrammarProgram
+ jtree.TreeNotationCodeMirrorMode = TreeNotationCodeMirrorMode
+ jtree.getVersion = () => TreeNode.getVersion()
+ window.jtree = jtree
- Response.prototype.toError = function () {
- var req = this.req;
- var method = req.method;
- var url = req.url;
- var msg = "cannot ".concat(method, " ").concat(url, " (").concat(this.status, ")");
- var err = new Error(msg);
- err.status = this.status;
- err.method = method;
- err.url = url;
- return err;
- };
- /**
- * Expose `Response`.
- */
+ //onsave jtree build produce jtable.browser.js
+ //onsave jtree build produce jtable.node.js
+ // todo: create a Tree Language for number formatting
+ // https://github.com/gentooboontoo/js-quantities
+ // https://github.com/moment/moment/issues/2469
+ // todo: ugly. how do we ditch this or test?
+ if (typeof moment !== "undefined")
+ moment.createFromInputFallback = function (momentConfig) {
+ momentConfig._d = new Date(momentConfig._i)
+ }
+ var VegaTypes
+ ;(function (VegaTypes) {
+ VegaTypes["nominal"] = "nominal"
+ VegaTypes["ordinal"] = "ordinal"
+ VegaTypes["geojson"] = "geojson"
+ VegaTypes["quantitative"] = "quantitative"
+ VegaTypes["temporal"] = "temporal"
+ })(VegaTypes || (VegaTypes = {}))
+ var JavascriptNativeTypeNames
+ ;(function (JavascriptNativeTypeNames) {
+ JavascriptNativeTypeNames["number"] = "number"
+ JavascriptNativeTypeNames["string"] = "string"
+ JavascriptNativeTypeNames["Date"] = "Date"
+ JavascriptNativeTypeNames["boolean"] = "boolean"
+ })(JavascriptNativeTypeNames || (JavascriptNativeTypeNames = {}))
+ class AbstractPrimitiveType {
+ constructor(typeName) {
+ this._name = typeName
+ }
+ getPrimitiveTypeName() {
+ return this._name
+ }
+ // Abstract methods:
+ toDisplayString(value, format) {
+ return value
+ }
+ getDefaultFormat(columnName, sample) {
+ return ""
+ }
+ getProbForColumnSpecimen(value) {
+ return 0
+ }
+ isInvalidValue(value) {
+ if (value === undefined || value === "") return true
+ return false
+ }
+ }
+ class BooleanType extends AbstractPrimitiveType {
+ getAsNativeJavascriptType(val) {
+ // todo: handle false, etc
+ return val ? 1 : 0
+ }
+ synthesizeValue(randomNumberFn) {
+ return Math.round(randomNumberFn())
+ }
+ getJavascriptTypeName() {
+ return JavascriptNativeTypeNames.boolean
+ }
+ fromStringToNumeric(val) {
+ return val.toString() === "true" ? 1 : 0
+ }
+ getStringExamples() {
+ return ["true"]
+ }
+ getVegaType() {
+ return VegaTypes.nominal
+ }
+ isNumeric() {
+ return false
+ }
+ isString() {
+ return false
+ }
+ isTemporal() {
+ return false
+ }
+ }
+ class AbstractNumeric extends AbstractPrimitiveType {
+ fromStringToNumeric(value) {
+ return parseFloat(value)
+ }
+ synthesizeValue(randomNumberFn) {
+ // todo: min/max etc
+ return this.getMin() + Math.floor((this.getMax() - this.getMin()) * randomNumberFn())
+ }
+ getMax() {
+ return 100
+ }
+ getMin() {
+ return 0
+ }
+ getAsNativeJavascriptType(val) {
+ if (val === undefined) return NaN
+ const valType = typeof val
+ if (valType === "string") return this.fromStringToNumeric(val)
+ else if (val instanceof Date) return Math.round(val.getDate() / 1000)
+ // Is a number
+ return val
+ }
+ getJavascriptTypeName() {
+ return JavascriptNativeTypeNames.number
+ }
+ getVegaType() {
+ return VegaTypes.quantitative
+ }
+ isString() {
+ return false
+ }
+ isTemporal() {
+ return false
+ }
+ isNumeric() {
+ return true
+ }
+ isInvalidValue(value) {
+ return super.isInvalidValue(value) || isNaN(value)
+ }
+ }
+ class IntType extends AbstractNumeric {
+ fromStringToNumeric(val) {
+ return parseInt(val)
+ }
+ getStringExamples() {
+ return ["30"]
+ }
+ getVegaType() {
+ return VegaTypes.quantitative
+ }
+ isNumeric() {
+ return true
+ }
+ isString() {
+ return false
+ }
+ isTemporal() {
+ return false
+ }
+ }
+ class Feet extends AbstractNumeric {
+ getProbForColumnSpecimen(sample) {
+ return isNaN(Feet.feetToInches(sample)) ? 0 : 1
+ }
+ fromStringToNumeric(val) {
+ return Feet.feetToInches(val)
+ }
+ toDisplayString(value, format) {
+ value = parseFloat(value)
+ const inches = Math.round(value % 12)
+ const feet = Math.floor(value / 12)
+ return `${feet}'${inches}"`
+ }
+ getStringExamples() {
+ return ["5'10\""]
+ }
+ // Return inches given formats like 6'1 6'2"
+ static feetToInches(numStr) {
+ let result = 0
+ const indexOfDelimited = numStr.search(/[^0-9\.]/)
+ if (indexOfDelimited < 1) {
+ result = parseFloat(numStr.replace(/[^0-9\.]/g, ""))
+ return isNaN(result) ? result : result * 12
+ }
+ const feetPart = parseFloat(numStr.substr(0, indexOfDelimited).replace(/[^0-9\.]/g, ""))
+ const inchesPart = parseFloat(numStr.substr(indexOfDelimited).replace(/[^0-9\.]/g, ""))
+ if (!isNaN(feetPart)) result += feetPart * 12
+ if (!isNaN(inchesPart)) result += inchesPart
+ return result
+ }
+ }
+ class AbstractCurrency extends AbstractNumeric {}
+ class USD extends AbstractCurrency {
+ toDisplayString(value, format) {
+ return format ? d3format.format(format)(value) : value
+ }
+ fromStringToNumeric(value) {
+ return parseFloat(value.toString().replace(/[\$\, \%]/g, ""))
+ }
+ getProbForColumnSpecimen(sample) {
+ return sample && sample.match && !!sample.match(/^\$[0-9\.\,]+$/) ? 1 : 0
+ }
+ getDefaultFormat() {
+ return "($.2f"
+ }
+ getStringExamples() {
+ return ["$2.22"]
+ }
+ }
+ class NumberCol extends AbstractNumeric {
+ // https://github.com/d3/d3-format
+ toDisplayString(value, format) {
+ if (format === "percent") return d3format.format("(.2f")(parseFloat(value)) + "%"
+ // Need the isNan bc numeral will throw otherwise
+ if (format && !isNaN(value) && value !== Infinity) return d3format.format(format)(value)
+ return value
+ }
+ getDefaultFormat(columnName, sample) {
+ if (columnName.match(/^(mile|pound|inch|feet)s?$/i)) return "(.1f"
+ if (columnName.match(/^(calorie|steps)s?$/i)) return ","
+ if (sample && !sample.toString().includes(".")) return ","
+ }
+ getStringExamples() {
+ return ["2.22"]
+ }
+ }
+ class NumberString extends AbstractNumeric {
+ toDisplayString(value, format) {
+ return format ? d3format.format(format)(value) : value
+ }
+ getDefaultFormat() {
+ return ","
+ }
+ fromStringToNumeric(str) {
+ return parseFloat(str.toString().replace(/[\$\, \%]/g, ""))
+ }
+ getStringExamples() {
+ return ["2,000"]
+ }
+ }
+ class ObjectType extends AbstractPrimitiveType {
+ getAsNativeJavascriptType(val) {
+ return val === undefined ? "" : val.toString()
+ }
+ // todo: not sure about this.
+ getStringExamples() {
+ return ["{score: 10}"]
+ }
+ synthesizeValue() {
+ return {}
+ }
+ fromStringToNumeric() {
+ return undefined
+ }
+ getJavascriptTypeName() {
+ return JavascriptNativeTypeNames.string
+ }
+ getVegaType() {
+ return VegaTypes.nominal
+ }
+ isNumeric() {
+ return false
+ }
+ isString() {
+ return false
+ }
+ isTemporal() {
+ return false
+ }
+ }
+ class AbstractStringCol extends AbstractPrimitiveType {
+ isString() {
+ return true
+ }
+ isNumeric() {
+ return false
+ }
+ getStringExamples() {
+ return ["Anything"]
+ }
+ getVegaType() {
+ return VegaTypes.nominal
+ }
+ synthesizeValue() {
+ return "randomString"
+ }
+ getJavascriptTypeName() {
+ return JavascriptNativeTypeNames.string
+ }
+ fromStringToNumeric() {
+ return undefined
+ }
+ isTemporal() {
+ return false
+ }
+ getAsNativeJavascriptType(val) {
+ return val === undefined ? "" : val.toString()
+ }
+ }
+ class StringCol extends AbstractStringCol {}
+ class UrlCol extends AbstractStringCol {
+ getStringExamples() {
+ return ["www.foo.com"]
+ }
+ }
+ class TextCol extends AbstractStringCol {}
+ class AbstractCodeCol extends AbstractStringCol {}
+ class CodeCol extends AbstractCodeCol {
+ getStringExamples() {
+ return ["i++"]
+ }
+ }
+ class HTMLCol extends AbstractCodeCol {
+ getStringExamples() {
+ return ["hi "]
+ }
+ }
+ class AbstractPathCol extends AbstractStringCol {}
+ // filepath
+ class PathCol extends AbstractPathCol {}
+ // Directory
+ class DirCol extends AbstractPathCol {}
+ class AbstractTemporal extends AbstractPrimitiveType {
+ _fromStringToDate(value) {
+ return moment(parseInt(value)).toDate()
+ }
+ getAsNativeJavascriptType(val) {
+ if (val === undefined) return undefined
+ const valType = typeof val
+ if (valType === "string") return this._fromStringToDate(val)
+ else if (val instanceof Date) return val
+ return this._fromNumericToDate(val)
+ }
+ fromDateToNumeric(date) {
+ return moment(date).unix()
+ }
+ getJavascriptTypeName() {
+ return JavascriptNativeTypeNames.Date
+ }
+ synthesizeValue() {
+ return new Date()
+ }
+ isNumeric() {
+ return true
+ }
+ isString() {
+ return false
+ }
+ isTemporal() {
+ return true
+ }
+ getVegaType() {
+ return VegaTypes.temporal
+ }
+ getVegaTimeUnit() {
+ return undefined
+ }
+ _fromNumericToDate(value) {
+ return moment(value).toDate()
+ }
+ }
+ class DateCol extends AbstractTemporal {
+ toDisplayString(value, format) {
+ if (!format) format = "MM/DD/YY"
+ if (format === "fromNow") return moment(parseFloat(value)).fromNow()
+ // todo: make sure we are working with numeric values?
+ return moment(value).format(format)
+ }
+ fromStringToNumeric(value) {
+ return DateCol.getDate(value).unix() * 1000
+ }
+ _fromStringToDate(value) {
+ return DateCol.getDate(value).toDate()
+ }
+ getProbForColumnSpecimen(value) {
+ const isValid = DateCol.getDate(value).isValid()
+ return isValid
+ }
+ getStringExamples() {
+ return ["01/01/01"]
+ }
+ static getDateAsUnixUtx(value) {
+ return this.getDate(value, moment.utc).unix()
+ }
+ static getDate(value, momentFn = moment) {
+ let result = momentFn(value)
+ if (result.isValid()) return result
+ if (typeof value === "string" && value.match(/^[0-9]{8}$/)) {
+ const first2 = parseInt(value.substr(0, 2))
+ const second2 = parseInt(value.substr(2, 2))
+ const third2 = parseInt(value.substr(4, 2))
+ const last2 = parseInt(value.substr(6, 2))
+ const first4 = parseInt(value.substr(0, 4))
+ const last4 = parseInt(value.substr(4, 4))
+ const first2couldBeDay = first2 < 32
+ const first2couldBeMonth = first2 < 13
+ const second2couldBeDay = second2 < 32
+ const second2couldBeMonth = second2 < 13
+ const third2couldBeDay = third2 < 32
+ const third2couldBeMonth = third2 < 13
+ const last2couldBeDay = last2 < 32
+ const last2couldBeMonth = last2 < 13
+ const last4looksLikeAYear = last4 > 1000 && last4 < 2100
+ const first4looksLikeAYear = first4 > 1000 && first4 < 2100
+ // MMDDYYYY
+ // YYYYMMDD
+ // Prioritize the above 2 american versions
+ // YYYYDDMM
+ // DDMMYYYY
+ if (first2couldBeMonth && second2couldBeDay && last4looksLikeAYear) result = momentFn(value, "MMDDYYYY")
+ else if (first4looksLikeAYear && third2couldBeMonth && last2couldBeDay) result = momentFn(value, "YYYYMMDD")
+ else if (first4looksLikeAYear && last2couldBeMonth) result = momentFn(value, "YYYYDDMM")
+ else result = momentFn(value, "DDMMYYYY")
+ return result
+ } else if (typeof value === "string" && value.match(/^[0-9]{2}\/[0-9]{4}$/))
+ // MM/YYYY
+ return momentFn(value, "MM/YYYY")
+ // Check if timestamp
+ if (value.match && !value.match(/[^0-9]/)) {
+ const num = parseFloat(value)
+ if (!isNaN(num)) {
+ if (value.length === 10) return momentFn(num * 1000)
+ else return momentFn(num)
+ }
+ }
+ // Okay to return an invalid result if we dont find a match
+ // todo: why??? should we return "" instead ?
+ return result
+ }
+ }
+ // Beginning of day
+ class Day extends AbstractTemporal {
+ toDisplayString(value, format) {
+ return moment(value).format(format || "MM/DD/YYYY")
+ }
+ fromStringToNumeric(value) {
+ return DateCol.getDate(value).startOf("day").unix() * 1000
+ }
+ getVegaTimeUnit() {
+ return undefined
+ }
+ _fromStringToDate(value) {
+ return DateCol.getDate(value).startOf("day").toDate()
+ }
+ fromDateToNumeric(date) {
+ return moment(date).startOf("day").unix() * 1000
+ }
+ getStringExamples() {
+ return ["01/01/01"]
+ }
+ getProbForColumnSpecimen(sample) {
+ const format = moment.parseFormat ? moment.parseFormat(sample) : parseFormat
+ return format === "MM/DD/YY" || format === "MM/DD/YYYY" || format === "M/D/YYYY" ? 1 : 0
+ }
+ }
+ class HourMinute extends AbstractTemporal {
+ // todo: is this correct? I dont think so.
+ fromStringToNumeric(value) {
+ return parseFloat(DateCol.getDate(value).format("H.m"))
+ }
+ getVegaTimeUnit() {
+ return "hoursminutes"
+ }
+ // todo: is this correct? I dont think so.
+ getStringExamples() {
+ return ["2:30"]
+ }
+ }
+ class Minute extends AbstractTemporal {
+ toDisplayString(value, format) {
+ return moment(value).format("m")
+ }
+ fromStringToNumeric(value) {
+ return DateCol.getDate(value).startOf("minute").unix() * 1000
+ }
+ getVegaTimeUnit() {
+ return "minutes"
+ }
+ getStringExamples() {
+ return ["30"]
+ }
+ }
+ class AbstractTemporalInt extends AbstractTemporal {
+ fromStringToNumeric(val) {
+ return parseInt(val)
+ }
+ // getAsNativeJavascriptType(val: any): number {
+ // const result = super.getAsNativeJavascriptType(val)
+ // return result === undefined ? undefined : this.fromDateToNumeric(result)
+ // }
+ getStringExamples() {
+ return ["30"]
+ }
+ isNumeric() {
+ return true
+ }
+ isString() {
+ return false
+ }
+ fromDateToNumeric(date) {
+ return moment(date).unix()
+ }
+ _fromStringToDate(val) {
+ return moment(parseFloat(val)).toDate()
+ }
+ _fromNumericToDate(value) {
+ return moment(value).toDate()
+ }
+ getVegaTimeUnit() {
+ return "seconds"
+ }
+ }
+ class Week extends AbstractTemporalInt {
+ toDisplayString(value, format) {
+ return moment(value).format("MM/DD/YYYY - WW")
+ }
+ fromDateToNumeric(date) {
+ return moment(date).startOf("week").unix() * 1000
+ }
+ getVegaTimeUnit() {
+ return "quartermonth"
+ }
+ }
+ class Month extends AbstractTemporalInt {
+ toDisplayString(value, format) {
+ return moment(value).format(format || "MMMM")
+ }
+ fromDateToNumeric(date) {
+ return moment(date).startOf("month").unix() * 1000
+ }
+ getVegaTimeUnit() {
+ return "month"
+ }
+ }
+ class MonthDay extends AbstractTemporalInt {
+ toDisplayString(value, format) {
+ return moment(value).format(format || "MMDD")
+ }
+ fromDateToNumeric(date) {
+ return moment(date).unix() * 1000
+ }
+ getVegaTimeUnit() {
+ return "monthdate"
+ }
+ }
+ class Hour extends AbstractTemporalInt {
+ fromDateToNumeric(date) {
+ return parseInt(moment(date).startOf("hour").format("H"))
+ }
+ getVegaTimeUnit() {
+ return "hours"
+ }
+ }
+ class Year extends AbstractTemporalInt {
+ fromDateToNumeric(date) {
+ return parseInt(moment(date).format("YYYY"))
+ }
+ _fromStringToDate(val) {
+ return moment(parseFloat(val), "YYYY").toDate()
+ }
+ toDisplayString(value, format) {
+ return moment(value).format(format || "YYYY")
+ }
+ getVegaTimeUnit() {
+ return "year"
+ }
+ _fromNumericToDate(value) {
+ return moment(value, "YYYY").toDate()
+ }
+ isTemporal() {
+ return true
+ }
+ }
+ class AbstractMillisecond extends AbstractTemporalInt {
+ toDisplayString(value, format) {
+ if (format === "fromNow") return moment(parseFloat(value)).fromNow()
+ return value
+ }
+ isTemporal() {
+ return true
+ }
+ getDefaultFormat() {
+ return "fromNow"
+ }
+ }
+ class MilliSecond extends AbstractMillisecond {
+ getVegaTimeUnit() {
+ return "milliseconds"
+ }
+ }
+ class Second extends AbstractMillisecond {
+ fromStringToNumeric(value) {
+ return parseInt(value) * 1000
+ }
+ getVegaTimeUnit() {
+ return "seconds"
+ }
+ _fromNumericToDate(number) {
+ return moment(number * 1000).toDate()
+ }
+ toDisplayString(value, format) {
+ if (format === "fromNow") return moment(parseFloat(value) * 1000).fromNow()
+ return value
+ }
+ }
+ // todo: ADD TYPINGS
+ class Column {
+ constructor(colDef = {}, rawAnyVector) {
+ this._colDefObject = colDef
+ this._rawAnyVectorFromSource = rawAnyVector
+ this._sampleSet = jtree.Utils.sampleWithoutReplacement(rawAnyVector, 30, Date.now())
+ }
+ static _getPrimitiveTypesCollection() {
+ if (!this._colTypes)
+ this._colTypes = {
+ millisecond: new MilliSecond("millisecond"),
+ second: new Second("second"),
+ date: new DateCol("date"),
+ day: new Day("day"),
+ week: new Week("week"),
+ month: new Month("month"),
+ monthDay: new MonthDay("monthDay"),
+ hour: new Hour("hour"),
+ hourMinute: new HourMinute("hourMinute"),
+ minute: new Minute("minute"),
+ year: new Year("year"),
+ feet: new Feet("feet"),
+ usd: new USD("usd"),
+ number: new NumberCol("number"),
+ numberString: new NumberString("numberString"),
+ string: new StringCol("string"),
+ text: new TextCol("text"),
+ path: new PathCol("path"),
+ dir: new DirCol("dir"),
+ code: new CodeCol("code"),
+ html: new HTMLCol("html"),
+ url: new UrlCol("url"),
+ object: new ObjectType("object"),
+ boolean: new BooleanType("boolean"),
+ int: new IntType("int"),
+ }
+ return this._colTypes
+ }
+ static getPrimitiveTypeByName(name) {
+ return this._getPrimitiveTypesCollection()[name]
+ }
+ getMathFn() {
+ return this._colDefObject.mathFn
+ }
+ getColumnName() {
+ return this._getColDefObject().name
+ }
+ _getSourceColumnName() {
+ return this._colDefObject.source
+ }
+ isInvalidValue(value) {
+ return this.getPrimitiveTypeObj().isInvalidValue(value)
+ }
+ _getFirstNonEmptyValueFromSampleSet() {
+ if (this._sample === undefined) {
+ const sampleSet = this._getSampleSet()
+ this._sample = sampleSet.length ? sampleSet.find((value) => !jtree.Utils.isValueEmpty(value)) : ""
+ }
+ return this._sample
+ }
+ _getColDefObject() {
+ return this._colDefObject
+ }
+ getPrimitiveTypeObj() {
+ if (!this._type) this._type = this._inferType()
+ return this._type
+ }
+ synthesizeValue(randomNumberFn) {
+ return this.getPrimitiveTypeObj().synthesizeValue(randomNumberFn)
+ }
+ isTemporal() {
+ return this.getPrimitiveTypeObj().isTemporal()
+ }
+ toDisplayString(value) {
+ return this.getPrimitiveTypeObj().toDisplayString(value, this.getFormat())
+ }
+ isString() {
+ return this.getPrimitiveTypeObj().isString()
+ }
+ isNumeric() {
+ return this.getPrimitiveTypeObj().isNumeric()
+ }
+ isHash() {
+ return this.isString() && false // todo: make this work. identify random hashes, et cetera.
+ }
+ // todo: isEnum/isSet
+ // todo: isUniqueTimestamp
+ getEntropy() {
+ if (this._entropy !== undefined) return this._entropy
+ const possibilities = {}
+ let bits = 1
+ const name = this.getColumnName()
+ this._getSampleSet().forEach((val) => {
+ if (possibilities[val]) return
+ bits++
+ possibilities[val] = true
+ })
+ this._entropy = bits
+ return this._entropy
+ }
+ isLink() {
+ if (this._isLink !== undefined) return this._isLink
+ const sample = this._getFirstNonEmptyValueFromSampleSet()
+ if (!this.isString() || !sample || !sample.match) this._isLink = false
+ else this._isLink = sample.match(/^(https?\:|\/)/) ? true : false
+ return this._isLink
+ }
+ _getSampleSet() {
+ return this._sampleSet
+ }
+ isUnique() {
+ return this.getEntropy() - 1 === this._getSampleSet().length
+ }
+ getTitlePotential() {
+ if (this._getColDefObject().title) return 1
+ if (this._titlePotential !== undefined) return this._titlePotential
+ const titleCols = {
+ title: 0.99,
+ name: 0.98,
+ label: 0.97,
+ category: 0.96,
+ }
+ const lowerCaseName = this.getColumnName().toLowerCase()
+ if (titleCols[lowerCaseName]) this._titlePotential = titleCols[lowerCaseName]
+ else if (this.getEstimatedTextLength() > 150) this._titlePotential = 0.01
+ else if (this.isString() && !this.isLink() && this.isUnique() && !this.isHash()) this._titlePotential = 0.75
+ else this._titlePotential = 0
+ return this._titlePotential
+ }
+ getVegaType() {
+ return this.getPrimitiveTypeObj().getVegaType()
+ }
+ getVegaTimeUnit() {
+ const type = this.getPrimitiveTypeObj()
+ return type.getVegaTimeUnit ? type.getVegaTimeUnit() : undefined
+ }
+ getEstimatedTextLength() {
+ if (!this.isString()) return 0
+ if (this._estimatedTextLength !== undefined) return this._estimatedTextLength
+ const name = this.getColumnName()
+ const sampleSet = this._getSampleSet()
+ const sum = sampleSet.map((val) => val && val.length).reduce((rowLength, cumulative) => rowLength + cumulative, 0)
+ this._estimatedTextLength = Math.floor(sum / sampleSet.length)
+ return this._estimatedTextLength
+ }
+ getFormat() {
+ if (this._getColDefObject().format) return this._getColDefObject().format
+ return this.getPrimitiveTypeObj().getDefaultFormat(this.getColumnName(), this._getFirstNonEmptyValueFromSampleSet())
+ }
+ getBlankPercentage() {
+ let blankCount = 0
+ let mistypedCount = 0 // todo.
+ const colName = this.getColumnName()
+ const sampleSet = this._getSampleSet()
+ sampleSet.forEach((value) => {
+ if (value === undefined || value === "") blankCount++
+ // todo: add mistyped data
+ })
+ return blankCount / (sampleSet.length || 1)
+ }
+ getMap() {
+ const map = this._map
+ if (map) return map
+ this._map = this._getSummaryVector().map
+ return this._map
+ }
+ getValues() {
+ return this._getSummaryVector().values
+ }
+ _getSummaryVector() {
+ if (!this._summaryVector) this._summaryVector = this._createSummaryVector()
+ return this._summaryVector
+ }
+ _getRawAnyVectorFromSource() {
+ return this._rawAnyVectorFromSource
+ }
+ _createSummaryVector() {
+ const values = []
+ const map = new Map()
+ let incompleteCount = 0
+ let uniques = 0
+ let index = 0
+ let rawVector = this._getRawAnyVectorFromSource()
+ // If needs conversion.
+ // todo: add tests
+ const primitiveType = this.getPrimitiveTypeObj()
+ if (primitiveType.isNumeric() && typeof rawVector[0] === "string") rawVector = rawVector.map(primitiveType.fromStringToNumeric)
+ rawVector.forEach((val) => {
+ if (this.isInvalidValue(val)) {
+ incompleteCount++
+ return true
+ }
+ if (!map.has(val)) {
+ map.set(val, { count: 0, index: index })
+ uniques++
+ } else map.get(val).count++
+ values.push(val)
+ })
+ return {
+ map: map,
+ values: values,
+ incompleteCount: incompleteCount,
+ uniqueValues: uniques,
+ }
+ }
+ getQuins() {
+ const deciles = this.getReductions().deciles
+ return [20, 40, 60, 80, 100].map((decile) => {
+ return {
+ value: deciles[decile],
+ percent: decile / 100,
+ }
+ })
+ }
+ getPrimitiveTypeName() {
+ return this.getPrimitiveTypeObj().getPrimitiveTypeName()
+ }
+ toObject() {
+ return {
+ name: this.getColumnName(),
+ type: this.getPrimitiveTypeName(),
+ vegaType: this.getVegaType(),
+ vegaTimeUnit: this.getVegaTimeUnit(),
+ reduction: this._getColDefObject().reduction,
+ titlePotential: this.getTitlePotential(),
+ isString: this.isString(),
+ isTemporal: this.isTemporal(),
+ isLink: this.isLink(),
+ estimatedTextLength: this.getEstimatedTextLength(),
+ }
+ }
+ getReductions() {
+ if (!this._reductions) this._reductions = this._getReductionResult(this._getSummaryVector(), this)
+ return this._reductions
+ }
+ getMax() {
+ return this.getReductions().max
+ }
+ getMin() {
+ return this.getReductions().min
+ }
+ getMean() {
+ return this.getReductions().mean
+ }
+ _getReductionResult(valuesObj, col) {
+ const values = valuesObj.values
+ const count = values.length
+ const reductionResult = {}
+ reductionResult.incompleteCount = valuesObj.incompleteCount
+ reductionResult.uniqueValues = valuesObj.uniqueValues
+ if (!count) return reductionResult
+ const numericCompare = (av, bv) => (av > bv ? 1 : av < bv ? -1 : 0)
+ const arr = values.slice()
+ col.isString() ? arr.sort() : arr.sort(numericCompare)
+ let min = arr[0]
+ let max = arr[0]
+ let sum = 0
+ let mode = undefined
+ let modeSize = 0
+ let currentBucketValue = undefined
+ let currentBucketSize = 0
+ for (let index = 0; index < count; index++) {
+ let value = arr[index]
+ sum += value
+ if (value > max) max = value
+ if (value < min) min = value
+ if (value === currentBucketValue) currentBucketSize++
+ else {
+ currentBucketValue = value
+ currentBucketSize = 1
+ }
+ if (currentBucketSize > modeSize) {
+ modeSize = currentBucketSize
+ mode = currentBucketValue
+ }
+ }
+ const medianIndex = Math.floor(count / 2)
+ reductionResult.count = count
+ reductionResult.sum = sum
+ reductionResult.median = arredianIndex]
+ reductionResult.mean = sum / count
+ reductionResult.min = min
+ reductionResult.max = max
+ reductionResult.range = max - min
+ reductionResult.mode = mode
+ reductionResult.modeSize = modeSize
+ if (col.isString()) {
+ reductionResult.sum = undefined
+ reductionResult.mean = undefined
+ } else if (col.isTemporal()) reductionResult.sum = undefined
+ reductionResult.deciles = {}
+ const deciles = [10, 20, 30, 40, 50, 60, 70, 80, 90, 99, 100]
+ deciles.forEach((decile) => {
+ let index = Math.floor(count * (decile / 100))
+ index = index === count ? index - 1 : index
+ reductionResult.deciles[decile] = arr[index]
+ })
+ return reductionResult
+ }
+ static _getColumnProbabilities(name, sample) {
+ // Assume data is trimmed.
+ const sampleType = typeof sample
+ const sampleStringLength = sample !== undefined ? sample.toString().length : 0
+ const guesses = {}
+ guesses.number = 0.5
+ guesses.date = 0.25
+ guesses.string = 0.75
+ guesses.feet = 0.01 // 5'11"
+ guesses.object = 0.1
+ guesses.boolean = 0.02
+ const isDate = sample instanceof Date
+ const isNumber = !isNaN(parseFloat(sample)) || Column.getPrimitiveTypeByName("usd").getProbForColumnSpecimen(sample)
+ if (sampleType === "object") guesses.object = 0.9
+ if (sample === true || sample === false) guesses.boolean = 0.96
+ if (name.match(/(team|name|link|image|description|permalink|title|label|status|thumb|ip|useragent)/i)) guesses.string = 0.95
+ if (name.match(/(gender|region|category|group|section|sector|field)/i)) guesses.string = 0.95
+ if (name.match(/(height|length)/i)) {
+ if (sample.toString().match(/[0-9]+(\'|\-)[0-9\.]+/) && Column.getPrimitiveTypeByName("feet").getProbForColumnSpecimen(sample)) guesses.feet = 0.97
+ }
+ if (isNumber) guesses.number = 0.8
+ else guesses.number = 0.1
+ const usdGuess = Column.getPrimitiveTypeByName("usd").getProbForColumnSpecimen(sample)
+ if (usdGuess || (!isNaN(sample) && name.match(/^(price|income|cost|revenue|budget|profit|amount|balance)$/i))) guesses.usd = 0.99
+ if (isNumber && name.match(/(year|born)/i) && sampleStringLength === 4) guesses.year = 0.99
+ if (isDate && name.match(/(year|born)/i)) guesses.year = 0.99
+ if (isNumber && name.match(/(time|second|created|edited)/i) && sampleStringLength === 10) guesses.second = 0.99
+ if (isNumber && name.match(/(time|second)/i) && sampleStringLength === 13) guesses.millisecond = 0.99
+ const isValidDate = Column.getPrimitiveTypeByName("date").getProbForColumnSpecimen(sample)
+ if (name.match(/(year|date|dob|birthday|day|month|time|birthdate|utc)/i) && isValidDate) guesses.date = 0.98
+ else if (isValidDate && sampleType === "string" && sample.includes("/")) guesses.date = 0.81
+ if (sampleType === "string" && sampleStringLength > 100) guesses.string = 0.98
+ return guesses
+ }
+ _inferType() {
+ const columnObj = this._getColDefObject()
+ const sample = this._getFirstNonEmptyValueFromSampleSet()
+ if (columnObj && columnObj.type && Column.getPrimitiveTypeByName(columnObj.type)) return Column.getPrimitiveTypeByName(columnObj.type)
+ const guesses = Column._getColumnProbabilities(this.getColumnName(), this._getFirstNonEmptyValueFromSampleSet())
+ let max = 0
+ let bestGuess = null
+ for (let typeScore in guesses) {
+ if (guesses[typeScore] > max) {
+ max = guesses[typeScore]
+ bestGuess = typeScore
+ }
+ }
+ if (bestGuess === "number" && typeof sample === "string") {
+ if (sample.match(",")) bestGuess = "numberString"
+ }
+ if (bestGuess === "date" && typeof sample === "string") {
+ if (Column.getPrimitiveTypeByName("day").getProbForColumnSpecimen(sample)) bestGuess = "day"
+ }
+ return Column.getPrimitiveTypeByName(bestGuess)
+ }
+ // Note: If it returns a string removes spaces
+ static convertValueToNumeric(value, sourceType, destinationType, mathFn) {
+ const destType = this.getPrimitiveTypeByName(destinationType)
+ if (value === undefined || !destType || value === "") return ""
+ const conversionFn = this._getConversionFn(sourceType, destinationType, value)
+ const res = conversionFn(value)
+ if (mathFn) return mathFn(res)
+ return res
+ }
+ static _getConversionFn(sourceType, destinationType, value) {
+ const sourceCol = this.getPrimitiveTypeByName(sourceType)
+ const destinationCol = this.getPrimitiveTypeByName(destinationType)
+ if (!sourceCol || !destinationCol) return destinationCol.fromStringToNumeric
+ if (destinationCol.isTemporal() && sourceCol.isTemporal()) return (val) => destinationCol.fromDateToNumeric(sourceCol._fromStringToDate(val))
+ return destinationCol.fromStringToNumeric
+ }
+ }
+ const PrimitiveTypes = {
+ AbstractPrimitiveType,
+ BooleanType,
+ ObjectType,
+ USD,
+ NumberCol,
+ NumberString,
+ Feet,
+ IntType,
+ UrlCol,
+ HTMLCol,
+ DirCol,
+ PathCol,
+ TextCol,
+ StringCol,
+ AbstractTemporal,
+ MilliSecond,
+ Second,
+ DateCol,
+ Day,
+ Month,
+ MonthDay,
+ Week,
+ Hour,
+ Minute,
+ Year,
+ HourMinute,
+ CodeCol,
+ }
+ window.Column = Column
+ window.PrimitiveTypes = PrimitiveTypes
+ //onsave jtree build produce jtable.browser.js
+ //onsave jtree build produce jtable.node.js
+ class Row {
+ constructor(sourceObject = {}, table) {
+ this._puid = this._getUniqueId()
+ this._sourceObject = sourceObject
+ this._table = table
+ }
+ _getUniqueId() {
+ Row._uniqueId++
+ return Row._uniqueId
+ }
+ destroy() {}
+ async destroyRow() {}
+ getAsArray(headerRow) {
+ const obj = this.rowToObjectWithOnlyNativeJavascriptTypes()
+ return headerRow.map((col) => obj[col])
+ }
+ getRowSourceObject() {
+ return this._sourceObject
+ }
+ toVector() {
+ return Object.values(this.rowToObjectWithOnlyNativeJavascriptTypes())
+ }
+ // todo: rowToObjectWithOnlyNativeJavascriptTypes method? Its numerics where we need and strings where we need.
+ _parseIntoObjectWithOnlyNativeJavascriptTypes() {
+ const columns = this._table.getColumnsMap()
+ const typedNode = {}
+ Object.keys(columns).forEach((colName) => {
+ typedNode[colName] = this._getRowValueFromSourceColOrOriginalCol(colName)
+ })
+ return typedNode
+ }
+ // why from source col? if we always copy, we shouldnt need that, correct? perhaps have an audit array of all operations on a row?
+ _getRowValueFromSourceColOrOriginalCol(colName) {
+ const columns = this._table.getColumnsMap()
+ const destColumn = columns[colName]
+ const sourceColName = destColumn._getSourceColumnName()
+ const sourceCol = columns[sourceColName]
+ // only use source if we still have access to it
+ const val =
+ sourceColName && sourceCol
+ ? this._getRowValueFromOriginalOrSource(sourceColName, sourceCol.getPrimitiveTypeName(), destColumn.getPrimitiveTypeName())
+ : this.getRowOriginalValue(colName)
+ const res = destColumn.getPrimitiveTypeObj().getAsNativeJavascriptType(val)
+ const mathFn = destColumn.getMathFn()
+ if (mathFn) return mathFn(res)
+ return res
+ }
+ _getRowValueFromOriginalOrSource(sourceColName, sourceColType, destType) {
+ return Column.convertValueToNumeric(this.getRowOriginalValue(sourceColName), sourceColType, destType)
+ }
+ rowToObjectWithOnlyNativeJavascriptTypes() {
+ if (!this._objectWithOnlyNativeJavascriptTypes) this._objectWithOnlyNativeJavascriptTypes = this._parseIntoObjectWithOnlyNativeJavascriptTypes()
+ return this._objectWithOnlyNativeJavascriptTypes
+ }
+ getRowKeys() {
+ return Object.keys(this.getRowSourceObject())
+ }
+ getFirstValue() {
+ return this.getRowOriginalValue(this.getRowKeys()[0])
+ }
+ // todo: get values from source/virtual columns
+ getRowOriginalValue(column) {
+ const value = this.getRowSourceObject()[column]
+ return value === null ? "" : value
+ }
+ getRowHtmlSafeValue(columnName) {
+ const val = this.getRowOriginalValue(columnName)
+ return val === undefined ? "" : jtree.Utils.stripHtml(val.toString()).toString() // todo: cache this?
+ }
+ getHoverTitle() {
+ return encodeURIComponent(this.rowToString().replace(/\n/g, " "))
+ }
+ getPuid() {
+ return this._puid
+ }
+ rowToString() {
+ return JSON.stringify(this.getRowSourceObject(), null, 2)
+ }
+ }
+ Row._uniqueId = 0
+ window.Row = Row
+ //onsave jtree build produce jtable.browser.js
+ //onsave jtree build produce jtable.node.js
+ var TableParserIds
+ ;(function (TableParserIds) {
+ TableParserIds["csv"] = "csv"
+ TableParserIds["ssv"] = "ssv"
+ TableParserIds["psv"] = "psv"
+ TableParserIds["tsv"] = "tsv"
+ TableParserIds["xml"] = "xml"
+ TableParserIds["html"] = "html"
+ TableParserIds["spaced"] = "spaced"
+ TableParserIds["tree"] = "tree"
+ TableParserIds["treeRows"] = "treeRows"
+ TableParserIds["sections"] = "sections"
+ TableParserIds["txt"] = "txt"
+ TableParserIds["list"] = "list"
+ TableParserIds["text"] = "text"
+ TableParserIds["jsonVector"] = "jsonVector"
+ TableParserIds["json"] = "json"
+ TableParserIds["jsonDataTableWithHeader"] = "jsonDataTableWithHeader"
+ TableParserIds["jsonMap"] = "jsonMap"
+ TableParserIds["jsonCounts"] = "jsonCounts"
+ })(TableParserIds || (TableParserIds = {}))
+ // todo: detect mixed format, like a csv file with a header. and then suggest ignore that part, or splitting it out?
+ // maybe we could split a string into sections, and say "we've detected 3 sections, which one do you want to use"?
+ // todo: split csv into normal csv and advanced delimited.
+ // todo: allow for metadata like filename and filetype header
+ class RowStringSpecimen {
+ constructor(str) {
+ const trimmedStr = str.trim()
+ const lines = trimmedStr.split(/\n/g)
+ const firstLine = lines[0]
+ const strCount = (str, reg) => (str.match(reg) || []).length
+ // todo: do these things lazily.
+ this.trimmedStr = trimmedStr
+ this.lines = lines
+ this.firstLine = firstLine
+ this.lineCount = lines.length
+ this.indentedLineCount = strCount(trimmedStr, /\n /g)
+ this.blankLineCount = strCount(trimmedStr, /\n\n/g)
+ this.commaCount = strCount(trimmedStr, /\,/g)
+ this.tabCount = strCount(trimmedStr, /\t/g)
+ this.verticalBarCount = strCount(trimmedStr, /\|/g)
+ this.firstLineCommaCount = strCount(firstLine, /\,/g)
+ this.firstLineTabCount = strCount(firstLine, /\t/g)
+ this.firstLineSpaceCount = strCount(firstLine, / /g)
+ this.firstLineVerticalBarCount = strCount(firstLine, /\|/g)
+ }
+ getParsedJsonAttemptResult() {
+ if (this._parsedJsonObject) return this._parsedJsonObject
+ try {
+ this._parsedJsonObject = { ok: true, result: JSON.parse(this.trimmedStr) }
+ } catch (err) {
+ this._parsedJsonObject = { ok: false }
+ }
+ return this._parsedJsonObject
+ }
+ }
+ class AbstractTableParser {
+ isNodeJs() {
+ return typeof exports !== "undefined"
+ }
+ }
+ class AbstractJsonParser extends AbstractTableParser {
+ getParserId() {
+ return TableParserIds.json
+ }
+ getProbForRowSpecimen(specimen) {
+ return 0
+ }
+ getExample() {
+ return JSON.stringify([
+ { name: "joe", age: 2 },
+ { name: "mike", age: 4 },
+ ])
+ }
+ _parseTableInputsFromString(str) {
+ const obj = JSON.parse(str)
+ return { rows: obj instanceof Array ? obj : [obj] }
+ }
+ }
+ class JsonParser extends AbstractJsonParser {}
+ class AbstractJsonArrayParser extends AbstractJsonParser {
+ getExample() {
+ return JSON.stringify([
+ { name: "jane", age: 33 },
+ { name: "bill", age: 25 },
+ ])
+ }
+ getProbForRowSpecimen(specimen) {
+ const str = specimen.trimmedStr
+ if (str.match(/^\s*\[/) && str.match(/\]\s*$/)) return 0.98
+ return 0
+ }
+ }
+ class JsonArrayParser extends AbstractJsonArrayParser {}
+ class JsonDataTableWithHeaderParser extends AbstractJsonArrayParser {
+ getExample() {
+ return JSON.stringify([
+ ["country", "income", "health", "population"],
+ ["Afghanistan", 1925, "57.63", 32526562],
+ ["Albania", 10620, "76", 2896679],
+ ])
+ }
+ _parseTableInputsFromString(str) {
+ return { rows: jtree.Utils.javascriptTableWithHeaderRowToObjects(JSON.parse(str)) }
+ }
+ getParserId() {
+ return TableParserIds.jsonDataTableWithHeader
+ }
+ getProbForRowSpecimen(specimen) {
+ const result = specimen.getParsedJsonAttemptResult()
+ if (!result.ok) return 0
+ if (JsonDataTableWithHeaderParser.isJavaScriptDataTable(result.result)) return 0.99
+ return 0.001
+ }
+ static isJavaScriptDataTable(obj) {
+ const isAnArray = obj instanceof Array
+ if (!isAnArray) return false
+ const isAnArrayOfArrays = obj.every((row) => row instanceof Array)
+ if (!isAnArrayOfArrays) return false
+ if (obj.length < 3) return false
+ const firstRowTypes = obj[0].map((item) => typeof item === "string").join(" ")
+ const secondRowTypes = obj[1].map((item) => typeof item === "string").join(" ")
+ const thirdRowTypes = obj[2].map((item) => typeof item === "string").join(" ")
+ const firstRowIsJustStrings = !firstRowTypes.replace(/true/g, "").trim()
+ if (secondRowTypes === thirdRowTypes && secondRowTypes !== firstRowTypes && firstRowIsJustStrings) return true
+ return false
+ }
+ }
+ class AbstractJsonObjectParser extends AbstractJsonParser {
+ getProbForRowSpecimen(specimen) {
+ const str = specimen.trimmedStr
+ if (str.match(/^\s*\{/) && str.match(/\}\s*$/)) return 0.99
+ return 0
+ }
+ }
+ class JsonObjectParser extends AbstractJsonObjectParser {}
+ // formerley flatobject
+ class JsonMapParser extends AbstractJsonObjectParser {
+ getExample() {
+ return JSON.stringify({ person1: { name: "joe", age: 2 }, person2: { name: "mike", age: 4 } })
+ }
+ getParserId() {
+ return TableParserIds.jsonMap
+ }
+ _parseTableInputsFromString(str) {
+ // todo: should we preserve keys?
+ return { rows: Object.values(JSON.parse(str)) }
+ }
+ }
+ // formerly flatarray
+ class JsonVectorParser extends AbstractJsonArrayParser {
+ getExample() {
+ return JSON.stringify([23, 32, 41])
+ }
+ getParserId() {
+ return TableParserIds.jsonVector
+ }
+ getProbForRowSpecimen(specimen) {
+ const result = specimen.getParsedJsonAttemptResult()
+ if (!result.ok) return 0
+ if (!(result.result instanceof Array)) return 0
+ return result.result.filter((item) => item && typeof item === "object" && item.hasOwnProperty).length === 0 ? 1 : 0
+ return 0
+ }
+ _parseTableInputsFromString(str) {
+ return {
+ rows: JSON.parse(str).map((num) => {
+ return { value: num }
+ }),
+ }
+ }
+ }
+ class JsonCountMapParser extends AbstractJsonObjectParser {
+ getParserId() {
+ return TableParserIds.jsonCounts
+ }
+ getProbForRowSpecimen(specimen) {
+ const result = specimen.getParsedJsonAttemptResult()
+ if (!result.ok) return 0
+ const keys = Object.keys(result.result)
+ if (keys.length < 2) return 0
+ return !keys.some((key) => typeof result.result[key] !== "number") ? 1 : 0
+ return 0
+ }
+ getExample() {
+ return JSON.stringify({ h1: 10, h2: 5, h3: 2 })
+ }
+ _parseTableInputsFromString(str) {
+ const obj = JSON.parse(str)
+ return {
+ rows: Object.keys(obj).map((key) => {
+ return {
+ name: key,
+ count: obj[key],
+ }
+ }),
+ }
+ }
+ }
+ // todo: remove?
+ class AbstractJTreeTableParser extends AbstractTableParser {
+ _parseTableInputsFromString(str) {
+ return {
+ rows: this._parseTrees(str)
+ .filter((node) => node.length)
+ .map((node) => node.toObject()),
+ }
+ }
+ _parseTrees(str) {
+ return []
+ }
+ }
+ class CsvParser extends AbstractJTreeTableParser {
+ getExample() {
+ return `name,age,height
+ john,12,50`
+ }
+ _parseTrees(str) {
+ return jtree.TreeNode.fromCsv(str)
+ }
+ getProbForRowSpecimen(specimen) {
+ if (!specimen.firstLineCommaCount) return 0
+ if (specimen.blankLineCount) return 0.05
+ return 0.49
+ }
+ getParserId() {
+ return TableParserIds.csv
+ }
+ }
+ class TsvParser extends AbstractJTreeTableParser {
+ getExample() {
+ return `name\tage\theight
+ john\t12\t50`
+ }
+ _parseTrees(str) {
+ return jtree.TreeNode.fromTsv(str)
+ }
+ getProbForRowSpecimen(specimen) {
+ if (!specimen.firstLineTabCount) return 0
+ else if (specimen.tabCount > 5) return 0.9
+ return 0.25
+ }
+ getParserId() {
+ return TableParserIds.tsv
+ }
+ }
+ class PsvParser extends AbstractJTreeTableParser {
+ getParserId() {
+ return TableParserIds.psv
+ }
+ getExample() {
+ return `name|age
+ mike|33`
+ }
+ _parseTrees(str) {
+ return jtree.TreeNode.fromDelimited(str, "|", '"')
+ }
+ getProbForRowSpecimen(specimen) {
+ // vertical bar separated file
+ if (!specimen.firstLineVerticalBarCount) return 0
+ else if (specimen.verticalBarCount >= specimen.lineCount) return 0.8
+ return 0.01
+ }
+ }
+ class SsvParser extends AbstractJTreeTableParser {
+ getExample() {
+ return `name age height
+ john 12 50`
+ }
+ getParserId() {
+ return TableParserIds.ssv
+ }
+ _parseTrees(str) {
+ return jtree.TreeNode.fromSsv(str)
+ }
+ getProbForRowSpecimen(specimen) {
+ if (!specimen.firstLineSpaceCount) return 0
+ if (specimen.blankLineCount) return 0.05
+ return 0.11
+ }
+ }
+ class XmlParser extends AbstractJTreeTableParser {
+ getProbForRowSpecimen(specimen) {
+ return specimen.trimmedStr.match(/^ *\) ? 1 : 0
+ }
+ getExample() {
+ }
+ getParserId() {
+ return TableParserIds.xml
+ }
+ _parseTrees(str) {
+ // todo: fix this! Create an XML Tree Language
+ if (this.isNodeJs()) return new jtree.TreeNode(str)
+ return jtree.TreeNode.fromXml(str)
+ }
+ }
+ class HtmlParser extends AbstractJTreeTableParser {
+ getProbForRowSpecimen(specimen) {
+ return specimen.trimmedStr.match(/^(\<\!doctype html\>|\
+ }
+ getExample() {
+ return `
+
+ bam`
+ }
+ getParserId() {
+ return TableParserIds.html
+ }
+ _parseTrees(str) {
+ if (this.isNodeJs()) return new jtree.TreeNode(str)
+ return jtree.TreeNode.fromXml(str)
+ }
+ }
+ class TreeRowsParser extends AbstractJTreeTableParser {
+ getExample() {
+ return `person
+ name john
+ age 12
+ height 50`
+ }
+ _parseTableInputsFromString(str) {
+ // todo: get columns on first pass.
+ const rows = new jtree.TreeNode(str)
+ return {
+ rows: rows.map((node) => node.toObject()),
+ columnDefinitions: rows.getColumnNames().map((name) => {
+ return { name: name }
+ }),
+ }
+ }
+ getProbForRowSpecimen(specimen) {
+ if (specimen.indentedLineCount < 1) return 0
+ return 0.1
+ }
+ getParserId() {
+ return TableParserIds.treeRows
+ }
+ }
+ class TreeParser extends AbstractJTreeTableParser {
+ getExample() {
+ return `country
+ name USA
+ state
+ name MA
+ city
+ name Brockton`
+ }
+ _parseTrees(str) {
+ // todo: add tests. Detected value(s) or undefined subtrees, treating as object.
+ const newTree = new jtree.TreeNode()
+ newTree.pushContentAndChildren(undefined, str instanceof jtree.TreeNode ? str : new jtree.TreeNode(str))
+ return newTree
+ }
+ getProbForRowSpecimen(specimen) {
+ return 0
+ }
+ getParserId() {
+ return TableParserIds.tree
+ }
+ }
+ class SpacedParser extends AbstractTableParser {
+ getExample() {
+ return `name john
+ age 12
+ name mary
+ age 20`
+ }
+ getParserId() {
+ return TableParserIds.spaced
+ }
+ getProbForRowSpecimen(specimen) {
+ if (specimen.blankLineCount > 10) return 0.95
+ return 0.05
+ }
+ _parseTableInputsFromString(str) {
+ // todo: clean this up. it looks like this is just trees, but not indented, with a newline as a delimiter.
+ const headerBreak = str.indexOf("\n\n")
+ const header = str.substr(0, headerBreak)
+ let names = header.split(/\n/g)
+ const rest = str
+ .substr(headerBreak + 2)
+ .replace(/\n\n/g, "\n")
+ .trim()
+ .split("\n")
+ const nodeCount = names.length
+ const lineCount = rest.length
+ const rows = []
+ // todo: should we do this here?
+ names = names.map((name) => name.replace(/ /g, ""))
+ for (let lineNumber = 0; lineNumber < lineCount; lineNumber = lineNumber + nodeCount) {
+ const obj = {}
+ names.forEach((col, index) => {
+ obj[col] = rest[lineNumber + index].trim()
+ })
+ rows.push(obj)
+ }
+ return { rows: rows }
+ }
+ }
+ class SectionsParser extends AbstractTableParser {
+ getExample() {
+ return `name
+ age
- request.Response = Response;
- /**
- * Initialize a new `Request` with the given `method` and `url`.
- *
- * @param {String} method
- * @param {String} url
- * @api public
- */
+ john
+ 12
- function Request(method, url) {
- var self = this;
- this._query = this._query || [];
- this.method = method;
- this.url = url;
- this.header = {}; // preserves header name case
-
- this._header = {}; // coerces header names to lowercase
-
- this.on('end', function () {
- var err = null;
- var res = null;
-
- try {
- res = new Response(self);
- } catch (err2) {
- err = new Error('Parser is unable to parse the response');
- err.parse = true;
- err.original = err2; // issue #675: return the raw response if the response parsing fails
-
- if (self.xhr) {
- // ie9 doesn't have 'response' property
- err.rawResponse = typeof self.xhr.responseType === 'undefined' ? self.xhr.responseText : self.xhr.response; // issue #876: return the http status code if the response parsing fails
-
- err.status = self.xhr.status ? self.xhr.status : null;
- err.statusCode = err.status; // backwards-compat only
- } else {
- err.rawResponse = null;
- err.status = null;
- }
-
- return self.callback(err);
- }
-
- self.emit('response', res);
- var new_err;
-
- try {
- if (!self._isResponseOK(res)) {
- new_err = new Error(res.statusText || 'Unsuccessful HTTP response');
- }
- } catch (err2) {
- new_err = err2; // ok() callback can throw
- } // #1000 don't catch errors from the callback to avoid double calling it
-
-
- if (new_err) {
- new_err.original = err;
- new_err.response = res;
- new_err.status = res.status;
- self.callback(new_err, res);
- } else {
- self.callback(null, res);
- }
- });
+ mary
+ 20`
+ }
+ getProbForRowSpecimen() {
+ return 0
+ }
+ getParserId() {
+ return TableParserIds.sections
+ }
+ _parseTableInputsFromString(str) {
+ const firstDoubleNewline = str.indexOf("\n\n")
+ const tiles = [str.slice(0, firstDoubleNewline), str.slice(firstDoubleNewline + 1)]
+ const header = tiles.shift()
+ const names = header.split(/\n/g)
+ const length = names.length
+ const lines = tiles[0].trim().split(/\n/g)
+ const lineCount = lines.length
+ const rowCount = lineCount / length
+ const rows = []
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
+ const startLine = rowIndex * length
+ const values = lines.slice(startLine, startLine + length)
+ const obj = {}
+ names.forEach((name, colIndex) => (obj[name] = values[colIndex]))
+ rows.push(obj)
+ }
+ return { rows: rows }
+ }
- /**
- * Mixin `Emitter` and `RequestBase`.
- */
- // eslint-disable-next-line new-cap
-
-
- Emitter(Request.prototype); // eslint-disable-next-line new-cap
-
- RequestBase(Request.prototype);
- /**
- * Set Content-Type to `type`, mapping values from `request.types`.
- *
- * Examples:
- *
- * superagent.types.xml = 'application/xml';
- *
- * request.post('/')
- * .type('xml')
- * .send(xmlstring)
- * .end(callback);
- *
- * request.post('/')
- * .type('application/xml')
- * .send(xmlstring)
- * .end(callback);
- *
- * @param {String} type
- * @return {Request} for chaining
- * @api public
- */
-
- Request.prototype.type = function (type) {
- this.set('Content-Type', request.types[type] || type);
- return this;
- };
- /**
- * Set Accept to `type`, mapping values from `request.types`.
- *
- * Examples:
- *
- * superagent.types.json = 'application/json';
- *
- * request.get('/agent')
- * .accept('json')
- * .end(callback);
- *
- * request.get('/agent')
- * .accept('application/json')
- * .end(callback);
- *
- * @param {String} accept
- * @return {Request} for chaining
- * @api public
- */
-
+ class ListParser extends AbstractTableParser {
+ getExample() {
+ return `john doe
+ frank jones`
+ }
+ getProbForRowSpecimen() {
+ return 0
+ }
+ getParserId() {
+ return TableParserIds.list
+ }
+ _parseTableInputsFromString(str) {
+ return {
+ rows: str.split(/\n/g).map((line, index) => {
+ return {
+ index: index,
+ name: line,
+ }
+ }),
+ }
+ }
+ }
+ class TextListParser extends ListParser {
+ getParserId() {
+ return TableParserIds.txt
+ }
+ }
+ class TextParser extends AbstractTableParser {
+ getParserId() {
+ return TableParserIds.text
+ }
+ getExample() {
+ return "hello world"
+ }
+ getProbForRowSpecimen(specimen) {
+ if (specimen.blankLineCount) return 0.12
+ return 0.05
+ }
+ _parseTableInputsFromString(str) {
+ return { rows: [{ text: str }] }
+ }
+ }
+ class TableParser {
+ constructor() {
+ this._parsers = [
+ new CsvParser(),
+ new TsvParser(),
+ new SsvParser(),
+ new PsvParser(),
+ new TreeRowsParser(),
+ new TreeParser(),
+ new XmlParser(),
+ new HtmlParser(),
+ new TextParser(),
+ new SectionsParser(),
+ new SpacedParser(),
+ new ListParser(),
+ new TextListParser(),
+ new JsonParser(),
+ new JsonArrayParser(),
+ new JsonDataTableWithHeaderParser(),
+ new JsonVectorParser(),
+ new JsonMapParser(),
+ new JsonCountMapParser(),
+ ]
+ this._parserMap = {}
+ this._parsers.forEach((parser) => {
+ const name = parser.getParserId()
+ if (!name) return // only allow leafs to be used as names?
+ this._parserMap[name] = parser
+ })
+ }
+ getAllParsers() {
+ return this._getParsersArray()
+ }
+ getAllTableParserIds() {
+ return Object.keys(this._getParserMap())
+ }
+ getExample(parserId) {
+ return this._getParser(parserId).getExample()
+ }
+ _getParser(parserId) {
+ const options = this._getParserMap()
+ return options[parserId] || options.text // todo: surface an error.
+ }
+ _getParserMap() {
+ return this._parserMap
+ }
+ _getParsersArray() {
+ return this._parsers
+ }
+ // todo: remove this?
+ parseTableInputsFromObject(data, parserId) {
+ if (data instanceof Array) {
+ if (JsonDataTableWithHeaderParser.isJavaScriptDataTable(data)) return { rows: jtree.Utils.javascriptTableWithHeaderRowToObjects(data) }
+ // test to see if it's primitives
+ if (typeof data[0] === "object") return { rows: data }
+ return { rows: data.map((row) => (typeof row === "object" ? row : { value: row })) }
+ } else if (parserId === TableParserIds.jsonMap) return { rows: Object.values(data) }
+ return { rows: [data] }
+ }
+ // todo: should this be inferAndParse? or 2 methods? parse and inferAndParse?
+ parseTableInputsFromString(str = "", parserId) {
+ str = str.trim() // Remove empty lines at end of string, which seem to be common.
+ if (!str) return { rows: [] }
+ parserId = parserId || this.guessTableParserId(str)
+ try {
+ return this._getParser(parserId)._parseTableInputsFromString(str)
+ } catch (err) {
+ console.error(err)
+ const snippet = str.substr(0, 30).replace(/[\n\r]/g, " ")
+ throw new Error(`Failed parsing string '${snippet}...' using parser '${parserId}'`)
+ }
+ }
+ guessProbabilitiesForAllTableParsers(str) {
+ const parsers = this._getParsersArray()
+ const length = parsers.length
+ const probabilities = {}
+ const specimen = new RowStringSpecimen(str)
+ for (let index = 0; index < parsers.length; index++) {
+ const parser = parsers[index]
+ const probability = parser.getProbForRowSpecimen(specimen)
+ const name = parser.getParserId()
+ if (probability === 1) {
+ const exact = {}
+ exact[name] = 1
+ return exact
+ }
+ probabilities[name] = probability
+ }
+ return probabilities
+ }
+ guessTableParserId(str) {
+ const probabilities = this.guessProbabilitiesForAllTableParsers(str)
+ let maxScore = 0
+ let bestGuess = null
+ for (let option in probabilities) {
+ if (probabilities[option] > maxScore) {
+ maxScore = probabilities[option]
+ bestGuess = option
+ }
+ }
+ return bestGuess
+ }
+ }
+ window.TableParser = TableParser
+ const DummyDataSets = {
+ flowPrograms: [
+ ["filename", "bytes", "link"],
+ [
+ "hello.flow",
+ `samples.iris
+ tables.basic`,
+ "",
+ ],
+ ],
+ amazonPurchases: [
+ ["OrderDate", "Title", "Category", "ItemTotal"],
+ [1329386400000, "3D Math Primer for Graphics and Game Development (Wordware Game Math Library)", "Paperback", "26.2"],
+ [1261735200000, "A Mathematician Reads the Newspaper", "Paperback", "1.19"],
+ [1447840800000, "A Most Incomprehensible Thing: Notes Towards a Very Gentle Introduction to the Mathematics of Relativity", "Paperback", "14.63"],
+ [1464429600000, "From Mathematics to Generic Programming", "Paperback", "34.37"],
+ [1268215200000, "Innumeracy: Mathematical Illiteracy and Its Consequences", "Paperback", "9.89"],
+ [1268215200000, "Irreligion: A Mathematician Explains Why the Arguments for God Just Don't Add Up", "Paperback", "9.89"],
+ [1379844000000, "Mathematics and the Imagination", "Paperback", "5.3"],
+ [1410688800000, "Medical Math (Laminated Reference Guide; Quick Study Academic)", "Pamphlet", "3.78"],
+ [1268215200000, "Once Upon A Number: The Hidden Mathematical Logic Of Stories", "Paperback", "14.35"],
+ [1408528800000, "The Language of Mathematics: Making the Invisible Visible", "Paperback", "18.37"],
+ ],
+ waterBill: [
+ ["Amount", "PaidOn", "Gallons"],
+ ["$64.86", "1/10/2018", 2087],
+ ["$73.32", "1/28/2018", 2451],
+ ["$62.65", "2/26/2018", 1968],
+ ["$71.51", "3/25/2018", 2365],
+ ["$65.03", "4/23/2018", 2075],
+ ["$81.39", "5/15/2018", 2757],
+ ["$65.01", "6/15/2018", 2047],
+ ["$93.09", "7/10/2018", 3051],
+ ["$196.58", "8/25/2018", 7309],
+ ["$130.68", "9/10/2018", 4597],
+ ["$55.03", "10/14/2018", 1484],
+ ["$63.44", "11/7/2018", 1967],
+ ["$71.88", "12/12/2018", 2335],
+ ["$53.18", "2/3/2019", 1483],
+ ["$52.05", "3/8/2019", 1429],
+ ["$54.73", "4/28/2019", 1544],
+ ],
+ gapMinder: [
+ ["country", "income", "health", "population"],
+ ["Afghanistan", 1925, "57.63", 32526562],
+ ["Albania", 10620, "76", 2896679],
+ ["Algeria", 13434, "76.5", 39666519],
+ ["Andorra", 46577, "84.1", 70473],
+ ["Angola", 7615, "61", 25021974],
+ ["Antigua and Barbuda", 21049, "75.2", 91818],
+ ["Argentina", 17344, "76.2", 43416755],
+ ["Armenia", 7763, "74.4", 3017712],
+ ["Australia", 44056, "81.8", 23968973],
+ ["Austria", 44401, "81", 8544586],
+ ["Azerbaijan", 16986, "72.9", 9753968],
+ ["Bahamas", 22818, "72.3", 388019],
+ ["Bahrain", 44138, "79.2", 1377237],
+ ["Bangladesh", 3161, "70.1", 160995642],
+ ["Barbados", 12984, "75.8", 284215],
+ ["Belarus", 17415, "70.4", 9495826],
+ ["Belgium", 41240, "80.4", 11299192],
+ ["Belize", 8501, "70", 359287],
+ ["Benin", 1830, "65.5", 10879829],
+ ["Bhutan", 7983, "70.2", 774830],
+ ["Bolivia", 6295, "72.3", 10724705],
+ ["Bosnia and Herzegovina", 9833, "77.9", 3810416],
+ ["Botswana", 17196, "66.4", 2262485],
+ ["Brazil", 15441, "75.6", 207847528],
+ ["Brunei", 73003, "78.7", 423188],
+ ["Bulgaria", 16371, "74.9", 7149787],
+ ["Burkina Faso", 1654, "62.8", 18105570],
+ ["Burundi", 777, "60.4", 11178921],
+ ["Cambodia", 3267, "68.4", 15577899],
+ ["Cameroon", 2897, "59.5", 23344179],
+ ["Canada", 43294, "81.7", 35939927],
+ ["Cape Verde", 6514, "74.6", 520502],
+ ["Central African Republic", 599, "53.8", 4900274],
+ ["Chad", 2191, "57.7", 14037472],
+ ["Chile", 22465, "79.3", 17948141],
+ ["China", 13334, "76.9", 1376048943],
+ ["Colombia", 12761, "75.8", 48228704],
+ ["Comoros", 1472, "64.1", 788474],
+ ["Congo, Dem. Rep.", 809, "58.3", 77266814],
+ ["Congo, Rep.", 6220, "61.9", 4620330],
+ ["Costa Rica", 14132, "80", 4807850],
+ ["Cote d'Ivoire", 3491, "60.33", 22701556],
+ ["Croatia", 20260, "78", 4240317],
+ ["Cuba", 21291, "78.5", 11389562],
+ ["Cyprus", 29797, "82.6", 1165300],
+ ["Czech Republic", 29437, "78.6", 10543186],
+ ["Denmark", 43495, "80.1", 5669081],
+ ["Djibouti", 3139, "64.63", 887861],
+ ["Dominica", 10503, "74.6", 72680],
+ ["Dominican Republic", 12837, "73.8", 10528391],
+ ["Ecuador", 10996, "75.2", 16144363],
+ ["Egypt", 11031, "71.3", 91508084],
+ ["El Salvador", 7776, "74.1", 6126583],
+ ["Equatorial Guinea", 31087, "60.63", 845060],
+ ["Eritrea", 1129, "62.9", 5227791],
+ ["Estonia", 26812, "76.8", 1312558],
+ ["Ethiopia", 1520, "63.6", 99390750],
+ ["Fiji", 7925, "66.3", 892145],
+ ["Finland", 38923, "80.8", 5503457],
+ ["France", 37599, "81.9", 64395345],
+ ["Gabon", 18627, "60.53", 1725292],
+ ["Gambia", 1644, "65.1", 1990924],
+ ["Georgia", 7474, "73.3", 3999812],
+ ["Germany", 44053, "81.1", 80688545],
+ ["Ghana", 4099, "65.5", 27409893],
+ ["Greece", 25430, "79.8", 10954617],
+ ["Grenada", 11593, "71.7", 106825],
+ ["Guatemala", 7279, "73.1", 16342897],
+ ["Guinea", 1225, "60.8", 12608590],
+ ["Guinea-Bissau", 1386, "53.4", 1844325],
+ ["Guyana", 6816, "64.4", 767085],
+ ["Haiti", 1710, "65.3", 10711067],
+ ["Honduras", 4270, "72.4", 8075060],
+ ["Hungary", 24200, "76.2", 9855023],
+ ["Iceland", 42182, "82.8", 329425],
+ ["India", 5903, "66.8", 1311050527],
+ ["Indonesia", 10504, "70.9", 257563815],
+ ["Iran", 15573, "78.5", 79109272],
+ ["Iraq", 14646, "72.1", 36423395],
+ ["Ireland", 47758, "80.4", 4688465],
+ ["Israel", 31590, "82.4", 8064036],
+ ["Italy", 33297, "82.1", 59797685],
+ ["Jamaica", 8606, "75.5", 2793335],
+ ["Japan", 36162, "83.5", 126573481],
+ ["Jordan", 11752, "78.3", 7594547],
+ ["Kazakhstan", 23468, "68.2", 17625226],
+ ["Kenya", 2898, "66.63", 46050302],
+ ["Kiribati", 1824, "62.4", 112423],
+ ["Kuwait", 82633, "80.7", 3892115],
+ ["Kyrgyz Republic", 3245, "69", 5939962],
+ ["Lao", 5212, "66.4", 6802023],
+ ["Latvia", 23282, "75.7", 1970503],
+ ["Lebanon", 17050, "78.5", 5850743],
+ ["Lesotho", 2598, "48.5", 2135022],
+ ["Liberia", 958, "63.9", 4503438],
+ ["Libya", 17261, "76.2", 6278438],
+ ["Lithuania", 26665, "75.4", 2878405],
+ ["Luxembourg", 88314, "81.1", 567110],
+ ["Macedonia, FYR", 12547, "77", 2078453],
+ ["Madagascar", 1400, "64.7", 24235390],
+ ["Malawi", 799, "60.22", 17215232],
+ ["Malaysia", 24320, "75.1", 30331007],
+ ["Maldives", 14408, "79.5", 363657],
+ ["Mali", 1684, "57.6", 17599694],
+ ["Malta", 30265, "82.1", 418670],
+ ["Marshall Islands", 3661, "65.1", 52993],
+ ["Mauritania", 3877, "65.7", 4067564],
+ ["Mauritius", 18350, "73.9", 1273212],
+ ["Mexico", 16850, "74.5", 127017224],
+ ["Micronesia, Fed. Sts.", 3510, "67", 104460],
+ ["Moldova", 4896, "72.7", 4068897],
+ ["Mongolia", 11819, "65.3", 2959134],
+ ["Montenegro", 14833, "75.8", 625781],
+ ["Morocco", 7319, "74.7", 34377511],
+ ["Mozambique", 1176, "56.4", 27977863],
+ ["Myanmar", 4012, "67.9", 53897154],
+ ["Namibia", 10040, "61", 2458830],
+ ["Nepal", 2352, "71.2", 28513700],
+ ["Netherlands", 45784, "80.6", 16924929],
+ ["New Zealand", 34186, "80.6", 4528526],
+ ["Nicaragua", 4712, "76.8", 6082032],
+ ["Niger", 943, "62.2", 19899120],
+ ["Nigeria", 5727, "61.33", 182201962],
+ ["North Korea", 1390, "71.4", 25155317],
+ ["Norway", 64304, "81.6", 5210967],
+ ["Oman", 48226, "75.7", 4490541],
+ ["Pakistan", 4743, "66.5", 188924874],
+ ["Panama", 20485, "78.2", 3929141],
+ ["Papua New Guinea", 2529, "60.6", 7619321],
+ ["Paraguay", 8219, "73.9", 6639123],
+ ["Peru", 11903, "77.5", 31376670],
+ ["Philippines", 6876, "70.2", 100699395],
+ ["Poland", 24787, "77.3", 38611794],
+ ["Portugal", 26437, "79.8", 10349803],
+ ["Qatar", 132877, "82", 2235355],
+ ["Romania", 19203, "76.8", 19511324],
+ ["Russia", 23038, "73.13", 143456918],
+ ["Rwanda", 1549, "66.53", 11609666],
+ ["Samoa", 5558, "72.2", 193228],
+ ["Sao Tome and Principe", 3003, "68.8", 190344],
+ ["Saudi Arabia", 52469, "78.1", 31540372],
+ ["Senegal", 2251, "66.1", 15129273],
+ ["Serbia", 12908, "78.1", 8850975],
+ ["Seychelles", 25684, "73.7", 96471],
+ ["Sierra Leone", 2085, "58.5", 6453184],
+ ["Singapore", 80794, "82.1", 5603740],
+ ["Slovak Republic", 27204, "76.4", 5426258],
+ ["Slovenia", 28550, "80.2", 2067526],
+ ["Solomon Islands", 2047, "64.1", 583591],
+ ["Somalia", 624, "58.7", 10787104],
+ ["South Africa", 12509, "63.72", 54490406],
+ ["South Korea", 34644, "80.7", 50293439],
+ ["South Sudan", 3047, "58", 12339812],
+ ["Spain", 32979, "81.7", 46121699],
+ ["Sri Lanka", 10624, "76.5", 20715010],
+ ["St. Lucia", 9997, "74.5", 184999],
+ ["St. Vincent and the Grenadines", 10435, "72.9", 109462],
+ ["Sudan", 3975, "69.5", 40234882],
+ ["Suriname", 17125, "70.5", 542975],
+ ["Swaziland", 6095, "51.5", 1286970],
+ ["Sweden", 44892, "82", 9779426],
+ ["Switzerland", 56118, "82.9", 8298663],
+ ["Syria", 4637, "70.26", 18502413],
+ ["Tajikistan", 2582, "71", 8481855],
+ ["Tanzania", 2571, "63.43", 53470420],
+ ["Thailand", 14512, "75.1", 67959359],
+ ["Timor-Leste", 2086, "72.4", 1184765],
+ ["Togo", 1433, "64.23", 7304578],
+ ["Tonga", 5069, "70.5", 106170],
+ ["Trinidad and Tobago", 30113, "71.4", 1360088],
+ ["Tunisia", 11126, "77.3", 11253554],
+ ["Turkey", 19360, "76.5", 78665830],
+ ["Turkmenistan", 15865, "67.9", 5373502],
+ ["Uganda", 1680, "60.8", 39032383],
+ ["Ukraine", 8449, "72.1", 44823765],
+ ["United Arab Emirates", 60749, "76.6", 9156963],
+ ["United Kingdom", 38225, "81.4", 64715810],
+ ["United States", 53354, "79.1", 321773631],
+ ["Uruguay", 20438, "77.3", 3431555],
+ ["Uzbekistan", 5598, "70.1", 29893488],
+ ["Vanuatu", 2912, "65", 264652],
+ ["Venezuela", 15753, "75.8", 31108083],
+ ["Vietnam", 5623, "76.5", 93447601],
+ ["West Bank and Gaza", 4319, "75.2", 4668466],
+ ["Yemen", 3887, "67.6", 26832215],
+ ["Zambia", 4034, "58.96", 16211767],
+ ["Zimbabwe", 1801, "60.01", 15602751],
+ ],
+ emojis: [
+ ["animal", "count"],
+ ["🐄", 9],
+ ["🐖", 12],
+ ["🐏", 3],
+ ],
+ telescopes: [
+ ["Name", "Type", "Url", "Location", "OperatedBy", "FundedBy"],
+ ["Hubble Space Telescope", "Space Telescope", "https://en.wikipedia.org/wiki/Hubble_Space_Telescope", "Space", "NASA", "NASA"],
+ ["LIGO Hanford", "Gravity Wave Observatory", "https://www.ligo.caltech.edu/WA", "Richland, WA, USA", "Caltech", "National Science Foundation"],
+ ["LIGO Livingston", "Gravity Wave Observatory", "https://www.ligo.caltech.edu/LA", "Livingston, LA, USA", "MIT", "National Science Foundation"],
+ [
+ "Gran Telescopio Canarias (GTC)",
+ "Astronomical Observatory",
+ "http://www.gtc.iac.es/gtc/gtc.php",
+ "La Palma, Garafía, Spain ",
+ "IAC",
+ "Observatorio Astronómico de Canarias, National Autonomous University of Mexico, University of Florida Edit this on Wikidata",
+ ],
+ [
+ "Hobby–Eberly Telescope",
+ "Astronomical Observatory",
+ "http://mcdonaldobservatory.org/research/telescopes/HET",
+ "Davis Mountains, Texas, US",
+ "Stanford University & Ludwig Maximilians University of Munich and Georg August University of Göttingen",
+ "YarCom Inc. and the Lott family ",
+ ],
+ ],
+ markdown: [
+ ["text"],
+ [
+ `# My header
+ ## My subheader
+ Hello world`,
+ ],
+ ],
+ webPages: [["name", "url"]],
+ outerSpace: [
+ ["text"],
+ [
+ `universe
+ galaxies
+ milkyWay
+ solarSystems
+ solarSystem
+ stars
+ sun
+ planets
+ mercury
+ venus
+ earth
+ moons
+ moon
+ mars
+ jupiter
+ saturn
+ uranus
+ neptune
+ pluto`,
+ ],
+ ],
+ wordCounts: [
+ ["word", "count"],
+ ["Two", 2],
+ ["roads", 2],
+ ["diverged", 2],
+ ["in", 3],
+ ["a", 3],
+ ["yellow", 1],
+ ["wood", 2],
+ ["And", 6],
+ ["sorry", 1],
+ ["I", 9],
+ ["could", 2],
+ ["not", 1],
+ ["travel", 1],
+ ["both", 2],
+ ["be", 2],
+ ["one", 3],
+ ["traveler", 1],
+ ["long", 1],
+ ["stood", 1],
+ ["looked", 1],
+ ],
+ treeProgram: [
+ ["source"],
+ [
+ `samples.iris
+ group.by Species
+ tables.basic
+ columns.first 3`,
+ ],
+ ],
+ poem: [
+ ["text"],
+ [
+ `Two roads diverged in a yellow wood,
+ And sorry I could not travel both
+ And be one traveler, long I stood
+ And looked down one as far as I could
+ To where it bent in the undergrowth;
- Request.prototype.accept = function (type) {
- this.set('Accept', request.types[type] || type);
- return this;
- };
- /**
- * Set Authorization field value with `user` and `pass`.
- *
- * @param {String} user
- * @param {String} [pass] optional in case of using 'bearer' as type
- * @param {Object} options with 'type' property 'auto', 'basic' or 'bearer' (default 'basic')
- * @return {Request} for chaining
- * @api public
- */
+ Then took the other, as just as fair,
+ And having perhaps the better claim,
+ Because it was grassy and wanted wear;
+ Though as for that the passing there
+ Had worn them really about the same,
+ And both that morning equally lay
+ In leaves no step had trodden black.
+ Oh, I kept the first for another day!
+ Yet knowing how way leads on to way,
+ I doubted if I should ever come back.
- Request.prototype.auth = function (user, pass, options) {
- if (arguments.length === 1) pass = '';
+ I shall be telling this with a sigh
+ Somewhere ages and ages hence:
+ Two roads diverged in a wood, and I—
+ I took the one less traveled by,
+ And that has made all the difference.`,
+ ],
+ ],
+ playerGoals: [
+ ["PlayerGoals", "Goals"],
+ ["Player1", 11],
+ ["Player2", 2],
+ ["Player3", 2],
+ ["Player4", 2],
+ ["Player5", 7],
+ ],
+ patients: [
+ ["Patient", "Gender", "Weight"],
+ ["Patient1", "Girl", "3.31"],
+ ["Patient2", "Male", "2.8"],
+ ["Patient3", "Male", "3.7"],
+ ["Patient4", "Girl", "2.5"],
+ ["Patient5", "Girl", "2.8"],
+ ],
+ regionalMarkets: [
+ ["Location", "Parent", "Market trade volume (size)", "Market increase/decrease (color)"],
+ ["Global", null, 0, 0],
+ ["America", "Global", 0, 0],
+ ["Europe", "Global", 0, 0],
+ ["Asia", "Global", 0, 0],
+ ["Australia", "Global", 0, 0],
+ ["Africa", "Global", 0, 0],
+ ["Brazil", "America", 11, 10],
+ ["USA", "America", 52, 31],
+ ["Mexico", "America", 24, 12],
+ ["Canada", "America", 16, -23],
+ ["France", "Europe", 42, -11],
+ ["Germany", "Europe", 31, -2],
+ ["Sweden", "Europe", 22, -13],
+ ["Italy", "Europe", 17, 4],
+ ["UK", "Europe", 21, -5],
+ ["China", "Asia", 36, 4],
+ ["Japan", "Asia", 20, -12],
+ ["India", "Asia", 40, 63],
+ ["Laos", "Asia", 4, 34],
+ ["Mongolia", "Asia", 1, -5],
+ ["Israel", "Asia", 12, 24],
+ ["Iran", "Asia", 18, 13],
+ ["Pakistan", "Asia", 11, -52],
+ ["Egypt", "Africa", 21, 0],
+ ["S. Africa", "Africa", 30, 43],
+ ["Sudan", "Africa", 12, 2],
+ ["Congo", "Africa", 10, 12],
+ ["Zaire", "Africa", 8, 10],
+ ],
+ stockPrice: [
+ ["Step", "Price"],
+ [0, 0],
+ [1, 10],
+ [2, 23],
+ [3, 17],
+ [4, 18],
+ [5, 9],
+ [6, 11],
+ [7, 27],
+ [8, 33],
+ [9, 40],
+ [10, 32],
+ [11, 35],
+ [12, 30],
+ [13, 40],
+ [14, 42],
+ [15, 47],
+ [16, 44],
+ [17, 48],
+ [18, 52],
+ [19, 54],
+ [20, 42],
+ [21, 55],
+ [22, 56],
+ [23, 57],
+ [24, 60],
+ [25, 50],
+ [26, 52],
+ [27, 51],
+ [28, 49],
+ [29, 53],
+ [30, 55],
+ [31, 60],
+ [32, 61],
+ [33, 59],
+ [34, 62],
+ [35, 65],
+ [36, 62],
+ [37, 58],
+ [38, 55],
+ [39, 61],
+ [40, 64],
+ [41, 65],
+ [42, 63],
+ [43, 66],
+ [44, 67],
+ [45, 69],
+ [46, 69],
+ [47, 70],
+ [48, 72],
+ [49, 68],
+ [50, 66],
+ [51, 65],
+ [52, 67],
+ [53, 70],
+ [54, 71],
+ [55, 72],
+ [56, 73],
+ [57, 75],
+ [58, 70],
+ [59, 68],
+ [60, 64],
+ [61, 60],
+ [62, 65],
+ [63, 67],
+ [64, 68],
+ [65, 69],
+ [66, 70],
+ [67, 72],
+ [68, 75],
+ [69, 80],
+ ],
+ }
+ window.DummyDataSets = DummyDataSets
+ //onsave jtree build produce jtable.browser.js
+ //onsave jtree build produce jtable.node.js
+ class PivotTable {
+ constructor(rows, inputColumns, outputColumns) {
+ this._columns = {}
+ this._rows = rows
+ inputColumns.forEach((col) => (this._columns[col.name] = col))
+ outputColumns.forEach((col) => (this._columns[col.name] = col))
+ }
+ _getGroups(allRows, groupByColNames) {
+ const rowsInGroups = new Map()
+ allRows.forEach((row) => {
+ const groupKey = groupByColNames.map((col) => row[col].toString().replace(/ /g, "")).join(" ")
+ if (!rowsInGroups.has(groupKey)) rowsInGroups.set(groupKey, [])
+ rowsInGroups.get(groupKey).push(row)
+ })
+ return rowsInGroups
+ }
+ getNewRows(groupByCols) {
+ // make new trees
+ const rowsInGroups = this._getGroups(this._rows, groupByCols)
+ // Any column in the group should be reused by the children
+ const columns = [
+ {
+ name: "count",
+ type: "number",
+ min: 0,
+ },
+ ]
+ groupByCols.forEach((colName) => columns.push(this._columns[colName]))
+ const colsToReduce = Object.values(this._columns).filter((col) => !!col.reduction)
+ colsToReduce.forEach((col) => columns.push(col))
+ // for each group
+ const newRows = []
+ const totalGroups = rowsInGroups.size
+ for (let [groupId, group] of rowsInGroups) {
+ const firstRow = group[0]
+ const newRow = {}
+ groupByCols.forEach((col) => {
+ newRow[col] = firstRow ? firstRow[col] : 0
+ })
+ newRow.count = group.length
+ // todo: add more reductions? count, stddev, median, variance.
+ colsToReduce.forEach((col) => {
+ const sourceColName = col.source
+ const values = group.map((row) => row[sourceColName]).filter((val) => typeof val === "number" && !isNaN(val))
+ const reduction = col.reduction
+ let reducedValue = firstRow[sourceColName]
+ if (reduction === "sum") reducedValue = values.reduce((prev, current) => prev + current, 0)
+ if (reduction === "max") reducedValue = Math.max(...values)
+ if (reduction === "min") reducedValue = Math.min(...values)
+ if (reduction === "mean") reducedValue = values.reduce((prev, current) => prev + current, 0) / values.length
+ newRow[col.name] = reducedValue
+ })
+ newRows.push(newRow)
+ }
+ // todo: add tests. figure out this api better.
+ Object.values(columns).forEach((col) => {
+ // For pivot columns, remove the source and reduction info for now. Treat things as immutable.
+ delete col.source
+ delete col.reduction
+ })
+ return {
+ rows: newRows,
+ columns,
+ }
+ }
+ }
+ var ComparisonOperators
+ ;(function (ComparisonOperators) {
+ ComparisonOperators["lessThan"] = "<"
+ ComparisonOperators["greaterThan"] = ">"
+ ComparisonOperators["lessThanOrEqual"] = "<="
+ ComparisonOperators["greaterThanOrEqual"] = ">="
+ ComparisonOperators["equal"] = "="
+ ComparisonOperators["notEqual"] = "!="
+ })(ComparisonOperators || (ComparisonOperators = {}))
+ // todo: remove detectAndAddParam?
+ // todo: remove rowclass param?
+ class Table {
+ constructor(rowsArray = [], columnsArrayOrMap = [], rowClass = Row, detectAndAddColumns = true, samplingSeed = Date.now()) {
+ this._columnsMap = {}
+ this._ctime = new jtree.TreeNode()._getProcessTimeInMilliseconds()
+ this._tableId = this._getUniqueId()
+ this._samplingSeed = samplingSeed
+ // if this is ALREADY CARDS, should we be a view?
+ this._rows = rowsArray.map((source) => (source instanceof Row ? source : new rowClass(source, this)))
+ // Add detected columns first, so they can be overwritten
+ if (detectAndAddColumns) this._getDetectedColumnNames().forEach((col) => this._registerColumn({ name: col }))
+ if (Array.isArray(columnsArrayOrMap)) columnsArrayOrMap.forEach((col) => this._registerColumn(col))
+ else if (columnsArrayOrMap) this._columnsMap = columnsArrayOrMap
+ }
+ _getUniqueId() {
+ Table._uniqueId++
+ return Table._uniqueId
+ }
+ _registerColumn(col) {
+ this._columnsMap[col.name] = new Column(col, this._getColumnValuesFromSourceAsAnyVector(col.source || col.name))
+ return this
+ }
+ _getColumnValuesFromSourceAsAnyVector(columnName) {
+ return this.getRows().map((row) => row.getRowOriginalValue(columnName))
+ }
+ // todo: ADD TYPINGS
+ _predictColumns(predictionHints, propertyNameToColumnNameMap = {}) {
+ // todo: use the available input table column names, coupled with column setting we are trying to predict.
+ // ie: "gender" should use "gender" col, if available
+ // check all the columns for one that matches all tests. if found, return it.
+ const columnsArray = this.getColumnsArray()
+ const tests = predictionHints.split(",")
+ const filterTests = tests.filter((test) => test.includes("=")).map((test) => test.split("="))
+ const filterFn = (col) => filterTests.every((test) => col[test[0]] !== undefined && col[test[0]]().toString() === test[1])
+ let colsThatPassed = columnsArray.filter((col) => filterFn(col))
+ const notIn = {}
+ const notEqualTests = tests
+ .filter((test) => test.startsWith("!"))
+ .map((test) => propertyNameToColumnNameMap[test.substr(1)])
+ .filter((identity) => identity)
+ .forEach((name) => {
+ notIn[name] = true
+ })
+ colsThatPassed = colsThatPassed.filter((col) => !notIn[col.getColumnName()])
+ // for now just 1 prop ranking.
+ const rankColumn = tests.find((test) => !test.includes("=") && !test.includes("!"))
+ let potentialCols = colsThatPassed
+ if (rankColumn) potentialCols = potentialCols.sort(jtree.Utils.makeSortByFn((col) => col[rankColumn]())).reverse()
+ return potentialCols
+ }
+ getRows() {
+ return this._rows
+ }
+ getFirstColumnAsString() {
+ return this.getRows()
+ .map((row) => row.getFirstValue())
+ .join("")
+ }
+ isBlankTable() {
+ return this.getRowCount() === 0 && this.getColumnCount() === 0
+ }
+ getRowCount() {
+ return this.getRows().length
+ }
+ getColumnCount() {
+ return this.getColumnNames().length
+ }
+ getColumnNames() {
+ return Object.keys(this.getColumnsMap())
+ }
+ getColumnsMap() {
+ return this._columnsMap
+ }
+ getColumnByName(name) {
+ return this.getColumnsMap()[name]
+ }
+ _getLowerCaseColumnsMap() {
+ const map = {}
+ Object.keys(this._columnsMap).forEach((key) => (map[key.toLowerCase()] = key))
+ return map
+ }
+ getTableCTime() {
+ return this._ctime
+ }
+ filterClonedRowsByScalar(columnName, comparisonOperator, scalarValueAsString) {
+ const column = this.getColumnByName(columnName)
+ let typedScalarValue = column.getPrimitiveTypeObj().getAsNativeJavascriptType(scalarValueAsString)
+ if (typedScalarValue instanceof Date) typedScalarValue = typedScalarValue.getTime() // todo: do I need this?
+ return new Table(
+ this.cloneNativeJavascriptTypedRows().filter((row) => {
+ let rowTypedValue = row[columnName]
+ if (rowTypedValue instanceof Date) rowTypedValue = rowTypedValue.getTime() // todo: do I need this?
+ if (comparisonOperator === ComparisonOperators.equal) return rowTypedValue == typedScalarValue
+ if (comparisonOperator === ComparisonOperators.notEqual) return rowTypedValue != typedScalarValue
+ if (comparisonOperator === ComparisonOperators.greaterThan) return rowTypedValue > typedScalarValue
+ if (comparisonOperator === ComparisonOperators.lessThan) return rowTypedValue < typedScalarValue
+ if (comparisonOperator === ComparisonOperators.lessThanOrEqual) return rowTypedValue <= typedScalarValue
+ if (comparisonOperator === ComparisonOperators.greaterThanOrEqual) return rowTypedValue >= typedScalarValue
+ }),
+ this.getColumnsArrayOfObjects(),
+ undefined,
+ false
+ )
+ }
+ getColumnsArray() {
+ return Object.values(this.getColumnsMap())
+ }
+ getColumnsArrayOfObjects() {
+ return this.getColumnsArray().map((col) => col.toObject())
+ }
+ getJavascriptNativeTypedValues() {
+ return this.getRows().map((row) => row.rowToObjectWithOnlyNativeJavascriptTypes())
+ }
+ toMatrix() {
+ return this.getRows().map((row) => row.toVector())
+ }
+ toNumericMatrix() {
+ // todo: right now it drops them. should we 1 hot them?
+ const numericNames = this.getColumnsArray()
+ .filter((col) => col.isNumeric())
+ .map((col) => col.getColumnName())
+ return this.getRows().map((row) => {
+ const obj = row.rowToObjectWithOnlyNativeJavascriptTypes()
+ return numericNames.map((name) => obj[name])
+ })
+ }
+ clone() {
+ return new Table(this.cloneNativeJavascriptTypedRows())
+ }
+ cloneNativeJavascriptTypedRows() {
+ return this.getRows()
+ .map((row) => row.rowToObjectWithOnlyNativeJavascriptTypes())
+ .map((obj) => Object.assign({}, obj))
+ }
+ fillMissing(columnName, value) {
+ const filled = this.cloneNativeJavascriptTypedRows().map((row) => {
+ if (jtree.Utils.isValueEmpty(row[columnName])) row[columnName] = value
+ return row
+ })
+ return new Table(filled, this.getColumnsArrayOfObjects())
+ }
+ getTableColumnByName(name) {
+ return this.getColumnsMap()[name]
+ }
+ _getUnionSample(sampleSet) {
+ const sample = {}
+ sampleSet.forEach((row) => {
+ row.getRowKeys().forEach((key) => {
+ if (!key) return
+ const currentVal = sample[key]
+ if (currentVal !== undefined && currentVal !== "") return
+ sample[key] = row.getRowOriginalValue(key)
+ })
+ })
+ return sample
+ }
+ _getSampleSet() {
+ const SAMPLE_SET_SIZE = 30 // todo: fix.
+ if (!this._sampleSet) this._sampleSet = jtree.Utils.sampleWithoutReplacement(this.getRows(), SAMPLE_SET_SIZE, this._samplingSeed)
+ return this._sampleSet
+ }
+ _getDetectedColumnNames() {
+ const columns = this.getColumnsMap()
+ // This is run AFTER we have all user definied columns, and AFTER we have all data.
+ // detect columns that appear in records
+ // todo: this is broken. if you only pull 30, and its a tree or other type with varying columsn, you
+ // will often miss columns.
+ return Object.keys(this._getUnionSample(this._getSampleSet()))
+ .map((columnName) => columnName.trim()) // todo: why do we filter empties?
+ .filter((identity) => identity)
+ .filter((col) => !columns[col]) // do not overwrite any custom columns
+ }
+ toTypeScriptInterface() {
+ const cols = this.getColumnsArray()
+ .map((col) => ` ${col.getColumnName()}: ${col.getPrimitiveTypeName()};`)
+ .join("\n")
+ return `interface Row {
+ ${cols}
+ }`
+ }
+ getColumnNamesAndTypes() {
+ return this._getColumnNamesAndTypes()
+ }
+ getColumnNamesAndTypesAndReductions() {
+ return this._getColumnNamesAndTypes(true)
+ }
+ _getColumnNamesAndTypes(withReductions = false) {
+ const columns = this.getColumnsMap()
+ return this.getColumnNames().map((name) => {
+ const column = columns[name]
+ const obj = {
+ Column: name,
+ JTableType: column.getPrimitiveTypeName(),
+ JavascriptType: column.getPrimitiveTypeObj().getJavascriptTypeName(),
+ }
+ if (withReductions) Object.assign(obj, column.getReductions())
+ return obj
+ })
+ }
+ getPredictionsForAPropertyNameToColumnNameMapGivenHintsNode(hintsNode, propertyNameToColumnNameMap) {
+ const results = {}
+ hintsNode
+ .map((columnHintNode) => this.getColumnNamePredictionsForProperty(columnHintNode.getFirstWord(), columnHintNode.getContent(), propertyNameToColumnNameMap))
+ .filter((pred) => pred.length)
+ .forEach((predictions) => {
+ const topPrediction = predictions[0]
+ results[topPrediction.propertyName] = topPrediction.columnName
+ })
+ return results
+ }
+ getColumnNamePredictionsForProperty(propertyName, predictionHints, propertyNameToColumnNameMap) {
+ const userDefinedColumnName = propertyNameToColumnNameMap[propertyName]
+ if (this.getColumnsMap()[userDefinedColumnName]) return [{ propertyName: propertyName, columnName: userDefinedColumnName }] // Table has a column named this, return okay.
+ // Table has a lowercase column named this. Return okay. Todo: do we want to do this?
+ if (userDefinedColumnName && this._getLowerCaseColumnsMap()[userDefinedColumnName.toLowerCase()]) return [this._getLowerCaseColumnsMap()[userDefinedColumnName.toLowerCase()]]
+ if (predictionHints) {
+ const potentialCols = this._predictColumns(predictionHints, propertyNameToColumnNameMap)
+ if (potentialCols.length) return [{ propertyName: propertyName, columnName: potentialCols[0].getColumnName() }]
+ }
+ const cols = this.getColumnsByImportance()
+ const name = cols.length && cols[0].getColumnName()
+ if (name) return [{ propertyName: propertyName, columnName: name }]
+ return []
+ }
+ toTree() {
+ return new jtree.TreeNode(this.getRows().map((row) => row.getRowSourceObject()))
+ }
+ filterRowsByFn(fn) {
+ return new Table(this.cloneNativeJavascriptTypedRows().filter((inputRow, index) => fn(inputRow, index)))
+ }
+ // todo: make more efficient?
+ // todo: preserve columns
+ addColumns(columnsToAdd) {
+ const inputColDefs = this.getColumnsMap()
+ return new Table(
+ this.cloneNativeJavascriptTypedRows().map((inputRow) => {
+ columnsToAdd.forEach((newCol) => {
+ let newValue
+ if (newCol.accessorFn) newValue = newCol.accessorFn(inputRow)
+ else newValue = Column.convertValueToNumeric(inputRow[newCol.source], inputColDefs[newCol.source].getPrimitiveTypeName(), newCol.type, newCol.mathFn)
+ inputRow[newCol.name] = newValue
+ })
+ return inputRow
+ })
+ )
+ }
+ // todo: can be made more effcicent
+ changeColumnType(columnName, newType) {
+ const cols = this.getColumnsArrayOfObjects()
+ cols.forEach((col) => {
+ if (col.name === columnName) col.type = newType
+ })
+ return new Table(this.cloneNativeJavascriptTypedRows(), cols, undefined, false)
+ }
+ renameColumns(nameMap) {
+ const rows = this.getRows()
+ .map((row) => row.rowToObjectWithOnlyNativeJavascriptTypes())
+ .map((obj) => {
+ Object.keys(nameMap).forEach((oldName) => {
+ const newName = nameMap[oldName]
+ if (newName === oldName) return
+ obj[newName] = obj[oldName]
+ delete obj[oldName]
+ })
+ return obj
+ })
+ const cols = this.getColumnsArrayOfObjects()
+ cols.forEach((col) => {
+ if (nameMap[col.name]) col.name = nameMap[col.name]
+ })
+ return new Table(rows, cols, undefined, false)
+ }
+ cloneWithCleanColumnNames() {
+ const nameMap = {}
+ const cols = this.getColumnsArrayOfObjects()
+ cols.forEach((col) => {
+ nameMap[col.name] = col.name.replace(/[^a-z0-9]/gi, "")
+ })
+ return this.renameColumns(nameMap)
+ }
+ // todo: can be made more effcicent
+ dropAllColumnsExcept(columnsToKeep) {
+ return new Table(
+ this.cloneNativeJavascriptTypedRows().map((inputRow, rowIndex) => {
+ const result = {}
+ columnsToKeep.forEach((name) => {
+ result[name] = inputRow[name]
+ })
+ return result
+ }),
+ columnsToKeep.map((colName) => this.getColumnByName(colName).toObject())
+ )
+ }
+ // todo: we don't need any cloning here--just create a new row, new rows array, new and add the pointers
+ // to same rows
+ addRow(rowWords) {
+ const rows = this.cloneNativeJavascriptTypedRows()
+ const newRow = {}
+ Object.keys(rows[0] || {}).forEach((key, index) => {
+ // todo: handle typings
+ newRow[key] = rowWords[index]
+ })
+ rows.push(newRow)
+ return new Table(rows, this.getColumnsMap())
+ }
+ _synthesizeRow(randomNumberFn) {
+ const row = {}
+ this.getColumnsArray().forEach((column) => {
+ row[column.getColumnName()] = column.synthesizeValue(randomNumberFn)
+ })
+ return row
+ }
+ synthesizeTable(rowcount, seed) {
+ const randomNumberFn = jtree.Utils.makeSemiRandomFn(seed)
+ const rows = []
+ while (rowcount) {
+ rows.push(this._synthesizeRow(randomNumberFn))
+ rowcount--
+ }
+ return new Table(
+ rows,
+ this.getColumnsArray().map((col) => col.toObject())
+ )
+ }
+ // todo: we don't need any cloning here--here create a new sorted array with poitners
+ // to same rows
+ shuffleRows() {
+ // todo: add seed!
+ // cellType randomSeed int
+ // description An integer to seed the random number generator with.
+ return new Table(jtree.Utils.shuffleInPlace(this.getRows().slice(0)), this.getColumnsMap())
+ }
+ reverseRows() {
+ const rows = this.getRows().slice(0)
+ rows.reverse()
+ return new Table(rows, this.getColumnsMap())
+ }
+ // Pivot is shorthand for group and reduce?
+ makePivotTable(groupByColumnNames, newCols) {
+ const inputColumns = this.getColumnsArrayOfObjects()
+ const colMap = {}
+ inputColumns.forEach((col) => (colMap[col.name] = true))
+ const groupByCols = groupByColumnNames.filter((col) => colMap[col])
+ const pivotTable = new PivotTable(this.getJavascriptNativeTypedValues(), inputColumns, newCols).getNewRows(groupByCols)
+ return new Table(pivotTable.rows, pivotTable.columns)
+ }
+ sortBy(colNames) {
+ const colAccessorFns = colNames.map((colName) => (row) => row.rowToObjectWithOnlyNativeJavascriptTypes()[colName])
+ const rows = this.getRows().sort(jtree.Utils.makeSortByFn(colAccessorFns))
+ return new Table(rows, this.getColumnsMap())
+ }
+ toDelimited(delimiter) {
+ return this.toTree().toDelimited(delimiter, this.getColumnNames())
+ }
+ toSimpleSchema() {
+ return this.getColumnsArray()
+ .map((col) => `${col.getColumnName()} ${col.getPrimitiveTypeName()}`)
+ .join("\n")
+ }
+ // todo: toProtoBuf, toSqlLite, toJsonSchema, toJsonLd, toCapnProto, toApacheArrow, toFlatBuffers
+ // guess which are the more important/informative/interesting columns
+ getColumnsByImportance() {
+ const columnsMap = this.getColumnsMap()
+ const aIsMoreImportant = -1
+ const bIsMoreImportant = 1
+ const cols = Object.keys(columnsMap).map((columnName) => columnsMap[columnName])
+ cols.sort((colA, colB) => {
+ if (colA.getTitlePotential() > colB.getTitlePotential()) return aIsMoreImportant
+ if (colB.getTitlePotential() > colA.getTitlePotential()) return bIsMoreImportant
+ if (colA.getBlankPercentage() > 0.5 || colB.getBlankPercentage() > 0.5) {
+ if (colA.getBlankPercentage() > colB.getBlankPercentage()) return bIsMoreImportant
+ else if (colB.getBlankPercentage() > colA.getBlankPercentage()) return aIsMoreImportant
+ }
+ if (colA.isTemporal() && !colB.isTemporal()) return aIsMoreImportant
+ if (!colA.isTemporal() && colB.isTemporal()) return bIsMoreImportant
+ if (colA.isLink() && !colB.isLink()) return bIsMoreImportant
+ else if (!colA.isLink() && colB.isLink()) return aIsMoreImportant
+ if (colA.isString() && !colB.isString()) return bIsMoreImportant
+ else if (!colA.isString() && colB.isString()) return aIsMoreImportant
+ if (colA.isString() && colB.isString()) {
+ if (colA.getEntropy() > 4 && colA.getEntropy() < 8 && colA.getEntropy() > colB.getEntropy()) return aIsMoreImportant
+ if (colA.getEstimatedTextLength() > colB.getEstimatedTextLength()) return bIsMoreImportant
+ else return aIsMoreImportant
+ }
+ return 0
+ })
+ return cols
+ }
+ }
+ Table._uniqueId = 0
+ window.Table = Table
+ window.ComparisonOperators = ComparisonOperators
- if (_typeof(pass) === 'object' && pass !== null) {
- // pass is optional and can be replaced with options
- options = pass;
- pass = '';
+ {
+ class stumpNode extends jtree.GrammarBackedNode {
+ createParser() {
+ return new jtree.TreeNode.Parser(
+ errorNode,
+ Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
+ blockquote: htmlTagNode,
+ colgroup: htmlTagNode,
+ datalist: htmlTagNode,
+ fieldset: htmlTagNode,
+ menuitem: htmlTagNode,
+ noscript: htmlTagNode,
+ optgroup: htmlTagNode,
+ progress: htmlTagNode,
+ styleTag: htmlTagNode,
+ template: htmlTagNode,
+ textarea: htmlTagNode,
+ titleTag: htmlTagNode,
+ address: htmlTagNode,
+ article: htmlTagNode,
+ caption: htmlTagNode,
+ details: htmlTagNode,
+ section: htmlTagNode,
+ summary: htmlTagNode,
+ button: htmlTagNode,
+ canvas: htmlTagNode,
+ dialog: htmlTagNode,
+ figure: htmlTagNode,
+ footer: htmlTagNode,
+ header: htmlTagNode,
+ hgroup: htmlTagNode,
+ iframe: htmlTagNode,
+ keygen: htmlTagNode,
+ legend: htmlTagNode,
+ object: htmlTagNode,
+ option: htmlTagNode,
+ output: htmlTagNode,
+ script: htmlTagNode,
+ select: htmlTagNode,
+ source: htmlTagNode,
+ strong: htmlTagNode,
+ aside: htmlTagNode,
+ embed: htmlTagNode,
+ input: htmlTagNode,
+ label: htmlTagNode,
+ meter: htmlTagNode,
+ param: htmlTagNode,
+ small: htmlTagNode,
+ table: htmlTagNode,
+ tbody: htmlTagNode,
+ tfoot: htmlTagNode,
+ thead: htmlTagNode,
+ track: htmlTagNode,
+ video: htmlTagNode,
+ abbr: htmlTagNode,
+ area: htmlTagNode,
+ base: htmlTagNode,
+ body: htmlTagNode,
+ code: htmlTagNode,
+ form: htmlTagNode,
+ head: htmlTagNode,
+ html: htmlTagNode,
+ link: htmlTagNode,
+ main: htmlTagNode,
+ mark: htmlTagNode,
+ menu: htmlTagNode,
+ meta: htmlTagNode,
+ ruby: htmlTagNode,
+ samp: htmlTagNode,
+ span: htmlTagNode,
+ time: htmlTagNode,
+ bdi: htmlTagNode,
+ bdo: htmlTagNode,
+ col: htmlTagNode,
+ del: htmlTagNode,
+ dfn: htmlTagNode,
+ div: htmlTagNode,
+ img: htmlTagNode,
+ ins: htmlTagNode,
+ kbd: htmlTagNode,
+ map: htmlTagNode,
+ nav: htmlTagNode,
+ pre: htmlTagNode,
+ rtc: htmlTagNode,
+ sub: htmlTagNode,
+ sup: htmlTagNode,
+ var: htmlTagNode,
+ wbr: htmlTagNode,
+ br: htmlTagNode,
+ dd: htmlTagNode,
+ dl: htmlTagNode,
+ dt: htmlTagNode,
+ em: htmlTagNode,
+ h1: htmlTagNode,
+ h2: htmlTagNode,
+ h3: htmlTagNode,
+ h4: htmlTagNode,
+ h5: htmlTagNode,
+ h6: htmlTagNode,
+ hr: htmlTagNode,
+ li: htmlTagNode,
+ ol: htmlTagNode,
+ rb: htmlTagNode,
+ rp: htmlTagNode,
+ rt: htmlTagNode,
+ td: htmlTagNode,
+ th: htmlTagNode,
+ tr: htmlTagNode,
+ ul: htmlTagNode,
+ a: htmlTagNode,
+ b: htmlTagNode,
+ i: htmlTagNode,
+ p: htmlTagNode,
+ q: htmlTagNode,
+ s: htmlTagNode,
+ u: htmlTagNode,
+ }),
+ [{ regex: /^$/, nodeConstructor: blankLineNode }]
+ )
+ }
+ compile() {
+ return this.toHtml()
+ }
+ _getHtmlJoinByCharacter() {
+ return ""
+ }
+ getHandGrammarProgram() {
+ if (!this._cachedHandGrammarProgramRoot)
+ this._cachedHandGrammarProgramRoot = new jtree.HandGrammarProgram(`tooling onsave jtree build produceLang stump
+ anyCell
+ keywordCell
+ emptyCell
+ extraCell
+ highlightScope invalid
+ anyHtmlContentCell
+ highlightScope string
+ attributeValueCell
+ highlightScope constant.language
+ htmlTagNameCell
+ highlightScope variable.function
+ extends keywordCell
+ enum a abbr address area article aside b base bdi bdo blockquote body br button canvas caption code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins kbd keygen label legend li link main map mark menu menuitem meta meter nav noscript object ol optgroup option output p param pre progress q rb rp rt rtc ruby s samp script section select small source span strong styleTag sub summary sup table tbody td template textarea tfoot th thead time titleTag tr track u ul var video wbr
+ htmlAttributeNameCell
+ highlightScope entity.name.type
+ extends keywordCell
+ enum accept accept-charset accesskey action align alt async autocomplete autofocus autoplay bgcolor border charset checked class color cols colspan content contenteditable controls coords datetime default defer dir dirname disabled download draggable dropzone enctype for formaction headers height hidden high href hreflang http-equiv id ismap kind lang list loop low max maxlength media method min multiple muted name novalidate onabort onafterprint onbeforeprint onbeforeunload onblur oncanplay oncanplaythrough onchange onclick oncontextmenu oncopy oncuechange oncut ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended onerror onfocus onhashchange oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onoffline ononline onpagehide onpageshow onpaste onpause onplay onplaying onpopstate onprogress onratechange onreset onresize onscroll onsearch onseeked onseeking onselect onstalled onstorage onsubmit onsuspend ontimeupdate ontoggle onunload onvolumechange onwaiting onwheel open optimum pattern placeholder poster preload readonly rel required reversed rows rowspan sandbox scope selected shape size sizes spellcheck src srcdoc srclang srcset start step style tabindex target title translate type usemap value width wrap
+ bernKeywordCell
+ enum bern
+ extends keywordCell
+ stumpNode
+ root
+ description A prefix Tree Language that compiles to HTML.
+ catchAllNodeType errorNode
+ inScope htmlTagNode blankLineNode
+ example
+ div
+ h1 hello world
+ compilesTo html
+ javascript
+ compile() {
+ return this.toHtml()
-
- if (!options) {
- options = {
- type: typeof btoa === 'function' ? 'basic' : 'auto'
- };
+ _getHtmlJoinByCharacter() {
+ return ""
-
- var encoder = function encoder(string) {
- if (typeof btoa === 'function') {
- return btoa(string);
- }
-
- throw new Error('Cannot use basic auth, btoa is not a function');
- };
-
- return this._auth(user, pass, options, encoder);
- };
- /**
- * Add query-string `val`.
- *
- * Examples:
- *
- * request.get('/shoes')
- * .query('size=10')
- * .query({ color: 'blue' })
- *
- * @param {Object|String} val
- * @return {Request} for chaining
- * @api public
- */
-
-
- Request.prototype.query = function (val) {
- if (typeof val !== 'string') val = serialize(val);
- if (val) this._query.push(val);
- return this;
- };
- /**
- * Queue the given `file` as an attachment to the specified `field`,
- * with optional `options` (or filename).
- *
- * ``` js
- * request.post('/upload')
- * .attach('content', new Blob(['
hey! '], { type: "text/html"}))
- * .end(callback);
- * ```
- *
- * @param {String} field
- * @param {Blob|File} file
- * @param {String|Object} options
- * @return {Request} for chaining
- * @api public
- */
-
-
- Request.prototype.attach = function (field, file, options) {
- if (file) {
- if (this._data) {
- throw new Error("superagent can't mix .send() and .attach()");
- }
-
- this._getFormData().append(field, file, options || file.name);
+ blankLineNode
+ pattern ^$
+ tags doNotSynthesize
+ cells emptyCell
+ javascript
+ _toHtml() {
+ return ""
-
- return this;
- };
-
- Request.prototype._getFormData = function () {
- if (!this._formData) {
- this._formData = new root.FormData();
+ getTextContent() {return ""}
+ htmlTagNode
+ inScope bernNode htmlTagNode htmlAttributeNode blankLineNode
+ catchAllCellType anyHtmlContentCell
+ cells htmlTagNameCell
+ javascript
+ getTag() {
+ // we need to remove the "Tag" bit to handle the style and title attribute/tag conflict.
+ const firstWord = this.getFirstWord()
+ const map = {
+ titleTag: "title",
+ styleTag: "style"
+ }
+ return map[firstWord] || firstWord
-
- return this._formData;
- };
- /**
- * Invoke the callback with `err` and `res`
- * and handle arity check.
- *
- * @param {Error} err
- * @param {Response} res
- * @api private
- */
-
-
- Request.prototype.callback = function (err, res) {
- if (this._shouldRetry(err, res)) {
- return this._retry();
+ _getHtmlJoinByCharacter() {
+ return ""
-
- var fn = this._callback;
- this.clearTimeout();
-
- if (err) {
- if (this._maxRetries) err.retries = this._retries - 1;
- this.emit('error', err);
+ toHtmlWithSuids() {
+ return this._toHtml(undefined, true)
-
- fn(err, res);
- };
- /**
- * Invoke callback with x-domain error.
- *
- * @api private
- */
-
-
- Request.prototype.crossDomainError = function () {
- var err = new Error('Request has been terminated\nPossible causes: the network is offline, Origin is not allowed by Access-Control-Allow-Origin, the page is being unloaded, etc.');
- err.crossDomain = true;
- err.status = this.status;
- err.method = this.method;
- err.url = this.url;
- this.callback(err);
- }; // This only warns, because the request is still likely to work
-
-
- Request.prototype.agent = function () {
- console.warn('This is not supported in browser version of superagent');
- return this;
- };
-
- Request.prototype.buffer = Request.prototype.ca;
- Request.prototype.ca = Request.prototype.agent; // This throws, because it can't send/receive data as expected
-
- Request.prototype.write = function () {
- throw new Error('Streaming is not supported in browser version of superagent');
- };
-
- Request.prototype.pipe = Request.prototype.write;
- /**
- * Check if `obj` is a host object,
- * we don't want to serialize these :)
- *
- * @param {Object} obj host object
- * @return {Boolean} is a host object
- * @api private
- */
-
- Request.prototype._isHost = function (obj) {
- // Native objects stringify to [object File], [object Blob], [object FormData], etc.
- return obj && _typeof(obj) === 'object' && !Array.isArray(obj) && Object.prototype.toString.call(obj) !== '[object Object]';
- };
- /**
- * Initiate request, invoking callback `fn(res)`
- * with an instanceof `Response`.
- *
- * @param {Function} fn
- * @return {Request} for chaining
- * @api public
- */
-
-
- Request.prototype.end = function (fn) {
- if (this._endCalled) {
- console.warn('Warning: .end() was called twice. This is not supported in superagent');
+ _getOneLiner() {
+ const oneLinerWords = this.getWordsFrom(1)
+ return oneLinerWords.length ? oneLinerWords.join(" ") : ""
-
- this._endCalled = true; // store callback
-
- this._callback = fn || noop; // querystring
-
- this._finalizeQueryString();
-
- this._end();
- };
-
- Request.prototype._setUploadTimeout = function () {
- var self = this; // upload timeout it's wokrs only if deadline timeout is off
-
- if (this._uploadTimeout && !this._uploadTimeoutTimer) {
- this._uploadTimeoutTimer = setTimeout(function () {
- self._timeoutError('Upload timeout of ', self._uploadTimeout, 'ETIMEDOUT');
- }, this._uploadTimeout);
+ getTextContent() {
+ return this._getOneLiner()
- }; // eslint-disable-next-line complexity
-
-
- Request.prototype._end = function () {
- if (this._aborted) return this.callback(new Error('The request has been aborted even before .end() was called'));
- var self = this;
- this.xhr = request.getXHR();
- var xhr = this.xhr;
- var data = this._formData || this._data;
-
- this._setTimeouts(); // state change
-
-
- xhr.onreadystatechange = function () {
- var readyState = xhr.readyState;
-
- if (readyState >= 2 && self._responseTimeoutTimer) {
- clearTimeout(self._responseTimeoutTimer);
- }
-
- if (readyState !== 4) {
- return;
- } // In IE9, reads to any property (e.g. status) off of an aborted XHR will
- // result in the error "Could not complete the operation due to error c00c023f"
-
-
- var status;
-
- try {
- status = xhr.status;
- } catch (err) {
- status = 0;
- }
-
- if (!status) {
- if (self.timedout || self._aborted) return;
- return self.crossDomainError();
- }
-
- self.emit('end');
- }; // progress
-
-
- var handleProgress = function handleProgress(direction, e) {
- if (e.total > 0) {
- e.percent = e.loaded / e.total * 100;
-
- if (e.percent === 100) {
- clearTimeout(self._uploadTimeoutTimer);
- }
- }
-
- e.direction = direction;
- self.emit('progress', e);
- };
-
- if (this.hasListeners('progress')) {
- try {
- xhr.addEventListener('progress', handleProgress.bind(null, 'download'));
-
- if (xhr.upload) {
- xhr.upload.addEventListener('progress', handleProgress.bind(null, 'upload'));
- }
- } catch (err) {// Accessing xhr.upload fails in IE from a web worker, so just pretend it doesn't exist.
- // Reported here:
- // https://connect.microsoft.com/IE/feedback/details/837245/xmlhttprequest-upload-throws-invalid-argument-when-used-from-web-worker-context
- }
+ shouldCollapse() {
+ return this.has("collapse")
-
- if (xhr.upload) {
- this._setUploadTimeout();
- } // initiate request
-
-
- try {
- if (this.username && this.password) {
- xhr.open(this.method, this.url, true, this.username, this.password);
- } else {
- xhr.open(this.method, this.url, true);
- }
- } catch (err) {
- // see #1149
- return this.callback(err);
- } // CORS
-
-
- if (this._withCredentials) xhr.withCredentials = true; // body
-
- if (!this._formData && this.method !== 'GET' && this.method !== 'HEAD' && typeof data !== 'string' && !this._isHost(data)) {
- // serialize stuff
- var contentType = this._header['content-type'];
-
- var _serialize = this._serializer || request.serialize[contentType ? contentType.split(';')[0] : ''];
-
- if (!_serialize && isJSON(contentType)) {
- _serialize = request.serialize['application/json'];
- }
-
- if (_serialize) data = _serialize(data);
- } // set header fields
-
-
- for (var field in this.header) {
- if (this.header[field] === null) continue;
- if (Object.prototype.hasOwnProperty.call(this.header, field)) xhr.setRequestHeader(field, this.header[field]);
+ _toHtml(indentCount, withSuid) {
+ const tag = this.getTag()
+ const children = this.map(child => child._toHtml(indentCount + 1, withSuid)).join("")
+ const attributesStr = this.filter(node => node.isAttributeNode)
+ .map(child => child.getAttribute())
+ .join("")
+ const indent = " ".repeat(indentCount)
+ const collapse = this.shouldCollapse()
+ const indentForChildNodes = !collapse && this.getChildInstancesOfNodeTypeId("htmlTagNode").length > 0
+ const suid = withSuid ? \` stumpUid="\${this._getUid()}"\` : ""
+ const oneLiner = this._getOneLiner()
+ return \`\${!collapse ? indent : ""}<\${tag}\${attributesStr}\${suid}>\${oneLiner}\${indentForChildNodes ? "\\n" : ""}\${children}\${tag}>\${collapse ? "" : "\\n"}\`
-
- if (this._responseType) {
- xhr.responseType = this._responseType;
- } // send stuff
-
-
- this.emit('request', this); // IE11 xhr.send(undefined) sends 'undefined' string as POST payload (instead of nothing)
- // We need null here if data is undefined
-
- xhr.send(typeof data === 'undefined' ? null : data);
- };
-
- request.agent = function () {
- return new Agent();
- };
-
- ['GET', 'POST', 'OPTIONS', 'PATCH', 'PUT', 'DELETE'].forEach(function (method) {
- Agent.prototypeethod.toLowerCase()] = function (url, fn) {
- var req = new request.Request(method, url);
-
- this._setDefaults(req);
-
- if (fn) {
- req.end(fn);
- }
-
- return req;
- };
- });
- Agent.prototype.del = Agent.prototype.delete;
- /**
- * GET `url` with optional callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed|Function} [data] or fn
- * @param {Function} [fn]
- * @return {Request}
- * @api public
- */
-
- request.get = function (url, data, fn) {
- var req = request('GET', url);
-
- if (typeof data === 'function') {
- fn = data;
- data = null;
+ removeCssStumpNode() {
+ return this.removeStumpNode()
-
- if (data) req.query(data);
- if (fn) req.end(fn);
- return req;
- };
- /**
- * HEAD `url` with optional callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed|Function} [data] or fn
- * @param {Function} [fn]
- * @return {Request}
- * @api public
- */
-
-
- request.head = function (url, data, fn) {
- var req = request('HEAD', url);
-
- if (typeof data === 'function') {
- fn = data;
- data = null;
+ removeStumpNode() {
+ this.getShadow().removeShadow()
+ return this.destroy()
-
- if (data) req.query(data);
- if (fn) req.end(fn);
- return req;
- };
- /**
- * OPTIONS query to `url` with optional callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed|Function} [data] or fn
- * @param {Function} [fn]
- * @return {Request}
- * @api public
- */
-
-
- request.options = function (url, data, fn) {
- var req = request('OPTIONS', url);
-
- if (typeof data === 'function') {
- fn = data;
- data = null;
+ getNodeByGuid(guid) {
+ return this.getTopDownArray().find(node => node._getUid() === guid)
-
- if (data) req.send(data);
- if (fn) req.end(fn);
- return req;
- };
- /**
- * DELETE `url` with optional `data` and callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed} [data]
- * @param {Function} [fn]
- * @return {Request}
- * @api public
- */
-
-
- function del(url, data, fn) {
- var req = request('DELETE', url);
-
- if (typeof data === 'function') {
- fn = data;
- data = null;
+ addClassToStumpNode(className) {
+ const classNode = this.touchNode("class")
+ const words = classNode.getWordsFrom(1)
+ // note: we call add on shadow regardless, because at the moment stump may have gotten out of
+ // sync with shadow, if things modified the dom. todo: cleanup.
+ this.getShadow().addClassToShadow(className)
+ if (words.includes(className)) return this
+ words.push(className)
+ classNode.setContent(words.join(this.getWordBreakSymbol()))
+ return this
-
- if (data) req.send(data);
- if (fn) req.end(fn);
- return req;
- }
-
- request.del = del;
- request.delete = del;
- /**
- * PATCH `url` with optional `data` and callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed} [data]
- * @param {Function} [fn]
- * @return {Request}
- * @api public
- */
-
- request.patch = function (url, data, fn) {
- var req = request('PATCH', url);
-
- if (typeof data === 'function') {
- fn = data;
- data = null;
+ removeClassFromStumpNode(className) {
+ const classNode = this.getNode("class")
+ if (!classNode) return this
+ const newClasses = classNode.getWords().filter(word => word !== className)
+ if (!newClasses.length) classNode.destroy()
+ else classNode.setContent(newClasses.join(" "))
+ this.getShadow().removeClassFromShadow(className)
+ return this
-
- if (data) req.send(data);
- if (fn) req.end(fn);
- return req;
- };
- /**
- * POST `url` with optional `data` and callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed} [data]
- * @param {Function} [fn]
- * @return {Request}
- * @api public
- */
-
-
- request.post = function (url, data, fn) {
- var req = request('POST', url);
-
- if (typeof data === 'function') {
- fn = data;
- data = null;
+ stumpNodeHasClass(className) {
+ const classNode = this.getNode("class")
+ return classNode && classNode.getWords().includes(className) ? true : false
-
- if (data) req.send(data);
- if (fn) req.end(fn);
- return req;
- };
- /**
- * PUT `url` with optional `data` and callback `fn(res)`.
- *
- * @param {String} url
- * @param {Mixed|Function} [data] or fn
- * @param {Function} [fn]
- * @return {Request}
- * @api public
- */
-
-
- request.put = function (url, data, fn) {
- var req = request('PUT', url);
-
- if (typeof data === 'function') {
- fn = data;
- data = null;
+ isStumpNodeCheckbox() {
+ return this.get("type") === "checkbox"
-
- if (data) req.send(data);
- if (fn) req.end(fn);
- return req;
- };
-
- },{"./agent-base":3,"./is-object":4,"./request-base":6,"./response-base":7,"component-emitter":1,"fast-safe-stringify":2}],6:[function(require,module,exports){
- "use strict";
-
- function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
-
- /**
- * Module of mixed-in functions shared between node and client code
- */
- var isObject = require('./is-object');
- /**
- * Expose `RequestBase`.
- */
-
-
- module.exports = RequestBase;
- /**
- * Initialize a new `RequestBase`.
- *
- * @api public
- */
-
- function RequestBase(obj) {
- if (obj) return mixin(obj);
- }
- /**
- * Mixin the prototype properties.
- *
- * @param {Object} obj
- * @return {Object}
- * @api private
- */
-
-
- function mixin(obj) {
- for (var key in RequestBase.prototype) {
- if (Object.prototype.hasOwnProperty.call(RequestBase.prototype, key)) obj[key] = RequestBase.prototype[key];
+ getShadow() {
+ if (!this._shadow) {
+ const shadowClass = this.getShadowClass()
+ this._shadow = new shadowClass(this)
+ }
+ return this._shadow
-
- return obj;
- }
- /**
- * Clear previous timeout.
- *
- * @return {Request} for chaining
- * @api public
- */
-
-
- RequestBase.prototype.clearTimeout = function () {
- clearTimeout(this._timer);
- clearTimeout(this._responseTimeoutTimer);
- clearTimeout(this._uploadTimeoutTimer);
- delete this._timer;
- delete this._responseTimeoutTimer;
- delete this._uploadTimeoutTimer;
- return this;
- };
- /**
- * Override default response body parser
- *
- * This function will be called to convert incoming data into request.body
- *
- * @param {Function}
- * @api public
- */
-
-
- RequestBase.prototype.parse = function (fn) {
- this._parser = fn;
- return this;
- };
- /**
- * Set format of binary response body.
- * In browser valid formats are 'blob' and 'arraybuffer',
- * which return Blob and ArrayBuffer, respectively.
- *
- * In Node all values result in Buffer.
- *
- * Examples:
- *
- * req.get('/')
- * .responseType('blob')
- * .end(callback);
- *
- * @param {String} val
- * @return {Request} for chaining
- * @api public
- */
-
-
- RequestBase.prototype.responseType = function (val) {
- this._responseType = val;
- return this;
- };
- /**
- * Override default request body serializer
- *
- * This function will be called to convert data set via .send or .attach into payload to send
- *
- * @param {Function}
- * @api public
- */
-
-
- RequestBase.prototype.serialize = function (fn) {
- this._serializer = fn;
- return this;
- };
- /**
- * Set timeouts.
- *
- * - response timeout is time between sending request and receiving the first byte of the response. Includes DNS and connection time.
- * - deadline is the time from start of the request to receiving response body in full. If the deadline is too short large files may not load at all on slow connections.
- * - upload is the time since last bit of data was sent or received. This timeout works only if deadline timeout is off
- *
- * Value of 0 or false means no timeout.
- *
- * @param {Number|Object} ms or {response, deadline}
- * @return {Request} for chaining
- * @api public
- */
-
-
- RequestBase.prototype.timeout = function (options) {
- if (!options || _typeof(options) !== 'object') {
- this._timeout = options;
- this._responseTimeout = 0;
- this._uploadTimeout = 0;
- return this;
+ insertCssChildNode(text, index) {
+ return this.insertChildNode(text, index)
-
- for (var option in options) {
- if (Object.prototype.hasOwnProperty.call(options, option)) {
- switch (option) {
- case 'deadline':
- this._timeout = options.deadline;
- break;
-
- case 'response':
- this._responseTimeout = options.response;
- break;
-
- case 'upload':
- this._uploadTimeout = options.upload;
- break;
-
- default:
- console.warn('Unknown timeout option', option);
- }
- }
+ insertChildNode(text, index) {
+ const singleNode = new jtree.TreeNode(text).getChildren()[0]
+ const newNode = this.insertLineAndChildren(singleNode.getLine(), singleNode.childrenToString(), index)
+ const stumpNodeIndex = this.getChildInstancesOfNodeTypeId("htmlTagNode").indexOf(newNode)
+ this.getShadow().insertHtmlNode(newNode, stumpNodeIndex)
+ return newNode
-
- return this;
- };
- /**
- * Set number of retry attempts on error.
- *
- * Failed requests will be retried 'count' times if timeout or err.code >= 500.
- *
- * @param {Number} count
- * @param {Function} [fn]
- * @return {Request} for chaining
- * @api public
- */
-
-
- RequestBase.prototype.retry = function (count, fn) {
- // Default to 1 if no count passed or true
- if (arguments.length === 0 || count === true) count = 1;
- if (count <= 0) count = 0;
- this._maxRetries = count;
- this._retries = 0;
- this._retryCallback = fn;
- return this;
- };
-
- var ERROR_CODES = ['ECONNRESET', 'ETIMEDOUT', 'EADDRINFO', 'ESOCKETTIMEDOUT'];
- /**
- * Determine if a request should be retried.
- * (Borrowed from segmentio/superagent-retry)
- *
- * @param {Error} err an error
- * @param {Response} [res] response
- * @returns {Boolean} if segment should be retried
- */
-
- RequestBase.prototype._shouldRetry = function (err, res) {
- if (!this._maxRetries || this._retries++ >= this._maxRetries) {
- return false;
+ isInputType() {
+ return ["input", "textarea"].includes(this.getTag()) || this.get("contenteditable") === "true"
-
- if (this._retryCallback) {
- try {
- var override = this._retryCallback(err, res);
-
- if (override === true) return true;
- if (override === false) return false; // undefined falls back to defaults
- } catch (err2) {
- console.error(err2);
- }
+ findStumpNodeByChild(line) {
+ return this.findStumpNodesByChild(line)[0]
-
- if (res && res.status && res.status >= 500 && res.status !== 501) return true;
-
- if (err) {
- if (err.code && ERROR_CODES.indexOf(err.code) !== -1) return true; // Superagent timeout
-
- if (err.timeout && err.code === 'ECONNABORTED') return true;
- if (err.crossDomain) return true;
+ findStumpNodeByChildString(line) {
+ return this.getTopDownArray().find(node =>
+ node
+ .map(child => child.getLine())
+ .join("\\n")
+ .includes(line)
+ )
-
- return false;
- };
- /**
- * Retry request
- *
- * @return {Request} for chaining
- * @api private
- */
-
-
- RequestBase.prototype._retry = function () {
- this.clearTimeout(); // node
-
- if (this.req) {
- this.req = null;
- this.req = this.request();
+ findStumpNodeByFirstWord(firstWord) {
+ return this._findStumpNodesByBase(firstWord)[0]
-
- this._aborted = false;
- this.timedout = false;
- return this._end();
- };
- /**
- * Promise support
- *
- * @param {Function} resolve
- * @param {Function} [reject]
- * @return {Request}
- */
-
-
- RequestBase.prototype.then = function (resolve, reject) {
- var _this = this;
-
- if (!this._fullfilledPromise) {
- var self = this;
-
- if (this._endCalled) {
- console.warn('Warning: superagent request was sent twice, because both .end() and .then() were called. Never call .end() if you use promises');
- }
-
- this._fullfilledPromise = new Promise(function (resolve, reject) {
- self.on('abort', function () {
- var err = new Error('Aborted');
- err.code = 'ABORTED';
- err.status = _this.status;
- err.method = _this.method;
- err.url = _this.url;
- reject(err);
- });
- self.end(function (err, res) {
- if (err) reject(err);else resolve(res);
- });
- });
- }
-
- return this._fullfilledPromise.then(resolve, reject);
- };
-
- RequestBase.prototype.catch = function (cb) {
- return this.then(undefined, cb);
- };
- /**
- * Allow for extension
- */
-
-
- RequestBase.prototype.use = function (fn) {
- fn(this);
- return this;
- };
-
- RequestBase.prototype.ok = function (cb) {
- if (typeof cb !== 'function') throw new Error('Callback required');
- this._okCallback = cb;
- return this;
- };
-
- RequestBase.prototype._isResponseOK = function (res) {
- if (!res) {
- return false;
+ _findStumpNodesByBase(firstWord) {
+ return this.getTopDownArray().filter(node => node.doesExtend("htmlTagNode") && node.getFirstWord() === firstWord)
-
- if (this._okCallback) {
- return this._okCallback(res);
+ hasLine(line) {
+ return this.getChildren().some(node => node.getLine() === line)
-
- return res.status >= 200 && res.status < 300;
- };
- /**
- * Get request header `field`.
- * Case-insensitive.
- *
- * @param {String} field
- * @return {String}
- * @api public
- */
-
-
- RequestBase.prototype.get = function (field) {
- return this._header[field.toLowerCase()];
- };
- /**
- * Get case-insensitive header `field` value.
- * This is a deprecated internal API. Use `.get(field)` instead.
- *
- * (getHeader is no longer used internally by the superagent code base)
- *
- * @param {String} field
- * @return {String}
- * @api private
- * @deprecated
- */
-
-
- RequestBase.prototype.getHeader = RequestBase.prototype.get;
- /**
- * Set header `field` to `val`, or multiple fields with one object.
- * Case-insensitive.
- *
- * Examples:
- *
- * req.get('/')
- * .set('Accept', 'application/json')
- * .set('X-API-Key', 'foobar')
- * .end(callback);
- *
- * req.get('/')
- * .set({ Accept: 'application/json', 'X-API-Key': 'foobar' })
- * .end(callback);
- *
- * @param {String|Object} field
- * @param {String} val
- * @return {Request} for chaining
- * @api public
- */
-
- RequestBase.prototype.set = function (field, val) {
- if (isObject(field)) {
- for (var key in field) {
- if (Object.prototype.hasOwnProperty.call(field, key)) this.set(key, field[key]);
- }
-
- return this;
+ findStumpNodesByChild(line) {
+ return this.getTopDownArray().filter(node => node.doesExtend("htmlTagNode") && node.hasLine(line))
-
- this._header[field.toLowerCase()] = val;
- this.header[field] = val;
- return this;
- }; // eslint-disable-next-line valid-jsdoc
-
- /**
- * Remove header `field`.
- * Case-insensitive.
- *
- * Example:
- *
- * req.get('/')
- * .unset('User-Agent')
- * .end(callback);
- *
- * @param {String} field field name
- */
-
-
- RequestBase.prototype.unset = function (field) {
- delete this._header[field.toLowerCase()];
- delete this.header[field];
- return this;
- };
- /**
- * Write the field `name` and `val`, or multiple fields with one object
- * for "multipart/form-data" request bodies.
- *
- * ``` js
- * request.post('/upload')
- * .field('foo', 'bar')
- * .end(callback);
- *
- * request.post('/upload')
- * .field({ foo: 'bar', baz: 'qux' })
- * .end(callback);
- * ```
- *
- * @param {String|Object} name name of field
- * @param {String|Blob|File|Buffer|fs.ReadStream} val value of field
- * @return {Request} for chaining
- * @api public
- */
-
-
- RequestBase.prototype.field = function (name, val) {
- // name should be either a string or an object.
- if (name === null || undefined === name) {
- throw new Error('.field(name, val) name can not be empty');
+ findStumpNodesWithClass(className) {
+ return this.getTopDownArray().filter(
+ node =>
+ node.doesExtend("htmlTagNode") &&
+ node.has("class") &&
+ node
+ .getNode("class")
+ .getWords()
+ .includes(className)
+ )
-
- if (this._data) {
- throw new Error(".field() can't be used if .send() is used. Please use only .send() or only .field() & .attach()");
+ getShadowClass() {
+ return this.getParent().getShadowClass()
-
- if (isObject(name)) {
- for (var key in name) {
- if (Object.prototype.hasOwnProperty.call(name, key)) this.field(key, name[key]);
- }
-
- return this;
+ // todo: should not be here
+ getStumpNodeTreeComponent() {
+ return this._treeComponent || this.getParent().getStumpNodeTreeComponent()
-
- if (Array.isArray(val)) {
- for (var i in val) {
- if (Object.prototype.hasOwnProperty.call(val, i)) this.field(name, val[i]);
- }
-
- return this;
- } // val should be defined now
-
-
- if (val === null || undefined === val) {
- throw new Error('.field(name, val) val can not be empty');
+ // todo: should not be here
+ setStumpNodeTreeComponent(treeComponent) {
+ this._treeComponent = treeComponent
+ return this
-
- if (typeof val === 'boolean') {
- val = String(val);
+ setStumpNodeCss(css) {
+ this.getShadow().setShadowCss(css)
+ return this
-
- this._getFormData().append(name, val);
-
- return this;
- };
- /**
- * Abort the request, and clear potential timeout.
- *
- * @return {Request} request
- * @api public
- */
-
-
- RequestBase.prototype.abort = function () {
- if (this._aborted) {
- return this;
- }
-
- this._aborted = true;
- if (this.xhr) this.xhr.abort(); // browser
-
- if (this.req) this.req.abort(); // node
-
- this.clearTimeout();
- this.emit('abort');
- return this;
- };
-
- RequestBase.prototype._auth = function (user, pass, options, base64Encoder) {
- switch (options.type) {
- case 'basic':
- this.set('Authorization', "Basic ".concat(base64Encoder("".concat(user, ":").concat(pass))));
- break;
-
- case 'auto':
- this.username = user;
- this.password = pass;
- break;
-
- case 'bearer':
- // usage would be .auth(accessToken, { type: 'bearer' })
- this.set('Authorization', "Bearer ".concat(user));
- break;
-
- default:
- break;
- }
-
- return this;
- };
- /**
- * Enable transmission of cookies with x-domain requests.
- *
- * Note that for this to work the origin must not be
- * using "Access-Control-Allow-Origin" with a wildcard,
- * and also must set "Access-Control-Allow-Credentials"
- * to "true".
- *
- * @api public
- */
-
-
- RequestBase.prototype.withCredentials = function (on) {
- // This is browser-only functionality. Node side is no-op.
- if (on === undefined) on = true;
- this._withCredentials = on;
- return this;
- };
- /**
- * Set the max redirects to `n`. Does nothing in browser XHR implementation.
- *
- * @param {Number} n
- * @return {Request} for chaining
- * @api public
- */
-
-
- RequestBase.prototype.redirects = function (n) {
- this._maxRedirects = n;
- return this;
- };
- /**
- * Maximum size of buffered response body, in bytes. Counts uncompressed size.
- * Default 200MB.
- *
- * @param {Number} n number of bytes
- * @return {Request} for chaining
- */
-
-
- RequestBase.prototype.maxResponseSize = function (n) {
- if (typeof n !== 'number') {
- throw new TypeError('Invalid argument');
- }
-
- this._maxResponseSize = n;
- return this;
- };
- /**
- * Convert to a plain javascript object (not JSON string) of scalar properties.
- * Note as this method is designed to return a useful non-this value,
- * it cannot be chained.
- *
- * @return {Object} describing method, url, and data of this request
- * @api public
- */
-
-
- RequestBase.prototype.toJSON = function () {
- return {
- method: this.method,
- url: this.url,
- data: this._data,
- headers: this._header
- };
- };
- /**
- * Send `data` as the request body, defaulting the `.type()` to "json" when
- * an object is given.
- *
- * Examples:
- *
- * // manual json
- * request.post('/user')
- * .type('json')
- * .send('{"name":"tj"}')
- * .end(callback)
- *
- * // auto json
- * request.post('/user')
- * .send({ name: 'tj' })
- * .end(callback)
- *
- * // manual x-www-form-urlencoded
- * request.post('/user')
- * .type('form')
- * .send('name=tj')
- * .end(callback)
- *
- * // auto x-www-form-urlencoded
- * request.post('/user')
- * .type('form')
- * .send({ name: 'tj' })
- * .end(callback)
- *
- * // defaults to x-www-form-urlencoded
- * request.post('/user')
- * .send('name=tobi')
- * .send('species=ferret')
- * .end(callback)
- *
- * @param {String|Object} data
- * @return {Request} for chaining
- * @api public
- */
- // eslint-disable-next-line complexity
-
-
- RequestBase.prototype.send = function (data) {
- var isObj = isObject(data);
- var type = this._header['content-type'];
-
- if (this._formData) {
- throw new Error(".send() can't be used if .attach() or .field() is used. Please use only .send() or only .field() & .attach()");
+ getStumpNodeCss(prop) {
+ return this.getShadow().getShadowCss(prop)
-
- if (isObj && !this._data) {
- if (Array.isArray(data)) {
- this._data = [];
- } else if (!this._isHost(data)) {
- this._data = {};
- }
- } else if (data && this._data && this._isHost(this._data)) {
- throw new Error("Can't merge these send calls");
- } // merge
-
-
- if (isObj && isObject(this._data)) {
- for (var key in data) {
- if (Object.prototype.hasOwnProperty.call(data, key)) this._data[key] = data[key];
- }
- } else if (typeof data === 'string') {
- // default to x-www-form-urlencoded
- if (!type) this.type('form');
- type = this._header['content-type'];
-
- if (type === 'application/x-www-form-urlencoded') {
- this._data = this._data ? "".concat(this._data, "&").concat(data) : data;
- } else {
- this._data = (this._data || '') + data;
- }
- } else {
- this._data = data;
+ getStumpNodeAttr(key) {
+ return this.get(key)
-
- if (!isObj || this._isHost(data)) {
- return this;
- } // default to json
-
-
- if (!type) this.type('json');
- return this;
- };
- /**
- * Sort `querystring` by the sort function
- *
- *
- * Examples:
- *
- * // default order
- * request.get('/user')
- * .query('name=Nick')
- * .query('search=Manny')
- * .sortQuery()
- * .end(callback)
- *
- * // customized sort function
- * request.get('/user')
- * .query('name=Nick')
- * .query('search=Manny')
- * .sortQuery(function(a, b){
- * return a.length - b.length;
- * })
- * .end(callback)
- *
- *
- * @param {Function} sort
- * @return {Request} for chaining
- * @api public
- */
-
-
- RequestBase.prototype.sortQuery = function (sort) {
- // _sort default to true but otherwise can be a function or boolean
- this._sort = typeof sort === 'undefined' ? true : sort;
- return this;
- };
- /**
- * Compose querystring to append to req.url
- *
- * @api private
- */
-
-
- RequestBase.prototype._finalizeQueryString = function () {
- var query = this._query.join('&');
-
- if (query) {
- this.url += (this.url.indexOf('?') >= 0 ? '&' : '?') + query;
+ setStumpNodeAttr(key, value) {
+ // todo
+ return this
-
- this._query.length = 0; // Makes the call idempotent
-
- if (this._sort) {
- var index = this.url.indexOf('?');
-
- if (index >= 0) {
- var queryArr = this.url.substring(index + 1).split('&');
-
- if (typeof this._sort === 'function') {
- queryArr.sort(this._sort);
- } else {
- queryArr.sort();
- }
-
- this.url = this.url.substring(0, index) + '?' + queryArr.join('&');
- }
+ toHtml() {
+ return this._toHtml()
- }; // For backwards compat only
-
-
- RequestBase.prototype._appendQueryString = function () {
- console.warn('Unsupported');
- };
- /**
- * Invoke callback with timeout error.
- *
- * @api private
- */
-
-
- RequestBase.prototype._timeoutError = function (reason, timeout, errno) {
- if (this._aborted) {
- return;
+ errorNode
+ baseNodeType errorNode
+ htmlAttributeNode
+ javascript
+ _toHtml() {
+ return ""
-
- var err = new Error("".concat(reason + timeout, "ms exceeded"));
- err.timeout = timeout;
- err.code = 'ECONNABORTED';
- err.errno = errno;
- this.timedout = true;
- this.abort();
- this.callback(err);
- };
-
- RequestBase.prototype._setTimeouts = function () {
- var self = this; // deadline
-
- if (this._timeout && !this._timer) {
- this._timer = setTimeout(function () {
- self._timeoutError('Timeout of ', self._timeout, 'ETIME');
- }, this._timeout);
- } // response timeout
-
-
- if (this._responseTimeout && !this._responseTimeoutTimer) {
- this._responseTimeoutTimer = setTimeout(function () {
- self._timeoutError('Response timeout of ', self._responseTimeout, 'ETIMEDOUT');
- }, this._responseTimeout);
+ getTextContent() {return ""}
+ getAttribute() {
+ return \` \${this.getFirstWord()}="\${this.getContent()}"\`
- };
-
- },{"./is-object":4}],7:[function(require,module,exports){
- "use strict";
-
- /**
- * Module dependencies.
- */
- var utils = require('./utils');
- /**
- * Expose `ResponseBase`.
- */
-
-
- module.exports = ResponseBase;
- /**
- * Initialize a new `ResponseBase`.
- *
- * @api public
- */
-
- function ResponseBase(obj) {
- if (obj) return mixin(obj);
- }
- /**
- * Mixin the prototype properties.
- *
- * @param {Object} obj
- * @return {Object}
- * @api private
- */
-
-
- function mixin(obj) {
- for (var key in ResponseBase.prototype) {
- if (Object.prototype.hasOwnProperty.call(ResponseBase.prototype, key)) obj[key] = ResponseBase.prototype[key];
+ boolean isAttributeNode true
+ boolean isTileAttribute true
+ catchAllNodeType errorNode
+ catchAllCellType attributeValueCell
+ cells htmlAttributeNameCell
+ stumpExtendedAttributeNameCell
+ extends htmlAttributeNameCell
+ enum collapse blurCommand changeCommand clickCommand contextMenuCommand doubleClickCommand lineClickCommand lineShiftClickCommand shiftClickCommand
+ stumpExtendedAttributeNode
+ description Node types not present in HTML but included in stump.
+ extends htmlAttributeNode
+ cells stumpExtendedAttributeNameCell
+ lineOfHtmlContentNode
+ boolean isTileAttribute true
+ catchAllNodeType lineOfHtmlContentNode
+ catchAllCellType anyHtmlContentCell
+ javascript
+ getTextContent() {return this.getLine()}
+ bernNode
+ boolean isTileAttribute true
+ todo Rename this node type
+ description This is a node where you can put any HTML content. It is called "bern" until someone comes up with a better name.
+ catchAllNodeType lineOfHtmlContentNode
+ javascript
+ _toHtml() {
+ return this.childrenToString()
-
- return obj;
+ getTextContent() {return ""}
+ cells bernKeywordCell`)
+ return this._cachedHandGrammarProgramRoot
+ }
+ static getNodeTypeMap() {
+ return {
+ stumpNode: stumpNode,
+ blankLineNode: blankLineNode,
+ htmlTagNode: htmlTagNode,
+ errorNode: errorNode,
+ htmlAttributeNode: htmlAttributeNode,
+ stumpExtendedAttributeNode: stumpExtendedAttributeNode,
+ lineOfHtmlContentNode: lineOfHtmlContentNode,
+ bernNode: bernNode,
+ }
+ }
+ }
+
+ class blankLineNode extends jtree.GrammarBackedNode {
+ get emptyCell() {
+ return this.getWord(0)
+ }
+ _toHtml() {
+ return ""
+ }
+ getTextContent() {
+ return ""
+ }
+ }
+
+ class htmlTagNode extends jtree.GrammarBackedNode {
+ createParser() {
+ return new jtree.TreeNode.Parser(
+ undefined,
+ Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
+ blockquote: htmlTagNode,
+ colgroup: htmlTagNode,
+ datalist: htmlTagNode,
+ fieldset: htmlTagNode,
+ menuitem: htmlTagNode,
+ noscript: htmlTagNode,
+ optgroup: htmlTagNode,
+ progress: htmlTagNode,
+ styleTag: htmlTagNode,
+ template: htmlTagNode,
+ textarea: htmlTagNode,
+ titleTag: htmlTagNode,
+ address: htmlTagNode,
+ article: htmlTagNode,
+ caption: htmlTagNode,
+ details: htmlTagNode,
+ section: htmlTagNode,
+ summary: htmlTagNode,
+ button: htmlTagNode,
+ canvas: htmlTagNode,
+ dialog: htmlTagNode,
+ figure: htmlTagNode,
+ footer: htmlTagNode,
+ header: htmlTagNode,
+ hgroup: htmlTagNode,
+ iframe: htmlTagNode,
+ keygen: htmlTagNode,
+ legend: htmlTagNode,
+ object: htmlTagNode,
+ option: htmlTagNode,
+ output: htmlTagNode,
+ script: htmlTagNode,
+ select: htmlTagNode,
+ source: htmlTagNode,
+ strong: htmlTagNode,
+ aside: htmlTagNode,
+ embed: htmlTagNode,
+ input: htmlTagNode,
+ label: htmlTagNode,
+ meter: htmlTagNode,
+ param: htmlTagNode,
+ small: htmlTagNode,
+ table: htmlTagNode,
+ tbody: htmlTagNode,
+ tfoot: htmlTagNode,
+ thead: htmlTagNode,
+ track: htmlTagNode,
+ video: htmlTagNode,
+ abbr: htmlTagNode,
+ area: htmlTagNode,
+ base: htmlTagNode,
+ body: htmlTagNode,
+ code: htmlTagNode,
+ form: htmlTagNode,
+ head: htmlTagNode,
+ html: htmlTagNode,
+ link: htmlTagNode,
+ main: htmlTagNode,
+ mark: htmlTagNode,
+ menu: htmlTagNode,
+ meta: htmlTagNode,
+ ruby: htmlTagNode,
+ samp: htmlTagNode,
+ span: htmlTagNode,
+ time: htmlTagNode,
+ bdi: htmlTagNode,
+ bdo: htmlTagNode,
+ col: htmlTagNode,
+ del: htmlTagNode,
+ dfn: htmlTagNode,
+ div: htmlTagNode,
+ img: htmlTagNode,
+ ins: htmlTagNode,
+ kbd: htmlTagNode,
+ map: htmlTagNode,
+ nav: htmlTagNode,
+ pre: htmlTagNode,
+ rtc: htmlTagNode,
+ sub: htmlTagNode,
+ sup: htmlTagNode,
+ var: htmlTagNode,
+ wbr: htmlTagNode,
+ br: htmlTagNode,
+ dd: htmlTagNode,
+ dl: htmlTagNode,
+ dt: htmlTagNode,
+ em: htmlTagNode,
+ h1: htmlTagNode,
+ h2: htmlTagNode,
+ h3: htmlTagNode,
+ h4: htmlTagNode,
+ h5: htmlTagNode,
+ h6: htmlTagNode,
+ hr: htmlTagNode,
+ li: htmlTagNode,
+ ol: htmlTagNode,
+ rb: htmlTagNode,
+ rp: htmlTagNode,
+ rt: htmlTagNode,
+ td: htmlTagNode,
+ th: htmlTagNode,
+ tr: htmlTagNode,
+ ul: htmlTagNode,
+ a: htmlTagNode,
+ b: htmlTagNode,
+ i: htmlTagNode,
+ p: htmlTagNode,
+ q: htmlTagNode,
+ s: htmlTagNode,
+ u: htmlTagNode,
+ oncanplaythrough: htmlAttributeNode,
+ ondurationchange: htmlAttributeNode,
+ onloadedmetadata: htmlAttributeNode,
+ contenteditable: htmlAttributeNode,
+ "accept-charset": htmlAttributeNode,
+ onbeforeunload: htmlAttributeNode,
+ onvolumechange: htmlAttributeNode,
+ onbeforeprint: htmlAttributeNode,
+ oncontextmenu: htmlAttributeNode,
+ autocomplete: htmlAttributeNode,
+ onafterprint: htmlAttributeNode,
+ onhashchange: htmlAttributeNode,
+ onloadeddata: htmlAttributeNode,
+ onmousewheel: htmlAttributeNode,
+ onratechange: htmlAttributeNode,
+ ontimeupdate: htmlAttributeNode,
+ oncuechange: htmlAttributeNode,
+ ondragenter: htmlAttributeNode,
+ ondragleave: htmlAttributeNode,
+ ondragstart: htmlAttributeNode,
+ onloadstart: htmlAttributeNode,
+ onmousedown: htmlAttributeNode,
+ onmousemove: htmlAttributeNode,
+ onmouseover: htmlAttributeNode,
+ placeholder: htmlAttributeNode,
+ formaction: htmlAttributeNode,
+ "http-equiv": htmlAttributeNode,
+ novalidate: htmlAttributeNode,
+ ondblclick: htmlAttributeNode,
+ ondragover: htmlAttributeNode,
+ onkeypress: htmlAttributeNode,
+ onmouseout: htmlAttributeNode,
+ onpagehide: htmlAttributeNode,
+ onpageshow: htmlAttributeNode,
+ onpopstate: htmlAttributeNode,
+ onprogress: htmlAttributeNode,
+ spellcheck: htmlAttributeNode,
+ accesskey: htmlAttributeNode,
+ autofocus: htmlAttributeNode,
+ draggable: htmlAttributeNode,
+ maxlength: htmlAttributeNode,
+ oncanplay: htmlAttributeNode,
+ ondragend: htmlAttributeNode,
+ onemptied: htmlAttributeNode,
+ oninvalid: htmlAttributeNode,
+ onkeydown: htmlAttributeNode,
+ onmouseup: htmlAttributeNode,
+ onoffline: htmlAttributeNode,
+ onplaying: htmlAttributeNode,
+ onseeking: htmlAttributeNode,
+ onstalled: htmlAttributeNode,
+ onstorage: htmlAttributeNode,
+ onsuspend: htmlAttributeNode,
+ onwaiting: htmlAttributeNode,
+ translate: htmlAttributeNode,
+ autoplay: htmlAttributeNode,
+ controls: htmlAttributeNode,
+ datetime: htmlAttributeNode,
+ disabled: htmlAttributeNode,
+ download: htmlAttributeNode,
+ dropzone: htmlAttributeNode,
+ hreflang: htmlAttributeNode,
+ multiple: htmlAttributeNode,
+ onchange: htmlAttributeNode,
+ ononline: htmlAttributeNode,
+ onresize: htmlAttributeNode,
+ onscroll: htmlAttributeNode,
+ onsearch: htmlAttributeNode,
+ onseeked: htmlAttributeNode,
+ onselect: htmlAttributeNode,
+ onsubmit: htmlAttributeNode,
+ ontoggle: htmlAttributeNode,
+ onunload: htmlAttributeNode,
+ readonly: htmlAttributeNode,
+ required: htmlAttributeNode,
+ reversed: htmlAttributeNode,
+ selected: htmlAttributeNode,
+ tabindex: htmlAttributeNode,
+ bgcolor: htmlAttributeNode,
+ charset: htmlAttributeNode,
+ checked: htmlAttributeNode,
+ colspan: htmlAttributeNode,
+ content: htmlAttributeNode,
+ default: htmlAttributeNode,
+ dirname: htmlAttributeNode,
+ enctype: htmlAttributeNode,
+ headers: htmlAttributeNode,
+ onabort: htmlAttributeNode,
+ onclick: htmlAttributeNode,
+ onended: htmlAttributeNode,
+ onerror: htmlAttributeNode,
+ onfocus: htmlAttributeNode,
+ oninput: htmlAttributeNode,
+ onkeyup: htmlAttributeNode,
+ onpaste: htmlAttributeNode,
+ onpause: htmlAttributeNode,
+ onreset: htmlAttributeNode,
+ onwheel: htmlAttributeNode,
+ optimum: htmlAttributeNode,
+ pattern: htmlAttributeNode,
+ preload: htmlAttributeNode,
+ rowspan: htmlAttributeNode,
+ sandbox: htmlAttributeNode,
+ srclang: htmlAttributeNode,
+ accept: htmlAttributeNode,
+ action: htmlAttributeNode,
+ border: htmlAttributeNode,
+ coords: htmlAttributeNode,
+ height: htmlAttributeNode,
+ hidden: htmlAttributeNode,
+ method: htmlAttributeNode,
+ onblur: htmlAttributeNode,
+ oncopy: htmlAttributeNode,
+ ondrag: htmlAttributeNode,
+ ondrop: htmlAttributeNode,
+ onload: htmlAttributeNode,
+ onplay: htmlAttributeNode,
+ poster: htmlAttributeNode,
+ srcdoc: htmlAttributeNode,
+ srcset: htmlAttributeNode,
+ target: htmlAttributeNode,
+ usemap: htmlAttributeNode,
+ align: htmlAttributeNode,
+ async: htmlAttributeNode,
+ class: htmlAttributeNode,
+ color: htmlAttributeNode,
+ defer: htmlAttributeNode,
+ ismap: htmlAttributeNode,
+ media: htmlAttributeNode,
+ muted: htmlAttributeNode,
+ oncut: htmlAttributeNode,
+ scope: htmlAttributeNode,
+ shape: htmlAttributeNode,
+ sizes: htmlAttributeNode,
+ start: htmlAttributeNode,
+ style: htmlAttributeNode,
+ title: htmlAttributeNode,
+ value: htmlAttributeNode,
+ width: htmlAttributeNode,
+ cols: htmlAttributeNode,
+ high: htmlAttributeNode,
+ href: htmlAttributeNode,
+ kind: htmlAttributeNode,
+ lang: htmlAttributeNode,
+ list: htmlAttributeNode,
+ loop: htmlAttributeNode,
+ name: htmlAttributeNode,
+ open: htmlAttributeNode,
+ rows: htmlAttributeNode,
+ size: htmlAttributeNode,
+ step: htmlAttributeNode,
+ type: htmlAttributeNode,
+ wrap: htmlAttributeNode,
+ alt: htmlAttributeNode,
+ dir: htmlAttributeNode,
+ for: htmlAttributeNode,
+ low: htmlAttributeNode,
+ max: htmlAttributeNode,
+ min: htmlAttributeNode,
+ rel: htmlAttributeNode,
+ src: htmlAttributeNode,
+ id: htmlAttributeNode,
+ lineShiftClickCommand: stumpExtendedAttributeNode,
+ contextMenuCommand: stumpExtendedAttributeNode,
+ doubleClickCommand: stumpExtendedAttributeNode,
+ shiftClickCommand: stumpExtendedAttributeNode,
+ lineClickCommand: stumpExtendedAttributeNode,
+ changeCommand: stumpExtendedAttributeNode,
+ clickCommand: stumpExtendedAttributeNode,
+ blurCommand: stumpExtendedAttributeNode,
+ collapse: stumpExtendedAttributeNode,
+ bern: bernNode,
+ }),
+ [{ regex: /^$/, nodeConstructor: blankLineNode }]
+ )
+ }
+ get htmlTagNameCell() {
+ return this.getWord(0)
+ }
+ get anyHtmlContentCell() {
+ return this.getWordsFrom(1)
+ }
+ getTag() {
+ // we need to remove the "Tag" bit to handle the style and title attribute/tag conflict.
+ const firstWord = this.getFirstWord()
+ const map = {
+ titleTag: "title",
+ styleTag: "style",
+ }
+ return map[firstWord] || firstWord
+ }
+ _getHtmlJoinByCharacter() {
+ return ""
+ }
+ toHtmlWithSuids() {
+ return this._toHtml(undefined, true)
+ }
+ _getOneLiner() {
+ const oneLinerWords = this.getWordsFrom(1)
+ return oneLinerWords.length ? oneLinerWords.join(" ") : ""
+ }
+ getTextContent() {
+ return this._getOneLiner()
+ }
+ shouldCollapse() {
+ return this.has("collapse")
+ }
+ _toHtml(indentCount, withSuid) {
+ const tag = this.getTag()
+ const children = this.map((child) => child._toHtml(indentCount + 1, withSuid)).join("")
+ const attributesStr = this.filter((node) => node.isAttributeNode)
+ .map((child) => child.getAttribute())
+ .join("")
+ const indent = " ".repeat(indentCount)
+ const collapse = this.shouldCollapse()
+ const indentForChildNodes = !collapse && this.getChildInstancesOfNodeTypeId("htmlTagNode").length > 0
+ const suid = withSuid ? ` stumpUid="${this._getUid()}"` : ""
+ const oneLiner = this._getOneLiner()
+ return `${!collapse ? indent : ""}<${tag}${attributesStr}${suid}>${oneLiner}${indentForChildNodes ? "\n" : ""}${children}${tag}>${collapse ? "" : "\n"}`
+ }
+ removeCssStumpNode() {
+ return this.removeStumpNode()
+ }
+ removeStumpNode() {
+ this.getShadow().removeShadow()
+ return this.destroy()
+ }
+ getNodeByGuid(guid) {
+ return this.getTopDownArray().find((node) => node._getUid() === guid)
+ }
+ addClassToStumpNode(className) {
+ const classNode = this.touchNode("class")
+ const words = classNode.getWordsFrom(1)
+ // note: we call add on shadow regardless, because at the moment stump may have gotten out of
+ // sync with shadow, if things modified the dom. todo: cleanup.
+ this.getShadow().addClassToShadow(className)
+ if (words.includes(className)) return this
+ words.push(className)
+ classNode.setContent(words.join(this.getWordBreakSymbol()))
+ return this
+ }
+ removeClassFromStumpNode(className) {
+ const classNode = this.getNode("class")
+ if (!classNode) return this
+ const newClasses = classNode.getWords().filter((word) => word !== className)
+ if (!newClasses.length) classNode.destroy()
+ else classNode.setContent(newClasses.join(" "))
+ this.getShadow().removeClassFromShadow(className)
+ return this
+ }
+ stumpNodeHasClass(className) {
+ const classNode = this.getNode("class")
+ return classNode && classNode.getWords().includes(className) ? true : false
+ }
+ isStumpNodeCheckbox() {
+ return this.get("type") === "checkbox"
+ }
+ getShadow() {
+ if (!this._shadow) {
+ const shadowClass = this.getShadowClass()
+ this._shadow = new shadowClass(this)
+ }
+ return this._shadow
+ }
+ insertCssChildNode(text, index) {
+ return this.insertChildNode(text, index)
+ }
+ insertChildNode(text, index) {
+ const singleNode = new jtree.TreeNode(text).getChildren()[0]
+ const newNode = this.insertLineAndChildren(singleNode.getLine(), singleNode.childrenToString(), index)
+ const stumpNodeIndex = this.getChildInstancesOfNodeTypeId("htmlTagNode").indexOf(newNode)
+ this.getShadow().insertHtmlNode(newNode, stumpNodeIndex)
+ return newNode
+ }
+ isInputType() {
+ return ["input", "textarea"].includes(this.getTag()) || this.get("contenteditable") === "true"
+ }
+ findStumpNodeByChild(line) {
+ return this.findStumpNodesByChild(line)[0]
+ }
+ findStumpNodeByChildString(line) {
+ return this.getTopDownArray().find((node) =>
+ node
+ .map((child) => child.getLine())
+ .join("\n")
+ .includes(line)
+ )
+ }
+ findStumpNodeByFirstWord(firstWord) {
+ return this._findStumpNodesByBase(firstWord)[0]
+ }
+ _findStumpNodesByBase(firstWord) {
+ return this.getTopDownArray().filter((node) => node.doesExtend("htmlTagNode") && node.getFirstWord() === firstWord)
+ }
+ hasLine(line) {
+ return this.getChildren().some((node) => node.getLine() === line)
+ }
+ findStumpNodesByChild(line) {
+ return this.getTopDownArray().filter((node) => node.doesExtend("htmlTagNode") && node.hasLine(line))
+ }
+ findStumpNodesWithClass(className) {
+ return this.getTopDownArray().filter((node) => node.doesExtend("htmlTagNode") && node.has("class") && node.getNode("class").getWords().includes(className))
+ }
+ getShadowClass() {
+ return this.getParent().getShadowClass()
+ }
+ // todo: should not be here
+ getStumpNodeTreeComponent() {
+ return this._treeComponent || this.getParent().getStumpNodeTreeComponent()
+ }
+ // todo: should not be here
+ setStumpNodeTreeComponent(treeComponent) {
+ this._treeComponent = treeComponent
+ return this
+ }
+ setStumpNodeCss(css) {
+ this.getShadow().setShadowCss(css)
+ return this
+ }
+ getStumpNodeCss(prop) {
+ return this.getShadow().getShadowCss(prop)
+ }
+ getStumpNodeAttr(key) {
+ return this.get(key)
+ }
+ setStumpNodeAttr(key, value) {
+ // todo
+ return this
+ }
+ toHtml() {
+ return this._toHtml()
+ }
+ }
+
+ class errorNode extends jtree.GrammarBackedNode {
+ getErrors() {
+ return this._getErrorNodeErrors()
+ }
+ }
+
+ class htmlAttributeNode extends jtree.GrammarBackedNode {
+ createParser() {
+ return new jtree.TreeNode.Parser(errorNode, undefined, undefined)
+ }
+ get htmlAttributeNameCell() {
+ return this.getWord(0)
+ }
+ get attributeValueCell() {
+ return this.getWordsFrom(1)
+ }
+ get isTileAttribute() {
+ return true
+ }
+ get isAttributeNode() {
+ return true
+ }
+ _toHtml() {
+ return ""
+ }
+ getTextContent() {
+ return ""
+ }
+ getAttribute() {
+ return ` ${this.getFirstWord()}="${this.getContent()}"`
+ }
+ }
+
+ class stumpExtendedAttributeNode extends htmlAttributeNode {
+ get stumpExtendedAttributeNameCell() {
+ return this.getWord(0)
+ }
+ }
+
+ class lineOfHtmlContentNode extends jtree.GrammarBackedNode {
+ createParser() {
+ return new jtree.TreeNode.Parser(lineOfHtmlContentNode, undefined, undefined)
+ }
+ get anyHtmlContentCell() {
+ return this.getWordsFrom(0)
+ }
+ get isTileAttribute() {
+ return true
+ }
+ getTextContent() {
+ return this.getLine()
+ }
+ }
+
+ class bernNode extends jtree.GrammarBackedNode {
+ createParser() {
+ return new jtree.TreeNode.Parser(lineOfHtmlContentNode, undefined, undefined)
+ }
+ get bernKeywordCell() {
+ return this.getWord(0)
+ }
+ get isTileAttribute() {
+ return true
+ }
+ _toHtml() {
+ return this.childrenToString()
+ }
+ getTextContent() {
+ return ""
+ }
+ }
+
+ window.stumpNode = stumpNode
- /**
- * Get case-insensitive `field` value.
- *
- * @param {String} field
- * @return {String}
- * @api public
- */
-
-
- ResponseBase.prototype.get = function (field) {
- return this.header[field.toLowerCase()];
- };
- /**
- * Set header related properties:
- *
- * - `.type` the content type without params
- *
- * A response of "Content-Type: text/plain; charset=utf-8"
- * will provide you with a `.type` of "text/plain".
- *
- * @param {Object} header
- * @api private
- */
-
-
- ResponseBase.prototype._setHeaderProperties = function (header) {
- // TODO: moar!
- // TODO: make this a util
- // content-type
- var ct = header['content-type'] || '';
- this.type = utils.type(ct); // params
-
- var params = utils.params(ct);
-
- for (var key in params) {
- if (Object.prototype.hasOwnProperty.call(params, key)) this[key] = params[key];
- }
-
- this.links = {}; // links
-
- try {
- if (header.link) {
- this.links = utils.parseLinks(header.link);
- }
- } catch (err) {// ignore
- }
- };
- /**
- * Set flags such as `.ok` based on `status`.
- *
- * For example a 2xx response will give you a `.ok` of __true__
- * whereas 5xx will be __false__ and `.error` will be __true__. The
- * `.clientError` and `.serverError` are also available to be more
- * specific, and `.statusType` is the class of error ranging from 1..5
- * sometimes useful for mapping respond colors etc.
- *
- * "sugar" properties are also defined for common cases. Currently providing:
- *
- * - .noContent
- * - .badRequest
- * - .unauthorized
- * - .notAcceptable
- * - .notFound
- *
- * @param {Number} status
- * @api private
- */
-
-
- ResponseBase.prototype._setStatusProperties = function (status) {
- var type = status / 100 | 0; // status / class
-
- this.statusCode = status;
- this.status = this.statusCode;
- this.statusType = type; // basics
-
- this.info = type === 1;
- this.ok = type === 2;
- this.redirect = type === 3;
- this.clientError = type === 4;
- this.serverError = type === 5;
- this.error = type === 4 || type === 5 ? this.toError() : false; // sugar
-
- this.created = status === 201;
- this.accepted = status === 202;
- this.noContent = status === 204;
- this.badRequest = status === 400;
- this.unauthorized = status === 401;
- this.notAcceptable = status === 406;
- this.forbidden = status === 403;
- this.notFound = status === 404;
- this.unprocessableEntity = status === 422;
- };
-
- },{"./utils":8}],8:[function(require,module,exports){
- "use strict";
-
- /**
- * Return the mime type for the given `str`.
- *
- * @param {String} str
- * @return {String}
- * @api private
- */
- exports.type = function (str) {
- return str.split(/ *; */).shift();
- };
- /**
- * Return header field parameters.
- *
- * @param {String} str
- * @return {Object}
- * @api private
- */
-
-
- exports.params = function (str) {
- return str.split(/ *; */).reduce(function (obj, str) {
- var parts = str.split(/ *= */);
- var key = parts.shift();
- var val = parts.shift();
- if (key && val) obj[key] = val;
- return obj;
- }, {});
- };
- /**
- * Parse Link header fields.
- *
- * @param {String} str
- * @return {Object}
- * @api private
- */
-
-
- exports.parseLinks = function (str) {
- return str.split(/ *, */).reduce(function (obj, str) {
- var parts = str.split(/ *; */);
- var url = parts[0].slice(1, -1);
- var rel = parts[1].split(/ *= */)[1].slice(1, -1);
- obj[rel] = url;
- return obj;
- }, {});
- };
- /**
- * Strip content related fields from `header`.
- *
- * @param {Object} header
- * @return {Object} header
- * @api private
- */
-
-
- exports.cleanHeader = function (header, changesOrigin) {
- delete header['content-type'];
- delete header['content-length'];
- delete header['transfer-encoding'];
- delete header.host; // secuirty
-
- if (changesOrigin) {
- delete header.authorization;
- delete header.cookie;
- }
-
- return header;
- };
-
- },{}]},{},[5])(5)
- });
- ;
-
- /* mousetrap v1.6.3 craig.is/killing/mice */
- (function(q,u,c){function v(a,b,g){a.addEventListener?a.addEventListener(b,g,!1):a.attachEvent("on"+b,g)}function z(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return n[a.which]?n[a.which]:r[a.which]?r[a.which]:String.fromCharCode(a.which).toLowerCase()}function F(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function w(a){return"shift"==a||"ctrl"==a||"alt"==a||
- "meta"==a}function A(a,b){var g,d=[];var e=a;"+"===e?e=["+"]:(e=e.replace(/\+{2}/g,"+plus"),e=e.split("+"));for(g=0;gc||n.hasOwnProperty(c)&&(p[n[c]]=c)}g=p[e]?"keydown":"keypress"}"keypress"==g&&d.length&&(g="keydown");return{key:m,modifiers:d,action:g}}function D(a,b){return null===a||a===u?!1:a===b?!0:D(a.parentNode,b)}function d(a){function b(a){a=
- a||{};var b=!1,l;for(l in p)a[l]?b=!0:p[l]=0;b||(x=!1)}function g(a,b,t,f,g,d){var l,E=[],h=t.type;if(!k._callbacks[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(l=0;l
- b.target||b.srcElement,c,f)||!1!==a(b,c)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0)}function e(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=z(a);b&&("keyup"==a.type&&y===b?y=!1:k.handleKey(b,F(a),a))}function m(a,g,t,f){function h(c){return function(){x=c;++p[a];clearTimeout(q);q=setTimeout(b,1E3)}}function l(g){c(t,g,a);"keyup"!==f&&(y=z(g));setTimeout(b,10)}for(var d=p[a]=0;d
- A(g[d+1]).action);n(g[d],e,f,a,d)}}function n(a,b,c,f,d){k._directMap[a+":"+c]=b;a=a.replace(/\s+/g," ");var e=a.split(" ");1
- d,e){var f=g(a,d,e),h;d={};var k=0,l=!1;for(h=0;h
- 18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},r={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},C={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},B={option:"alt",command:"meta","return":"enter",
- escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p;for(c=1;20>c;++c)n[111+c]="f"+c;for(c=0;9>=c;++c)n[c+96]=c.toString();d.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};d.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};d.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};d.prototype.reset=function(){this._callbacks={};
- this._directMap={};return this};d.prototype.stopCallback=function(a,b){if(-1<(" "+b.className+" ").indexOf(" mousetrap ")||D(b,this.target))return!1;if("composedPath"in a&&"function"===typeof a.composedPath){var c=a.composedPath()[0];c!==a.target&&(b=c)}return"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};d.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};d.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(n[b]=a[b]);p=null};
- d.init=function(){var a=d(u),b;for(b in a)"_"!==b.charAt(0)&&(d[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))};d.init();q.Mousetrap=d;"undefined"!==typeof module&&module.exports&&(module.exports=d);"function"===typeof define&&define.amd&&define(function(){return d})}})("undefined"!==typeof window?window:null,"undefined"!==typeof window?document:null);
- ;
-
- /*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */
- !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0
+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML=" ",e.querySelectorAll("sallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML=" ";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""," "],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/ -
-
- const sampleCode = \`${sampleCode.toString()}\`
- ${testCode}
- `
- const samplePath = "sample." + this.getExtensionName()
- files[samplePath] = sampleCode.toString()
- files[GrammarBundleFiles.testJs] = `const ${languageName} = require("./index.js")
- /*keep-line*/ const sampleCode = require("fs").readFileSync("${samplePath}", "utf8")
- ${testCode}`
- return files
- }
- getTargetExtension() {
- return this.getRootNodeTypeDefinitionNode().get(GrammarConstants.compilesTo)
- }
- getCellTypeDefinitions() {
- if (!this._cache_cellTypes) this._cache_cellTypes = this._getCellTypeDefinitions()
- return this._cache_cellTypes
- }
- getCellTypeDefinitionById(cellTypeId) {
- // todo: return unknownCellTypeDefinition? or is that handled somewhere else?
- return this.getCellTypeDefinitions()[cellTypeId]
- }
- getNodeTypeFamilyTree() {
- const tree = new TreeNode()
- Object.values(this.getValidConcreteAndAbstractNodeTypeDefinitions()).forEach(node => {
- const path = node.getAncestorNodeTypeIdsArray().join(" ")
- tree.touchNode(path)
- })
- return tree
- }
- _getCellTypeDefinitions() {
- const types = {}
- // todo: add built in word types?
- this.getChildrenByNodeConstructor(cellTypeDefinitionNode).forEach(type => (types[type.getCellTypeId()] = type))
- return types
- }
- getLanguageDefinitionProgram() {
- return this
- }
- getValidConcreteAndAbstractNodeTypeDefinitions() {
- return this.getChildrenByNodeConstructor(nodeTypeDefinitionNode).filter(node => node._hasValidNodeTypeId())
- }
- _getLastRootNodeTypeDefinitionNode() {
- return this.findLast(def => def instanceof AbstractGrammarDefinitionNode && def.has(GrammarConstants.root) && def._hasValidNodeTypeId())
- }
- _initRootNodeTypeDefinitionNode() {
- if (this._cache_rootNodeTypeNode) return
- if (!this._cache_rootNodeTypeNode) this._cache_rootNodeTypeNode = this._getLastRootNodeTypeDefinitionNode()
- // By default, have a very permissive basic root node.
- // todo: whats the best design pattern to use for this sort of thing?
- if (!this._cache_rootNodeTypeNode) {
- this._cache_rootNodeTypeNode = this.concat(`${GrammarConstants.defaultRootNode}
- ${GrammarConstants.root}
- ${GrammarConstants.catchAllNodeType} ${GrammarConstants.BlobNode}`)[0]
- this._addDefaultCatchAllBlobNode()
- }
- }
- getRootNodeTypeDefinitionNode() {
- this._initRootNodeTypeDefinitionNode()
- return this._cache_rootNodeTypeNode
- }
- // todo: whats the best design pattern to use for this sort of thing?
- _addDefaultCatchAllBlobNode() {
- delete this._cache_nodeTypeDefinitions
- this.concat(`${GrammarConstants.BlobNode}
- ${GrammarConstants.baseNodeType} ${GrammarConstants.blobNode}`)
- }
- getExtensionName() {
- return this.getGrammarName()
- }
- getRootNodeTypeId() {
- return this.getRootNodeTypeDefinitionNode().getNodeTypeIdFromDefinition()
- }
- getGrammarName() {
- return this.getRootNodeTypeId().replace(HandGrammarProgram.nodeTypeSuffixRegex, "")
- }
- _getMyInScopeNodeTypeIds() {
- const nodeTypesNode = this.getRootNodeTypeDefinitionNode().getNode(GrammarConstants.inScope)
- return nodeTypesNode ? nodeTypesNode.getWordsFrom(1) : []
- }
- _getInScopeNodeTypeIds() {
- const nodeTypesNode = this.getRootNodeTypeDefinitionNode().getNode(GrammarConstants.inScope)
- return nodeTypesNode ? nodeTypesNode.getWordsFrom(1) : []
- }
- _initProgramNodeTypeDefinitionCache() {
- if (this._cache_nodeTypeDefinitions) return undefined
- this._cache_nodeTypeDefinitions = {}
- this.getChildrenByNodeConstructor(nodeTypeDefinitionNode).forEach(nodeTypeDefinitionNode => {
- this._cache_nodeTypeDefinitions[nodeTypeDefinitionNode.getNodeTypeIdFromDefinition()] = nodeTypeDefinitionNode
- })
- }
- _getProgramNodeTypeDefinitionCache() {
- this._initProgramNodeTypeDefinitionCache()
- return this._cache_nodeTypeDefinitions
- }
- compileAndReturnRootConstructor() {
- if (!this._cache_rootConstructorClass) {
- const def = this.getRootNodeTypeDefinitionNode()
- const rootNodeTypeId = def.getNodeTypeIdFromDefinition()
- this._cache_rootConstructorClass = def.getLanguageDefinitionProgram()._compileAndReturnNodeTypeMap()[rootNodeTypeId]
- }
- return this._cache_rootConstructorClass
- }
- _getFileExtensions() {
- return this.getRootNodeTypeDefinitionNode().get(GrammarConstants.extensions)
- ? this.getRootNodeTypeDefinitionNode()
- .get(GrammarConstants.extensions)
- .split(" ")
- .join(",")
- : this.getExtensionName()
- }
- toNodeJsJavascript(jtreePath = "jtree") {
- return this._rootNodeDefToJavascriptClass(jtreePath, true).trim()
- }
- toBrowserJavascript() {
- return this._rootNodeDefToJavascriptClass("", false).trim()
- }
- _getProperName() {
- return TreeUtils.ucfirst(this.getExtensionName())
- }
- _rootNodeDefToJavascriptClass(jtreePath, forNodeJs = true) {
- const defs = this.getValidConcreteAndAbstractNodeTypeDefinitions()
- // todo: throw if there is no root node defined
- const nodeTypeClasses = defs.map(def => def._nodeDefToJavascriptClass()).join("\n\n")
- const rootDef = this.getRootNodeTypeDefinitionNode()
- const rootNodeJsHeader = forNodeJs && rootDef._getConcatBlockStringFromExtended(GrammarConstants._rootNodeJsHeader)
- const rootName = rootDef._getGeneratedClassName()
- if (!rootName) throw new Error(`Root Node Type Has No Name`)
- let exportScript = ""
- if (forNodeJs) {
- exportScript = `module.exports = ${rootName};
- ${rootName}`
- } else {
- exportScript = `window.${rootName} = ${rootName}`
- }
- // todo: we can expose the previous "constants" export, if needed, via the grammar, which we preserve.
- return `{
- ${forNodeJs ? `const {jtree} = require("${jtreePath}")` : ""}
- ${rootNodeJsHeader ? rootNodeJsHeader : ""}
- ${nodeTypeClasses}
-
- ${exportScript}
- }
- `
- }
- toSublimeSyntaxFile() {
- const cellTypeDefs = this.getCellTypeDefinitions()
- const variables = Object.keys(cellTypeDefs)
- .map(name => ` ${name}: '${cellTypeDefs[name].getRegexString()}'`)
- .join("\n")
- const defs = this.getValidConcreteAndAbstractNodeTypeDefinitions().filter(kw => !kw._isAbstract())
- const nodeTypeContexts = defs.map(def => def._toSublimeMatchBlock()).join("\n\n")
- const includes = defs.map(nodeTypeDef => ` - include: '${nodeTypeDef.getNodeTypeIdFromDefinition()}'`).join("\n")
- return `%YAML 1.2
- name: ${this.getExtensionName()}
- file_extensions: [${this._getFileExtensions()}]
- scope: source.${this.getExtensionName()}
-
- variables:
- ${variables}
-
- contexts:
- main:
- ${includes}
-
- ${nodeTypeContexts}`
- }
- }
- HandGrammarProgram.makeNodeTypeId = str => TreeUtils._replaceNonAlphaNumericCharactersWithCharCodes(str).replace(HandGrammarProgram.nodeTypeSuffixRegex, "") + GrammarConstants.nodeTypeSuffix
- HandGrammarProgram.makeCellTypeId = str => TreeUtils._replaceNonAlphaNumericCharactersWithCharCodes(str).replace(HandGrammarProgram.cellTypeSuffixRegex, "") + GrammarConstants.cellTypeSuffix
- HandGrammarProgram.nodeTypeSuffixRegex = new RegExp(GrammarConstants.nodeTypeSuffix + "$")
- HandGrammarProgram.nodeTypeFullRegex = new RegExp("^[a-zA-Z0-9_]+" + GrammarConstants.nodeTypeSuffix + "$")
- HandGrammarProgram.cellTypeSuffixRegex = new RegExp(GrammarConstants.cellTypeSuffix + "$")
- HandGrammarProgram.cellTypeFullRegex = new RegExp("^[a-zA-Z0-9_]+" + GrammarConstants.cellTypeSuffix + "$")
- HandGrammarProgram._languages = {}
- HandGrammarProgram._nodeTypes = {}
- const PreludeKinds = {}
- PreludeKinds[PreludeCellTypeIds.anyCell] = GrammarAnyCell
- PreludeKinds[PreludeCellTypeIds.keywordCell] = GrammarKeywordCell
- PreludeKinds[PreludeCellTypeIds.floatCell] = GrammarFloatCell
- PreludeKinds[PreludeCellTypeIds.numberCell] = GrammarFloatCell
- PreludeKinds[PreludeCellTypeIds.bitCell] = GrammarBitCell
- PreludeKinds[PreludeCellTypeIds.boolCell] = GrammarBoolCell
- PreludeKinds[PreludeCellTypeIds.intCell] = GrammarIntCell
- window.GrammarConstants = GrammarConstants
- window.PreludeCellTypeIds = PreludeCellTypeIds
- window.HandGrammarProgram = HandGrammarProgram
- window.GrammarBackedNode = GrammarBackedNode
- window.UnknownNodeTypeError = UnknownNodeTypeError
- class Upgrader extends TreeNode {
- upgradeManyInPlace(globPatterns, fromVersion, toVersion) {
- this._upgradeMany(globPatterns, fromVersion, toVersion).forEach(file => file.tree.toDisk(file.path))
- return this
- }
- upgradeManyPreview(globPatterns, fromVersion, toVersion) {
- return this._upgradeMany(globPatterns, fromVersion, toVersion)
- }
- _upgradeMany(globPatterns, fromVersion, toVersion) {
- const glob = this.require("glob")
- const files = TreeUtils.flatten(globPatterns.map(pattern => glob.sync(pattern)))
- console.log(`${files.length} files to upgrade`)
- return files.map(path => {
- console.log("Upgrading " + path)
- return {
- tree: this.upgrade(TreeNode.fromDisk(path), fromVersion, toVersion),
- path: path
- }
- })
- }
- upgrade(code, fromVersion, toVersion) {
- const updateFromMap = this.getUpgradeFromMap()
- const semver = this.require("semver")
- let fromMap
- while ((fromMap = updateFromMap[fromVersion])) {
- const toNextVersion = Object.keys(fromMap)[0] // todo: currently we just assume 1 step at a time
- if (semver.lt(toVersion, toNextVersion)) break
- const fn = Object.values(fromMap)[0]
- code = fn(code)
- fromVersion = toNextVersion
- }
- return code
- }
- }
- window.Upgrader = Upgrader
- class UnknownGrammarProgram extends TreeNode {
- _inferRootNodeForAPrefixLanguage(grammarName) {
- grammarName = HandGrammarProgram.makeNodeTypeId(grammarName)
- const rootNode = new TreeNode(`${grammarName}
- ${GrammarConstants.root}`)
- // note: right now we assume 1 global cellTypeMap and nodeTypeMap per grammar. But we may have scopes in the future?
- const rootNodeNames = this.getFirstWords()
- .filter(identity => identity)
- .map(word => HandGrammarProgram.makeNodeTypeId(word))
- rootNode
- .nodeAt(0)
- .touchNode(GrammarConstants.inScope)
- .setWordsFrom(1, Array.from(new Set(rootNodeNames)))
- return rootNode
- }
- _renameIntegerKeywords(clone) {
- // todo: why are we doing this?
- for (let node of clone.getTopDownArrayIterator()) {
- const firstWordIsAnInteger = !!node.getFirstWord().match(/^\d+$/)
- const parentFirstWord = node.getParent().getFirstWord()
- if (firstWordIsAnInteger && parentFirstWord) node.setFirstWord(HandGrammarProgram.makeNodeTypeId(parentFirstWord + UnknownGrammarProgram._childSuffix))
- }
- }
- _getKeywordMaps(clone) {
- const keywordsToChildKeywords = {}
- const keywordsToNodeInstances = {}
- for (let node of clone.getTopDownArrayIterator()) {
- const firstWord = node.getFirstWord()
- if (!keywordsToChildKeywords[firstWord]) keywordsToChildKeywords[firstWord] = {}
- if (!keywordsToNodeInstances[firstWord]) keywordsToNodeInstances[firstWord] = []
- keywordsToNodeInstances[firstWord].push(node)
- node.forEach(child => {
- keywordsToChildKeywords[firstWord][child.getFirstWord()] = true
- })
- }
- return { keywordsToChildKeywords: keywordsToChildKeywords, keywordsToNodeInstances: keywordsToNodeInstances }
- }
- _inferNodeTypeDef(firstWord, globalCellTypeMap, childFirstWords, instances) {
- const edgeSymbol = this.getEdgeSymbol()
- const nodeTypeId = HandGrammarProgram.makeNodeTypeId(firstWord)
- const nodeDefNode = new TreeNode(nodeTypeId).nodeAt(0)
- const childNodeTypeIds = childFirstWords.map(word => HandGrammarProgram.makeNodeTypeId(word))
- if (childNodeTypeIds.length) nodeDefNode.touchNode(GrammarConstants.inScope).setWordsFrom(1, childNodeTypeIds)
- const cellsForAllInstances = instances
- .map(line => line.getContent())
- .filter(identity => identity)
- .map(line => line.split(edgeSymbol))
- const instanceCellCounts = new Set(cellsForAllInstances.map(cells => cells.length))
- const maxCellsOnLine = Math.max(...Array.from(instanceCellCounts))
- const minCellsOnLine = Math.min(...Array.from(instanceCellCounts))
- let catchAllCellType
- let cellTypeIds = []
- for (let cellIndex = 0; cellIndex < maxCellsOnLine; cellIndex++) {
- const cellType = this._getBestCellType(firstWord, instances.length, maxCellsOnLine, cellsForAllInstances.map(cells => cells[cellIndex]))
- if (!globalCellTypeMap.has(cellType.cellTypeId)) globalCellTypeMap.set(cellType.cellTypeId, cellType.cellTypeDefinition)
- cellTypeIds.push(cellType.cellTypeId)
- }
- if (maxCellsOnLine > minCellsOnLine) {
- //columns = columns.slice(0, min)
- catchAllCellType = cellTypeIds.pop()
- while (cellTypeIds[cellTypeIds.length - 1] === catchAllCellType) {
- cellTypeIds.pop()
- }
- }
- const needsCruxProperty = !firstWord.endsWith(UnknownGrammarProgram._childSuffix + "Node") // todo: cleanup
- if (needsCruxProperty) nodeDefNode.set(GrammarConstants.crux, firstWord)
- if (catchAllCellType) nodeDefNode.set(GrammarConstants.catchAllCellType, catchAllCellType)
- const cellLine = cellTypeIds.slice()
- cellLine.unshift(PreludeCellTypeIds.keywordCell)
- if (cellLine.length > 0) nodeDefNode.set(GrammarConstants.cells, cellLine.join(edgeSymbol))
- //if (!catchAllCellType && cellTypeIds.length === 1) nodeDefNode.set(GrammarConstants.cells, cellTypeIds[0])
- // Todo: add conditional frequencies
- return nodeDefNode.getParent().toString()
- }
- // inferGrammarFileForAnSSVLanguage(grammarName: string): string {
- // grammarName = HandGrammarProgram.makeNodeTypeId(grammarName)
- // const rootNode = new TreeNode(`${grammarName}
- // ${GrammarConstants.root}`)
- // // note: right now we assume 1 global cellTypeMap and nodeTypeMap per grammar. But we may have scopes in the future?
- // const rootNodeNames = this.getFirstWords().map(word => HandGrammarProgram.makeNodeTypeId(word))
- // rootNode
- // .nodeAt(0)
- // .touchNode(GrammarConstants.inScope)
- // .setWordsFrom(1, Array.from(new Set(rootNodeNames)))
- // return rootNode
- // }
- inferGrammarFileForAKeywordLanguage(grammarName) {
- const clone = this.clone()
- this._renameIntegerKeywords(clone)
- const { keywordsToChildKeywords, keywordsToNodeInstances } = this._getKeywordMaps(clone)
- const globalCellTypeMap = new Map()
- globalCellTypeMap.set(PreludeCellTypeIds.keywordCell, undefined)
- const nodeTypeDefs = Object.keys(keywordsToChildKeywords)
- .filter(identity => identity)
- .map(firstWord => this._inferNodeTypeDef(firstWord, globalCellTypeMap, Object.keys(keywordsToChildKeywords[firstWord]), keywordsToNodeInstances[firstWord]))
- const cellTypeDefs = []
- globalCellTypeMap.forEach((def, id) => cellTypeDefs.push(def ? def : id))
- const nodeBreakSymbol = this.getNodeBreakSymbol()
- return this._formatCode([this._inferRootNodeForAPrefixLanguage(grammarName).toString(), cellTypeDefs.join(nodeBreakSymbol), nodeTypeDefs.join(nodeBreakSymbol)].filter(identity => identity).join("\n"))
- }
- _formatCode(code) {
- // todo: make this run in browser too
- if (!this.isNodeJs()) return code
- const grammarProgram = new HandGrammarProgram(TreeNode.fromDisk(__dirname + "/../langs/grammar/grammar.grammar"))
- const programConstructor = grammarProgram.compileAndReturnRootConstructor()
- const program = new programConstructor(code)
- return program.format().toString()
- }
- _getBestCellType(firstWord, instanceCount, maxCellsOnLine, allValues) {
- const asSet = new Set(allValues)
- const edgeSymbol = this.getEdgeSymbol()
- const values = Array.from(asSet).filter(identity => identity)
- const every = fn => {
- for (let index = 0; index < values.length; index++) {
- if (!fn(values[index])) return false
- }
- return true
- }
- if (every(str => str === "0" || str === "1")) return { cellTypeId: PreludeCellTypeIds.bitCell }
- if (
- every(str => {
- const num = parseInt(str)
- if (isNaN(num)) return false
- return num.toString() === str
- })
- ) {
- return { cellTypeId: PreludeCellTypeIds.intCell }
- }
- if (every(str => str.match(/^-?\d*.?\d+$/))) return { cellTypeId: PreludeCellTypeIds.floatCell }
- const bools = new Set(["1", "0", "true", "false", "t", "f", "yes", "no"])
- if (every(str => bools.has(str.toLowerCase()))) return { cellTypeId: PreludeCellTypeIds.boolCell }
- // todo: cleanup
- const enumLimit = 30
- if (instanceCount > 1 && maxCellsOnLine === 1 && allValues.length > asSet.size && asSet.size < enumLimit)
- return {
- cellTypeId: HandGrammarProgram.makeCellTypeId(firstWord),
- cellTypeDefinition: `${HandGrammarProgram.makeCellTypeId(firstWord)}
- enum ${values.join(edgeSymbol)}`
- }
- return { cellTypeId: PreludeCellTypeIds.anyCell }
- }
- }
- UnknownGrammarProgram._childSuffix = "Child"
- window.UnknownGrammarProgram = UnknownGrammarProgram
- // Adapted from https://github.com/NeekSandhu/codemirror-textmate/blob/master/src/tmToCm.ts
- var CmToken
- ;(function(CmToken) {
- CmToken["Atom"] = "atom"
- CmToken["Attribute"] = "attribute"
- CmToken["Bracket"] = "bracket"
- CmToken["Builtin"] = "builtin"
- CmToken["Comment"] = "comment"
- CmToken["Def"] = "def"
- CmToken["Error"] = "error"
- CmToken["Header"] = "header"
- CmToken["HR"] = "hr"
- CmToken["Keyword"] = "keyword"
- CmToken["Link"] = "link"
- CmToken["Meta"] = "meta"
- CmToken["Number"] = "number"
- CmToken["Operator"] = "operator"
- CmToken["Property"] = "property"
- CmToken["Qualifier"] = "qualifier"
- CmToken["Quote"] = "quote"
- CmToken["String"] = "string"
- CmToken["String2"] = "string-2"
- CmToken["Tag"] = "tag"
- CmToken["Type"] = "type"
- CmToken["Variable"] = "variable"
- CmToken["Variable2"] = "variable-2"
- CmToken["Variable3"] = "variable-3"
- })(CmToken || (CmToken = {}))
- const tmToCm = {
- comment: {
- $: CmToken.Comment
- },
- constant: {
- // TODO: Revision
- $: CmToken.Def,
- character: {
- escape: {
- $: CmToken.String2
- }
- },
- language: {
- $: CmToken.Atom
- },
- numeric: {
- $: CmToken.Number
- },
- other: {
- email: {
- link: {
- $: CmToken.Link
- }
- },
- symbol: {
- // TODO: Revision
- $: CmToken.Def
- }
- }
- },
- entity: {
- name: {
- class: {
- $: CmToken.Def
- },
- function: {
- $: CmToken.Def
- },
- tag: {
- $: CmToken.Tag
- },
- type: {
- $: CmToken.Type,
- class: {
- $: CmToken.Variable
- }
- }
- },
- other: {
- "attribute-name": {
- $: CmToken.Attribute
- },
- "inherited-class": {
- // TODO: Revision
- $: CmToken.Def
- }
- },
- support: {
- function: {
- // TODO: Revision
- $: CmToken.Def
- }
- }
- },
- invalid: {
- $: CmToken.Error,
- illegal: { $: CmToken.Error },
- deprecated: {
- $: CmToken.Error
- }
- },
- keyword: {
- $: CmToken.Keyword,
- operator: {
- $: CmToken.Operator
- },
- other: {
- "special-method": CmToken.Def
- }
- },
- punctuation: {
- $: CmToken.Operator,
- definition: {
- comment: {
- $: CmToken.Comment
- },
- tag: {
- $: CmToken.Bracket
- }
- // 'template-expression': {
- // $: CodeMirrorToken.Operator,
- // },
- }
- // terminator: {
- // $: CodeMirrorToken.Operator,
- // },
- },
- storage: {
- $: CmToken.Keyword
- },
- string: {
- $: CmToken.String,
- regexp: {
- $: CmToken.String2
- }
- },
- support: {
- class: {
- $: CmToken.Def
- },
- constant: {
- $: CmToken.Variable2
- },
- function: {
- $: CmToken.Def
- },
- type: {
- $: CmToken.Type
- },
- variable: {
- $: CmToken.Variable2,
- property: {
- $: CmToken.Property
- }
- }
- },
- variable: {
- $: CmToken.Def,
- language: {
- // TODO: Revision
- $: CmToken.Variable3
- },
- other: {
- object: {
- $: CmToken.Variable,
- property: {
- $: CmToken.Property
- }
- },
- property: {
- $: CmToken.Property
- }
- },
- parameter: {
- $: CmToken.Def
- }
- }
- }
- const textMateScopeToCodeMirrorStyle = (scopeSegments, styleTree = tmToCm) => {
- const matchingBranch = styleTree[scopeSegments.shift()]
- return matchingBranch ? textMateScopeToCodeMirrorStyle(scopeSegments, matchingBranch) || matchingBranch.$ || null : null
- }
- class TreeNotationCodeMirrorMode {
- constructor(name, getProgramConstructorFn, getProgramCodeFn, codeMirrorLib = undefined) {
- this._name = name
- this._getProgramConstructorFn = getProgramConstructorFn
- this._getProgramCodeFn = getProgramCodeFn || (instance => (instance ? instance.getValue() : this._originalValue))
- this._codeMirrorLib = codeMirrorLib
- }
- _getParsedProgram() {
- const source = this._getProgramCodeFn(this._cmInstance) || ""
- if (!this._cachedProgram || this._cachedSource !== source) {
- this._cachedSource = source
- this._cachedProgram = new (this._getProgramConstructorFn())(source)
- }
- return this._cachedProgram
- }
- _getExcludedIntelliSenseTriggerKeys() {
- return {
- "8": "backspace",
- "9": "tab",
- "13": "enter",
- "16": "shift",
- "17": "ctrl",
- "18": "alt",
- "19": "pause",
- "20": "capslock",
- "27": "escape",
- "33": "pageup",
- "34": "pagedown",
- "35": "end",
- "36": "home",
- "37": "left",
- "38": "up",
- "39": "right",
- "40": "down",
- "45": "insert",
- "46": "delete",
- "91": "left window key",
- "92": "right window key",
- "93": "select",
- "112": "f1",
- "113": "f2",
- "114": "f3",
- "115": "f4",
- "116": "f5",
- "117": "f6",
- "118": "f7",
- "119": "f8",
- "120": "f9",
- "121": "f10",
- "122": "f11",
- "123": "f12",
- "144": "numlock",
- "145": "scrolllock"
- }
- }
- token(stream, state) {
- return this._advanceStreamAndReturnTokenType(stream, state)
- }
- fromTextAreaWithAutocomplete(area, options) {
- this._originalValue = area.value
- const defaultOptions = {
- lineNumbers: true,
- mode: this._name,
- tabSize: 1,
- indentUnit: 1,
- hintOptions: {
- hint: (cmInstance, options) => this.codeMirrorAutocomplete(cmInstance, options)
- }
- }
- Object.assign(defaultOptions, options)
- this._cmInstance = this._getCodeMirrorLib().fromTextArea(area, defaultOptions)
- this._enableAutoComplete(this._cmInstance)
- return this._cmInstance
- }
- _enableAutoComplete(cmInstance) {
- const excludedKeys = this._getExcludedIntelliSenseTriggerKeys()
- const codeMirrorLib = this._getCodeMirrorLib()
- cmInstance.on("keyup", (cm, event) => {
- // https://stackoverflow.com/questions/13744176/codemirror-autocomplete-after-any-keyup
- if (!cm.state.completionActive && !excludedKeys[event.keyCode.toString()])
- // Todo: get typings for CM autocomplete
- codeMirrorLib.commands.autocomplete(cm, null, { completeSingle: false })
- })
- }
- _getCodeMirrorLib() {
- return this._codeMirrorLib
- }
- async codeMirrorAutocomplete(cmInstance, options) {
- const cursor = cmInstance.getDoc().getCursor()
- const codeMirrorLib = this._getCodeMirrorLib()
- const result = await this._getParsedProgram().getAutocompleteResultsAt(cursor.line, cursor.ch)
- // It seems to be better UX if there's only 1 result, and its the word the user entered, to close autocomplete
- if (result.matches.length === 1 && result.matches[0].text === result.word) return null
- return result.matches.length
- ? {
- list: result.matches,
- from: codeMirrorLib.Pos(cursor.line, result.startCharIndex),
- to: codeMirrorLib.Pos(cursor.line, result.endCharIndex)
- }
- : null
- }
- register() {
- const codeMirrorLib = this._getCodeMirrorLib()
- codeMirrorLib.defineMode(this._name, () => this)
- codeMirrorLib.defineMIME("text/" + this._name, this._name)
- return this
- }
- _advanceStreamAndReturnTokenType(stream, state) {
- let nextCharacter = stream.next()
- const lineNumber = stream.lineOracle.line + 1 // state.lineIndex
- const WordBreakSymbol = " "
- const NodeBreakSymbol = "\n"
- while (typeof nextCharacter === "string") {
- const peek = stream.peek()
- if (nextCharacter === WordBreakSymbol) {
- if (peek === undefined || peek === NodeBreakSymbol) {
- stream.skipToEnd() // advance string to end
- this._incrementLine(state)
- }
- if (peek === WordBreakSymbol && state.cellIndex) {
- // If we are missing a cell.
- // TODO: this is broken for a blank 1st cell. We need to track WordBreakSymbol level.
- state.cellIndex++
- }
- return "bracket"
- }
- if (peek === WordBreakSymbol) {
- state.cellIndex++
- return this._getCellStyle(lineNumber, state.cellIndex)
- }
- nextCharacter = stream.next()
- }
- state.cellIndex++
- const style = this._getCellStyle(lineNumber, state.cellIndex)
- this._incrementLine(state)
- return style
- }
- _getCellStyle(lineIndex, cellIndex) {
- const program = this._getParsedProgram()
- // todo: if the current word is an error, don't show red?
- if (!program.getCellHighlightScopeAtPosition) console.log(program)
- const highlightScope = program.getCellHighlightScopeAtPosition(lineIndex, cellIndex)
- const style = highlightScope ? textMateScopeToCodeMirrorStyle(highlightScope.split(".")) : undefined
- return style || "noHighlightScopeDefinedInGrammar"
- }
- // todo: remove.
- startState() {
- return {
- cellIndex: 0
- }
- }
- _incrementLine(state) {
- state.cellIndex = 0
- }
- }
- window.TreeNotationCodeMirrorMode = TreeNotationCodeMirrorMode
- class jtree {}
- jtree.GrammarBackedNode = GrammarBackedNode
- jtree.GrammarConstants = GrammarConstants
- jtree.Utils = TreeUtils
- jtree.UnknownNodeTypeError = UnknownNodeTypeError
- jtree.TestRacer = TestRacer
- jtree.TreeEvents = TreeEvents
- jtree.TreeNode = TreeNode
- jtree.ExtendibleTreeNode = ExtendibleTreeNode
- jtree.HandGrammarProgram = HandGrammarProgram
- jtree.UnknownGrammarProgram = UnknownGrammarProgram
- jtree.TreeNotationCodeMirrorMode = TreeNotationCodeMirrorMode
- jtree.getVersion = () => TreeNode.getVersion()
- window.jtree = jtree
- ;
-
- //onsave jtree build produce jtable.browser.js
- //onsave jtree build produce jtable.node.js
- // todo: create a Tree Language for number formatting
- // https://github.com/gentooboontoo/js-quantities
- // https://github.com/moment/moment/issues/2469
- // todo: ugly. how do we ditch this or test?
- if (typeof moment !== "undefined")
- moment.createFromInputFallback = function(momentConfig) {
- momentConfig._d = new Date(momentConfig._i)
- }
- var VegaTypes
- ;(function(VegaTypes) {
- VegaTypes["nominal"] = "nominal"
- VegaTypes["ordinal"] = "ordinal"
- VegaTypes["geojson"] = "geojson"
- VegaTypes["quantitative"] = "quantitative"
- VegaTypes["temporal"] = "temporal"
- })(VegaTypes || (VegaTypes = {}))
- var JavascriptNativeTypeNames
- ;(function(JavascriptNativeTypeNames) {
- JavascriptNativeTypeNames["number"] = "number"
- JavascriptNativeTypeNames["string"] = "string"
- JavascriptNativeTypeNames["Date"] = "Date"
- JavascriptNativeTypeNames["boolean"] = "boolean"
- })(JavascriptNativeTypeNames || (JavascriptNativeTypeNames = {}))
- class AbstractPrimitiveType {
- constructor(typeName) {
- this._name = typeName
- }
- getPrimitiveTypeName() {
- return this._name
- }
- // Abstract methods:
- toDisplayString(value, format) {
- return value
- }
- getDefaultFormat(columnName, sample) {
- return ""
- }
- getProbForColumnSpecimen(value) {
- return 0
- }
- isInvalidValue(value) {
- if (value === undefined || value === "") return true
- return false
- }
- }
- class BooleanType extends AbstractPrimitiveType {
- getAsNativeJavascriptType(val) {
- // todo: handle false, etc
- return val ? 1 : 0
- }
- synthesizeValue(randomNumberFn) {
- return Math.round(randomNumberFn())
- }
- getJavascriptTypeName() {
- return JavascriptNativeTypeNames.boolean
- }
- fromStringToNumeric(val) {
- return val.toString() === "true" ? 1 : 0
- }
- getStringExamples() {
- return ["true"]
- }
- getVegaType() {
- return VegaTypes.nominal
- }
- isNumeric() {
- return false
- }
- isString() {
- return false
- }
- isTemporal() {
- return false
- }
- }
- class AbstractNumeric extends AbstractPrimitiveType {
- fromStringToNumeric(value) {
- return parseFloat(value)
- }
- synthesizeValue(randomNumberFn) {
- // todo: min/max etc
- return this.getMin() + Math.floor((this.getMax() - this.getMin()) * randomNumberFn())
- }
- getMax() {
- return 100
- }
- getMin() {
- return 0
- }
- getAsNativeJavascriptType(val) {
- if (val === undefined) return NaN
- const valType = typeof val
- if (valType === "string") return this.fromStringToNumeric(val)
- else if (val instanceof Date) return Math.round(val.getDate() / 1000)
- // Is a number
- return val
- }
- getJavascriptTypeName() {
- return JavascriptNativeTypeNames.number
- }
- getVegaType() {
- return VegaTypes.quantitative
- }
- isString() {
- return false
- }
- isTemporal() {
- return false
- }
- isNumeric() {
- return true
- }
- isInvalidValue(value) {
- return super.isInvalidValue(value) || isNaN(value)
- }
- }
- class IntType extends AbstractNumeric {
- fromStringToNumeric(val) {
- return parseInt(val)
- }
- getStringExamples() {
- return ["30"]
- }
- getVegaType() {
- return VegaTypes.quantitative
- }
- isNumeric() {
- return true
- }
- isString() {
- return false
- }
- isTemporal() {
- return false
- }
- }
- class Feet extends AbstractNumeric {
- getProbForColumnSpecimen(sample) {
- return isNaN(Feet.feetToInches(sample)) ? 0 : 1
- }
- fromStringToNumeric(val) {
- return Feet.feetToInches(val)
- }
- toDisplayString(value, format) {
- value = parseFloat(value)
- const inches = Math.round(value % 12)
- const feet = Math.floor(value / 12)
- return `${feet}'${inches}"`
- }
- getStringExamples() {
- return ["5'10\""]
- }
- // Return inches given formats like 6'1 6'2"
- static feetToInches(numStr) {
- let result = 0
- const indexOfDelimited = numStr.search(/[^0-9\.]/)
- if (indexOfDelimited < 1) {
- result = parseFloat(numStr.replace(/[^0-9\.]/g, ""))
- return isNaN(result) ? result : result * 12
- }
- const feetPart = parseFloat(numStr.substr(0, indexOfDelimited).replace(/[^0-9\.]/g, ""))
- const inchesPart = parseFloat(numStr.substr(indexOfDelimited).replace(/[^0-9\.]/g, ""))
- if (!isNaN(feetPart)) result += feetPart * 12
- if (!isNaN(inchesPart)) result += inchesPart
- return result
- }
- }
- class AbstractCurrency extends AbstractNumeric {}
- class USD extends AbstractCurrency {
- toDisplayString(value, format) {
- return format ? d3format.format(format)(value) : value
- }
- fromStringToNumeric(value) {
- return parseFloat(value.toString().replace(/[\$\, \%]/g, ""))
- }
- getProbForColumnSpecimen(sample) {
- return sample && sample.match && !!sample.match(/^\$[0-9\.\,]+$/) ? 1 : 0
- }
- getDefaultFormat() {
- return "($.2f"
- }
- getStringExamples() {
- return ["$2.22"]
- }
- }
- class NumberCol extends AbstractNumeric {
- // https://github.com/d3/d3-format
- toDisplayString(value, format) {
- if (format === "percent") return d3format.format("(.2f")(parseFloat(value)) + "%"
- // Need the isNan bc numeral will throw otherwise
- if (format && !isNaN(value) && value !== Infinity) return d3format.format(format)(value)
- return value
- }
- getDefaultFormat(columnName, sample) {
- if (columnName.match(/^(mile|pound|inch|feet)s?$/i)) return "(.1f"
- if (columnName.match(/^(calorie|steps)s?$/i)) return ","
- if (sample && !sample.toString().includes(".")) return ","
- }
- getStringExamples() {
- return ["2.22"]
- }
- }
- class NumberString extends AbstractNumeric {
- toDisplayString(value, format) {
- return format ? d3format.format(format)(value) : value
- }
- getDefaultFormat() {
- return ","
- }
- fromStringToNumeric(str) {
- return parseFloat(str.toString().replace(/[\$\, \%]/g, ""))
- }
- getStringExamples() {
- return ["2,000"]
- }
- }
- class ObjectType extends AbstractPrimitiveType {
- getAsNativeJavascriptType(val) {
- return val === undefined ? "" : val.toString()
- }
- // todo: not sure about this.
- getStringExamples() {
- return ["{score: 10}"]
- }
- synthesizeValue() {
- return {}
- }
- fromStringToNumeric() {
- return undefined
- }
- getJavascriptTypeName() {
- return JavascriptNativeTypeNames.string
- }
- getVegaType() {
- return VegaTypes.nominal
- }
- isNumeric() {
- return false
- }
- isString() {
- return false
- }
- isTemporal() {
- return false
- }
- }
- class AbstractStringCol extends AbstractPrimitiveType {
- isString() {
- return true
- }
- isNumeric() {
- return false
- }
- getStringExamples() {
- return ["Anything"]
- }
- getVegaType() {
- return VegaTypes.nominal
- }
- synthesizeValue() {
- return "randomString"
- }
- getJavascriptTypeName() {
- return JavascriptNativeTypeNames.string
- }
- fromStringToNumeric() {
- return undefined
- }
- isTemporal() {
- return false
- }
- getAsNativeJavascriptType(val) {
- return val === undefined ? "" : val.toString()
- }
- }
- class StringCol extends AbstractStringCol {}
- class UrlCol extends AbstractStringCol {
- getStringExamples() {
- return ["www.foo.com"]
- }
- }
- class TextCol extends AbstractStringCol {}
- class AbstractCodeCol extends AbstractStringCol {}
- class CodeCol extends AbstractCodeCol {
- getStringExamples() {
- return ["i++"]
- }
- }
- class HTMLCol extends AbstractCodeCol {
- getStringExamples() {
- return ["hi "]
- }
- }
- class AbstractPathCol extends AbstractStringCol {}
- // filepath
- class PathCol extends AbstractPathCol {}
- // Directory
- class DirCol extends AbstractPathCol {}
- class AbstractTemporal extends AbstractPrimitiveType {
- _fromStringToDate(value) {
- return moment(parseInt(value)).toDate()
- }
- getAsNativeJavascriptType(val) {
- if (val === undefined) return undefined
- const valType = typeof val
- if (valType === "string") return this._fromStringToDate(val)
- else if (val instanceof Date) return val
- return this._fromNumericToDate(val)
- }
- fromDateToNumeric(date) {
- return moment(date).unix()
- }
- getJavascriptTypeName() {
- return JavascriptNativeTypeNames.Date
- }
- synthesizeValue() {
- return new Date()
- }
- isNumeric() {
- return true
- }
- isString() {
- return false
- }
- isTemporal() {
- return true
- }
- getVegaType() {
- return VegaTypes.temporal
- }
- getVegaTimeUnit() {
- return undefined
- }
- _fromNumericToDate(value) {
- return moment(value).toDate()
- }
- }
- class DateCol extends AbstractTemporal {
- toDisplayString(value, format) {
- if (!format) format = "MM/DD/YY"
- if (format === "fromNow") return moment(parseFloat(value)).fromNow()
- // todo: make sure we are working with numeric values?
- return moment(value).format(format)
- }
- fromStringToNumeric(value) {
- return DateCol.getDate(value).unix() * 1000
- }
- _fromStringToDate(value) {
- return DateCol.getDate(value).toDate()
- }
- getProbForColumnSpecimen(value) {
- const isValid = DateCol.getDate(value).isValid()
- return isValid
- }
- getStringExamples() {
- return ["01/01/01"]
- }
- static getDateAsUnixUtx(value) {
- return this.getDate(value, moment.utc).unix()
- }
- static getDate(value, momentFn = moment) {
- let result = momentFn(value)
- if (result.isValid()) return result
- if (typeof value === "string" && value.match(/^[0-9]{8}$/)) {
- const first2 = parseInt(value.substr(0, 2))
- const second2 = parseInt(value.substr(2, 2))
- const third2 = parseInt(value.substr(4, 2))
- const last2 = parseInt(value.substr(6, 2))
- const first4 = parseInt(value.substr(0, 4))
- const last4 = parseInt(value.substr(4, 4))
- const first2couldBeDay = first2 < 32
- const first2couldBeMonth = first2 < 13
- const second2couldBeDay = second2 < 32
- const second2couldBeMonth = second2 < 13
- const third2couldBeDay = third2 < 32
- const third2couldBeMonth = third2 < 13
- const last2couldBeDay = last2 < 32
- const last2couldBeMonth = last2 < 13
- const last4looksLikeAYear = last4 > 1000 && last4 < 2100
- const first4looksLikeAYear = first4 > 1000 && first4 < 2100
- // MMDDYYYY
- // YYYYMMDD
- // Prioritize the above 2 american versions
- // YYYYDDMM
- // DDMMYYYY
- if (first2couldBeMonth && second2couldBeDay && last4looksLikeAYear) result = momentFn(value, "MMDDYYYY")
- else if (first4looksLikeAYear && third2couldBeMonth && last2couldBeDay) result = momentFn(value, "YYYYMMDD")
- else if (first4looksLikeAYear && last2couldBeMonth) result = momentFn(value, "YYYYDDMM")
- else result = momentFn(value, "DDMMYYYY")
- return result
- } else if (typeof value === "string" && value.match(/^[0-9]{2}\/[0-9]{4}$/))
- // MM/YYYY
- return momentFn(value, "MM/YYYY")
- // Check if timestamp
- if (value.match && !value.match(/[^0-9]/)) {
- const num = parseFloat(value)
- if (!isNaN(num)) {
- if (value.length === 10) return momentFn(num * 1000)
- else return momentFn(num)
- }
- }
- // Okay to return an invalid result if we dont find a match
- // todo: why??? should we return "" instead ?
- return result
- }
- }
- // Beginning of day
- class Day extends AbstractTemporal {
- toDisplayString(value, format) {
- return moment(value).format(format || "MM/DD/YYYY")
- }
- fromStringToNumeric(value) {
- return (
- DateCol.getDate(value)
- .startOf("day")
- .unix() * 1000
- )
- }
- getVegaTimeUnit() {
- return undefined
- }
- _fromStringToDate(value) {
- return DateCol.getDate(value)
- .startOf("day")
- .toDate()
- }
- fromDateToNumeric(date) {
- return (
- moment(date)
- .startOf("day")
- .unix() * 1000
- )
- }
- getStringExamples() {
- return ["01/01/01"]
- }
- getProbForColumnSpecimen(sample) {
- const format = moment.parseFormat ? moment.parseFormat(sample) : parseFormat
- return format === "MM/DD/YY" || format === "MM/DD/YYYY" || format === "M/D/YYYY" ? 1 : 0
- }
- }
- class HourMinute extends AbstractTemporal {
- // todo: is this correct? I dont think so.
- fromStringToNumeric(value) {
- return parseFloat(DateCol.getDate(value).format("H.m"))
- }
- getVegaTimeUnit() {
- return "hoursminutes"
- }
- // todo: is this correct? I dont think so.
- getStringExamples() {
- return ["2:30"]
- }
- }
- class Minute extends AbstractTemporal {
- toDisplayString(value, format) {
- return moment(value).format("m")
- }
- fromStringToNumeric(value) {
- return (
- DateCol.getDate(value)
- .startOf("minute")
- .unix() * 1000
- )
- }
- getVegaTimeUnit() {
- return "minutes"
- }
- getStringExamples() {
- return ["30"]
- }
- }
- class AbstractTemporalInt extends AbstractTemporal {
- fromStringToNumeric(val) {
- return parseInt(val)
- }
- // getAsNativeJavascriptType(val: any): number {
- // const result = super.getAsNativeJavascriptType(val)
- // return result === undefined ? undefined : this.fromDateToNumeric(result)
- // }
- getStringExamples() {
- return ["30"]
- }
- isNumeric() {
- return true
- }
- isString() {
- return false
- }
- fromDateToNumeric(date) {
- return moment(date).unix()
- }
- _fromStringToDate(val) {
- return moment(parseFloat(val)).toDate()
- }
- _fromNumericToDate(value) {
- return moment(value).toDate()
- }
- getVegaTimeUnit() {
- return "seconds"
- }
- }
- class Week extends AbstractTemporalInt {
- toDisplayString(value, format) {
- return moment(value).format("MM/DD/YYYY - WW")
- }
- fromDateToNumeric(date) {
- return (
- moment(date)
- .startOf("week")
- .unix() * 1000
- )
- }
- getVegaTimeUnit() {
- return "quartermonth"
- }
- }
- class Month extends AbstractTemporalInt {
- toDisplayString(value, format) {
- return moment(value).format(format || "MMMM")
- }
- fromDateToNumeric(date) {
- return (
- moment(date)
- .startOf("month")
- .unix() * 1000
- )
- }
- getVegaTimeUnit() {
- return "month"
- }
- }
- class MonthDay extends AbstractTemporalInt {
- toDisplayString(value, format) {
- return moment(value).format(format || "MMDD")
- }
- fromDateToNumeric(date) {
- return moment(date).unix() * 1000
- }
- getVegaTimeUnit() {
- return "monthdate"
- }
- }
- class Hour extends AbstractTemporalInt {
- fromDateToNumeric(date) {
- return parseInt(
- moment(date)
- .startOf("hour")
- .format("H")
- )
- }
- getVegaTimeUnit() {
- return "hours"
- }
- }
- class Year extends AbstractTemporalInt {
- fromDateToNumeric(date) {
- return parseInt(moment(date).format("YYYY"))
- }
- _fromStringToDate(val) {
- return moment(parseFloat(val), "YYYY").toDate()
- }
- toDisplayString(value, format) {
- return moment(value).format(format || "YYYY")
- }
- getVegaTimeUnit() {
- return "year"
- }
- _fromNumericToDate(value) {
- return moment(value, "YYYY").toDate()
- }
- isTemporal() {
- return true
- }
- }
- class AbstractMillisecond extends AbstractTemporalInt {
- toDisplayString(value, format) {
- if (format === "fromNow") return moment(parseFloat(value)).fromNow()
- return value
- }
- isTemporal() {
- return true
- }
- getDefaultFormat() {
- return "fromNow"
- }
- }
- class MilliSecond extends AbstractMillisecond {
- getVegaTimeUnit() {
- return "milliseconds"
- }
- }
- class Second extends AbstractMillisecond {
- fromStringToNumeric(value) {
- return parseInt(value) * 1000
- }
- getVegaTimeUnit() {
- return "seconds"
- }
- _fromNumericToDate(number) {
- return moment(number * 1000).toDate()
- }
- toDisplayString(value, format) {
- if (format === "fromNow") return moment(parseFloat(value) * 1000).fromNow()
- return value
- }
- }
- // todo: ADD TYPINGS
- class Column {
- constructor(colDef = {}, rawAnyVector) {
- this._colDefObject = colDef
- this._rawAnyVectorFromSource = rawAnyVector
- this._sampleSet = jtree.Utils.sampleWithoutReplacement(rawAnyVector, 30, Date.now())
- }
- static _getPrimitiveTypesCollection() {
- if (!this._colTypes)
- this._colTypes = {
- millisecond: new MilliSecond("millisecond"),
- second: new Second("second"),
- date: new DateCol("date"),
- day: new Day("day"),
- week: new Week("week"),
- month: new Month("month"),
- monthDay: new MonthDay("monthDay"),
- hour: new Hour("hour"),
- hourMinute: new HourMinute("hourMinute"),
- minute: new Minute("minute"),
- year: new Year("year"),
- feet: new Feet("feet"),
- usd: new USD("usd"),
- number: new NumberCol("number"),
- numberString: new NumberString("numberString"),
- string: new StringCol("string"),
- text: new TextCol("text"),
- path: new PathCol("path"),
- dir: new DirCol("dir"),
- code: new CodeCol("code"),
- html: new HTMLCol("html"),
- url: new UrlCol("url"),
- object: new ObjectType("object"),
- boolean: new BooleanType("boolean"),
- int: new IntType("int")
- }
- return this._colTypes
- }
- static getPrimitiveTypeByName(name) {
- return this._getPrimitiveTypesCollection()[name]
- }
- getMathFn() {
- return this._colDefObject.mathFn
- }
- getColumnName() {
- return this._getColDefObject().name
- }
- _getSourceColumnName() {
- return this._colDefObject.source
- }
- isInvalidValue(value) {
- return this.getPrimitiveTypeObj().isInvalidValue(value)
- }
- _getFirstNonEmptyValueFromSampleSet() {
- if (this._sample === undefined) {
- const sampleSet = this._getSampleSet()
- this._sample = sampleSet.length ? sampleSet.find(value => !jtree.Utils.isValueEmpty(value)) : ""
- }
- return this._sample
- }
- _getColDefObject() {
- return this._colDefObject
- }
- getPrimitiveTypeObj() {
- if (!this._type) this._type = this._inferType()
- return this._type
- }
- synthesizeValue(randomNumberFn) {
- return this.getPrimitiveTypeObj().synthesizeValue(randomNumberFn)
- }
- isTemporal() {
- return this.getPrimitiveTypeObj().isTemporal()
- }
- toDisplayString(value) {
- return this.getPrimitiveTypeObj().toDisplayString(value, this.getFormat())
- }
- isString() {
- return this.getPrimitiveTypeObj().isString()
- }
- isNumeric() {
- return this.getPrimitiveTypeObj().isNumeric()
- }
- isHash() {
- return this.isString() && false // todo: make this work. identify random hashes, et cetera.
- }
- // todo: isEnum/isSet
- // todo: isUniqueTimestamp
- getEntropy() {
- if (this._entropy !== undefined) return this._entropy
- const possibilities = {}
- let bits = 1
- const name = this.getColumnName()
- this._getSampleSet().forEach(val => {
- if (possibilities[val]) return
- bits++
- possibilities[val] = true
- })
- this._entropy = bits
- return this._entropy
- }
- isLink() {
- if (this._isLink !== undefined) return this._isLink
- const sample = this._getFirstNonEmptyValueFromSampleSet()
- if (!this.isString() || !sample || !sample.match) this._isLink = false
- else this._isLink = sample.match(/^(https?\:|\/)/) ? true : false
- return this._isLink
- }
- _getSampleSet() {
- return this._sampleSet
- }
- isUnique() {
- return this.getEntropy() - 1 === this._getSampleSet().length
- }
- getTitlePotential() {
- if (this._getColDefObject().title) return 1
- if (this._titlePotential !== undefined) return this._titlePotential
- const titleCols = {
- title: 0.99,
- name: 0.98,
- label: 0.97,
- category: 0.96
- }
- const lowerCaseName = this.getColumnName().toLowerCase()
- if (titleCols[lowerCaseName]) this._titlePotential = titleCols[lowerCaseName]
- else if (this.getEstimatedTextLength() > 150) this._titlePotential = 0.01
- else if (this.isString() && !this.isLink() && this.isUnique() && !this.isHash()) this._titlePotential = 0.75
- else this._titlePotential = 0
- return this._titlePotential
- }
- getVegaType() {
- return this.getPrimitiveTypeObj().getVegaType()
- }
- getVegaTimeUnit() {
- const type = this.getPrimitiveTypeObj()
- return type.getVegaTimeUnit ? type.getVegaTimeUnit() : undefined
- }
- getEstimatedTextLength() {
- if (!this.isString()) return 0
- if (this._estimatedTextLength !== undefined) return this._estimatedTextLength
- const name = this.getColumnName()
- const sampleSet = this._getSampleSet()
- const sum = sampleSet.map(val => val && val.length).reduce((rowLength, cumulative) => rowLength + cumulative, 0)
- this._estimatedTextLength = Math.floor(sum / sampleSet.length)
- return this._estimatedTextLength
- }
- getFormat() {
- if (this._getColDefObject().format) return this._getColDefObject().format
- return this.getPrimitiveTypeObj().getDefaultFormat(this.getColumnName(), this._getFirstNonEmptyValueFromSampleSet())
- }
- getBlankPercentage() {
- let blankCount = 0
- let mistypedCount = 0 // todo.
- const colName = this.getColumnName()
- const sampleSet = this._getSampleSet()
- sampleSet.forEach(value => {
- if (value === undefined || value === "") blankCount++
- // todo: add mistyped data
- })
- return blankCount / (sampleSet.length || 1)
- }
- getMap() {
- const map = this._map
- if (map) return map
- this._map = this._getSummaryVector().map
- return this._map
- }
- getValues() {
- return this._getSummaryVector().values
- }
- _getSummaryVector() {
- if (!this._summaryVector) this._summaryVector = this._createSummaryVector()
- return this._summaryVector
- }
- _getRawAnyVectorFromSource() {
- return this._rawAnyVectorFromSource
- }
- _createSummaryVector() {
- const values = []
- const map = new Map()
- let incompleteCount = 0
- let uniques = 0
- let index = 0
- let rawVector = this._getRawAnyVectorFromSource()
- // If needs conversion.
- // todo: add tests
- const primitiveType = this.getPrimitiveTypeObj()
- if (primitiveType.isNumeric() && typeof rawVector[0] === "string") rawVector = rawVector.map(primitiveType.fromStringToNumeric)
- rawVector.forEach(val => {
- if (this.isInvalidValue(val)) {
- incompleteCount++
- return true
- }
- if (!map.has(val)) {
- map.set(val, { count: 0, index: index })
- uniques++
- } else map.get(val).count++
- values.push(val)
- })
- return {
- map: map,
- values: values,
- incompleteCount: incompleteCount,
- uniqueValues: uniques
- }
- }
- getQuins() {
- const deciles = this.getReductions().deciles
- return [20, 40, 60, 80, 100].map(decile => {
- return {
- value: deciles[decile],
- percent: decile / 100
- }
- })
- }
- getPrimitiveTypeName() {
- return this.getPrimitiveTypeObj().getPrimitiveTypeName()
- }
- toObject() {
- return {
- name: this.getColumnName(),
- type: this.getPrimitiveTypeName(),
- vegaType: this.getVegaType(),
- vegaTimeUnit: this.getVegaTimeUnit(),
- reduction: this._getColDefObject().reduction,
- titlePotential: this.getTitlePotential(),
- isString: this.isString(),
- isTemporal: this.isTemporal(),
- isLink: this.isLink(),
- estimatedTextLength: this.getEstimatedTextLength()
- }
- }
- getReductions() {
- if (!this._reductions) this._reductions = this._getReductionResult(this._getSummaryVector(), this)
- return this._reductions
- }
- getMax() {
- return this.getReductions().max
- }
- getMin() {
- return this.getReductions().min
- }
- getMean() {
- return this.getReductions().mean
- }
- _getReductionResult(valuesObj, col) {
- const values = valuesObj.values
- const count = values.length
- const reductionResult = {}
- reductionResult.incompleteCount = valuesObj.incompleteCount
- reductionResult.uniqueValues = valuesObj.uniqueValues
- if (!count) return reductionResult
- const numericCompare = (av, bv) => (av > bv ? 1 : av < bv ? -1 : 0)
- const arr = values.slice()
- col.isString() ? arr.sort() : arr.sort(numericCompare)
- let min = arr[0]
- let max = arr[0]
- let sum = 0
- let mode = undefined
- let modeSize = 0
- let currentBucketValue = undefined
- let currentBucketSize = 0
- for (let index = 0; index < count; index++) {
- let value = arr[index]
- sum += value
- if (value > max) max = value
- if (value < min) min = value
- if (value === currentBucketValue) currentBucketSize++
- else {
- currentBucketValue = value
- currentBucketSize = 1
- }
- if (currentBucketSize > modeSize) {
- modeSize = currentBucketSize
- mode = currentBucketValue
- }
- }
- const medianIndex = Math.floor(count / 2)
- reductionResult.count = count
- reductionResult.sum = sum
- reductionResult.median = arredianIndex]
- reductionResult.mean = sum / count
- reductionResult.min = min
- reductionResult.max = max
- reductionResult.range = max - min
- reductionResult.mode = mode
- reductionResult.modeSize = modeSize
- if (col.isString()) {
- reductionResult.sum = undefined
- reductionResult.mean = undefined
- } else if (col.isTemporal()) reductionResult.sum = undefined
- reductionResult.deciles = {}
- const deciles = [10, 20, 30, 40, 50, 60, 70, 80, 90, 99, 100]
- deciles.forEach(decile => {
- let index = Math.floor(count * (decile / 100))
- index = index === count ? index - 1 : index
- reductionResult.deciles[decile] = arr[index]
- })
- return reductionResult
- }
- static _getColumnProbabilities(name, sample) {
- // Assume data is trimmed.
- const sampleType = typeof sample
- const sampleStringLength = sample !== undefined ? sample.toString().length : 0
- const guesses = {}
- guesses.number = 0.5
- guesses.date = 0.25
- guesses.string = 0.75
- guesses.feet = 0.01 // 5'11"
- guesses.object = 0.1
- guesses.boolean = 0.02
- const isDate = sample instanceof Date
- const isNumber = !isNaN(parseFloat(sample)) || Column.getPrimitiveTypeByName("usd").getProbForColumnSpecimen(sample)
- if (sampleType === "object") guesses.object = 0.9
- if (sample === true || sample === false) guesses.boolean = 0.96
- if (name.match(/(team|name|link|image|description|permalink|title|label|status|thumb|ip|useragent)/i)) guesses.string = 0.95
- if (name.match(/(gender|region|category|group|section|sector|field)/i)) guesses.string = 0.95
- if (name.match(/(height|length)/i)) {
- if (sample.toString().match(/[0-9]+(\'|\-)[0-9\.]+/) && Column.getPrimitiveTypeByName("feet").getProbForColumnSpecimen(sample)) guesses.feet = 0.97
- }
- if (isNumber) guesses.number = 0.8
- else guesses.number = 0.1
- const usdGuess = Column.getPrimitiveTypeByName("usd").getProbForColumnSpecimen(sample)
- if (usdGuess || (!isNaN(sample) && name.match(/^(price|income|cost|revenue|budget|profit|amount|balance)$/i))) guesses.usd = 0.99
- if (isNumber && name.match(/(year|born)/i) && sampleStringLength === 4) guesses.year = 0.99
- if (isDate && name.match(/(year|born)/i)) guesses.year = 0.99
- if (isNumber && name.match(/(time|second|created|edited)/i) && sampleStringLength === 10) guesses.second = 0.99
- if (isNumber && name.match(/(time|second)/i) && sampleStringLength === 13) guesses.millisecond = 0.99
- const isValidDate = Column.getPrimitiveTypeByName("date").getProbForColumnSpecimen(sample)
- if (name.match(/(year|date|dob|birthday|day|month|time|birthdate|utc)/i) && isValidDate) guesses.date = 0.98
- else if (isValidDate && sampleType === "string" && sample.includes("/")) guesses.date = 0.81
- if (sampleType === "string" && sampleStringLength > 100) guesses.string = 0.98
- return guesses
- }
- _inferType() {
- const columnObj = this._getColDefObject()
- const sample = this._getFirstNonEmptyValueFromSampleSet()
- if (columnObj && columnObj.type && Column.getPrimitiveTypeByName(columnObj.type)) return Column.getPrimitiveTypeByName(columnObj.type)
- const guesses = Column._getColumnProbabilities(this.getColumnName(), this._getFirstNonEmptyValueFromSampleSet())
- let max = 0
- let bestGuess = null
- for (let typeScore in guesses) {
- if (guesses[typeScore] > max) {
- max = guesses[typeScore]
- bestGuess = typeScore
- }
- }
- if (bestGuess === "number" && typeof sample === "string") {
- if (sample.match(",")) bestGuess = "numberString"
- }
- if (bestGuess === "date" && typeof sample === "string") {
- if (Column.getPrimitiveTypeByName("day").getProbForColumnSpecimen(sample)) bestGuess = "day"
- }
- return Column.getPrimitiveTypeByName(bestGuess)
- }
- // Note: If it returns a string removes spaces
- static convertValueToNumeric(value, sourceType, destinationType, mathFn) {
- const destType = this.getPrimitiveTypeByName(destinationType)
- if (value === undefined || !destType || value === "") return ""
- const conversionFn = this._getConversionFn(sourceType, destinationType, value)
- const res = conversionFn(value)
- if (mathFn) return mathFn(res)
- return res
- }
- static _getConversionFn(sourceType, destinationType, value) {
- const sourceCol = this.getPrimitiveTypeByName(sourceType)
- const destinationCol = this.getPrimitiveTypeByName(destinationType)
- if (!sourceCol || !destinationCol) return destinationCol.fromStringToNumeric
- if (destinationCol.isTemporal() && sourceCol.isTemporal()) return val => destinationCol.fromDateToNumeric(sourceCol._fromStringToDate(val))
- return destinationCol.fromStringToNumeric
- }
- }
- const PrimitiveTypes = {
- AbstractPrimitiveType,
- BooleanType,
- ObjectType,
- USD,
- NumberCol,
- NumberString,
- Feet,
- IntType,
- UrlCol,
- HTMLCol,
- DirCol,
- PathCol,
- TextCol,
- StringCol,
- AbstractTemporal,
- MilliSecond,
- Second,
- DateCol,
- Day,
- Month,
- MonthDay,
- Week,
- Hour,
- Minute,
- Year,
- HourMinute,
- CodeCol
- }
- window.Column = Column
- window.PrimitiveTypes = PrimitiveTypes
- //onsave jtree build produce jtable.browser.js
- //onsave jtree build produce jtable.node.js
- class Row {
- constructor(sourceObject = {}, table) {
- this._puid = this._getUniqueId()
- this._sourceObject = sourceObject
- this._table = table
- }
- _getUniqueId() {
- Row._uniqueId++
- return Row._uniqueId
- }
- destroy() {}
- async destroyRow() {}
- getAsArray(headerRow) {
- const obj = this.rowToObjectWithOnlyNativeJavascriptTypes()
- return headerRow.map(col => obj[col])
- }
- getRowSourceObject() {
- return this._sourceObject
- }
- toVector() {
- return Object.values(this.rowToObjectWithOnlyNativeJavascriptTypes())
- }
- // todo: rowToObjectWithOnlyNativeJavascriptTypes method? Its numerics where we need and strings where we need.
- _parseIntoObjectWithOnlyNativeJavascriptTypes() {
- const columns = this._table.getColumnsMap()
- const typedNode = {}
- Object.keys(columns).forEach(colName => {
- typedNode[colName] = this._getRowValueFromSourceColOrOriginalCol(colName)
- })
- return typedNode
- }
- // why from source col? if we always copy, we shouldnt need that, correct? perhaps have an audit array of all operations on a row?
- _getRowValueFromSourceColOrOriginalCol(colName) {
- const columns = this._table.getColumnsMap()
- const destColumn = columns[colName]
- const sourceColName = destColumn._getSourceColumnName()
- const sourceCol = columns[sourceColName]
- // only use source if we still have access to it
- const val = sourceColName && sourceCol ? this._getRowValueFromOriginalOrSource(sourceColName, sourceCol.getPrimitiveTypeName(), destColumn.getPrimitiveTypeName()) : this.getRowOriginalValue(colName)
- const res = destColumn.getPrimitiveTypeObj().getAsNativeJavascriptType(val)
- const mathFn = destColumn.getMathFn()
- if (mathFn) return mathFn(res)
- return res
- }
- _getRowValueFromOriginalOrSource(sourceColName, sourceColType, destType) {
- return Column.convertValueToNumeric(this.getRowOriginalValue(sourceColName), sourceColType, destType)
- }
- rowToObjectWithOnlyNativeJavascriptTypes() {
- if (!this._objectWithOnlyNativeJavascriptTypes) this._objectWithOnlyNativeJavascriptTypes = this._parseIntoObjectWithOnlyNativeJavascriptTypes()
- return this._objectWithOnlyNativeJavascriptTypes
- }
- getRowKeys() {
- return Object.keys(this.getRowSourceObject())
- }
- getFirstValue() {
- return this.getRowOriginalValue(this.getRowKeys()[0])
- }
- // todo: get values from source/virtual columns
- getRowOriginalValue(column) {
- const value = this.getRowSourceObject()[column]
- return value === null ? "" : value
- }
- getRowHtmlSafeValue(columnName) {
- const val = this.getRowOriginalValue(columnName)
- return val === undefined ? "" : jtree.Utils.stripHtml(val.toString()).toString() // todo: cache this?
- }
- getHoverTitle() {
- return encodeURIComponent(this.rowToString().replace(/\n/g, " "))
- }
- getPuid() {
- return this._puid
- }
- rowToString() {
- return JSON.stringify(this.getRowSourceObject(), null, 2)
- }
- }
- Row._uniqueId = 0
- window.Row = Row
- //onsave jtree build produce jtable.browser.js
- //onsave jtree build produce jtable.node.js
- var TableParserIds
- ;(function(TableParserIds) {
- TableParserIds["csv"] = "csv"
- TableParserIds["ssv"] = "ssv"
- TableParserIds["psv"] = "psv"
- TableParserIds["tsv"] = "tsv"
- TableParserIds["xml"] = "xml"
- TableParserIds["html"] = "html"
- TableParserIds["spaced"] = "spaced"
- TableParserIds["tree"] = "tree"
- TableParserIds["treeRows"] = "treeRows"
- TableParserIds["sections"] = "sections"
- TableParserIds["txt"] = "txt"
- TableParserIds["list"] = "list"
- TableParserIds["text"] = "text"
- TableParserIds["jsonVector"] = "jsonVector"
- TableParserIds["json"] = "json"
- TableParserIds["jsonDataTableWithHeader"] = "jsonDataTableWithHeader"
- TableParserIds["jsonMap"] = "jsonMap"
- TableParserIds["jsonCounts"] = "jsonCounts"
- })(TableParserIds || (TableParserIds = {}))
- // todo: detect mixed format, like a csv file with a header. and then suggest ignore that part, or splitting it out?
- // maybe we could split a string into sections, and say "we've detected 3 sections, which one do you want to use"?
- // todo: split csv into normal csv and advanced delimited.
- // todo: allow for metadata like filename and filetype header
- class RowStringSpecimen {
- constructor(str) {
- const trimmedStr = str.trim()
- const lines = trimmedStr.split(/\n/g)
- const firstLine = lines[0]
- const strCount = (str, reg) => (str.match(reg) || []).length
- // todo: do these things lazily.
- this.trimmedStr = trimmedStr
- this.lines = lines
- this.firstLine = firstLine
- this.lineCount = lines.length
- this.indentedLineCount = strCount(trimmedStr, /\n /g)
- this.blankLineCount = strCount(trimmedStr, /\n\n/g)
- this.commaCount = strCount(trimmedStr, /\,/g)
- this.tabCount = strCount(trimmedStr, /\t/g)
- this.verticalBarCount = strCount(trimmedStr, /\|/g)
- this.firstLineCommaCount = strCount(firstLine, /\,/g)
- this.firstLineTabCount = strCount(firstLine, /\t/g)
- this.firstLineSpaceCount = strCount(firstLine, / /g)
- this.firstLineVerticalBarCount = strCount(firstLine, /\|/g)
- }
- getParsedJsonAttemptResult() {
- if (this._parsedJsonObject) return this._parsedJsonObject
- try {
- this._parsedJsonObject = { ok: true, result: JSON.parse(this.trimmedStr) }
- } catch (err) {
- this._parsedJsonObject = { ok: false }
- }
- return this._parsedJsonObject
- }
- }
- class AbstractTableParser {
- isNodeJs() {
- return typeof exports !== "undefined"
- }
- }
- class AbstractJsonParser extends AbstractTableParser {
- getParserId() {
- return TableParserIds.json
- }
- getProbForRowSpecimen(specimen) {
- return 0
- }
- getExample() {
- return JSON.stringify([{ name: "joe", age: 2 }, { name: "mike", age: 4 }])
- }
- _parseTableInputsFromString(str) {
- const obj = JSON.parse(str)
- return { rows: obj instanceof Array ? obj : [obj] }
- }
- }
- class JsonParser extends AbstractJsonParser {}
- class AbstractJsonArrayParser extends AbstractJsonParser {
- getExample() {
- return JSON.stringify([{ name: "jane", age: 33 }, { name: "bill", age: 25 }])
- }
- getProbForRowSpecimen(specimen) {
- const str = specimen.trimmedStr
- if (str.match(/^\s*\[/) && str.match(/\]\s*$/)) return 0.98
- return 0
- }
- }
- class JsonArrayParser extends AbstractJsonArrayParser {}
- class JsonDataTableWithHeaderParser extends AbstractJsonArrayParser {
- getExample() {
- return JSON.stringify([["country", "income", "health", "population"], ["Afghanistan", 1925, "57.63", 32526562], ["Albania", 10620, "76", 2896679]])
- }
- _parseTableInputsFromString(str) {
- return { rows: jtree.Utils.javascriptTableWithHeaderRowToObjects(JSON.parse(str)) }
- }
- getParserId() {
- return TableParserIds.jsonDataTableWithHeader
- }
- getProbForRowSpecimen(specimen) {
- const result = specimen.getParsedJsonAttemptResult()
- if (!result.ok) return 0
- if (JsonDataTableWithHeaderParser.isJavaScriptDataTable(result.result)) return 0.99
- return 0.001
- }
- static isJavaScriptDataTable(obj) {
- const isAnArray = obj instanceof Array
- if (!isAnArray) return false
- const isAnArrayOfArrays = obj.every(row => row instanceof Array)
- if (!isAnArrayOfArrays) return false
- if (obj.length < 3) return false
- const firstRowTypes = obj[0].map(item => typeof item === "string").join(" ")
- const secondRowTypes = obj[1].map(item => typeof item === "string").join(" ")
- const thirdRowTypes = obj[2].map(item => typeof item === "string").join(" ")
- const firstRowIsJustStrings = !firstRowTypes.replace(/true/g, "").trim()
- if (secondRowTypes === thirdRowTypes && secondRowTypes !== firstRowTypes && firstRowIsJustStrings) return true
- return false
- }
- }
- class AbstractJsonObjectParser extends AbstractJsonParser {
- getProbForRowSpecimen(specimen) {
- const str = specimen.trimmedStr
- if (str.match(/^\s*\{/) && str.match(/\}\s*$/)) return 0.99
- return 0
- }
- }
- class JsonObjectParser extends AbstractJsonObjectParser {}
- // formerley flatobject
- class JsonMapParser extends AbstractJsonObjectParser {
- getExample() {
- return JSON.stringify({ person1: { name: "joe", age: 2 }, person2: { name: "mike", age: 4 } })
- }
- getParserId() {
- return TableParserIds.jsonMap
- }
- _parseTableInputsFromString(str) {
- // todo: should we preserve keys?
- return { rows: Object.values(JSON.parse(str)) }
- }
- }
- // formerly flatarray
- class JsonVectorParser extends AbstractJsonArrayParser {
- getExample() {
- return JSON.stringify([23, 32, 41])
- }
- getParserId() {
- return TableParserIds.jsonVector
- }
- getProbForRowSpecimen(specimen) {
- const result = specimen.getParsedJsonAttemptResult()
- if (!result.ok) return 0
- if (!(result.result instanceof Array)) return 0
- return result.result.filter(item => item && typeof item === "object" && item.hasOwnProperty).length === 0 ? 1 : 0
- return 0
- }
- _parseTableInputsFromString(str) {
- return {
- rows: JSON.parse(str).map(num => {
- return { value: num }
- })
- }
- }
- }
- class JsonCountMapParser extends AbstractJsonObjectParser {
- getParserId() {
- return TableParserIds.jsonCounts
- }
- getProbForRowSpecimen(specimen) {
- const result = specimen.getParsedJsonAttemptResult()
- if (!result.ok) return 0
- const keys = Object.keys(result.result)
- if (keys.length < 2) return 0
- return !keys.some(key => typeof result.result[key] !== "number") ? 1 : 0
- return 0
- }
- getExample() {
- return JSON.stringify({ h1: 10, h2: 5, h3: 2 })
- }
- _parseTableInputsFromString(str) {
- const obj = JSON.parse(str)
- return {
- rows: Object.keys(obj).map(key => {
- return {
- name: key,
- count: obj[key]
- }
- })
- }
- }
- }
- // todo: remove?
- class AbstractJTreeTableParser extends AbstractTableParser {
- _parseTableInputsFromString(str) {
- return {
- rows: this._parseTrees(str)
- .filter(node => node.length)
- .map(node => node.toObject())
- }
- }
- _parseTrees(str) {
- return []
- }
- }
- class CsvParser extends AbstractJTreeTableParser {
- getExample() {
- return `name,age,height
- john,12,50`
- }
- _parseTrees(str) {
- return jtree.TreeNode.fromCsv(str)
- }
- getProbForRowSpecimen(specimen) {
- if (!specimen.firstLineCommaCount) return 0
- if (specimen.blankLineCount) return 0.05
- return 0.49
- }
- getParserId() {
- return TableParserIds.csv
- }
- }
- class TsvParser extends AbstractJTreeTableParser {
- getExample() {
- return `name\tage\theight
- john\t12\t50`
- }
- _parseTrees(str) {
- return jtree.TreeNode.fromTsv(str)
- }
- getProbForRowSpecimen(specimen) {
- if (!specimen.firstLineTabCount) return 0
- else if (specimen.tabCount > 5) return 0.9
- return 0.25
- }
- getParserId() {
- return TableParserIds.tsv
- }
- }
- class PsvParser extends AbstractJTreeTableParser {
- getParserId() {
- return TableParserIds.psv
- }
- getExample() {
- return `name|age
- mike|33`
- }
- _parseTrees(str) {
- return jtree.TreeNode.fromDelimited(str, "|", '"')
- }
- getProbForRowSpecimen(specimen) {
- // vertical bar separated file
- if (!specimen.firstLineVerticalBarCount) return 0
- else if (specimen.verticalBarCount >= specimen.lineCount) return 0.8
- return 0.01
- }
- }
- class SsvParser extends AbstractJTreeTableParser {
- getExample() {
- return `name age height
- john 12 50`
- }
- getParserId() {
- return TableParserIds.ssv
- }
- _parseTrees(str) {
- return jtree.TreeNode.fromSsv(str)
- }
- getProbForRowSpecimen(specimen) {
- if (!specimen.firstLineSpaceCount) return 0
- if (specimen.blankLineCount) return 0.05
- return 0.11
- }
- }
- class XmlParser extends AbstractJTreeTableParser {
- getProbForRowSpecimen(specimen) {
- return specimen.trimmedStr.match(/^ *\) ? 1 : 0
- }
- getExample() {
- }
- getParserId() {
- return TableParserIds.xml
- }
- _parseTrees(str) {
- // todo: fix this! Create an XML Tree Language
- if (this.isNodeJs()) return new jtree.TreeNode(str)
- return jtree.TreeNode.fromXml(str)
- }
- }
- class HtmlParser extends AbstractJTreeTableParser {
- getProbForRowSpecimen(specimen) {
- return specimen.trimmedStr.match(/^(\<\!doctype html\>|\
- }
- getExample() {
- return `
-
- bam`
- }
- getParserId() {
- return TableParserIds.html
- }
- _parseTrees(str) {
- if (this.isNodeJs()) return new jtree.TreeNode(str)
- return jtree.TreeNode.fromXml(str)
- }
- }
- class TreeRowsParser extends AbstractJTreeTableParser {
- getExample() {
- return `person
- name john
- age 12
- height 50`
- }
- _parseTableInputsFromString(str) {
- // todo: get columns on first pass.
- const rows = new jtree.TreeNode(str)
- return {
- rows: rows.map(node => node.toObject()),
- columnDefinitions: rows.getColumnNames().map(name => {
- return { name: name }
- })
- }
- }
- getProbForRowSpecimen(specimen) {
- if (specimen.indentedLineCount < 1) return 0
- return 0.1
- }
- getParserId() {
- return TableParserIds.treeRows
- }
- }
- class TreeParser extends AbstractJTreeTableParser {
- getExample() {
- return `country
- name USA
- state
- name MA
- city
- name Brockton`
- }
- _parseTrees(str) {
- // todo: add tests. Detected value(s) or undefined subtrees, treating as object.
- const newTree = new jtree.TreeNode()
- newTree.pushContentAndChildren(undefined, str instanceof jtree.TreeNode ? str : new jtree.TreeNode(str))
- return newTree
- }
- getProbForRowSpecimen(specimen) {
- return 0
- }
- getParserId() {
- return TableParserIds.tree
- }
- }
- class SpacedParser extends AbstractTableParser {
- getExample() {
- return `name john
- age 12
-
- name mary
- age 20`
- }
- getParserId() {
- return TableParserIds.spaced
- }
- getProbForRowSpecimen(specimen) {
- if (specimen.blankLineCount > 10) return 0.95
- return 0.05
- }
- _parseTableInputsFromString(str) {
- // todo: clean this up. it looks like this is just trees, but not indented, with a newline as a delimiter.
- const headerBreak = str.indexOf("\n\n")
- const header = str.substr(0, headerBreak)
- let names = header.split(/\n/g)
- const rest = str
- .substr(headerBreak + 2)
- .replace(/\n\n/g, "\n")
- .trim()
- .split("\n")
- const nodeCount = names.length
- const lineCount = rest.length
- const rows = []
- // todo: should we do this here?
- names = names.map(name => name.replace(/ /g, ""))
- for (let lineNumber = 0; lineNumber < lineCount; lineNumber = lineNumber + nodeCount) {
- const obj = {}
- names.forEach((col, index) => {
- obj[col] = rest[lineNumber + index].trim()
- })
- rows.push(obj)
- }
- return { rows: rows }
- }
- }
- class SectionsParser extends AbstractTableParser {
- getExample() {
- return `name
- age
-
- john
- 12
-
- mary
- 20`
- }
- getProbForRowSpecimen() {
- return 0
- }
- getParserId() {
- return TableParserIds.sections
- }
- _parseTableInputsFromString(str) {
- const firstDoubleNewline = str.indexOf("\n\n")
- const tiles = [str.slice(0, firstDoubleNewline), str.slice(firstDoubleNewline + 1)]
- const header = tiles.shift()
- const names = header.split(/\n/g)
- const length = names.length
- const lines = tiles[0].trim().split(/\n/g)
- const lineCount = lines.length
- const rowCount = lineCount / length
- const rows = []
- for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
- const startLine = rowIndex * length
- const values = lines.slice(startLine, startLine + length)
- const obj = {}
- names.forEach((name, colIndex) => (obj[name] = values[colIndex]))
- rows.push(obj)
- }
- return { rows: rows }
- }
- }
- class ListParser extends AbstractTableParser {
- getExample() {
- return `john doe
- frank jones`
- }
- getProbForRowSpecimen() {
- return 0
- }
- getParserId() {
- return TableParserIds.list
- }
- _parseTableInputsFromString(str) {
- return {
- rows: str.split(/\n/g).map((line, index) => {
- return {
- index: index,
- name: line
- }
- })
- }
- }
- }
- class TextListParser extends ListParser {
- getParserId() {
- return TableParserIds.txt
- }
- }
- class TextParser extends AbstractTableParser {
- getParserId() {
- return TableParserIds.text
- }
- getExample() {
- return "hello world"
- }
- getProbForRowSpecimen(specimen) {
- if (specimen.blankLineCount) return 0.12
- return 0.05
- }
- _parseTableInputsFromString(str) {
- return { rows: [{ text: str }] }
- }
- }
- class TableParser {
- constructor() {
- this._parsers = [
- new CsvParser(),
- new TsvParser(),
- new SsvParser(),
- new PsvParser(),
- new TreeRowsParser(),
- new TreeParser(),
- new XmlParser(),
- new HtmlParser(),
- new TextParser(),
- new SectionsParser(),
- new SpacedParser(),
- new ListParser(),
- new TextListParser(),
- new JsonParser(),
- new JsonArrayParser(),
- new JsonDataTableWithHeaderParser(),
- new JsonVectorParser(),
- new JsonMapParser(),
- new JsonCountMapParser()
- ]
- this._parserMap = {}
- this._parsers.forEach(parser => {
- const name = parser.getParserId()
- if (!name) return // only allow leafs to be used as names?
- this._parserMap[name] = parser
- })
- }
- getAllParsers() {
- return this._getParsersArray()
- }
- getAllTableParserIds() {
- return Object.keys(this._getParserMap())
- }
- getExample(parserId) {
- return this._getParser(parserId).getExample()
- }
- _getParser(parserId) {
- const options = this._getParserMap()
- return options[parserId] || options.text // todo: surface an error.
- }
- _getParserMap() {
- return this._parserMap
- }
- _getParsersArray() {
- return this._parsers
- }
- // todo: remove this?
- parseTableInputsFromObject(data, parserId) {
- if (data instanceof Array) {
- if (JsonDataTableWithHeaderParser.isJavaScriptDataTable(data)) return { rows: jtree.Utils.javascriptTableWithHeaderRowToObjects(data) }
- // test to see if it's primitives
- if (typeof data[0] === "object") return { rows: data }
- return { rows: data.map(row => (typeof row === "object" ? row : { value: row })) }
- } else if (parserId === TableParserIds.jsonMap) return { rows: Object.values(data) }
- return { rows: [data] }
- }
- // todo: should this be inferAndParse? or 2 methods? parse and inferAndParse?
- parseTableInputsFromString(str = "", parserId) {
- str = str.trim() // Remove empty lines at end of string, which seem to be common.
- if (!str) return { rows: [] }
- parserId = parserId || this.guessTableParserId(str)
- try {
- return this._getParser(parserId)._parseTableInputsFromString(str)
- } catch (err) {
- console.error(err)
- const snippet = str.substr(0, 30).replace(/[\n\r]/g, " ")
- throw new Error(`Failed parsing string '${snippet}...' using parser '${parserId}'`)
- }
- }
- guessProbabilitiesForAllTableParsers(str) {
- const parsers = this._getParsersArray()
- const length = parsers.length
- const probabilities = {}
- const specimen = new RowStringSpecimen(str)
- for (let index = 0; index < parsers.length; index++) {
- const parser = parsers[index]
- const probability = parser.getProbForRowSpecimen(specimen)
- const name = parser.getParserId()
- if (probability === 1) {
- const exact = {}
- exact[name] = 1
- return exact
- }
- probabilities[name] = probability
- }
- return probabilities
- }
- guessTableParserId(str) {
- const probabilities = this.guessProbabilitiesForAllTableParsers(str)
- let maxScore = 0
- let bestGuess = null
- for (let option in probabilities) {
- if (probabilities[option] > maxScore) {
- maxScore = probabilities[option]
- bestGuess = option
- }
- }
- return bestGuess
- }
- }
- window.TableParser = TableParser
- const DummyDataSets = {
- flowPrograms: [
- ["filename", "bytes", "link"],
- [
- "hello.flow",
- `samples.iris
- tables.basic`,
- ""
- ]
- ],
- amazonPurchases: [
- ["OrderDate", "Title", "Category", "ItemTotal"],
- [1329386400000, "3D Math Primer for Graphics and Game Development (Wordware Game Math Library)", "Paperback", "26.2"],
- [1261735200000, "A Mathematician Reads the Newspaper", "Paperback", "1.19"],
- [1447840800000, "A Most Incomprehensible Thing: Notes Towards a Very Gentle Introduction to the Mathematics of Relativity", "Paperback", "14.63"],
- [1464429600000, "From Mathematics to Generic Programming", "Paperback", "34.37"],
- [1268215200000, "Innumeracy: Mathematical Illiteracy and Its Consequences", "Paperback", "9.89"],
- [1268215200000, "Irreligion: A Mathematician Explains Why the Arguments for God Just Don't Add Up", "Paperback", "9.89"],
- [1379844000000, "Mathematics and the Imagination", "Paperback", "5.3"],
- [1410688800000, "Medical Math (Laminated Reference Guide; Quick Study Academic)", "Pamphlet", "3.78"],
- [1268215200000, "Once Upon A Number: The Hidden Mathematical Logic Of Stories", "Paperback", "14.35"],
- [1408528800000, "The Language of Mathematics: Making the Invisible Visible", "Paperback", "18.37"]
- ],
- waterBill: [
- ["Amount", "PaidOn", "Gallons"],
- ["$64.86", "1/10/2018", 2087],
- ["$73.32", "1/28/2018", 2451],
- ["$62.65", "2/26/2018", 1968],
- ["$71.51", "3/25/2018", 2365],
- ["$65.03", "4/23/2018", 2075],
- ["$81.39", "5/15/2018", 2757],
- ["$65.01", "6/15/2018", 2047],
- ["$93.09", "7/10/2018", 3051],
- ["$196.58", "8/25/2018", 7309],
- ["$130.68", "9/10/2018", 4597],
- ["$55.03", "10/14/2018", 1484],
- ["$63.44", "11/7/2018", 1967],
- ["$71.88", "12/12/2018", 2335],
- ["$53.18", "2/3/2019", 1483],
- ["$52.05", "3/8/2019", 1429],
- ["$54.73", "4/28/2019", 1544]
- ],
- gapMinder: [
- ["country", "income", "health", "population"],
- ["Afghanistan", 1925, "57.63", 32526562],
- ["Albania", 10620, "76", 2896679],
- ["Algeria", 13434, "76.5", 39666519],
- ["Andorra", 46577, "84.1", 70473],
- ["Angola", 7615, "61", 25021974],
- ["Antigua and Barbuda", 21049, "75.2", 91818],
- ["Argentina", 17344, "76.2", 43416755],
- ["Armenia", 7763, "74.4", 3017712],
- ["Australia", 44056, "81.8", 23968973],
- ["Austria", 44401, "81", 8544586],
- ["Azerbaijan", 16986, "72.9", 9753968],
- ["Bahamas", 22818, "72.3", 388019],
- ["Bahrain", 44138, "79.2", 1377237],
- ["Bangladesh", 3161, "70.1", 160995642],
- ["Barbados", 12984, "75.8", 284215],
- ["Belarus", 17415, "70.4", 9495826],
- ["Belgium", 41240, "80.4", 11299192],
- ["Belize", 8501, "70", 359287],
- ["Benin", 1830, "65.5", 10879829],
- ["Bhutan", 7983, "70.2", 774830],
- ["Bolivia", 6295, "72.3", 10724705],
- ["Bosnia and Herzegovina", 9833, "77.9", 3810416],
- ["Botswana", 17196, "66.4", 2262485],
- ["Brazil", 15441, "75.6", 207847528],
- ["Brunei", 73003, "78.7", 423188],
- ["Bulgaria", 16371, "74.9", 7149787],
- ["Burkina Faso", 1654, "62.8", 18105570],
- ["Burundi", 777, "60.4", 11178921],
- ["Cambodia", 3267, "68.4", 15577899],
- ["Cameroon", 2897, "59.5", 23344179],
- ["Canada", 43294, "81.7", 35939927],
- ["Cape Verde", 6514, "74.6", 520502],
- ["Central African Republic", 599, "53.8", 4900274],
- ["Chad", 2191, "57.7", 14037472],
- ["Chile", 22465, "79.3", 17948141],
- ["China", 13334, "76.9", 1376048943],
- ["Colombia", 12761, "75.8", 48228704],
- ["Comoros", 1472, "64.1", 788474],
- ["Congo, Dem. Rep.", 809, "58.3", 77266814],
- ["Congo, Rep.", 6220, "61.9", 4620330],
- ["Costa Rica", 14132, "80", 4807850],
- ["Cote d'Ivoire", 3491, "60.33", 22701556],
- ["Croatia", 20260, "78", 4240317],
- ["Cuba", 21291, "78.5", 11389562],
- ["Cyprus", 29797, "82.6", 1165300],
- ["Czech Republic", 29437, "78.6", 10543186],
- ["Denmark", 43495, "80.1", 5669081],
- ["Djibouti", 3139, "64.63", 887861],
- ["Dominica", 10503, "74.6", 72680],
- ["Dominican Republic", 12837, "73.8", 10528391],
- ["Ecuador", 10996, "75.2", 16144363],
- ["Egypt", 11031, "71.3", 91508084],
- ["El Salvador", 7776, "74.1", 6126583],
- ["Equatorial Guinea", 31087, "60.63", 845060],
- ["Eritrea", 1129, "62.9", 5227791],
- ["Estonia", 26812, "76.8", 1312558],
- ["Ethiopia", 1520, "63.6", 99390750],
- ["Fiji", 7925, "66.3", 892145],
- ["Finland", 38923, "80.8", 5503457],
- ["France", 37599, "81.9", 64395345],
- ["Gabon", 18627, "60.53", 1725292],
- ["Gambia", 1644, "65.1", 1990924],
- ["Georgia", 7474, "73.3", 3999812],
- ["Germany", 44053, "81.1", 80688545],
- ["Ghana", 4099, "65.5", 27409893],
- ["Greece", 25430, "79.8", 10954617],
- ["Grenada", 11593, "71.7", 106825],
- ["Guatemala", 7279, "73.1", 16342897],
- ["Guinea", 1225, "60.8", 12608590],
- ["Guinea-Bissau", 1386, "53.4", 1844325],
- ["Guyana", 6816, "64.4", 767085],
- ["Haiti", 1710, "65.3", 10711067],
- ["Honduras", 4270, "72.4", 8075060],
- ["Hungary", 24200, "76.2", 9855023],
- ["Iceland", 42182, "82.8", 329425],
- ["India", 5903, "66.8", 1311050527],
- ["Indonesia", 10504, "70.9", 257563815],
- ["Iran", 15573, "78.5", 79109272],
- ["Iraq", 14646, "72.1", 36423395],
- ["Ireland", 47758, "80.4", 4688465],
- ["Israel", 31590, "82.4", 8064036],
- ["Italy", 33297, "82.1", 59797685],
- ["Jamaica", 8606, "75.5", 2793335],
- ["Japan", 36162, "83.5", 126573481],
- ["Jordan", 11752, "78.3", 7594547],
- ["Kazakhstan", 23468, "68.2", 17625226],
- ["Kenya", 2898, "66.63", 46050302],
- ["Kiribati", 1824, "62.4", 112423],
- ["Kuwait", 82633, "80.7", 3892115],
- ["Kyrgyz Republic", 3245, "69", 5939962],
- ["Lao", 5212, "66.4", 6802023],
- ["Latvia", 23282, "75.7", 1970503],
- ["Lebanon", 17050, "78.5", 5850743],
- ["Lesotho", 2598, "48.5", 2135022],
- ["Liberia", 958, "63.9", 4503438],
- ["Libya", 17261, "76.2", 6278438],
- ["Lithuania", 26665, "75.4", 2878405],
- ["Luxembourg", 88314, "81.1", 567110],
- ["Macedonia, FYR", 12547, "77", 2078453],
- ["Madagascar", 1400, "64.7", 24235390],
- ["Malawi", 799, "60.22", 17215232],
- ["Malaysia", 24320, "75.1", 30331007],
- ["Maldives", 14408, "79.5", 363657],
- ["Mali", 1684, "57.6", 17599694],
- ["Malta", 30265, "82.1", 418670],
- ["Marshall Islands", 3661, "65.1", 52993],
- ["Mauritania", 3877, "65.7", 4067564],
- ["Mauritius", 18350, "73.9", 1273212],
- ["Mexico", 16850, "74.5", 127017224],
- ["Micronesia, Fed. Sts.", 3510, "67", 104460],
- ["Moldova", 4896, "72.7", 4068897],
- ["Mongolia", 11819, "65.3", 2959134],
- ["Montenegro", 14833, "75.8", 625781],
- ["Morocco", 7319, "74.7", 34377511],
- ["Mozambique", 1176, "56.4", 27977863],
- ["Myanmar", 4012, "67.9", 53897154],
- ["Namibia", 10040, "61", 2458830],
- ["Nepal", 2352, "71.2", 28513700],
- ["Netherlands", 45784, "80.6", 16924929],
- ["New Zealand", 34186, "80.6", 4528526],
- ["Nicaragua", 4712, "76.8", 6082032],
- ["Niger", 943, "62.2", 19899120],
- ["Nigeria", 5727, "61.33", 182201962],
- ["North Korea", 1390, "71.4", 25155317],
- ["Norway", 64304, "81.6", 5210967],
- ["Oman", 48226, "75.7", 4490541],
- ["Pakistan", 4743, "66.5", 188924874],
- ["Panama", 20485, "78.2", 3929141],
- ["Papua New Guinea", 2529, "60.6", 7619321],
- ["Paraguay", 8219, "73.9", 6639123],
- ["Peru", 11903, "77.5", 31376670],
- ["Philippines", 6876, "70.2", 100699395],
- ["Poland", 24787, "77.3", 38611794],
- ["Portugal", 26437, "79.8", 10349803],
- ["Qatar", 132877, "82", 2235355],
- ["Romania", 19203, "76.8", 19511324],
- ["Russia", 23038, "73.13", 143456918],
- ["Rwanda", 1549, "66.53", 11609666],
- ["Samoa", 5558, "72.2", 193228],
- ["Sao Tome and Principe", 3003, "68.8", 190344],
- ["Saudi Arabia", 52469, "78.1", 31540372],
- ["Senegal", 2251, "66.1", 15129273],
- ["Serbia", 12908, "78.1", 8850975],
- ["Seychelles", 25684, "73.7", 96471],
- ["Sierra Leone", 2085, "58.5", 6453184],
- ["Singapore", 80794, "82.1", 5603740],
- ["Slovak Republic", 27204, "76.4", 5426258],
- ["Slovenia", 28550, "80.2", 2067526],
- ["Solomon Islands", 2047, "64.1", 583591],
- ["Somalia", 624, "58.7", 10787104],
- ["South Africa", 12509, "63.72", 54490406],
- ["South Korea", 34644, "80.7", 50293439],
- ["South Sudan", 3047, "58", 12339812],
- ["Spain", 32979, "81.7", 46121699],
- ["Sri Lanka", 10624, "76.5", 20715010],
- ["St. Lucia", 9997, "74.5", 184999],
- ["St. Vincent and the Grenadines", 10435, "72.9", 109462],
- ["Sudan", 3975, "69.5", 40234882],
- ["Suriname", 17125, "70.5", 542975],
- ["Swaziland", 6095, "51.5", 1286970],
- ["Sweden", 44892, "82", 9779426],
- ["Switzerland", 56118, "82.9", 8298663],
- ["Syria", 4637, "70.26", 18502413],
- ["Tajikistan", 2582, "71", 8481855],
- ["Tanzania", 2571, "63.43", 53470420],
- ["Thailand", 14512, "75.1", 67959359],
- ["Timor-Leste", 2086, "72.4", 1184765],
- ["Togo", 1433, "64.23", 7304578],
- ["Tonga", 5069, "70.5", 106170],
- ["Trinidad and Tobago", 30113, "71.4", 1360088],
- ["Tunisia", 11126, "77.3", 11253554],
- ["Turkey", 19360, "76.5", 78665830],
- ["Turkmenistan", 15865, "67.9", 5373502],
- ["Uganda", 1680, "60.8", 39032383],
- ["Ukraine", 8449, "72.1", 44823765],
- ["United Arab Emirates", 60749, "76.6", 9156963],
- ["United Kingdom", 38225, "81.4", 64715810],
- ["United States", 53354, "79.1", 321773631],
- ["Uruguay", 20438, "77.3", 3431555],
- ["Uzbekistan", 5598, "70.1", 29893488],
- ["Vanuatu", 2912, "65", 264652],
- ["Venezuela", 15753, "75.8", 31108083],
- ["Vietnam", 5623, "76.5", 93447601],
- ["West Bank and Gaza", 4319, "75.2", 4668466],
- ["Yemen", 3887, "67.6", 26832215],
- ["Zambia", 4034, "58.96", 16211767],
- ["Zimbabwe", 1801, "60.01", 15602751]
- ],
- emojis: [["animal", "count"], ["🐄", 9], ["🐖", 12], ["🐏", 3]],
- telescopes: [
- ["Name", "Type", "Url", "Location", "OperatedBy", "FundedBy"],
- ["Hubble Space Telescope", "Space Telescope", "https://en.wikipedia.org/wiki/Hubble_Space_Telescope", "Space", "NASA", "NASA"],
- ["LIGO Hanford", "Gravity Wave Observatory", "https://www.ligo.caltech.edu/WA", "Richland, WA, USA", "Caltech", "National Science Foundation"],
- ["LIGO Livingston", "Gravity Wave Observatory", "https://www.ligo.caltech.edu/LA", "Livingston, LA, USA", "MIT", "National Science Foundation"],
- [
- "Gran Telescopio Canarias (GTC)",
- "Astronomical Observatory",
- "http://www.gtc.iac.es/gtc/gtc.php",
- "La Palma, Garafía, Spain ",
- "IAC",
- "Observatorio Astronómico de Canarias, National Autonomous University of Mexico, University of Florida Edit this on Wikidata"
- ],
- [
- "Hobby–Eberly Telescope",
- "Astronomical Observatory",
- "http://mcdonaldobservatory.org/research/telescopes/HET",
- "Davis Mountains, Texas, US",
- "Stanford University & Ludwig Maximilians University of Munich and Georg August University of Göttingen",
- "YarCom Inc. and the Lott family "
- ]
- ],
- markdown: [
- ["text"],
- [
- `# My header
- ## My subheader
- Hello world`
- ]
- ],
- webPages: [["name", "url"]],
- outerSpace: [
- ["text"],
- [
- `universe
- galaxies
- milkyWay
- solarSystems
- solarSystem
- stars
- sun
- planets
- mercury
- venus
- earth
- moons
- moon
- mars
- jupiter
- saturn
- uranus
- neptune
- pluto`
- ]
- ],
- wordCounts: [
- ["word", "count"],
- ["Two", 2],
- ["roads", 2],
- ["diverged", 2],
- ["in", 3],
- ["a", 3],
- ["yellow", 1],
- ["wood", 2],
- ["And", 6],
- ["sorry", 1],
- ["I", 9],
- ["could", 2],
- ["not", 1],
- ["travel", 1],
- ["both", 2],
- ["be", 2],
- ["one", 3],
- ["traveler", 1],
- ["long", 1],
- ["stood", 1],
- ["looked", 1]
- ],
- treeProgram: [
- ["source"],
- [
- `samples.iris
- group.by Species
- tables.basic
- columns.first 3`
- ]
- ],
- poem: [
- ["text"],
- [
- `Two roads diverged in a yellow wood,
- And sorry I could not travel both
- And be one traveler, long I stood
- And looked down one as far as I could
- To where it bent in the undergrowth;
-
- Then took the other, as just as fair,
- And having perhaps the better claim,
- Because it was grassy and wanted wear;
- Though as for that the passing there
- Had worn them really about the same,
-
- And both that morning equally lay
- In leaves no step had trodden black.
- Oh, I kept the first for another day!
- Yet knowing how way leads on to way,
- I doubted if I should ever come back.
-
- I shall be telling this with a sigh
- Somewhere ages and ages hence:
- Two roads diverged in a wood, and I—
- I took the one less traveled by,
- And that has made all the difference.`
- ]
- ],
- playerGoals: [["PlayerGoals", "Goals"], ["Player1", 11], ["Player2", 2], ["Player3", 2], ["Player4", 2], ["Player5", 7]],
- patients: [["Patient", "Gender", "Weight"], ["Patient1", "Girl", "3.31"], ["Patient2", "Male", "2.8"], ["Patient3", "Male", "3.7"], ["Patient4", "Girl", "2.5"], ["Patient5", "Girl", "2.8"]],
- regionalMarkets: [
- ["Location", "Parent", "Market trade volume (size)", "Market increase/decrease (color)"],
- ["Global", null, 0, 0],
- ["America", "Global", 0, 0],
- ["Europe", "Global", 0, 0],
- ["Asia", "Global", 0, 0],
- ["Australia", "Global", 0, 0],
- ["Africa", "Global", 0, 0],
- ["Brazil", "America", 11, 10],
- ["USA", "America", 52, 31],
- ["Mexico", "America", 24, 12],
- ["Canada", "America", 16, -23],
- ["France", "Europe", 42, -11],
- ["Germany", "Europe", 31, -2],
- ["Sweden", "Europe", 22, -13],
- ["Italy", "Europe", 17, 4],
- ["UK", "Europe", 21, -5],
- ["China", "Asia", 36, 4],
- ["Japan", "Asia", 20, -12],
- ["India", "Asia", 40, 63],
- ["Laos", "Asia", 4, 34],
- ["Mongolia", "Asia", 1, -5],
- ["Israel", "Asia", 12, 24],
- ["Iran", "Asia", 18, 13],
- ["Pakistan", "Asia", 11, -52],
- ["Egypt", "Africa", 21, 0],
- ["S. Africa", "Africa", 30, 43],
- ["Sudan", "Africa", 12, 2],
- ["Congo", "Africa", 10, 12],
- ["Zaire", "Africa", 8, 10]
- ],
- stockPrice: [
- ["Step", "Price"],
- [0, 0],
- [1, 10],
- [2, 23],
- [3, 17],
- [4, 18],
- [5, 9],
- [6, 11],
- [7, 27],
- [8, 33],
- [9, 40],
- [10, 32],
- [11, 35],
- [12, 30],
- [13, 40],
- [14, 42],
- [15, 47],
- [16, 44],
- [17, 48],
- [18, 52],
- [19, 54],
- [20, 42],
- [21, 55],
- [22, 56],
- [23, 57],
- [24, 60],
- [25, 50],
- [26, 52],
- [27, 51],
- [28, 49],
- [29, 53],
- [30, 55],
- [31, 60],
- [32, 61],
- [33, 59],
- [34, 62],
- [35, 65],
- [36, 62],
- [37, 58],
- [38, 55],
- [39, 61],
- [40, 64],
- [41, 65],
- [42, 63],
- [43, 66],
- [44, 67],
- [45, 69],
- [46, 69],
- [47, 70],
- [48, 72],
- [49, 68],
- [50, 66],
- [51, 65],
- [52, 67],
- [53, 70],
- [54, 71],
- [55, 72],
- [56, 73],
- [57, 75],
- [58, 70],
- [59, 68],
- [60, 64],
- [61, 60],
- [62, 65],
- [63, 67],
- [64, 68],
- [65, 69],
- [66, 70],
- [67, 72],
- [68, 75],
- [69, 80]
- ]
- }
- window.DummyDataSets = DummyDataSets
- //onsave jtree build produce jtable.browser.js
- //onsave jtree build produce jtable.node.js
- class PivotTable {
- constructor(rows, inputColumns, outputColumns) {
- this._columns = {}
- this._rows = rows
- inputColumns.forEach(col => (this._columns[col.name] = col))
- outputColumns.forEach(col => (this._columns[col.name] = col))
- }
- _getGroups(allRows, groupByColNames) {
- const rowsInGroups = new Map()
- allRows.forEach(row => {
- const groupKey = groupByColNames.map(col => row[col].toString().replace(/ /g, "")).join(" ")
- if (!rowsInGroups.has(groupKey)) rowsInGroups.set(groupKey, [])
- rowsInGroups.get(groupKey).push(row)
- })
- return rowsInGroups
- }
- getNewRows(groupByCols) {
- // make new trees
- const rowsInGroups = this._getGroups(this._rows, groupByCols)
- // Any column in the group should be reused by the children
- const columns = [
- {
- name: "count",
- type: "number",
- min: 0
- }
- ]
- groupByCols.forEach(colName => columns.push(this._columns[colName]))
- const colsToReduce = Object.values(this._columns).filter(col => !!col.reduction)
- colsToReduce.forEach(col => columns.push(col))
- // for each group
- const newRows = []
- const totalGroups = rowsInGroups.size
- for (let [groupId, group] of rowsInGroups) {
- const firstRow = group[0]
- const newRow = {}
- groupByCols.forEach(col => {
- newRow[col] = firstRow ? firstRow[col] : 0
- })
- newRow.count = group.length
- // todo: add more reductions? count, stddev, median, variance.
- colsToReduce.forEach(col => {
- const sourceColName = col.source
- const values = group.map(row => row[sourceColName]).filter(val => typeof val === "number" && !isNaN(val))
- const reduction = col.reduction
- let reducedValue = firstRow[sourceColName]
- if (reduction === "sum") reducedValue = values.reduce((prev, current) => prev + current, 0)
- if (reduction === "max") reducedValue = Math.max(...values)
- if (reduction === "min") reducedValue = Math.min(...values)
- if (reduction === "mean") reducedValue = values.reduce((prev, current) => prev + current, 0) / values.length
- newRow[col.name] = reducedValue
- })
- newRows.push(newRow)
- }
- // todo: add tests. figure out this api better.
- Object.values(columns).forEach(col => {
- // For pivot columns, remove the source and reduction info for now. Treat things as immutable.
- delete col.source
- delete col.reduction
- })
- return {
- rows: newRows,
- columns
- }
- }
- }
- var ComparisonOperators
- ;(function(ComparisonOperators) {
- ComparisonOperators["lessThan"] = "<"
- ComparisonOperators["greaterThan"] = ">"
- ComparisonOperators["lessThanOrEqual"] = "<="
- ComparisonOperators["greaterThanOrEqual"] = ">="
- ComparisonOperators["equal"] = "="
- ComparisonOperators["notEqual"] = "!="
- })(ComparisonOperators || (ComparisonOperators = {}))
- // todo: remove detectAndAddParam?
- // todo: remove rowclass param?
- class Table {
- constructor(rowsArray = [], columnsArrayOrMap = [], rowClass = Row, detectAndAddColumns = true, samplingSeed = Date.now()) {
- this._columnsMap = {}
- this._ctime = new jtree.TreeNode()._getProcessTimeInMilliseconds()
- this._tableId = this._getUniqueId()
- this._samplingSeed = samplingSeed
- // if this is ALREADY CARDS, should we be a view?
- this._rows = rowsArray.map(source => (source instanceof Row ? source : new rowClass(source, this)))
- // Add detected columns first, so they can be overwritten
- if (detectAndAddColumns) this._getDetectedColumnNames().forEach(col => this._registerColumn({ name: col }))
- if (Array.isArray(columnsArrayOrMap)) columnsArrayOrMap.forEach(col => this._registerColumn(col))
- else if (columnsArrayOrMap) this._columnsMap = columnsArrayOrMap
- }
- _getUniqueId() {
- Table._uniqueId++
- return Table._uniqueId
- }
- _registerColumn(col) {
- this._columnsMap[col.name] = new Column(col, this._getColumnValuesFromSourceAsAnyVector(col.source || col.name))
- return this
- }
- _getColumnValuesFromSourceAsAnyVector(columnName) {
- return this.getRows().map(row => row.getRowOriginalValue(columnName))
- }
- // todo: ADD TYPINGS
- _predictColumns(predictionHints, propertyNameToColumnNameMap = {}) {
- // todo: use the available input table column names, coupled with column setting we are trying to predict.
- // ie: "gender" should use "gender" col, if available
- // check all the columns for one that matches all tests. if found, return it.
- const columnsArray = this.getColumnsArray()
- const tests = predictionHints.split(",")
- const filterTests = tests.filter(test => test.includes("=")).map(test => test.split("="))
- const filterFn = col => filterTests.every(test => col[test[0]] !== undefined && col[test[0]]().toString() === test[1])
- let colsThatPassed = columnsArray.filter(col => filterFn(col))
- const notIn = {}
- const notEqualTests = tests
- .filter(test => test.startsWith("!"))
- .map(test => propertyNameToColumnNameMap[test.substr(1)])
- .filter(identity => identity)
- .forEach(name => {
- notIn[name] = true
- })
- colsThatPassed = colsThatPassed.filter(col => !notIn[col.getColumnName()])
- // for now just 1 prop ranking.
- const rankColumn = tests.find(test => !test.includes("=") && !test.includes("!"))
- let potentialCols = colsThatPassed
- if (rankColumn) potentialCols = potentialCols.sort(jtree.Utils.makeSortByFn(col => col[rankColumn]())).reverse()
- return potentialCols
- }
- getRows() {
- return this._rows
- }
- getFirstColumnAsString() {
- return this.getRows()
- .map(row => row.getFirstValue())
- .join("")
- }
- isBlankTable() {
- return this.getRowCount() === 0 && this.getColumnCount() === 0
- }
- getRowCount() {
- return this.getRows().length
- }
- getColumnCount() {
- return this.getColumnNames().length
- }
- getColumnNames() {
- return Object.keys(this.getColumnsMap())
- }
- getColumnsMap() {
- return this._columnsMap
- }
- getColumnByName(name) {
- return this.getColumnsMap()[name]
- }
- _getLowerCaseColumnsMap() {
- const map = {}
- Object.keys(this._columnsMap).forEach(key => (map[key.toLowerCase()] = key))
- return map
- }
- getTableCTime() {
- return this._ctime
- }
- filterClonedRowsByScalar(columnName, comparisonOperator, scalarValueAsString) {
- const column = this.getColumnByName(columnName)
- let typedScalarValue = column.getPrimitiveTypeObj().getAsNativeJavascriptType(scalarValueAsString)
- if (typedScalarValue instanceof Date) typedScalarValue = typedScalarValue.getTime() // todo: do I need this?
- return new Table(
- this.cloneNativeJavascriptTypedRows().filter(row => {
- let rowTypedValue = row[columnName]
- if (rowTypedValue instanceof Date) rowTypedValue = rowTypedValue.getTime() // todo: do I need this?
- if (comparisonOperator === ComparisonOperators.equal) return rowTypedValue == typedScalarValue
- if (comparisonOperator === ComparisonOperators.notEqual) return rowTypedValue != typedScalarValue
- if (comparisonOperator === ComparisonOperators.greaterThan) return rowTypedValue > typedScalarValue
- if (comparisonOperator === ComparisonOperators.lessThan) return rowTypedValue < typedScalarValue
- if (comparisonOperator === ComparisonOperators.lessThanOrEqual) return rowTypedValue <= typedScalarValue
- if (comparisonOperator === ComparisonOperators.greaterThanOrEqual) return rowTypedValue >= typedScalarValue
- }),
- this.getColumnsArrayOfObjects(),
- undefined,
- false
- )
- }
- getColumnsArray() {
- return Object.values(this.getColumnsMap())
- }
- getColumnsArrayOfObjects() {
- return this.getColumnsArray().map(col => col.toObject())
- }
- getJavascriptNativeTypedValues() {
- return this.getRows().map(row => row.rowToObjectWithOnlyNativeJavascriptTypes())
- }
- toMatrix() {
- return this.getRows().map(row => row.toVector())
- }
- toNumericMatrix() {
- // todo: right now it drops them. should we 1 hot them?
- const numericNames = this.getColumnsArray()
- .filter(col => col.isNumeric())
- .map(col => col.getColumnName())
- return this.getRows().map(row => {
- const obj = row.rowToObjectWithOnlyNativeJavascriptTypes()
- return numericNames.map(name => obj[name])
- })
- }
- clone() {
- return new Table(this.cloneNativeJavascriptTypedRows())
- }
- cloneNativeJavascriptTypedRows() {
- return this.getRows()
- .map(row => row.rowToObjectWithOnlyNativeJavascriptTypes())
- .map(obj => Object.assign({}, obj))
- }
- fillMissing(columnName, value) {
- const filled = this.cloneNativeJavascriptTypedRows().map(row => {
- if (jtree.Utils.isValueEmpty(row[columnName])) row[columnName] = value
- return row
- })
- return new Table(filled, this.getColumnsArrayOfObjects())
- }
- getTableColumnByName(name) {
- return this.getColumnsMap()[name]
- }
- _getUnionSample(sampleSet) {
- const sample = {}
- sampleSet.forEach(row => {
- row.getRowKeys().forEach(key => {
- if (!key) return
- const currentVal = sample[key]
- if (currentVal !== undefined && currentVal !== "") return
- sample[key] = row.getRowOriginalValue(key)
- })
- })
- return sample
- }
- _getSampleSet() {
- const SAMPLE_SET_SIZE = 30 // todo: fix.
- if (!this._sampleSet) this._sampleSet = jtree.Utils.sampleWithoutReplacement(this.getRows(), SAMPLE_SET_SIZE, this._samplingSeed)
- return this._sampleSet
- }
- _getDetectedColumnNames() {
- const columns = this.getColumnsMap()
- // This is run AFTER we have all user definied columns, and AFTER we have all data.
- // detect columns that appear in records
- // todo: this is broken. if you only pull 30, and its a tree or other type with varying columsn, you
- // will often miss columns.
- return Object.keys(this._getUnionSample(this._getSampleSet()))
- .map(columnName => columnName.trim()) // todo: why do we filter empties?
- .filter(identity => identity)
- .filter(col => !columns[col]) // do not overwrite any custom columns
- }
- toTypeScriptInterface() {
- const cols = this.getColumnsArray()
- .map(col => ` ${col.getColumnName()}: ${col.getPrimitiveTypeName()};`)
- .join("\n")
- return `interface Row {
- ${cols}
- }`
- }
- getColumnNamesAndTypes() {
- return this._getColumnNamesAndTypes()
- }
- getColumnNamesAndTypesAndReductions() {
- return this._getColumnNamesAndTypes(true)
- }
- _getColumnNamesAndTypes(withReductions = false) {
- const columns = this.getColumnsMap()
- return this.getColumnNames().map(name => {
- const column = columns[name]
- const obj = {
- Column: name,
- JTableType: column.getPrimitiveTypeName(),
- JavascriptType: column.getPrimitiveTypeObj().getJavascriptTypeName()
- }
- if (withReductions) Object.assign(obj, column.getReductions())
- return obj
- })
- }
- getPredictionsForAPropertyNameToColumnNameMapGivenHintsNode(hintsNode, propertyNameToColumnNameMap) {
- const results = {}
- hintsNode
- .map(columnHintNode => this.getColumnNamePredictionsForProperty(columnHintNode.getFirstWord(), columnHintNode.getContent(), propertyNameToColumnNameMap))
- .filter(pred => pred.length)
- .forEach(predictions => {
- const topPrediction = predictions[0]
- results[topPrediction.propertyName] = topPrediction.columnName
- })
- return results
- }
- getColumnNamePredictionsForProperty(propertyName, predictionHints, propertyNameToColumnNameMap) {
- const userDefinedColumnName = propertyNameToColumnNameMap[propertyName]
- if (this.getColumnsMap()[userDefinedColumnName]) return [{ propertyName: propertyName, columnName: userDefinedColumnName }] // Table has a column named this, return okay.
- // Table has a lowercase column named this. Return okay. Todo: do we want to do this?
- if (userDefinedColumnName && this._getLowerCaseColumnsMap()[userDefinedColumnName.toLowerCase()]) return [this._getLowerCaseColumnsMap()[userDefinedColumnName.toLowerCase()]]
- if (predictionHints) {
- const potentialCols = this._predictColumns(predictionHints, propertyNameToColumnNameMap)
- if (potentialCols.length) return [{ propertyName: propertyName, columnName: potentialCols[0].getColumnName() }]
- }
- const cols = this.getColumnsByImportance()
- const name = cols.length && cols[0].getColumnName()
- if (name) return [{ propertyName: propertyName, columnName: name }]
- return []
- }
- toTree() {
- return new jtree.TreeNode(this.getRows().map(row => row.getRowSourceObject()))
- }
- filterRowsByFn(fn) {
- return new Table(this.cloneNativeJavascriptTypedRows().filter((inputRow, index) => fn(inputRow, index)))
- }
- // todo: make more efficient?
- // todo: preserve columns
- addColumns(columnsToAdd) {
- const inputColDefs = this.getColumnsMap()
- return new Table(
- this.cloneNativeJavascriptTypedRows().map(inputRow => {
- columnsToAdd.forEach(newCol => {
- let newValue
- if (newCol.accessorFn) newValue = newCol.accessorFn(inputRow)
- else newValue = Column.convertValueToNumeric(inputRow[newCol.source], inputColDefs[newCol.source].getPrimitiveTypeName(), newCol.type, newCol.mathFn)
- inputRow[newCol.name] = newValue
- })
- return inputRow
- })
- )
- }
- // todo: can be made more effcicent
- changeColumnType(columnName, newType) {
- const cols = this.getColumnsArrayOfObjects()
- cols.forEach(col => {
- if (col.name === columnName) col.type = newType
- })
- return new Table(this.cloneNativeJavascriptTypedRows(), cols, undefined, false)
- }
- renameColumns(nameMap) {
- const rows = this.getRows()
- .map(row => row.rowToObjectWithOnlyNativeJavascriptTypes())
- .map(obj => {
- Object.keys(nameMap).forEach(oldName => {
- const newName = nameMap[oldName]
- if (newName === oldName) return
- obj[newName] = obj[oldName]
- delete obj[oldName]
- })
- return obj
- })
- const cols = this.getColumnsArrayOfObjects()
- cols.forEach(col => {
- if (nameMap[col.name]) col.name = nameMap[col.name]
- })
- return new Table(rows, cols, undefined, false)
- }
- cloneWithCleanColumnNames() {
- const nameMap = {}
- const cols = this.getColumnsArrayOfObjects()
- cols.forEach(col => {
- nameMap[col.name] = col.name.replace(/[^a-z0-9]/gi, "")
- })
- return this.renameColumns(nameMap)
- }
- // todo: can be made more effcicent
- dropAllColumnsExcept(columnsToKeep) {
- return new Table(
- this.cloneNativeJavascriptTypedRows().map((inputRow, rowIndex) => {
- const result = {}
- columnsToKeep.forEach(name => {
- result[name] = inputRow[name]
- })
- return result
- }),
- columnsToKeep.map(colName => this.getColumnByName(colName).toObject())
- )
- }
- // todo: we don't need any cloning here--just create a new row, new rows array, new and add the pointers
- // to same rows
- addRow(rowWords) {
- const rows = this.cloneNativeJavascriptTypedRows()
- const newRow = {}
- Object.keys(rows[0] || {}).forEach((key, index) => {
- // todo: handle typings
- newRow[key] = rowWords[index]
- })
- rows.push(newRow)
- return new Table(rows, this.getColumnsMap())
- }
- _synthesizeRow(randomNumberFn) {
- const row = {}
- this.getColumnsArray().forEach(column => {
- row[column.getColumnName()] = column.synthesizeValue(randomNumberFn)
- })
- return row
- }
- synthesizeTable(rowcount, seed) {
- const randomNumberFn = jtree.Utils.makeSemiRandomFn(seed)
- const rows = []
- while (rowcount) {
- rows.push(this._synthesizeRow(randomNumberFn))
- rowcount--
- }
- return new Table(rows, this.getColumnsArray().map(col => col.toObject()))
- }
- // todo: we don't need any cloning here--here create a new sorted array with poitners
- // to same rows
- shuffleRows() {
- // todo: add seed!
- // cellType randomSeed int
- // description An integer to seed the random number generator with.
- return new Table(jtree.Utils.shuffleInPlace(this.getRows().slice(0)), this.getColumnsMap())
- }
- reverseRows() {
- const rows = this.getRows().slice(0)
- rows.reverse()
- return new Table(rows, this.getColumnsMap())
- }
- // Pivot is shorthand for group and reduce?
- makePivotTable(groupByColumnNames, newCols) {
- const inputColumns = this.getColumnsArrayOfObjects()
- const colMap = {}
- inputColumns.forEach(col => (colMap[col.name] = true))
- const groupByCols = groupByColumnNames.filter(col => colMap[col])
- const pivotTable = new PivotTable(this.getJavascriptNativeTypedValues(), inputColumns, newCols).getNewRows(groupByCols)
- return new Table(pivotTable.rows, pivotTable.columns)
- }
- sortBy(colNames) {
- const colAccessorFns = colNames.map(colName => row => row.rowToObjectWithOnlyNativeJavascriptTypes()[colName])
- const rows = this.getRows().sort(jtree.Utils.makeSortByFn(colAccessorFns))
- return new Table(rows, this.getColumnsMap())
- }
- toDelimited(delimiter) {
- return this.toTree().toDelimited(delimiter, this.getColumnNames())
- }
- toSimpleSchema() {
- return this.getColumnsArray()
- .map(col => `${col.getColumnName()} ${col.getPrimitiveTypeName()}`)
- .join("\n")
- }
- // todo: toProtoBuf, toSqlLite, toJsonSchema, toJsonLd, toCapnProto, toApacheArrow, toFlatBuffers
- // guess which are the more important/informative/interesting columns
- getColumnsByImportance() {
- const columnsMap = this.getColumnsMap()
- const aIsMoreImportant = -1
- const bIsMoreImportant = 1
- const cols = Object.keys(columnsMap).map(columnName => columnsMap[columnName])
- cols.sort((colA, colB) => {
- if (colA.getTitlePotential() > colB.getTitlePotential()) return aIsMoreImportant
- if (colB.getTitlePotential() > colA.getTitlePotential()) return bIsMoreImportant
- if (colA.getBlankPercentage() > 0.5 || colB.getBlankPercentage() > 0.5) {
- if (colA.getBlankPercentage() > colB.getBlankPercentage()) return bIsMoreImportant
- else if (colB.getBlankPercentage() > colA.getBlankPercentage()) return aIsMoreImportant
- }
- if (colA.isTemporal() && !colB.isTemporal()) return aIsMoreImportant
- if (!colA.isTemporal() && colB.isTemporal()) return bIsMoreImportant
- if (colA.isLink() && !colB.isLink()) return bIsMoreImportant
- else if (!colA.isLink() && colB.isLink()) return aIsMoreImportant
- if (colA.isString() && !colB.isString()) return bIsMoreImportant
- else if (!colA.isString() && colB.isString()) return aIsMoreImportant
- if (colA.isString() && colB.isString()) {
- if (colA.getEntropy() > 4 && colA.getEntropy() < 8 && colA.getEntropy() > colB.getEntropy()) return aIsMoreImportant
- if (colA.getEstimatedTextLength() > colB.getEstimatedTextLength()) return bIsMoreImportant
- else return aIsMoreImportant
- }
- return 0
- })
- return cols
- }
- }
- Table._uniqueId = 0
- window.Table = Table
- window.ComparisonOperators = ComparisonOperators
- ;
-
- {
- class stumpNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- errorNode,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- blockquote: htmlTagNode,
- colgroup: htmlTagNode,
- datalist: htmlTagNode,
- fieldset: htmlTagNode,
- menuitem: htmlTagNode,
- noscript: htmlTagNode,
- optgroup: htmlTagNode,
- progress: htmlTagNode,
- styleTag: htmlTagNode,
- template: htmlTagNode,
- textarea: htmlTagNode,
- titleTag: htmlTagNode,
- address: htmlTagNode,
- article: htmlTagNode,
- caption: htmlTagNode,
- details: htmlTagNode,
- section: htmlTagNode,
- summary: htmlTagNode,
- button: htmlTagNode,
- canvas: htmlTagNode,
- dialog: htmlTagNode,
- figure: htmlTagNode,
- footer: htmlTagNode,
- header: htmlTagNode,
- hgroup: htmlTagNode,
- iframe: htmlTagNode,
- keygen: htmlTagNode,
- legend: htmlTagNode,
- object: htmlTagNode,
- option: htmlTagNode,
- output: htmlTagNode,
- script: htmlTagNode,
- select: htmlTagNode,
- source: htmlTagNode,
- strong: htmlTagNode,
- aside: htmlTagNode,
- embed: htmlTagNode,
- input: htmlTagNode,
- label: htmlTagNode,
- meter: htmlTagNode,
- param: htmlTagNode,
- small: htmlTagNode,
- table: htmlTagNode,
- tbody: htmlTagNode,
- tfoot: htmlTagNode,
- thead: htmlTagNode,
- track: htmlTagNode,
- video: htmlTagNode,
- abbr: htmlTagNode,
- area: htmlTagNode,
- base: htmlTagNode,
- body: htmlTagNode,
- code: htmlTagNode,
- form: htmlTagNode,
- head: htmlTagNode,
- html: htmlTagNode,
- link: htmlTagNode,
- main: htmlTagNode,
- mark: htmlTagNode,
- menu: htmlTagNode,
- meta: htmlTagNode,
- ruby: htmlTagNode,
- samp: htmlTagNode,
- span: htmlTagNode,
- time: htmlTagNode,
- bdi: htmlTagNode,
- bdo: htmlTagNode,
- col: htmlTagNode,
- del: htmlTagNode,
- dfn: htmlTagNode,
- div: htmlTagNode,
- img: htmlTagNode,
- ins: htmlTagNode,
- kbd: htmlTagNode,
- map: htmlTagNode,
- nav: htmlTagNode,
- pre: htmlTagNode,
- rtc: htmlTagNode,
- sub: htmlTagNode,
- sup: htmlTagNode,
- var: htmlTagNode,
- wbr: htmlTagNode,
- br: htmlTagNode,
- dd: htmlTagNode,
- dl: htmlTagNode,
- dt: htmlTagNode,
- em: htmlTagNode,
- h1: htmlTagNode,
- h2: htmlTagNode,
- h3: htmlTagNode,
- h4: htmlTagNode,
- h5: htmlTagNode,
- h6: htmlTagNode,
- hr: htmlTagNode,
- li: htmlTagNode,
- ol: htmlTagNode,
- rb: htmlTagNode,
- rp: htmlTagNode,
- rt: htmlTagNode,
- td: htmlTagNode,
- th: htmlTagNode,
- tr: htmlTagNode,
- ul: htmlTagNode,
- a: htmlTagNode,
- b: htmlTagNode,
- i: htmlTagNode,
- p: htmlTagNode,
- q: htmlTagNode,
- s: htmlTagNode,
- u: htmlTagNode
- }),
- [{ regex: /^$/, nodeConstructor: blankLineNode }]
- )
- }
- compile() {
- return this.toHtml()
- }
- _getHtmlJoinByCharacter() {
- return ""
- }
- getHandGrammarProgram() {
- if (!this._cachedHandGrammarProgramRoot)
- this._cachedHandGrammarProgramRoot = new jtree.HandGrammarProgram(`tooling onsave jtree build produceLang stump
- anyCell
- keywordCell
- emptyCell
- extraCell
- highlightScope invalid
- anyHtmlContentCell
- highlightScope string
- attributeValueCell
- highlightScope constant.language
- htmlTagNameCell
- highlightScope variable.function
- extends keywordCell
- enum a abbr address area article aside b base bdi bdo blockquote body br button canvas caption code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figure footer form h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins kbd keygen label legend li link main map mark menu menuitem meta meter nav noscript object ol optgroup option output p param pre progress q rb rp rt rtc ruby s samp script section select small source span strong styleTag sub summary sup table tbody td template textarea tfoot th thead time titleTag tr track u ul var video wbr
- htmlAttributeNameCell
- highlightScope entity.name.type
- extends keywordCell
- enum accept accept-charset accesskey action align alt async autocomplete autofocus autoplay bgcolor border charset checked class color cols colspan content contenteditable controls coords datetime default defer dir dirname disabled download draggable dropzone enctype for formaction headers height hidden high href hreflang http-equiv id ismap kind lang list loop low max maxlength media method min multiple muted name novalidate onabort onafterprint onbeforeprint onbeforeunload onblur oncanplay oncanplaythrough onchange onclick oncontextmenu oncopy oncuechange oncut ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended onerror onfocus onhashchange oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onoffline ononline onpagehide onpageshow onpaste onpause onplay onplaying onpopstate onprogress onratechange onreset onresize onscroll onsearch onseeked onseeking onselect onstalled onstorage onsubmit onsuspend ontimeupdate ontoggle onunload onvolumechange onwaiting onwheel open optimum pattern placeholder poster preload readonly rel required reversed rows rowspan sandbox scope selected shape size sizes spellcheck src srcdoc srclang srcset start step style tabindex target title translate type usemap value width wrap
- bernKeywordCell
- enum bern
- extends keywordCell
- stumpNode
- root
- description A prefix Tree Language that compiles to HTML.
- catchAllNodeType errorNode
- inScope htmlTagNode blankLineNode
- example
- div
- h1 hello world
- compilesTo html
- javascript
- compile() {
- return this.toHtml()
- }
- _getHtmlJoinByCharacter() {
- return ""
- }
- blankLineNode
- pattern ^$
- tags doNotSynthesize
- cells emptyCell
- javascript
- _toHtml() {
- return ""
- }
- getTextContent() {return ""}
- htmlTagNode
- inScope bernNode htmlTagNode htmlAttributeNode blankLineNode
- catchAllCellType anyHtmlContentCell
- cells htmlTagNameCell
- javascript
- getTag() {
- // we need to remove the "Tag" bit to handle the style and title attribute/tag conflict.
- const firstWord = this.getFirstWord()
- const map = {
- titleTag: "title",
- styleTag: "style"
- }
- return map[firstWord] || firstWord
- }
- _getHtmlJoinByCharacter() {
- return ""
- }
- toHtmlWithSuids() {
- return this._toHtml(undefined, true)
- }
- _getOneLiner() {
- const oneLinerWords = this.getWordsFrom(1)
- return oneLinerWords.length ? oneLinerWords.join(" ") : ""
- }
- getTextContent() {
- return this._getOneLiner()
- }
- shouldCollapse() {
- return this.has("collapse")
- }
- _toHtml(indentCount, withSuid) {
- const tag = this.getTag()
- const children = this.map(child => child._toHtml(indentCount + 1, withSuid)).join("")
- const attributesStr = this.filter(node => node.isAttributeNode)
- .map(child => child.getAttribute())
- .join("")
- const indent = " ".repeat(indentCount)
- const collapse = this.shouldCollapse()
- const indentForChildNodes = !collapse && this.getChildInstancesOfNodeTypeId("htmlTagNode").length > 0
- const suid = withSuid ? \` stumpUid="\${this._getUid()}"\` : ""
- const oneLiner = this._getOneLiner()
- return \`\${!collapse ? indent : ""}<\${tag}\${attributesStr}\${suid}>\${oneLiner}\${indentForChildNodes ? "\\n" : ""}\${children}\${tag}>\${collapse ? "" : "\\n"}\`
- }
- removeCssStumpNode() {
- return this.removeStumpNode()
- }
- removeStumpNode() {
- this.getShadow().removeShadow()
- return this.destroy()
- }
- getNodeByGuid(guid) {
- return this.getTopDownArray().find(node => node._getUid() === guid)
- }
- addClassToStumpNode(className) {
- const classNode = this.touchNode("class")
- const words = classNode.getWordsFrom(1)
- // note: we call add on shadow regardless, because at the moment stump may have gotten out of
- // sync with shadow, if things modified the dom. todo: cleanup.
- this.getShadow().addClassToShadow(className)
- if (words.includes(className)) return this
- words.push(className)
- classNode.setContent(words.join(this.getWordBreakSymbol()))
- return this
- }
- removeClassFromStumpNode(className) {
- const classNode = this.getNode("class")
- if (!classNode) return this
- const newClasses = classNode.getWords().filter(word => word !== className)
- if (!newClasses.length) classNode.destroy()
- else classNode.setContent(newClasses.join(" "))
- this.getShadow().removeClassFromShadow(className)
- return this
- }
- stumpNodeHasClass(className) {
- const classNode = this.getNode("class")
- return classNode && classNode.getWords().includes(className) ? true : false
- }
- isStumpNodeCheckbox() {
- return this.get("type") === "checkbox"
- }
- getShadow() {
- if (!this._shadow) {
- const shadowClass = this.getShadowClass()
- this._shadow = new shadowClass(this)
- }
- return this._shadow
- }
- insertCssChildNode(text, index) {
- return this.insertChildNode(text, index)
- }
- insertChildNode(text, index) {
- const singleNode = new jtree.TreeNode(text).getChildren()[0]
- const newNode = this.insertLineAndChildren(singleNode.getLine(), singleNode.childrenToString(), index)
- const stumpNodeIndex = this.getChildInstancesOfNodeTypeId("htmlTagNode").indexOf(newNode)
- this.getShadow().insertHtmlNode(newNode, stumpNodeIndex)
- return newNode
- }
- isInputType() {
- return ["input", "textarea"].includes(this.getTag()) || this.get("contenteditable") === "true"
- }
- findStumpNodeByChild(line) {
- return this.findStumpNodesByChild(line)[0]
- }
- findStumpNodeByChildString(line) {
- return this.getTopDownArray().find(node =>
- node
- .map(child => child.getLine())
- .join("\\n")
- .includes(line)
- )
- }
- findStumpNodeByFirstWord(firstWord) {
- return this._findStumpNodesByBase(firstWord)[0]
- }
- _findStumpNodesByBase(firstWord) {
- return this.getTopDownArray().filter(node => node.doesExtend("htmlTagNode") && node.getFirstWord() === firstWord)
- }
- hasLine(line) {
- return this.getChildren().some(node => node.getLine() === line)
- }
- findStumpNodesByChild(line) {
- return this.getTopDownArray().filter(node => node.doesExtend("htmlTagNode") && node.hasLine(line))
- }
- findStumpNodesWithClass(className) {
- return this.getTopDownArray().filter(
- node =>
- node.doesExtend("htmlTagNode") &&
- node.has("class") &&
- node
- .getNode("class")
- .getWords()
- .includes(className)
- )
- }
- getShadowClass() {
- return this.getParent().getShadowClass()
- }
- // todo: should not be here
- getStumpNodeTreeComponent() {
- return this._treeComponent || this.getParent().getStumpNodeTreeComponent()
- }
- // todo: should not be here
- setStumpNodeTreeComponent(treeComponent) {
- this._treeComponent = treeComponent
- return this
- }
- setStumpNodeCss(css) {
- this.getShadow().setShadowCss(css)
- return this
- }
- getStumpNodeCss(prop) {
- return this.getShadow().getShadowCss(prop)
- }
- getStumpNodeAttr(key) {
- return this.get(key)
- }
- setStumpNodeAttr(key, value) {
- // todo
- return this
- }
- toHtml() {
- return this._toHtml()
- }
- errorNode
- baseNodeType errorNode
- htmlAttributeNode
- javascript
- _toHtml() {
- return ""
- }
- getTextContent() {return ""}
- getAttribute() {
- return \` \${this.getFirstWord()}="\${this.getContent()}"\`
- }
- boolean isAttributeNode true
- boolean isTileAttribute true
- catchAllNodeType errorNode
- catchAllCellType attributeValueCell
- cells htmlAttributeNameCell
- stumpExtendedAttributeNameCell
- extends htmlAttributeNameCell
- enum collapse blurCommand changeCommand clickCommand contextMenuCommand doubleClickCommand lineClickCommand lineShiftClickCommand shiftClickCommand
- stumpExtendedAttributeNode
- description Node types not present in HTML but included in stump.
- extends htmlAttributeNode
- cells stumpExtendedAttributeNameCell
- lineOfHtmlContentNode
- boolean isTileAttribute true
- catchAllNodeType lineOfHtmlContentNode
- catchAllCellType anyHtmlContentCell
- javascript
- getTextContent() {return this.getLine()}
- bernNode
- boolean isTileAttribute true
- todo Rename this node type
- description This is a node where you can put any HTML content. It is called "bern" until someone comes up with a better name.
- catchAllNodeType lineOfHtmlContentNode
- javascript
- _toHtml() {
- return this.childrenToString()
- }
- getTextContent() {return ""}
- cells bernKeywordCell`)
- return this._cachedHandGrammarProgramRoot
- }
- static getNodeTypeMap() {
- return {
- stumpNode: stumpNode,
- blankLineNode: blankLineNode,
- htmlTagNode: htmlTagNode,
- errorNode: errorNode,
- htmlAttributeNode: htmlAttributeNode,
- stumpExtendedAttributeNode: stumpExtendedAttributeNode,
- lineOfHtmlContentNode: lineOfHtmlContentNode,
- bernNode: bernNode
- }
- }
- }
-
- class blankLineNode extends jtree.GrammarBackedNode {
- get emptyCell() {
- return this.getWord(0)
- }
- _toHtml() {
- return ""
- }
- getTextContent() {
- return ""
- }
- }
-
- class htmlTagNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- blockquote: htmlTagNode,
- colgroup: htmlTagNode,
- datalist: htmlTagNode,
- fieldset: htmlTagNode,
- menuitem: htmlTagNode,
- noscript: htmlTagNode,
- optgroup: htmlTagNode,
- progress: htmlTagNode,
- styleTag: htmlTagNode,
- template: htmlTagNode,
- textarea: htmlTagNode,
- titleTag: htmlTagNode,
- address: htmlTagNode,
- article: htmlTagNode,
- caption: htmlTagNode,
- details: htmlTagNode,
- section: htmlTagNode,
- summary: htmlTagNode,
- button: htmlTagNode,
- canvas: htmlTagNode,
- dialog: htmlTagNode,
- figure: htmlTagNode,
- footer: htmlTagNode,
- header: htmlTagNode,
- hgroup: htmlTagNode,
- iframe: htmlTagNode,
- keygen: htmlTagNode,
- legend: htmlTagNode,
- object: htmlTagNode,
- option: htmlTagNode,
- output: htmlTagNode,
- script: htmlTagNode,
- select: htmlTagNode,
- source: htmlTagNode,
- strong: htmlTagNode,
- aside: htmlTagNode,
- embed: htmlTagNode,
- input: htmlTagNode,
- label: htmlTagNode,
- meter: htmlTagNode,
- param: htmlTagNode,
- small: htmlTagNode,
- table: htmlTagNode,
- tbody: htmlTagNode,
- tfoot: htmlTagNode,
- thead: htmlTagNode,
- track: htmlTagNode,
- video: htmlTagNode,
- abbr: htmlTagNode,
- area: htmlTagNode,
- base: htmlTagNode,
- body: htmlTagNode,
- code: htmlTagNode,
- form: htmlTagNode,
- head: htmlTagNode,
- html: htmlTagNode,
- link: htmlTagNode,
- main: htmlTagNode,
- mark: htmlTagNode,
- menu: htmlTagNode,
- meta: htmlTagNode,
- ruby: htmlTagNode,
- samp: htmlTagNode,
- span: htmlTagNode,
- time: htmlTagNode,
- bdi: htmlTagNode,
- bdo: htmlTagNode,
- col: htmlTagNode,
- del: htmlTagNode,
- dfn: htmlTagNode,
- div: htmlTagNode,
- img: htmlTagNode,
- ins: htmlTagNode,
- kbd: htmlTagNode,
- map: htmlTagNode,
- nav: htmlTagNode,
- pre: htmlTagNode,
- rtc: htmlTagNode,
- sub: htmlTagNode,
- sup: htmlTagNode,
- var: htmlTagNode,
- wbr: htmlTagNode,
- br: htmlTagNode,
- dd: htmlTagNode,
- dl: htmlTagNode,
- dt: htmlTagNode,
- em: htmlTagNode,
- h1: htmlTagNode,
- h2: htmlTagNode,
- h3: htmlTagNode,
- h4: htmlTagNode,
- h5: htmlTagNode,
- h6: htmlTagNode,
- hr: htmlTagNode,
- li: htmlTagNode,
- ol: htmlTagNode,
- rb: htmlTagNode,
- rp: htmlTagNode,
- rt: htmlTagNode,
- td: htmlTagNode,
- th: htmlTagNode,
- tr: htmlTagNode,
- ul: htmlTagNode,
- a: htmlTagNode,
- b: htmlTagNode,
- i: htmlTagNode,
- p: htmlTagNode,
- q: htmlTagNode,
- s: htmlTagNode,
- u: htmlTagNode,
- oncanplaythrough: htmlAttributeNode,
- ondurationchange: htmlAttributeNode,
- onloadedmetadata: htmlAttributeNode,
- contenteditable: htmlAttributeNode,
- "accept-charset": htmlAttributeNode,
- onbeforeunload: htmlAttributeNode,
- onvolumechange: htmlAttributeNode,
- onbeforeprint: htmlAttributeNode,
- oncontextmenu: htmlAttributeNode,
- autocomplete: htmlAttributeNode,
- onafterprint: htmlAttributeNode,
- onhashchange: htmlAttributeNode,
- onloadeddata: htmlAttributeNode,
- onmousewheel: htmlAttributeNode,
- onratechange: htmlAttributeNode,
- ontimeupdate: htmlAttributeNode,
- oncuechange: htmlAttributeNode,
- ondragenter: htmlAttributeNode,
- ondragleave: htmlAttributeNode,
- ondragstart: htmlAttributeNode,
- onloadstart: htmlAttributeNode,
- onmousedown: htmlAttributeNode,
- onmousemove: htmlAttributeNode,
- onmouseover: htmlAttributeNode,
- placeholder: htmlAttributeNode,
- formaction: htmlAttributeNode,
- "http-equiv": htmlAttributeNode,
- novalidate: htmlAttributeNode,
- ondblclick: htmlAttributeNode,
- ondragover: htmlAttributeNode,
- onkeypress: htmlAttributeNode,
- onmouseout: htmlAttributeNode,
- onpagehide: htmlAttributeNode,
- onpageshow: htmlAttributeNode,
- onpopstate: htmlAttributeNode,
- onprogress: htmlAttributeNode,
- spellcheck: htmlAttributeNode,
- accesskey: htmlAttributeNode,
- autofocus: htmlAttributeNode,
- draggable: htmlAttributeNode,
- maxlength: htmlAttributeNode,
- oncanplay: htmlAttributeNode,
- ondragend: htmlAttributeNode,
- onemptied: htmlAttributeNode,
- oninvalid: htmlAttributeNode,
- onkeydown: htmlAttributeNode,
- onmouseup: htmlAttributeNode,
- onoffline: htmlAttributeNode,
- onplaying: htmlAttributeNode,
- onseeking: htmlAttributeNode,
- onstalled: htmlAttributeNode,
- onstorage: htmlAttributeNode,
- onsuspend: htmlAttributeNode,
- onwaiting: htmlAttributeNode,
- translate: htmlAttributeNode,
- autoplay: htmlAttributeNode,
- controls: htmlAttributeNode,
- datetime: htmlAttributeNode,
- disabled: htmlAttributeNode,
- download: htmlAttributeNode,
- dropzone: htmlAttributeNode,
- hreflang: htmlAttributeNode,
- multiple: htmlAttributeNode,
- onchange: htmlAttributeNode,
- ononline: htmlAttributeNode,
- onresize: htmlAttributeNode,
- onscroll: htmlAttributeNode,
- onsearch: htmlAttributeNode,
- onseeked: htmlAttributeNode,
- onselect: htmlAttributeNode,
- onsubmit: htmlAttributeNode,
- ontoggle: htmlAttributeNode,
- onunload: htmlAttributeNode,
- readonly: htmlAttributeNode,
- required: htmlAttributeNode,
- reversed: htmlAttributeNode,
- selected: htmlAttributeNode,
- tabindex: htmlAttributeNode,
- bgcolor: htmlAttributeNode,
- charset: htmlAttributeNode,
- checked: htmlAttributeNode,
- colspan: htmlAttributeNode,
- content: htmlAttributeNode,
- default: htmlAttributeNode,
- dirname: htmlAttributeNode,
- enctype: htmlAttributeNode,
- headers: htmlAttributeNode,
- onabort: htmlAttributeNode,
- onclick: htmlAttributeNode,
- onended: htmlAttributeNode,
- onerror: htmlAttributeNode,
- onfocus: htmlAttributeNode,
- oninput: htmlAttributeNode,
- onkeyup: htmlAttributeNode,
- onpaste: htmlAttributeNode,
- onpause: htmlAttributeNode,
- onreset: htmlAttributeNode,
- onwheel: htmlAttributeNode,
- optimum: htmlAttributeNode,
- pattern: htmlAttributeNode,
- preload: htmlAttributeNode,
- rowspan: htmlAttributeNode,
- sandbox: htmlAttributeNode,
- srclang: htmlAttributeNode,
- accept: htmlAttributeNode,
- action: htmlAttributeNode,
- border: htmlAttributeNode,
- coords: htmlAttributeNode,
- height: htmlAttributeNode,
- hidden: htmlAttributeNode,
- method: htmlAttributeNode,
- onblur: htmlAttributeNode,
- oncopy: htmlAttributeNode,
- ondrag: htmlAttributeNode,
- ondrop: htmlAttributeNode,
- onload: htmlAttributeNode,
- onplay: htmlAttributeNode,
- poster: htmlAttributeNode,
- srcdoc: htmlAttributeNode,
- srcset: htmlAttributeNode,
- target: htmlAttributeNode,
- usemap: htmlAttributeNode,
- align: htmlAttributeNode,
- async: htmlAttributeNode,
- class: htmlAttributeNode,
- color: htmlAttributeNode,
- defer: htmlAttributeNode,
- ismap: htmlAttributeNode,
- media: htmlAttributeNode,
- muted: htmlAttributeNode,
- oncut: htmlAttributeNode,
- scope: htmlAttributeNode,
- shape: htmlAttributeNode,
- sizes: htmlAttributeNode,
- start: htmlAttributeNode,
- style: htmlAttributeNode,
- title: htmlAttributeNode,
- value: htmlAttributeNode,
- width: htmlAttributeNode,
- cols: htmlAttributeNode,
- high: htmlAttributeNode,
- href: htmlAttributeNode,
- kind: htmlAttributeNode,
- lang: htmlAttributeNode,
- list: htmlAttributeNode,
- loop: htmlAttributeNode,
- name: htmlAttributeNode,
- open: htmlAttributeNode,
- rows: htmlAttributeNode,
- size: htmlAttributeNode,
- step: htmlAttributeNode,
- type: htmlAttributeNode,
- wrap: htmlAttributeNode,
- alt: htmlAttributeNode,
- dir: htmlAttributeNode,
- for: htmlAttributeNode,
- low: htmlAttributeNode,
- max: htmlAttributeNode,
- min: htmlAttributeNode,
- rel: htmlAttributeNode,
- src: htmlAttributeNode,
- id: htmlAttributeNode,
- lineShiftClickCommand: stumpExtendedAttributeNode,
- contextMenuCommand: stumpExtendedAttributeNode,
- doubleClickCommand: stumpExtendedAttributeNode,
- shiftClickCommand: stumpExtendedAttributeNode,
- lineClickCommand: stumpExtendedAttributeNode,
- changeCommand: stumpExtendedAttributeNode,
- clickCommand: stumpExtendedAttributeNode,
- blurCommand: stumpExtendedAttributeNode,
- collapse: stumpExtendedAttributeNode,
- bern: bernNode
- }),
- [{ regex: /^$/, nodeConstructor: blankLineNode }]
- )
- }
- get htmlTagNameCell() {
- return this.getWord(0)
- }
- get anyHtmlContentCell() {
- return this.getWordsFrom(1)
- }
- getTag() {
- // we need to remove the "Tag" bit to handle the style and title attribute/tag conflict.
- const firstWord = this.getFirstWord()
- const map = {
- titleTag: "title",
- styleTag: "style"
- }
- return map[firstWord] || firstWord
- }
- _getHtmlJoinByCharacter() {
- return ""
- }
- toHtmlWithSuids() {
- return this._toHtml(undefined, true)
- }
- _getOneLiner() {
- const oneLinerWords = this.getWordsFrom(1)
- return oneLinerWords.length ? oneLinerWords.join(" ") : ""
- }
- getTextContent() {
- return this._getOneLiner()
- }
- shouldCollapse() {
- return this.has("collapse")
- }
- _toHtml(indentCount, withSuid) {
- const tag = this.getTag()
- const children = this.map(child => child._toHtml(indentCount + 1, withSuid)).join("")
- const attributesStr = this.filter(node => node.isAttributeNode)
- .map(child => child.getAttribute())
- .join("")
- const indent = " ".repeat(indentCount)
- const collapse = this.shouldCollapse()
- const indentForChildNodes = !collapse && this.getChildInstancesOfNodeTypeId("htmlTagNode").length > 0
- const suid = withSuid ? ` stumpUid="${this._getUid()}"` : ""
- const oneLiner = this._getOneLiner()
- return `${!collapse ? indent : ""}<${tag}${attributesStr}${suid}>${oneLiner}${indentForChildNodes ? "\n" : ""}${children}${tag}>${collapse ? "" : "\n"}`
- }
- removeCssStumpNode() {
- return this.removeStumpNode()
- }
- removeStumpNode() {
- this.getShadow().removeShadow()
- return this.destroy()
- }
- getNodeByGuid(guid) {
- return this.getTopDownArray().find(node => node._getUid() === guid)
- }
- addClassToStumpNode(className) {
- const classNode = this.touchNode("class")
- const words = classNode.getWordsFrom(1)
- // note: we call add on shadow regardless, because at the moment stump may have gotten out of
- // sync with shadow, if things modified the dom. todo: cleanup.
- this.getShadow().addClassToShadow(className)
- if (words.includes(className)) return this
- words.push(className)
- classNode.setContent(words.join(this.getWordBreakSymbol()))
- return this
- }
- removeClassFromStumpNode(className) {
- const classNode = this.getNode("class")
- if (!classNode) return this
- const newClasses = classNode.getWords().filter(word => word !== className)
- if (!newClasses.length) classNode.destroy()
- else classNode.setContent(newClasses.join(" "))
- this.getShadow().removeClassFromShadow(className)
- return this
- }
- stumpNodeHasClass(className) {
- const classNode = this.getNode("class")
- return classNode && classNode.getWords().includes(className) ? true : false
- }
- isStumpNodeCheckbox() {
- return this.get("type") === "checkbox"
- }
- getShadow() {
- if (!this._shadow) {
- const shadowClass = this.getShadowClass()
- this._shadow = new shadowClass(this)
- }
- return this._shadow
- }
- insertCssChildNode(text, index) {
- return this.insertChildNode(text, index)
- }
- insertChildNode(text, index) {
- const singleNode = new jtree.TreeNode(text).getChildren()[0]
- const newNode = this.insertLineAndChildren(singleNode.getLine(), singleNode.childrenToString(), index)
- const stumpNodeIndex = this.getChildInstancesOfNodeTypeId("htmlTagNode").indexOf(newNode)
- this.getShadow().insertHtmlNode(newNode, stumpNodeIndex)
- return newNode
- }
- isInputType() {
- return ["input", "textarea"].includes(this.getTag()) || this.get("contenteditable") === "true"
- }
- findStumpNodeByChild(line) {
- return this.findStumpNodesByChild(line)[0]
- }
- findStumpNodeByChildString(line) {
- return this.getTopDownArray().find(node =>
- node
- .map(child => child.getLine())
- .join("\n")
- .includes(line)
- )
- }
- findStumpNodeByFirstWord(firstWord) {
- return this._findStumpNodesByBase(firstWord)[0]
- }
- _findStumpNodesByBase(firstWord) {
- return this.getTopDownArray().filter(node => node.doesExtend("htmlTagNode") && node.getFirstWord() === firstWord)
- }
- hasLine(line) {
- return this.getChildren().some(node => node.getLine() === line)
- }
- findStumpNodesByChild(line) {
- return this.getTopDownArray().filter(node => node.doesExtend("htmlTagNode") && node.hasLine(line))
- }
- findStumpNodesWithClass(className) {
- return this.getTopDownArray().filter(
- node =>
- node.doesExtend("htmlTagNode") &&
- node.has("class") &&
- node
- .getNode("class")
- .getWords()
- .includes(className)
- )
- }
- getShadowClass() {
- return this.getParent().getShadowClass()
- }
- // todo: should not be here
- getStumpNodeTreeComponent() {
- return this._treeComponent || this.getParent().getStumpNodeTreeComponent()
- }
- // todo: should not be here
- setStumpNodeTreeComponent(treeComponent) {
- this._treeComponent = treeComponent
- return this
- }
- setStumpNodeCss(css) {
- this.getShadow().setShadowCss(css)
- return this
- }
- getStumpNodeCss(prop) {
- return this.getShadow().getShadowCss(prop)
- }
- getStumpNodeAttr(key) {
- return this.get(key)
- }
- setStumpNodeAttr(key, value) {
- // todo
- return this
- }
- toHtml() {
- return this._toHtml()
- }
- }
-
- class errorNode extends jtree.GrammarBackedNode {
- getErrors() {
- return this._getErrorNodeErrors()
- }
- }
-
- class htmlAttributeNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(errorNode, undefined, undefined)
- }
- get htmlAttributeNameCell() {
- return this.getWord(0)
- }
- get attributeValueCell() {
- return this.getWordsFrom(1)
- }
- get isTileAttribute() {
- return true
- }
- get isAttributeNode() {
- return true
- }
- _toHtml() {
- return ""
- }
- getTextContent() {
- return ""
- }
- getAttribute() {
- return ` ${this.getFirstWord()}="${this.getContent()}"`
- }
- }
-
- class stumpExtendedAttributeNode extends htmlAttributeNode {
- get stumpExtendedAttributeNameCell() {
- return this.getWord(0)
- }
- }
-
- class lineOfHtmlContentNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(lineOfHtmlContentNode, undefined, undefined)
- }
- get anyHtmlContentCell() {
- return this.getWordsFrom(0)
- }
- get isTileAttribute() {
- return true
- }
- getTextContent() {
- return this.getLine()
- }
- }
-
- class bernNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(lineOfHtmlContentNode, undefined, undefined)
- }
- get bernKeywordCell() {
- return this.getWord(0)
- }
- get isTileAttribute() {
- return true
- }
- _toHtml() {
- return this.childrenToString()
- }
- getTextContent() {
- return ""
- }
- }
-
- window.stumpNode = stumpNode
- }
- ;
-
- {
- class fireNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- errorNode,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- block: blockNode,
- function: functionNode,
- if: ifNode,
- while: whileNode,
- divide: divideNode,
- modulo: moduloNode,
- multiply: multiplyNode,
- substract: substractNode,
- add: addNode,
- greaterThan: greaterThanNode,
- greaterThanOrEqual: greaterThanOrEqualNode,
- lessThan: lessThanNode,
- lessThanOrEqual: lessThanOrEqualNode,
- sum: sumNode,
- boolean: booleanNode,
- callFunctionAndSet: callFunctionAndSetNode,
- callMethodAndSet: callMethodAndSetNode,
- join: joinNode,
- mutableNumber: mutableNumberNode,
- number: numberNode,
- numbers: numbersNode,
- string: stringNode,
- callFunction: callFunctionNode,
- decrement: decrementNode,
- dumpIdentifier: dumpIdentifierNode,
- export: exportNode,
- increment: incrementNode,
- printNumber: printNumberNode,
- printString: printStringNode,
- require: requireNode,
- return: returnNode,
- "#!": hashbangNode
- }),
- undefined
- )
- }
- async execute() {
- let outputLines = []
- const _originalConsoleLog = console.log
- const tempConsoleLog = (...params) => outputLines.push(params)
- console.log = tempConsoleLog
- const compiled = this.compile("js")
- eval(compiled)
- console.log = _originalConsoleLog
- console.log(outputLines.join("\n"))
- return outputLines
- }
- getHandGrammarProgram() {
- if (!this._cachedHandGrammarProgramRoot)
- this._cachedHandGrammarProgramRoot = new jtree.HandGrammarProgram(`tooling onsave jtree build produceLang fire
- todo Explore best ways to add polymorphism
- anyCell
- booleanCell
- enum false true
- filepathCell
- identifierCell
- regex [$A-Za-z_][0-9a-zA-Z_$]*
- highlightScope variable
- examples myVarA someVarB
- numberCell
- regex \\-?[0-9]*\\.?[0-9]*
- highlightScope constant.numeric
- numberIdentifierCell
- extends identifierCell
- hashBangCell
- highlightScope comment
- hashBangKeywordCell
- highlightScope comment
- stringCell
- highlightScope string
- booleanIdentifierCell
- extends identifierCell
- functionIdentifierCell
- extends identifierCell
- identifiersCell
- extends identifierCell
- instanceIdentifierCell
- extends identifierCell
- methodIdentifierCell
- extends identifierCell
- resultIdentifierCell
- extends identifierCell
- keywordCell
- highlightScope keyword
- stringIdentifierCell
- extends identifierCell
- stringCellsCell
- extends stringCell
- leftNumberCell
- extends numberCell
- leftAnyCell
- extends anyCell
- fireNode
- root
- description A useless prefix Tree Language that compiles to Javascript for testing Tree Notation features.
- compilesTo js
- inScope hashbangNode abstractTerminalNode abstractNonTerminalNode
- catchAllNodeType errorNode
- javascript
- async execute() {
- let outputLines = []
- const _originalConsoleLog = console.log
- const tempConsoleLog = (...params) => outputLines.push(params)
- console.log = tempConsoleLog
- const compiled = this.compile("js")
- eval(compiled)
- console.log = _originalConsoleLog
- console.log(outputLines.join("\\n"))
- return outputLines
- }
- abstractNonTerminalNode
- inScope abstractTerminalNode abstractNonTerminalNode
- abstract
- cells keywordCell
- abstractJsblockNode
- compiler
- openChildren {
- closeChildren }
- extends abstractNonTerminalNode
- abstract
- blockNode
- description block of code
- frequency .2
- compiler
- stringTemplate /* {identifierCell} */
- extends abstractJsblockNode
- crux block
- functionNode
- crux function
- description Function Assignment
- cells keywordCell functionIdentifierCell
- catchAllCellType anyCell
- compiler
- stringTemplate const {functionIdentifierCell} = ({anyCell}) =>
- catchAllCellDelimiter ,
- frequency .1
- extends abstractJsblockNode
- ifNode
- crux if
- description If tile
- cells keywordCell identifierCell
- frequency .2
- compiler
- stringTemplate if ({identifierCell})
- extends abstractJsblockNode
- whileNode
- crux while
- description While tile
- cells keywordCell identifierCell
- frequency .1
- compiler
- stringTemplate while ({identifierCell})
- extends abstractJsblockNode
- abstractTerminalNode
- abstract
- cells keywordCell
- abstractAssignmentNode
- extends abstractTerminalNode
- abstract
- abstractArithmeticNode
- cells keywordCell identifierCell
- catchAllCellType anyCell
- compiler
- stringTemplate const {identifierCell} = {anyCell}
- frequency .2
- extends abstractAssignmentNode
- abstract
- divideNode
- description Divide Numbers
- compiler
- catchAllCellDelimiter /
- extends abstractArithmeticNode
- crux divide
- moduloNode
- description Modulo Numbers
- compiler
- catchAllCellDelimiter %
- extends abstractArithmeticNode
- crux modulo
- multiplyNode
- description Multiply Numbers
- compiler
- catchAllCellDelimiter *
- extends abstractArithmeticNode
- crux multiply
- substractNode
- description Subtract Numbers
- compiler
- catchAllCellDelimiter -
- extends abstractArithmeticNode
- crux substract
- addNode
- crux add
- example
- add ten 2 3 5
- description Add numbers and store result
- compiler
- catchAllCellDelimiter +
- extends abstractArithmeticNode
- abstractBooleanOperatorNode
- description Runs a boolean test and saves result.
- extends abstractAssignmentNode
- abstract
- greaterThanNode
- description Greater than test
- cells keywordCell identifierCell leftNumberCell numberCell
- compiler
- stringTemplate const {identifierCell} = {leftNumberCell} > {numberCell}
- frequency .1
- extends abstractBooleanOperatorNode
- crux greaterThan
- greaterThanOrEqualNode
- description Greater than or equal to test
- cells keywordCell identifierCell leftNumberCell numberCell
- compiler
- stringTemplate const {identifierCell} = {leftNumberCell} >= {numberCell}
- frequency .1
- extends abstractBooleanOperatorNode
- crux greaterThanOrEqual
- lessThanNode
- description Less than test
- cells keywordCell identifierCell leftAnyCell anyCell
- compiler
- stringTemplate const {identifierCell} = {leftAnyCell} < {anyCell}
- frequency .1
- extends abstractBooleanOperatorNode
- crux lessThan
- lessThanOrEqualNode
- crux lessThanOrEqual
- description Less than or equal to test
- cells keywordCell identifierCell leftAnyCell anyCell
- compiler
- stringTemplate const {identifierCell} = {leftAnyCell} <= {anyCell}
- frequency .1
- extends abstractBooleanOperatorNode
- sumNode
- crux sum
- description Add numbers and store result
- cells keywordCell numberIdentifierCell
- catchAllCellType numberCell
- compiler
- stringTemplate const {numberIdentifierCell} = [{numberCell}].reduce((sum, num) => sum + num)
- catchAllCellDelimiter ,
- frequency .1
- extends abstractAssignmentNode
- booleanNode
- crux boolean
- description Boolean Assignment
- cells keywordCell booleanIdentifierCell booleanCell
- compiler
- stringTemplate const {booleanIdentifierCell} = {booleanCell}
- extends abstractAssignmentNode
- callFunctionAndSetNode
- crux callFunctionAndSet
- description Function Call
- frequency .5
- cells keywordCell resultIdentifierCell functionIdentifierCell
- catchAllCellType anyCell
- compiler
- stringTemplate const {resultIdentifierCell} = {functionIdentifierCell}({anyCell})
- catchAllCellDelimiter ,
- extends abstractAssignmentNode
- callMethodAndSetNode
- crux callMethodAndSet
- description Method Call
- frequency .5
- cells keywordCell resultIdentifierCell instanceIdentifierCell methodIdentifierCell
- catchAllCellType anyCell
- compiler
- stringTemplate const {resultIdentifierCell} = {instanceIdentifierCell}.{methodIdentifierCell}({anyCell})
- catchAllCellDelimiter ,
- extends abstractAssignmentNode
- joinNode
- crux join
- description Join strings to form new string
- cells keywordCell identifierCell
- catchAllCellType identifiersCell
- compiler
- stringTemplate const {identifierCell} = [{identifiersCell}].join("")
- catchAllCellDelimiter ,
- frequency .2
- extends abstractAssignmentNode
- mutableNumberNode
- crux mutableNumber
- description Mutable Number Assignment
- cells keywordCell identifierCell numberCell
- compiler
- stringTemplate let {identifierCell} = {numberCell}
- extends abstractAssignmentNode
- numberNode
- crux number
- description Number Assignment
- cells keywordCell identifierCell numberCell
- compiler
- stringTemplate const {identifierCell} = {numberCell}
- frequency .3
- extends abstractAssignmentNode
- numbersNode
- crux numbers
- description Number Array Assignment
- cells keywordCell identifierCell
- catchAllCellType numberCell
- frequency .4
- compiler
- stringTemplate const {identifierCell} = [{numberCell}]
- catchAllCellDelimiter ,
- extends abstractAssignmentNode
- stringNode
- crux string
- description String Assignment
- cells keywordCell stringIdentifierCell
- catchAllCellType anyCell
- compiler
- stringTemplate const {stringIdentifierCell} = "{anyCell}"
- frequency .2
- extends abstractAssignmentNode
- callFunctionNode
- crux callFunction
- description Function call ignore result.
- frequency .1
- cells keywordCell functionIdentifierCell
- catchAllCellType anyCell
- compiler
- stringTemplate {functionIdentifierCell}({anyCell})
- catchAllCellDelimiter ,
- extends abstractTerminalNode
- decrementNode
- crux decrement
- description Decrement
- cells keywordCell numberIdentifierCell
- compiler
- stringTemplate {numberIdentifierCell}--
- frequency .1
- extends abstractTerminalNode
- dumpIdentifierNode
- crux dumpIdentifier
- description Dump variable(s) to console
- catchAllCellType identifierCell
- compiler
- stringTemplate console.log({identifierCell})
- catchAllCellDelimiter ,
- frequency .5
- extends abstractTerminalNode
- exportNode
- crux export
- description Export This
- cells keywordCell identifierCell
- compiler
- stringTemplate module.exports = {identifierCell}
- frequency .1
- extends abstractTerminalNode
- incrementNode
- crux increment
- description Increment
- frequency .3
- cells keywordCell numberIdentifierCell
- compiler
- stringTemplate {numberIdentifierCell}++
- extends abstractTerminalNode
- printNumberNode
- crux printNumber
- extends abstractTerminalNode
- catchAllCellType numberIdentifierCell
- compiler
- stringTemplate console.log({numberIdentifierCell})
- printStringNode
- crux printString
- todo Allow printing of multiline strings
- extends abstractTerminalNode
- catchAllCellType stringCellsCell
- compiler
- stringTemplate console.log("{stringCells}")
- requireNode
- crux require
- description Require Something
- cells keywordCell identifierCell filepathCell
- compiler
- stringTemplate const {identifierCell} = require("{filepathCell}")
- frequency .1
- extends abstractTerminalNode
- returnNode
- crux return
- cells keywordCell anyCell
- compiler
- stringTemplate return {anyCell}
- frequency .1
- extends abstractTerminalNode
- hashbangNode
- crux #!
- description Standard bash hashbang line.
- catchAllCellType hashBangCell
- compiler
- stringTemplate // #! {hashBangCell}
- cells hashBangKeywordCell
- errorNode
- baseNodeType errorNode
- compiler
- stringTemplate // error`)
- return this._cachedHandGrammarProgramRoot
- }
- static getNodeTypeMap() {
- return {
- fireNode: fireNode,
- abstractNonTerminalNode: abstractNonTerminalNode,
- abstractJsblockNode: abstractJsblockNode,
- blockNode: blockNode,
- functionNode: functionNode,
- ifNode: ifNode,
- whileNode: whileNode,
- abstractTerminalNode: abstractTerminalNode,
- abstractAssignmentNode: abstractAssignmentNode,
- abstractArithmeticNode: abstractArithmeticNode,
- divideNode: divideNode,
- moduloNode: moduloNode,
- multiplyNode: multiplyNode,
- substractNode: substractNode,
- addNode: addNode,
- abstractBooleanOperatorNode: abstractBooleanOperatorNode,
- greaterThanNode: greaterThanNode,
- greaterThanOrEqualNode: greaterThanOrEqualNode,
- lessThanNode: lessThanNode,
- lessThanOrEqualNode: lessThanOrEqualNode,
- sumNode: sumNode,
- booleanNode: booleanNode,
- callFunctionAndSetNode: callFunctionAndSetNode,
- callMethodAndSetNode: callMethodAndSetNode,
- joinNode: joinNode,
- mutableNumberNode: mutableNumberNode,
- numberNode: numberNode,
- numbersNode: numbersNode,
- stringNode: stringNode,
- callFunctionNode: callFunctionNode,
- decrementNode: decrementNode,
- dumpIdentifierNode: dumpIdentifierNode,
- exportNode: exportNode,
- incrementNode: incrementNode,
- printNumberNode: printNumberNode,
- printStringNode: printStringNode,
- requireNode: requireNode,
- returnNode: returnNode,
- hashbangNode: hashbangNode,
- errorNode: errorNode
- }
- }
- }
-
- class abstractNonTerminalNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- block: blockNode,
- function: functionNode,
- if: ifNode,
- while: whileNode,
- divide: divideNode,
- modulo: moduloNode,
- multiply: multiplyNode,
- substract: substractNode,
- add: addNode,
- greaterThan: greaterThanNode,
- greaterThanOrEqual: greaterThanOrEqualNode,
- lessThan: lessThanNode,
- lessThanOrEqual: lessThanOrEqualNode,
- sum: sumNode,
- boolean: booleanNode,
- callFunctionAndSet: callFunctionAndSetNode,
- callMethodAndSet: callMethodAndSetNode,
- join: joinNode,
- mutableNumber: mutableNumberNode,
- number: numberNode,
- numbers: numbersNode,
- string: stringNode,
- callFunction: callFunctionNode,
- decrement: decrementNode,
- dumpIdentifier: dumpIdentifierNode,
- export: exportNode,
- increment: incrementNode,
- printNumber: printNumberNode,
- printString: printStringNode,
- require: requireNode,
- return: returnNode
- }),
- undefined
- )
- }
- get keywordCell() {
- return this.getWord(0)
- }
- }
-
- class abstractJsblockNode extends abstractNonTerminalNode {}
-
- class blockNode extends abstractJsblockNode {}
-
- class functionNode extends abstractJsblockNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get functionIdentifierCell() {
- return this.getWord(1)
- }
- get anyCell() {
- return this.getWordsFrom(2)
- }
- }
-
- class ifNode extends abstractJsblockNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- }
-
- class whileNode extends abstractJsblockNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- }
-
- class abstractTerminalNode extends jtree.GrammarBackedNode {
- get keywordCell() {
- return this.getWord(0)
- }
- }
-
- class abstractAssignmentNode extends abstractTerminalNode {}
-
- class abstractArithmeticNode extends abstractAssignmentNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- get anyCell() {
- return this.getWordsFrom(2)
- }
- }
-
- class divideNode extends abstractArithmeticNode {}
-
- class moduloNode extends abstractArithmeticNode {}
-
- class multiplyNode extends abstractArithmeticNode {}
-
- class substractNode extends abstractArithmeticNode {}
-
- class addNode extends abstractArithmeticNode {}
-
- class abstractBooleanOperatorNode extends abstractAssignmentNode {}
-
- class greaterThanNode extends abstractBooleanOperatorNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- get leftNumberCell() {
- return parseFloat(this.getWord(2))
- }
- get numberCell() {
- return parseFloat(this.getWord(3))
- }
- }
-
- class greaterThanOrEqualNode extends abstractBooleanOperatorNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- get leftNumberCell() {
- return parseFloat(this.getWord(2))
- }
- get numberCell() {
- return parseFloat(this.getWord(3))
- }
- }
-
- class lessThanNode extends abstractBooleanOperatorNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- get leftAnyCell() {
- return this.getWord(2)
- }
- get anyCell() {
- return this.getWord(3)
- }
- }
-
- class lessThanOrEqualNode extends abstractBooleanOperatorNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- get leftAnyCell() {
- return this.getWord(2)
- }
- get anyCell() {
- return this.getWord(3)
- }
- }
-
- class sumNode extends abstractAssignmentNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get numberIdentifierCell() {
- return this.getWord(1)
- }
- get numberCell() {
- return this.getWordsFrom(2).map(val => parseFloat(val))
- }
- }
-
- class booleanNode extends abstractAssignmentNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get booleanIdentifierCell() {
- return this.getWord(1)
- }
- get booleanCell() {
- return this.getWord(2)
- }
- }
-
- class callFunctionAndSetNode extends abstractAssignmentNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get resultIdentifierCell() {
- return this.getWord(1)
- }
- get functionIdentifierCell() {
- return this.getWord(2)
- }
- get anyCell() {
- return this.getWordsFrom(3)
- }
- }
-
- class callMethodAndSetNode extends abstractAssignmentNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get resultIdentifierCell() {
- return this.getWord(1)
- }
- get instanceIdentifierCell() {
- return this.getWord(2)
- }
- get methodIdentifierCell() {
- return this.getWord(3)
- }
- get anyCell() {
- return this.getWordsFrom(4)
- }
- }
-
- class joinNode extends abstractAssignmentNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- get identifiersCell() {
- return this.getWordsFrom(2)
- }
- }
-
- class mutableNumberNode extends abstractAssignmentNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- get numberCell() {
- return parseFloat(this.getWord(2))
- }
- }
-
- class numberNode extends abstractAssignmentNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- get numberCell() {
- return parseFloat(this.getWord(2))
- }
- }
-
- class numbersNode extends abstractAssignmentNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- get numberCell() {
- return this.getWordsFrom(2).map(val => parseFloat(val))
- }
- }
-
- class stringNode extends abstractAssignmentNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get stringIdentifierCell() {
- return this.getWord(1)
- }
- get anyCell() {
- return this.getWordsFrom(2)
- }
- }
-
- class callFunctionNode extends abstractTerminalNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get functionIdentifierCell() {
- return this.getWord(1)
- }
- get anyCell() {
- return this.getWordsFrom(2)
- }
- }
-
- class decrementNode extends abstractTerminalNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get numberIdentifierCell() {
- return this.getWord(1)
- }
- }
-
- class dumpIdentifierNode extends abstractTerminalNode {
- get identifierCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class exportNode extends abstractTerminalNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- }
-
- class incrementNode extends abstractTerminalNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get numberIdentifierCell() {
- return this.getWord(1)
- }
- }
-
- class printNumberNode extends abstractTerminalNode {
- get numberIdentifierCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class printStringNode extends abstractTerminalNode {
- get stringCellsCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class requireNode extends abstractTerminalNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get identifierCell() {
- return this.getWord(1)
- }
- get filepathCell() {
- return this.getWord(2)
- }
- }
-
- class returnNode extends abstractTerminalNode {
- get keywordCell() {
- return this.getWord(0)
- }
- get anyCell() {
- return this.getWord(1)
- }
- }
-
- class hashbangNode extends jtree.GrammarBackedNode {
- get hashBangKeywordCell() {
- return this.getWord(0)
- }
- get hashBangCell() {
- return this.getWordsFrom(1)
- }
- }
-
- class errorNode extends jtree.GrammarBackedNode {
- getErrors() {
- return this._getErrorNodeErrors()
- }
- }
-
- window.fireNode = fireNode
- }
- ;
-
- {
- class hakonNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- selectorNode,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { comment: commentNode }),
- undefined
- )
- }
- getSelector() {
- return ""
- }
- compile() {
- return this.getTopDownArray()
- .filter(node => node.isSelectorNode)
- .map(child => child.compile())
- .join("")
- }
- getHandGrammarProgram() {
- if (!this._cachedHandGrammarProgramRoot)
- this._cachedHandGrammarProgramRoot = new jtree.HandGrammarProgram(`tooling onsave jtree build produceLang hakon
- anyCell
- keywordCell
- commentKeywordCell
- extends keywordCell
- highlightScope comment
- enum comment
- extraCell
- highlightScope invalid
- cssValueCell
- highlightScope constant.numeric
- selectorCell
- highlightScope keyword.control
- examples body h1
- todo add html tags, css and ids selector regexes, etc
- propertyKeywordCell
- highlightScope variable.function
- extends keywordCell
- enum align-content align-items align-self all animation animation-delay animation-direction animation-duration animation-fill-mode animation-iteration-count animation-name animation-play-state animation-timing-function backface-visibility background background-attachment background-blend-mode background-clip background-color background-image background-origin background-position background-repeat background-size border border-bottom border-bottom-color border-bottom-left-radius border-bottom-right-radius border-bottom-style border-bottom-width border-collapse border-color border-image border-image-outset border-image-repeat border-image-slice border-image-source border-image-width border-left border-left-color border-left-style border-left-width border-radius border-right border-right-color border-right-style border-right-width border-spacing border-style border-top border-top-color border-top-left-radius border-top-right-radius border-top-style border-top-width border-width bottom box-shadow box-sizing caption-side clear clip color column-count column-fill column-gap column-rule column-rule-color column-rule-style column-rule-width column-span column-width columns content counter-increment counter-reset cursor direction display empty-cells fill filter flex flex-basis flex-direction flex-flow flex-grow flex-shrink flex-wrap float font @font-face font-family font-size font-size-adjust font-stretch font-style font-variant font-weight hanging-punctuation height justify-content @keyframes left letter-spacing line-height list-style list-style-image list-style-position list-style-type margin margin-bottom margin-left margin-right margin-top max-height max-width @media min-height min-width nav-down nav-index nav-left nav-right nav-up opacity order outline outline-color outline-offset outline-style outline-width overflow overflow-x overflow-y padding padding-bottom padding-left padding-right padding-top page-break-after page-break-before page-break-inside perspective perspective-origin position quotes resize right tab-size table-layout text-align text-align-last text-decoration text-decoration-color text-decoration-line text-decoration-style text-indent text-justify text-overflow text-shadow text-transform top transform transform-origin transform-style transition transition-delay transition-duration transition-property transition-timing-function unicode-bidi vertical-align visibility white-space width word-break word-spacing word-wrap z-index overscroll-behavior-x user-select -ms-touch-action -webkit-user-select -webkit-touch-callout -moz-user-select touch-action -ms-user-select -khtml-user-select
- errorCell
- highlightScope invalid
- commentCell
- highlightScope comment
- hakonNode
- root
- todo Add variables?
- description A prefix Tree Language that compiles to CSS
- compilesTo css
- inScope commentNode
- catchAllNodeType selectorNode
- javascript
- getSelector() {
- return ""
- }
- compile() {
- return this.getTopDownArray()
- .filter(node => node.isSelectorNode)
- .map(child => child.compile())
- .join("")
- }
- example A basic example
- body
- font-size 12px
- h1,h2
- color red
- a
- &:hover
- color blue
- font-size 17px
- propertyNode
- catchAllCellType cssValueCell
- catchAllNodeType errorNode
- javascript
- compile(spaces) {
- return \`\${spaces}\${this.getFirstWord()}: \${this.getContent()};\`
- }
- cells propertyKeywordCell
- variableNode
- extends propertyNode
- pattern --
- errorNode
- catchAllNodeType errorNode
- catchAllCellType errorCell
- baseNodeType errorNode
- commentNode
- cells commentKeywordCell
- catchAllCellType commentCell
- catchAllNodeType commentNode
- selectorNode
- inScope propertyNode variableNode commentNode
- catchAllNodeType selectorNode
- boolean isSelectorNode true
- javascript
- getSelector() {
- const parentSelector = this.getParent().getSelector()
- return this.getFirstWord()
- .split(",")
- .map(part => {
- if (part.startsWith("&")) return parentSelector + part.substr(1)
- return parentSelector ? parentSelector + " " + part : part
- })
- .join(",")
- }
- compile() {
- const propertyNodes = this.getChildren().filter(node => node.doesExtend("propertyNode"))
- if (!propertyNodes.length) return ""
- const spaces = " "
- return \`\${this.getSelector()} {
- \${propertyNodes.map(child => child.compile(spaces)).join("\\n")}
- }\\n\`
- }
- cells selectorCell`)
- return this._cachedHandGrammarProgramRoot
- }
- static getNodeTypeMap() {
- return {
- hakonNode: hakonNode,
- propertyNode: propertyNode,
- variableNode: variableNode,
- errorNode: errorNode,
- commentNode: commentNode,
- selectorNode: selectorNode
- }
- }
- }
-
- class propertyNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(errorNode, undefined, undefined)
- }
- get propertyKeywordCell() {
- return this.getWord(0)
- }
- get cssValueCell() {
- return this.getWordsFrom(1)
- }
- compile(spaces) {
- return `${spaces}${this.getFirstWord()}: ${this.getContent()};`
- }
- }
-
- class variableNode extends propertyNode {}
-
- class errorNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(errorNode, undefined, undefined)
- }
- getErrors() {
- return this._getErrorNodeErrors()
- }
- get errorCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class commentNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(commentNode, undefined, undefined)
- }
- get commentKeywordCell() {
- return this.getWord(0)
- }
- get commentCell() {
- return this.getWordsFrom(1)
- }
- }
-
- class selectorNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- selectorNode,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- "border-bottom-right-radius": propertyNode,
- "transition-timing-function": propertyNode,
- "animation-iteration-count": propertyNode,
- "animation-timing-function": propertyNode,
- "border-bottom-left-radius": propertyNode,
- "border-top-right-radius": propertyNode,
- "border-top-left-radius": propertyNode,
- "background-attachment": propertyNode,
- "background-blend-mode": propertyNode,
- "text-decoration-color": propertyNode,
- "text-decoration-style": propertyNode,
- "overscroll-behavior-x": propertyNode,
- "-webkit-touch-callout": propertyNode,
- "animation-play-state": propertyNode,
- "text-decoration-line": propertyNode,
- "animation-direction": propertyNode,
- "animation-fill-mode": propertyNode,
- "backface-visibility": propertyNode,
- "background-position": propertyNode,
- "border-bottom-color": propertyNode,
- "border-bottom-style": propertyNode,
- "border-bottom-width": propertyNode,
- "border-image-outset": propertyNode,
- "border-image-repeat": propertyNode,
- "border-image-source": propertyNode,
- "hanging-punctuation": propertyNode,
- "list-style-position": propertyNode,
- "transition-duration": propertyNode,
- "transition-property": propertyNode,
- "-webkit-user-select": propertyNode,
- "animation-duration": propertyNode,
- "border-image-slice": propertyNode,
- "border-image-width": propertyNode,
- "border-right-color": propertyNode,
- "border-right-style": propertyNode,
- "border-right-width": propertyNode,
- "perspective-origin": propertyNode,
- "-khtml-user-select": propertyNode,
- "background-origin": propertyNode,
- "background-repeat": propertyNode,
- "border-left-color": propertyNode,
- "border-left-style": propertyNode,
- "border-left-width": propertyNode,
- "column-rule-color": propertyNode,
- "column-rule-style": propertyNode,
- "column-rule-width": propertyNode,
- "counter-increment": propertyNode,
- "page-break-before": propertyNode,
- "page-break-inside": propertyNode,
- "background-color": propertyNode,
- "background-image": propertyNode,
- "border-top-color": propertyNode,
- "border-top-style": propertyNode,
- "border-top-width": propertyNode,
- "font-size-adjust": propertyNode,
- "list-style-image": propertyNode,
- "page-break-after": propertyNode,
- "transform-origin": propertyNode,
- "transition-delay": propertyNode,
- "-ms-touch-action": propertyNode,
- "-moz-user-select": propertyNode,
- "animation-delay": propertyNode,
- "background-clip": propertyNode,
- "background-size": propertyNode,
- "border-collapse": propertyNode,
- "justify-content": propertyNode,
- "list-style-type": propertyNode,
- "text-align-last": propertyNode,
- "text-decoration": propertyNode,
- "transform-style": propertyNode,
- "-ms-user-select": propertyNode,
- "animation-name": propertyNode,
- "border-spacing": propertyNode,
- "flex-direction": propertyNode,
- "letter-spacing": propertyNode,
- "outline-offset": propertyNode,
- "padding-bottom": propertyNode,
- "text-transform": propertyNode,
- "vertical-align": propertyNode,
- "align-content": propertyNode,
- "border-bottom": propertyNode,
- "border-radius": propertyNode,
- "counter-reset": propertyNode,
- "margin-bottom": propertyNode,
- "outline-color": propertyNode,
- "outline-style": propertyNode,
- "outline-width": propertyNode,
- "padding-right": propertyNode,
- "text-overflow": propertyNode,
- "border-color": propertyNode,
- "border-image": propertyNode,
- "border-right": propertyNode,
- "border-style": propertyNode,
- "border-width": propertyNode,
- "caption-side": propertyNode,
- "column-count": propertyNode,
- "column-width": propertyNode,
- "font-stretch": propertyNode,
- "font-variant": propertyNode,
- "margin-right": propertyNode,
- "padding-left": propertyNode,
- "table-layout": propertyNode,
- "text-justify": propertyNode,
- "unicode-bidi": propertyNode,
- "word-spacing": propertyNode,
- "touch-action": propertyNode,
- "align-items": propertyNode,
- "border-left": propertyNode,
- "column-fill": propertyNode,
- "column-rule": propertyNode,
- "column-span": propertyNode,
- "empty-cells": propertyNode,
- "flex-shrink": propertyNode,
- "font-family": propertyNode,
- "font-weight": propertyNode,
- "line-height": propertyNode,
- "margin-left": propertyNode,
- "padding-top": propertyNode,
- perspective: propertyNode,
- "text-indent": propertyNode,
- "text-shadow": propertyNode,
- "white-space": propertyNode,
- "user-select": propertyNode,
- "align-self": propertyNode,
- background: propertyNode,
- "border-top": propertyNode,
- "box-shadow": propertyNode,
- "box-sizing": propertyNode,
- "column-gap": propertyNode,
- "flex-basis": propertyNode,
- "@font-face": propertyNode,
- "font-style": propertyNode,
- "@keyframes": propertyNode,
- "list-style": propertyNode,
- "margin-top": propertyNode,
- "max-height": propertyNode,
- "min-height": propertyNode,
- "overflow-x": propertyNode,
- "overflow-y": propertyNode,
- "text-align": propertyNode,
- transition: propertyNode,
- visibility: propertyNode,
- "word-break": propertyNode,
- animation: propertyNode,
- direction: propertyNode,
- "flex-flow": propertyNode,
- "flex-grow": propertyNode,
- "flex-wrap": propertyNode,
- "font-size": propertyNode,
- "max-width": propertyNode,
- "min-width": propertyNode,
- "nav-index": propertyNode,
- "nav-right": propertyNode,
- transform: propertyNode,
- "word-wrap": propertyNode,
- "nav-down": propertyNode,
- "nav-left": propertyNode,
- overflow: propertyNode,
- position: propertyNode,
- "tab-size": propertyNode,
- columns: propertyNode,
- content: propertyNode,
- display: propertyNode,
- opacity: propertyNode,
- outline: propertyNode,
- padding: propertyNode,
- "z-index": propertyNode,
- border: propertyNode,
- bottom: propertyNode,
- cursor: propertyNode,
- filter: propertyNode,
- height: propertyNode,
- margin: propertyNode,
- "@media": propertyNode,
- "nav-up": propertyNode,
- quotes: propertyNode,
- resize: propertyNode,
- clear: propertyNode,
- color: propertyNode,
- float: propertyNode,
- order: propertyNode,
- right: propertyNode,
- width: propertyNode,
- clip: propertyNode,
- fill: propertyNode,
- flex: propertyNode,
- font: propertyNode,
- left: propertyNode,
- all: propertyNode,
- top: propertyNode,
- comment: commentNode
- }),
- [{ regex: /--/, nodeConstructor: variableNode }]
- )
- }
- get selectorCell() {
- return this.getWord(0)
- }
- get isSelectorNode() {
- return true
- }
- getSelector() {
- const parentSelector = this.getParent().getSelector()
- return this.getFirstWord()
- .split(",")
- .map(part => {
- if (part.startsWith("&")) return parentSelector + part.substr(1)
- return parentSelector ? parentSelector + " " + part : part
- })
- .join(",")
- }
- compile() {
- const propertyNodes = this.getChildren().filter(node => node.doesExtend("propertyNode"))
- if (!propertyNodes.length) return ""
- const spaces = " "
- return `${this.getSelector()} {
- ${propertyNodes.map(child => child.compile(spaces)).join("\n")}
- }\n`
- }
- }
-
- window.hakonNode = hakonNode
- }
- ;
-
- const BrowserEvents = {}
- BrowserEvents.click = "click"
- BrowserEvents.change = "change"
- BrowserEvents.mouseover = "mouseover"
- BrowserEvents.mouseout = "mouseout"
- BrowserEvents.mousedown = "mousedown"
- BrowserEvents.contextmenu = "contextmenu"
- BrowserEvents.keypress = "keypress"
- BrowserEvents.keyup = "keyup"
- BrowserEvents.focus = "focus"
- BrowserEvents.mousemove = "mousemove"
- BrowserEvents.dblclick = "dblclick"
- BrowserEvents.submit = "submit"
- BrowserEvents.blur = "blur"
- BrowserEvents.paste = "paste"
- BrowserEvents.copy = "copy"
- BrowserEvents.resize = "resize"
- BrowserEvents.cut = "cut"
- BrowserEvents.drop = "drop"
- BrowserEvents.dragover = "dragover"
- BrowserEvents.dragenter = "dragenter"
- BrowserEvents.dragleave = "dragleave"
- BrowserEvents.ready = "ready"
- const WillowConstants = {}
- // todo: cleanup
- WillowConstants.clickCommand = "clickCommand"
- WillowConstants.shiftClickCommand = "shiftClickCommand"
- WillowConstants.blurCommand = "blurCommand"
- WillowConstants.contextMenuCommand = "contextMenuCommand"
- WillowConstants.changeCommand = "changeCommand"
- WillowConstants.doubleClickCommand = "doubleClickCommand"
- // todo: cleanup
- WillowConstants.titleTag = "titleTag"
- WillowConstants.styleTag = "styleTag"
- WillowConstants.tagMap = {}
- WillowConstants.tagMap[WillowConstants.styleTag] = "style"
- WillowConstants.tagMap[WillowConstants.titleTag] = "title"
- WillowConstants.tags = {}
- WillowConstants.tags.html = "html"
- WillowConstants.tags.head = "head"
- WillowConstants.tags.body = "body"
- WillowConstants.collapse = "collapse"
- WillowConstants.uidAttribute = "stumpUid"
- WillowConstants.class = "class"
- WillowConstants.type = "type"
- WillowConstants.value = "value"
- WillowConstants.name = "name"
- WillowConstants.checkbox = "checkbox"
- WillowConstants.checkedSelector = ":checked"
- WillowConstants.contenteditable = "contenteditable"
- WillowConstants.inputTypes = ["input", "textarea"]
- var CacheType
- ;(function(CacheType) {
- CacheType["inBrowserMemory"] = "inBrowserMemory"
- })(CacheType || (CacheType = {}))
- class WillowHTTPResponse {
- constructor(superAgentResponse) {
- this._cacheType = CacheType.inBrowserMemory
- this._fromCache = false
- this._cacheTime = Date.now()
- this._superAgentResponse = superAgentResponse
- this._mimeType = superAgentResponse && superAgentResponse.type
- }
- // todo: ServerMemoryCacheTime and ServerMemoryDiskCacheTime
- get cacheTime() {
- return this._cacheTime
- }
- get cacheType() {
- return this._cacheType
- }
- get body() {
- return this._superAgentResponse && this._superAgentResponse.body
- }
- get text() {
- if (this._text === undefined) this._text = this._superAgentResponse && this._superAgentResponse.text ? this._superAgentResponse.text : this.body ? JSON.stringify(this.body, null, 2) : ""
- return this._text
- }
- get asJson() {
- return this.body ? this.body : JSON.parse(this.text)
- }
- get fromCache() {
- return this._fromCache
- }
- setFromCache(val) {
- this._fromCache = val
- return this
- }
- getParsedDataOrText() {
- if (this._mimeType === "text/csv") return this.text
- return this.body || this.text
- }
- }
- class WillowHTTPProxyCacheResponse extends WillowHTTPResponse {
- constructor(proxyServerResponse) {
- super()
- this._proxyServerResponse = proxyServerResponse
- this._cacheType = proxyServerResponse.body.cacheType
- this._cacheTime = proxyServerResponse.body.cacheTime
- this._text = proxyServerResponse.body.text
- }
- }
- class AbstractWillowShadow {
- constructor(stumpNode) {
- this._stumpNode = stumpNode
- }
- getShadowStumpNode() {
- return this._stumpNode
- }
- getShadowValue() {
- return this._val
- }
- removeShadow() {
- return this
- }
- setInputOrTextAreaValue(value) {
- this._val = value
- return this
- }
- getShadowParent() {
- return this.getShadowStumpNode()
- .getParent()
- .getShadow()
- }
- getPositionAndDimensions(gridSize = 1) {
- const offset = this.getShadowOffset()
- const parentOffset = this.getShadowParent().getShadowOffset()
- return {
- left: Math.floor((offset.left - parentOffset.left) / gridSize),
- top: Math.floor((offset.top - parentOffset.top) / gridSize),
- width: Math.floor(this.getShadowWidth() / gridSize),
- height: Math.floor(this.getShadowHeight() / gridSize)
- }
- }
- shadowHasClass(name) {
- return false
- }
- getShadowAttr(name) {
- return ""
- }
- makeResizable(options) {
- return this
- }
- makeDraggable(options) {
- return this
- }
- makeSelectable(options) {
- return this
- }
- isShadowChecked() {
- return false
- }
- getShadowHtml() {
- return ""
- }
- getShadowOffset() {
- return { left: 111, top: 111 }
- }
- getShadowWidth() {
- return 111
- }
- getShadowHeight() {
- return 111
- }
- setShadowAttr(name, value) {
- return this
- }
- isShadowDraggable() {
- return this.shadowHasClass("draggable")
- }
- toggleShadow() {}
- addClassToShadow(className) {}
- removeClassFromShadow(className) {
- return this
- }
- onShadowEvent(event, selector, fn) {
- // todo:
- return this
- }
- offShadowEvent(event, fn) {
- // todo:
- return this
- }
- triggerShadowEvent(name) {
- return this
- }
- getShadowPosition() {
- return {
- left: 111,
- top: 111
- }
- }
- getShadowOuterHeight() {
- return 11
- }
- getShadowOuterWidth() {
- return 11
- }
- getShadowCss(property) {
- return ""
- }
- setShadowCss(css) {
- return this
- }
- insertHtmlNode(childNode, index) {}
- getShadowElement() {}
- }
- class WillowShadow extends AbstractWillowShadow {}
- class WillowStore {
- constructor() {
- this._values = {}
- }
- get(key) {
- return this._values[key]
- }
- set(key, value) {
- this._values[key] = value
- return this
- }
- remove(key) {
- delete this._values[key]
- }
- each(fn) {
- Object.keys(this._values).forEach(key => {
- fn(this._values[key], key)
- })
- }
- clearAll() {
- this._values = {}
- }
- }
- class WillowMousetrap {
- constructor() {
- this.prototype = {}
- }
- bind() {}
- }
- // this one should have no document, window, $, et cetera.
- class AbstractWillowBrowser extends stumpNode {
- constructor(fullHtmlPageUrlIncludingProtocolAndFileName) {
- super(`${WillowConstants.tags.html}
- ${WillowConstants.tags.head}
- ${WillowConstants.tags.body}`)
- this._offlineMode = false
- this._httpGetResponseCache = {}
- this.location = {}
- this._htmlStumpNode = this.nodeAt(0)
- this._headStumpNode = this.nodeAt(0).nodeAt(0)
- this._bodyStumpNode = this.nodeAt(0).nodeAt(1)
- this.addSuidsToHtmlHeadAndBodyShadows()
- this._fullHtmlPageUrlIncludingProtocolAndFileName = fullHtmlPageUrlIncludingProtocolAndFileName
- const url = new URL(fullHtmlPageUrlIncludingProtocolAndFileName)
- this.location.port = url.port
- this.location.protocol = url.protocol
- this.location.hostname = url.hostname
- this.location.host = url.host
- }
- _getPort() {
- return this.location.port ? ":" + this.location.port : ""
- }
- getHash() {
- return this.location.hash || ""
- }
- setHash(value) {
- this.location.hash = value
- }
- queryObjectToQueryString(obj) {
- const params = new URLSearchParams()
- for (const [key, value] of Object.entries(obj)) {
- params.set(key, String(value))
- }
- return params.toString()
- }
- toPrettyDeepLink(treeCode, queryObject) {
- // todo: move things to a constant.
- const nodeBreakSymbol = "~"
- const edgeSymbol = "_"
- const obj = Object.assign({}, queryObject)
- if (!treeCode.includes(nodeBreakSymbol) && !treeCode.includes(edgeSymbol)) {
- obj.nodeBreakSymbol = nodeBreakSymbol
- obj.edgeSymbol = edgeSymbol
- obj.data = encodeURIComponent(treeCode.replace(/ /g, edgeSymbol).replace(/\n/g, nodeBreakSymbol))
- } else obj.data = encodeURIComponent(treeCode)
- return this.getAppWebPageUrl() + "?" + this.queryObjectToQueryString(obj)
- }
- getHost() {
- return this.location.host
- }
- reload() {}
- toggleOfflineMode() {
- this._offlineMode = !this._offlineMode
- }
- addSuidsToHtmlHeadAndBodyShadows() {}
- getShadowClass() {
- return WillowShadow
- }
- getMockMouseEvent() {
- return {
- clientX: 0,
- clientY: 0,
- offsetX: 0,
- offsetY: 0
- }
- }
- toggleFullScreen() {}
- getMousetrap() {
- if (!this._mousetrap) this._mousetrap = new WillowMousetrap()
- return this._mousetrap
- }
- _getFocusedShadow() {
- return this._focusedShadow || this.getBodyStumpNode().getShadow()
- }
- getHeadStumpNode() {
- return this._headStumpNode
- }
- getBodyStumpNode() {
- return this._bodyStumpNode
- }
- getHtmlStumpNode() {
- return this._htmlStumpNode
- }
- getStore() {
- if (!this._store) this._store = new WillowStore()
- return this._store
- }
- someInputHasFocus() {
- const focusedShadow = this._getFocusedShadow()
- if (!focusedShadow) return false
- const stumpNode = focusedShadow.getShadowStumpNode()
- return stumpNode && stumpNode.isInputType()
- }
- copyTextToClipboard(text) {}
- setCopyData(evt, str) {}
- getAppWebPageUrl() {
- return this._fullHtmlPageUrlIncludingProtocolAndFileName
- }
- getAppWebPageParentFolderWithoutTrailingSlash() {
- return jtree.Utils.getPathWithoutFileName(this._fullHtmlPageUrlIncludingProtocolAndFileName)
- }
- _makeRelativeUrlAbsolute(url) {
- if (url.startsWith("http://") || url.startsWith("https://")) return url
- return this.getAppWebPageParentFolderWithoutTrailingSlash() + "/" + url.replace(/^\//, "")
- }
- async makeUrlAbsoluteAndHttpGetUrl(url, queryStringObject, responseClass = WillowHTTPResponse) {
- return this.httpGetUrl(this._makeRelativeUrlAbsolute(url), queryStringObject, responseClass)
- }
- async httpGetUrl(url, queryStringObject, responseClass = WillowHTTPResponse) {
- if (this._offlineMode) return new WillowHTTPResponse()
- const superAgentResponse = await superagent
- .get(url)
- .query(queryStringObject)
- .set(this._headers || {})
- return new responseClass(superAgentResponse)
- }
- _getFromResponseCache(cacheKey) {
- const hit = this._httpGetResponseCache[cacheKey]
- if (hit) hit.setFromCache(true)
- return hit
- }
- _setInResponseCache(url, res) {
- this._httpGetResponseCache[url] = res
- return this
- }
- async httpGetUrlFromCache(url, queryStringMap = {}, responseClass = WillowHTTPResponse) {
- const cacheKey = url + JSON.stringify(queryStringMap)
- const cacheHit = this._getFromResponseCache(cacheKey)
- if (!cacheHit) {
- const res = await this.httpGetUrl(url, queryStringMap, responseClass)
- this._setInResponseCache(cacheKey, res)
- return res
- }
- return cacheHit
- }
- async httpGetUrlFromProxyCache(url) {
- const queryStringMap = {}
- queryStringMap.url = url
- queryStringMap.cacheOnServer = "true"
- return await this.httpGetUrlFromCache("/proxy", queryStringMap, WillowHTTPProxyCacheResponse)
- }
- async httpPostUrl(url, data) {
- if (this._offlineMode) return new WillowHTTPResponse()
- const superAgentResponse = await superagent
- .post(this._makeRelativeUrlAbsolute(url))
- .set(this._headers || {})
- .send(data)
- return new WillowHTTPResponse(superAgentResponse)
- }
- encodeURIComponent(str) {
- return encodeURIComponent(str)
- }
- downloadFile(data, filename, filetype) {
- // noop
- }
- async appendScript(url) {}
- getWindowTitle() {
- // todo: deep getNodeByBase/withBase/type/word or something?
- const nodes = this.getTopDownArray()
- const titleNode = nodes.find(node => node.getFirstWord() === WillowConstants.titleTag)
- return titleNode ? titleNode.getContent() : ""
- }
- setWindowTitle(value) {
- const nodes = this.getTopDownArray()
- const headNode = nodes.find(node => node.getFirstWord() === WillowConstants.tags.head)
- headNode.touchNode(WillowConstants.titleTag).setContent(value)
- return this
- }
- _getHostname() {
- return this.location.hostname || ""
- }
- openUrl(link) {
- // noop in willow
- }
- getPageHtml() {
- return this.getHtmlStumpNode().toHtmlWithSuids()
- }
- getStumpNodeFromElement(el) {}
- setPasteHandler(fn) {
- return this
- }
- setErrorHandler(fn) {
- return this
- }
- setCopyHandler(fn) {
- return this
- }
- setCutHandler(fn) {
- return this
- }
- setResizeEndHandler(fn) {
- return this
- }
- async confirmThen(message) {
- return true
- }
- async promptThen(message, value) {
- return value
- }
- setLoadedDroppedFileHandler(callback, helpText = "") {}
- getWindowSize() {
- return {
- width: 1111,
- height: 1111
- }
- }
- getDocumentSize() {
- return this.getWindowSize()
- }
- isExternalLink(link) {
- if (link && link.substr(0, 1) === "/") return false
- if (!link.includes("//")) return false
- const hostname = this._getHostname()
- const url = new URL(link)
- return url.hostname && hostname !== url.hostname
- }
- forceRepaint() {}
- blurFocusedInput() {}
- }
- class WillowBrowser extends AbstractWillowBrowser {
- constructor(fullHtmlPageUrlIncludingProtocolAndFileName) {
- super(fullHtmlPageUrlIncludingProtocolAndFileName)
- this._offlineMode = true
- }
- }
- WillowBrowser._stumpsOnPage = 0
- class WillowBrowserShadow extends AbstractWillowShadow {
- _getJQElement() {
- // todo: speedup?
- if (!this._cachedEl) this._cachedEl = jQuery(`[${WillowConstants.uidAttribute}="${this.getShadowStumpNode()._getUid()}"]`)
- return this._cachedEl
- }
- getShadowElement() {
- return this._getJQElement()[0]
- }
- getShadowPosition() {
- return this._getJQElement().position()
- }
- shadowHasClass(name) {
- return this._getJQElement().hasClass(name)
- }
- getShadowHtml() {
- return this._getJQElement().html()
- }
- getShadowValue() {
- // todo: cleanup, add tests
- if (this.getShadowStumpNode().isInputType()) return this._getJQElement().val()
- return this._getJQElement().val() || this.getShadowValueFromAttr()
- }
- getShadowValueFromAttr() {
- return this._getJQElement().attr(WillowConstants.value)
- }
- getShadowOuterHeight() {
- return this._getJQElement().outerHeight()
- }
- getShadowOuterWidth() {
- return this._getJQElement().outerWidth()
- }
- isShadowChecked() {
- return this._getJQElement().is(WillowConstants.checkedSelector)
- }
- getShadowWidth() {
- return this._getJQElement().width()
- }
- getShadowHeight() {
- return this._getJQElement().height()
- }
- getShadowOffset() {
- return this._getJQElement().offset()
- }
- getShadowAttr(name) {
- return this._getJQElement().attr(name)
- }
- _logMessage(type) {
- if (true) return true
- WillowBrowserShadow._shadowUpdateNumber++
- console.log(`DOM Update ${WillowBrowserShadow._shadowUpdateNumber}: ${type}`)
- }
- getShadowCss(prop) {
- return this._getJQElement().css(prop)
- }
- triggerShadowEvent(event) {
- this._getJQElement().trigger(event)
- this._logMessage("trigger")
- return this
- }
- // BEGIN MUTABLE METHODS:
- // todo: add tests
- // todo: idea, don't "paint" wall (dont append it to parent, until done.)
- insertHtmlNode(childStumpNode, index) {
- const newChildJqElement = jQuery(childStumpNode.toHtmlWithSuids())
- newChildJqElement.data("stumpNode", childStumpNode) // todo: what do we use this for?
- const jqEl = this._getJQElement()
- // todo: can we virtualize this?
- // would it be a "virtual shadow?"
- if (index === undefined) jqEl.append(newChildJqElement)
- else if (index === 0) jqEl.prepend(newChildJqElement)
- else jQuery(jqEl.children().get(index - 1)).after(newChildJqElement)
- WillowBrowser._stumpsOnPage++
- this._logMessage("insert")
- }
- addClassToShadow(className) {
- this._getJQElement().addClass(className)
- this._logMessage("addClass")
- return this
- }
- removeClassFromShadow(className) {
- this._getJQElement().removeClass(className)
- this._logMessage("removeClass")
- return this
- }
- onShadowEvent(event, two, three) {
- this._getJQElement().on(event, two, three)
- this._logMessage("bind on")
- return this
- }
- offShadowEvent(event, fn) {
- this._getJQElement().off(event, fn)
- this._logMessage("bind off")
- return this
- }
- toggleShadow() {
- this._getJQElement().toggle()
- this._logMessage("toggle")
- return this
- }
- makeResizable(options) {
- this._getJQElement().resizable(options)
- this._logMessage("resizable")
- return this
- }
- removeShadow() {
- this._getJQElement().remove()
- WillowBrowser._stumpsOnPage--
- this._logMessage("remove")
- return this
- }
- setInputOrTextAreaValue(value) {
- this._getJQElement().val(value)
- this._logMessage("val")
- return this
- }
- setShadowAttr(name, value) {
- this._getJQElement().attr(name, value)
- this._logMessage("attr")
- return this
- }
- makeDraggable(options) {
- this._logMessage("draggable")
- this._getJQElement().draggable(options)
- return this
- }
- setShadowCss(css) {
- this._getJQElement().css(css)
- this._logMessage("css")
- return this
- }
- makeSelectable(options) {
- this._getJQElement().selectable(options)
- this._logMessage("selectable")
- return this
- }
- }
- WillowBrowserShadow._shadowUpdateNumber = 0 // todo: what is this for, debugging perf?
- // same thing, except with side effects.
- class RealWillowBrowser extends AbstractWillowBrowser {
- findStumpNodesByShadowClass(className) {
- const stumpNodes = []
- const that = this
- jQuery("." + className).each(function() {
- stumpNodes.push(that.getStumpNodeFromElement(this))
- })
- return stumpNodes
- }
- addSuidsToHtmlHeadAndBodyShadows() {
- jQuery(WillowConstants.tags.html).attr(WillowConstants.uidAttribute, this.getHtmlStumpNode()._getUid())
- jQuery(WillowConstants.tags.head).attr(WillowConstants.uidAttribute, this.getHeadStumpNode()._getUid())
- jQuery(WillowConstants.tags.body).attr(WillowConstants.uidAttribute, this.getBodyStumpNode()._getUid())
- }
- getShadowClass() {
- return WillowBrowserShadow
- }
- setCopyHandler(fn) {
- jQuery(document).on(BrowserEvents.copy, fn)
- return this
- }
- setCutHandler(fn) {
- jQuery(document).on(BrowserEvents.cut, fn)
- return this
- }
- setPasteHandler(fn) {
- window.addEventListener(BrowserEvents.paste, fn, false)
- return this
- }
- setErrorHandler(fn) {
- window.addEventListener("error", fn)
- window.addEventListener("unhandledrejection", fn)
- return this
- }
- toggleFullScreen() {
- const doc = document
- if ((doc.fullScreenElement && doc.fullScreenElement !== null) || (!doc.mozFullScreen && !doc.webkitIsFullScreen)) {
- if (doc.documentElement.requestFullScreen) doc.documentElement.requestFullScreen()
- else if (doc.documentElement.mozRequestFullScreen) doc.documentElement.mozRequestFullScreen()
- else if (doc.documentElement.webkitRequestFullScreen) doc.documentElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT)
- } else {
- if (doc.cancelFullScreen) doc.cancelFullScreen()
- else if (doc.mozCancelFullScreen) doc.mozCancelFullScreen()
- else if (doc.webkitCancelFullScreen) doc.webkitCancelFullScreen()
- }
- }
- setCopyData(evt, str) {
- const originalEvent = evt.originalEvent
- originalEvent.preventDefault()
- originalEvent.clipboardData.setData("text/plain", str)
- originalEvent.clipboardData.setData("text/html", str)
- }
- getMousetrap() {
- return window.Mousetrap
- }
- copyTextToClipboard(text) {
- // http://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript
- const textArea = document.createElement("textarea")
- textArea.style.position = "fixed"
- textArea.style.top = "0"
- textArea.style.left = "0"
- textArea.style.width = "2em"
- textArea.style.height = "2em"
- textArea.style.padding = "0"
- textArea.style.border = "none"
- textArea.style.outline = "none"
- textArea.style.boxShadow = "none"
- textArea.style.background = "transparent"
- textArea.value = text
- document.body.appendChild(textArea)
- textArea.select()
- try {
- const successful = document.execCommand("copy")
- } catch (err) {}
- document.body.removeChild(textArea)
- }
- getStore() {
- return window.store
- }
- getHash() {
- return location.hash || ""
- }
- setHash(value) {
- location.hash = value
- }
- getHost() {
- return location.host
- }
- _getHostname() {
- return location.hostname
- }
- async appendScript(url) {
- if (!url) return undefined
- if (!this._loadingPromises) this._loadingPromises = {}
- if (this._loadingPromises[url]) return this._loadingPromises[url]
- if (this.isNodeJs()) return undefined
- this._loadingPromises[url] = this._appendScript(url)
- return this._loadingPromises[url]
- }
- _appendScript(url) {
- //https://bradb.net/blog/promise-based-js-script-loader/
- return new Promise(function(resolve, reject) {
- let resolved = false
- const scriptEl = document.createElement("script")
- scriptEl.type = "text/javascript"
- scriptEl.src = url
- scriptEl.async = true
- scriptEl.onload = scriptEl.onreadystatechange = function() {
- if (!resolved && (!this.readyState || this.readyState == "complete")) {
- resolved = true
- resolve(this)
- }
- }
- scriptEl.onerror = scriptEl.onabort = reject
- document.head.appendChild(scriptEl)
- })
- }
- downloadFile(data, filename, filetype) {
- const downloadLink = document.createElement("a")
- downloadLink.setAttribute("href", `data:${filetype},` + encodeURIComponent(data))
- downloadLink.setAttribute("download", filename)
- downloadLink.click()
- }
- reload() {
- window.location.reload()
- }
- openUrl(link) {
- window.open(link)
- }
- setResizeEndHandler(fn) {
- let resizeTimer
- jQuery(window).on(BrowserEvents.resize, evt => {
- const target = jQuery(evt.target)
- if (target.is("div")) return // dont resize on div resizes
- clearTimeout(resizeTimer)
- resizeTimer = setTimeout(() => {
- fn({ width: target.width(), height: target.height() })
- }, 100)
- })
- return this
- }
- getStumpNodeFromElement(el) {
- const jqEl = jQuery(el)
- return this.getHtmlStumpNode().getNodeByGuid(parseInt(jqEl.attr(WillowConstants.uidAttribute)))
- }
- forceRepaint() {
- jQuery(window).width()
- }
- getBrowserHtml() {
- return document.documentElement.outerHTML
- }
- async confirmThen(message) {
- return confirm(message)
- }
- async promptThen(message, value) {
- return prompt(message, value)
- }
- getWindowSize() {
- const windowStumpNode = jQuery(window)
- return {
- width: windowStumpNode.width(),
- height: windowStumpNode.height()
- }
- }
- getDocumentSize() {
- const documentStumpNode = jQuery(document)
- return {
- width: documentStumpNode.width(),
- height: documentStumpNode.height()
- }
- }
- // todo: denote the side effect
- blurFocusedInput() {
- // todo: test against browser.
- document.activeElement.blur()
- }
- setLoadedDroppedFileHandler(callback, helpText = "") {
- const bodyStumpNode = this.getBodyStumpNode()
- const bodyShadow = bodyStumpNode.getShadow()
- // Added the below to ensure dragging from the chrome downloads bar works
- // http://stackoverflow.com/questions/19526430/drag-and-drop-file-uploads-from-chrome-downloads-bar
- const handleChromeBug = event => {
- const originalEvent = event.originalEvent
- const effect = originalEvent.dataTransfer.effectAllowed
- originalEvent.dataTransfer.dropEffect = effect === "move" || effect === "linkMove" ? "move" : "copy"
- }
- const dragoverHandler = event => {
- handleChromeBug(event)
- event.preventDefault()
- event.stopPropagation()
- if (!bodyStumpNode.stumpNodeHasClass("dragOver")) {
- bodyStumpNode.insertChildNode(`div ${helpText}
- id dragOverHelp`)
- bodyStumpNode.addClassToStumpNode("dragOver")
- // Add the help, and then hopefull we'll get a dragover event on the dragOverHelp, then
- // 50ms later, add the dragleave handler, and from now on drag leave will only happen on the help
- // div
- setTimeout(function() {
- bodyShadow.onShadowEvent(BrowserEvents.dragleave, dragleaveHandler)
- }, 50)
- }
- }
- const dragleaveHandler = event => {
- event.preventDefault()
- event.stopPropagation()
- bodyStumpNode.removeClassFromStumpNode("dragOver")
- bodyStumpNode.findStumpNodeByChild("id dragOverHelp").removeStumpNode()
- bodyShadow.offShadowEvent(BrowserEvents.dragleave, dragleaveHandler)
- }
- const dropHandler = async event => {
- event.preventDefault()
- event.stopPropagation()
- bodyStumpNode.removeClassFromStumpNode("dragOver")
- bodyStumpNode.findStumpNodeByChild("id dragOverHelp").removeStumpNode()
- const droppedItems = event.originalEvent.dataTransfer.items
- // NOTE: YOU NEED TO STAY IN THE "DROP" EVENT, OTHERWISE THE DATATRANSFERITEMS MUTATE
- // (BY DESIGN) https://bugs.chromium.org/p/chromium/issues/detail?id=137231
- // DO NOT ADD AN AWAIT IN THIS LOOP. IT WILL BREAK.
- const items = []
- for (let droppedItem of droppedItems) {
- const entry = droppedItem.webkitGetAsEntry()
- items.push(this._handleDroppedEntry(entry))
- }
- const results = await Promise.all(items)
- callback(results)
- }
- bodyShadow.onShadowEvent(BrowserEvents.dragover, dragoverHandler)
- bodyShadow.onShadowEvent(BrowserEvents.drop, dropHandler)
- // todo: why do we do this?
- bodyShadow.onShadowEvent(BrowserEvents.dragenter, function(event) {
- event.preventDefault()
- event.stopPropagation()
- })
- }
- _handleDroppedEntry(item, path = "") {
- // http://stackoverflow.com/questions/3590058/does-html5-allow-drag-drop-upload-of-folders-or-a-folder-tree
- // http://stackoverflow.com/questions/6756583/prevent-browser-from-loading-a-drag-and-dropped-file
- return item.isFile ? this._handleDroppedFile(item) : this._handleDroppedDirectory(item, path)
- }
- _handleDroppedDirectory(item, path) {
- return new Promise((resolve, reject) => {
- item.createReader().readEntries(async entries => {
- const promises = []
- for (let i = 0; i < entries.length; i++) {
- promises.push(this._handleDroppedEntry(entries[i], path + item.name + "/"))
- }
- const res = await Promise.all(promises)
- resolve(res)
- })
- })
- }
- _handleDroppedFile(file) {
- // https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
- // http://www.sitepoint.com/html5-javascript-open-dropped-files/
- return new Promise((resolve, reject) => {
- file.file(data => {
- const reader = new FileReader()
- reader.onload = evt => {
- resolve({ data: evt.target.result, filename: data.name })
- }
- reader.onerror = err => reject(err)
- reader.readAsText(data)
- })
- })
- }
- _getFocusedShadow() {
- const stumpNode = this.getStumpNodeFromElement(document.activeElement)
- return stumpNode && stumpNode.getShadow()
- }
- }
- class AbstractTheme {
- hakonToCss(str) {
- const hakonProgram = new hakonNode(str)
- // console.log(hakonProgram.getAllErrors())
- return hakonProgram.compile()
- }
- }
- class DefaultTheme extends AbstractTheme {}
- class AbstractTreeComponent extends jtree.GrammarBackedNode {
- async startWhenReady() {
- if (this.isNodeJs()) return this.start()
- document.addEventListener(
- "DOMContentLoaded",
- async () => {
- this.start()
- },
- false
- )
- }
- start() {
- this._bindTreeComponentFrameworkCommandListenersOnBody()
- this.renderAndGetRenderReport(this.getWillowBrowser().getBodyStumpNode())
- }
- getWillowBrowser() {
- if (!this._willowBrowser) {
- if (this.isNodeJs()) {
- this._willowBrowser = new WillowBrowser("http://localhost:8000/index.html")
- } else {
- this._willowBrowser = new RealWillowBrowser(window.location.href)
- }
- }
- return this._willowBrowser
- }
- onCommandError(err) {
- throw err
- }
- _setMouseEvent(evt) {
- this._mouseEvent = evt
- return this
- }
- getMouseEvent() {
- return this._mouseEvent || this.getWillowBrowser().getMockMouseEvent()
- }
- _onCommandWillRun() {
- // todo: remove. currently used by ohayo
- }
- _getCommandArgumentsFromStumpNode(stumpNode, commandMethod) {
- if (commandMethod.includes(" ")) {
- // todo: cleanup and document
- // It seems the command arguments can from the method string or from form values.
- const parts = commandMethod.split(" ")
- return {
- uno: parts[1],
- dos: parts[2]
- }
- }
- const shadow = stumpNode.getShadow()
- let valueParam
- if (stumpNode.isStumpNodeCheckbox()) valueParam = shadow.isShadowChecked() ? true : false
- // todo: fix bug if nothing is entered.
- else if (shadow.getShadowValue() !== undefined) valueParam = shadow.getShadowValue()
- else valueParam = stumpNode.getStumpNodeAttr("value")
- const nameParam = stumpNode.getStumpNodeAttr("name")
- return {
- uno: valueParam,
- dos: nameParam
- }
- }
- getStumpNodeString() {
- return this.getWillowBrowser()
- .getHtmlStumpNode()
- .toString()
- }
- _getHtmlOnlyNodes() {
- const nodes = []
- this.getWillowBrowser()
- .getHtmlStumpNode()
- .deepVisit(node => {
- if (node.getFirstWord() === "styleTag" || (node.getContent() || "").startsWith("
- nodes.push(node)
- })
- return nodes
- }
- getStumpNodeStringWithoutCssAndSvg() {
- // todo: cleanup. feels hacky.
- const clone = new jtree.TreeNode(
- this.getWillowBrowser()
- .getHtmlStumpNode()
- .toString()
- )
- clone.getTopDownArray().forEach(node => {
- if (node.getFirstWord() === "styleTag" || (node.getContent() || "").startsWith("
- })
- return clone.toString()
- }
- getTextContent() {
- return this._getHtmlOnlyNodes()
- .map(node => node.getTextContent())
- .filter(text => text)
- .join("\n")
- }
- getCommandNames() {
- return Object.getOwnPropertyNames(Object.getPrototypeOf(this)).filter(word => word.endsWith("Command"))
- }
- async _executeCommandOnStumpNode(stumpNode, commandMethod) {
- const params = this._getCommandArgumentsFromStumpNode(stumpNode, commandMethod)
- if (commandMethod.includes(" "))
- // todo: cleanup
- commandMethod = commandMethod.split(" ")[0]
- this.addToCommandLog([commandMethod, params.uno, params.dos].filter(identity => identity).join(" "))
- this._onCommandWillRun() // todo: remove. currently used by ohayo
- let treeComponent = stumpNode.getStumpNodeTreeComponent()
- while (!treeComponent[commandMethod]) {
- const parent = treeComponent.getParent()
- if (parent === treeComponent) throw new Error(`Unknown command "${commandMethod}"`)
- if (!parent) debugger
- treeComponent = parent
- }
- try {
- await treeComponent[commandMethod](params.uno, params.dos)
- } catch (err) {
- this.onCommandError(err)
- }
- }
- _bindTreeComponentFrameworkCommandListenersOnBody() {
- const willowBrowser = this.getWillowBrowser()
- const bodyShadow = willowBrowser.getBodyStumpNode().getShadow()
- const app = this
- const checkAndExecute = (el, attr, evt) => {
- const stumpNode = willowBrowser.getStumpNodeFromElement(el)
- evt.preventDefault()
- evt.stopImmediatePropagation()
- this._executeCommandOnStumpNode(stumpNode, stumpNode.getStumpNodeAttr(attr))
- return false
- }
- bodyShadow.onShadowEvent(BrowserEvents.contextmenu, `[${WillowConstants.contextMenuCommand}]`, function(evt) {
- if (evt.ctrlKey) return true
- app._setMouseEvent(evt) // todo: remove?
- return checkAndExecute(this, WillowConstants.contextMenuCommand, evt)
- })
- bodyShadow.onShadowEvent(BrowserEvents.click, `[${WillowConstants.clickCommand}]`, function(evt) {
- if (evt.shiftKey) return checkAndExecute(this, WillowConstants.shiftClickCommand, evt)
- app._setMouseEvent(evt) // todo: remove?
- return checkAndExecute(this, WillowConstants.clickCommand, evt)
- })
- bodyShadow.onShadowEvent(BrowserEvents.dblclick, `[${WillowConstants.doubleClickCommand}]`, function(evt) {
- if (evt.target !== evt.currentTarget) return true // direct dblclicks only
- app._setMouseEvent(evt) // todo: remove?
- return checkAndExecute(this, WillowConstants.doubleClickCommand, evt)
- })
- bodyShadow.onShadowEvent(BrowserEvents.blur, `[${WillowConstants.blurCommand}]`, function(evt) {
- return checkAndExecute(this, WillowConstants.blurCommand, evt)
- })
- bodyShadow.onShadowEvent(BrowserEvents.change, `[${WillowConstants.changeCommand}]`, function(evt) {
- return checkAndExecute(this, WillowConstants.changeCommand, evt)
- })
- }
- stopPropagationCommand() {
- // todo: remove?
- // intentional noop
- }
- // todo: remove?
- async clearMessageBufferCommand() {
- delete this._messageBuffer
- }
- // todo: remove?
- async unmountAndDestroyCommand() {
- this.unmountAndDestroy()
- }
- toggleTreeComponentFrameworkDebuggerCommand() {
- // todo: move somewhere else?
- // todo: cleanup
- const app = this.getRootNode()
- const node = app.getNode("TreeComponentFrameworkDebuggerComponent")
- if (node) {
- node.unmountAndDestroy()
- } else {
- app.appendLine("TreeComponentFrameworkDebuggerComponent")
- app.renderAndGetRenderReport()
- }
- }
- getStumpNode() {
- return this._htmlStumpNode
- }
- toHakonCode() {
- return ""
- }
- getTheme() {
- if (!this.isRoot()) return this.getRootNode().getTheme()
- if (!this._theme) this._theme = new DefaultTheme()
- return this._theme
- }
- getCommandsBuffer() {
- if (!this._commandsBuffer) this._commandsBuffer = []
- return this._commandsBuffer
- }
- addToCommandLog(command) {
- this.getCommandsBuffer().push({
- command: command,
- time: this._getProcessTimeInMilliseconds()
- })
- }
- getMessageBuffer() {
- if (!this._messageBuffer) this._messageBuffer = new jtree.TreeNode()
- return this._messageBuffer
- }
- // todo: move this to tree class? or other higher level class?
- addStumpCodeMessageToLog(message) {
- // note: we have 1 parameter, and are going to do type inference first.
- // Todo: add actions that can be taken from a message?
- // todo: add tests
- this.getMessageBuffer().appendLineAndChildren("message", message)
- }
- addStumpErrorMessageToLog(errorMessage) {
- // todo: cleanup!
- return this.addStumpCodeMessageToLog(`div
- class OhayoError
- bern${jtree.TreeNode.nest(errorMessage, 2)}`)
- }
- logMessageText(message = "") {
- const pre = `pre
- bern${jtree.TreeNode.nest(message, 2)}`
- return this.addStumpCodeMessageToLog(pre)
- }
- unmount() {
- if (
- !this.isMounted() // todo: why do we need this check?
- )
- return undefined
- this._getChildTreeComponents().forEach(child => child.unmount())
- this.treeComponentWillUnmount()
- this._removeCss()
- this._removeHtml()
- delete this._lastRenderedTime
- this.treeComponentDidUnmount()
- }
- _removeHtml() {
- this._htmlStumpNode.removeStumpNode()
- delete this._htmlStumpNode
- }
- toStumpCode() {
- return `div
- class ${this.getCssClassNames().join(" ")}`
- }
- getCssClassNames() {
- return this._getJavascriptPrototypeChainUpTo("AbstractTreeComponent")
- }
- treeComponentWillMount() {}
- async treeComponentDidMount() {
- AbstractTreeComponent._mountedTreeComponents++
- }
- treeComponentDidUnmount() {
- AbstractTreeComponent._mountedTreeComponents--
- }
- treeComponentWillUnmount() {}
- getNewestTimeToRender() {
- return this._lastTimeToRender
- }
- _setLastRenderedTime(time) {
- this._lastRenderedTime = time
- return this
- }
- async treeComponentDidUpdate() {}
- _getChildTreeComponents() {
- return this.getChildrenByNodeConstructor(AbstractTreeComponent)
- }
- _hasChildrenTreeComponents() {
- return this._getChildTreeComponents().length > 0
- }
- // todo: this is hacky. we do it so we can just mount all tiles to wall.
- getStumpNodeForChildren() {
- return this.getStumpNode()
- }
- _getLastRenderedTime() {
- return this._lastRenderedTime
- }
- _getCss() {
- return this.getTheme().hakonToCss(this.toHakonCode())
- }
- toPlainHtml(containerId) {
- return `
-
- ${new stumpNode(this.toStumpCode()).compile()}
-
`
- }
- _getCssStumpCode() {
- return `styleTag
- for ${this.constructor.name}
- bern${jtree.TreeNode.nest(this._getCss(), 2)}`
- }
- _updateAndGetUpdateReport() {
- const reasonForUpdatingOrNot = this.getWhetherToUpdateAndReason()
- if (!reasonForUpdatingOrNot.shouldUpdate) return reasonForUpdatingOrNot
- this._setLastRenderedTime(this._getProcessTimeInMilliseconds())
- this._removeCss()
- this._mountCss()
- // todo: fucking switch to react? looks like we don't update parent because we dont want to nuke children.
- // okay. i see why we might do that for non tile treeComponents. but for Tile treeComponents, seems like we arent nesting, so why not?
- // for now
- if (this._hasChildrenTreeComponents()) return { shouldUpdate: false, reason: "did not update because is a parent" }
- this._updateHtml()
- this._lastTimeToRender = this._getProcessTimeInMilliseconds() - this._getLastRenderedTime()
- return reasonForUpdatingOrNot
- }
- _updateHtml() {
- const stumpNodeToMountOn = this._htmlStumpNode.getParent()
- const currentIndex = this._htmlStumpNode.getIndex()
- this._removeHtml()
- this._mountHtml(stumpNodeToMountOn, this._toLoadedOrLoadingStumpCode(), currentIndex)
- }
- unmountAndDestroy() {
- this.unmount()
- return this.destroy()
- }
- // todo: move to keyword node class?
- toggle(firstWord, contentOptions) {
- const currentNode = this.getNode(firstWord)
- if (!contentOptions) return currentNode ? currentNode.unmountAndDestroy() : this.appendLine(firstWord)
- const currentContent = currentNode === undefined ? undefined : currentNode.getContent()
- const index = contentOptions.indexOf(currentContent)
- const newContent = index === -1 || index + 1 === contentOptions.length ? contentOptions[0] : contentOptions[index + 1]
- this.delete(firstWord)
- if (newContent) this.touchNode(firstWord).setContent(newContent)
- return newContent
- }
- isMounted() {
- return !!this._htmlStumpNode
- }
- toggleAndRender(firstWord, contentOptions) {
- this.toggle(firstWord, contentOptions)
- this.getRootNode().renderAndGetRenderReport()
- }
- _getFirstOutdatedDependency(lastRenderedTime = this._getLastRenderedTime() || 0) {
- return this.getDependencies().find(dep => dep.getLineModifiedTime() > lastRenderedTime)
- }
- getWhetherToUpdateAndReason() {
- const mTime = this.getLineModifiedTime()
- const lastRenderedTime = this._getLastRenderedTime() || 0
- const staleTime = mTime - lastRenderedTime
- if (lastRenderedTime === 0)
- return {
- shouldUpdate: true,
- reason: "shouldUpdate because this TreeComponent hasn't been rendered yet",
- staleTime: staleTime
- }
- if (staleTime > 0)
- return {
- shouldUpdate: true,
- reason: "shouldUpdate because this TreeComponent changed",
- staleTime: staleTime
- }
- const outdatedDependency = this._getFirstOutdatedDependency(lastRenderedTime)
- if (outdatedDependency)
- return {
- shouldUpdate: true,
- reason: "Should update because a dependency updated",
- dependency: outdatedDependency,
- staleTime: outdatedDependency.getLineModifiedTime() - lastRenderedTime
- }
- return {
- shouldUpdate: false,
- reason: "Should NOT update because no dependency changed",
- lastRenderedTime: lastRenderedTime,
- mTime: mTime
- }
- }
- getDependencies() {
- return []
- }
- _getTreeComponentsThatNeedRendering(arr) {
- this._getChildTreeComponents().forEach(child => {
- const reasonForUpdatingOrNot = child.getWhetherToUpdateAndReason()
- if (!child.isMounted() || reasonForUpdatingOrNot.shouldUpdate) arr.push({ child: child, childUpdateBecause: reasonForUpdatingOrNot })
- child._getTreeComponentsThatNeedRendering(arr)
- })
- }
- toStumpLoadingCode() {
- return `div Loading ${this.getFirstWord()}...
- class ${this.getCssClassNames().join(" ")}
- id ${this.getTreeComponentId()}`
- }
- getTreeComponentId() {
- // html ids can't begin with a number
- return "treeComponent" + this._getUid()
- }
- _toLoadedOrLoadingStumpCode() {
- if (!this.isLoaded()) return this.toStumpLoadingCode()
- this.setRunTimePhaseError("renderPhase")
- try {
- return this.toStumpCode()
- } catch (err) {
- console.error(err)
- this.setRunTimePhaseError("renderPhase", err)
- return this.toStumpErrorStateCode(err)
- }
- }
- toStumpErrorStateCode(err) {
- return `div ${err}
- class ${this.getCssClassNames().join(" ")}
- id ${this.getTreeComponentId()}`
- }
- _mount(stumpNodeToMountOn, index) {
- this._setLastRenderedTime(this._getProcessTimeInMilliseconds())
- this.treeComponentWillMount()
- this._mountCss()
- this._mountHtml(stumpNodeToMountOn, this._toLoadedOrLoadingStumpCode(), index) // todo: add index back?
- this._lastTimeToRender = this._getProcessTimeInMilliseconds() - this._getLastRenderedTime()
- return this
- }
- // todo: we might be able to squeeze virtual dom in here on the mountCss and mountHtml methods.
- _mountCss() {
- // todo: only insert css once per class? have a set?
- this._cssStumpNode = this._getPageHeadStump().insertCssChildNode(this._getCssStumpCode())
- }
- _getPageHeadStump() {
- return this.getRootNode()
- .getWillowBrowser()
- .getHeadStumpNode()
- }
- _removeCss() {
- this._cssStumpNode.removeCssStumpNode()
- delete this._cssStumpNode
- }
- _mountHtml(stumpNodeToMountOn, htmlCode, index) {
- this._htmlStumpNode = stumpNodeToMountOn.insertChildNode(htmlCode, index)
- this._htmlStumpNode.setStumpNodeTreeComponent(this)
- }
- renderAndGetRenderReport(stumpNode, index) {
- const isUpdateOp = this.isMounted()
- let treeComponentUpdateReport = {
- shouldUpdate: false,
- reason: ""
- }
- if (isUpdateOp) treeComponentUpdateReport = this._updateAndGetUpdateReport()
- else this._mount(stumpNode, index)
- const stumpNodeForChildren = this.getStumpNodeForChildren()
- // Todo: insert delayed rendering?
- const childResults = this._getChildTreeComponents().map((child, index) => child.renderAndGetRenderReport(stumpNodeForChildren, index))
- if (isUpdateOp) {
- if (treeComponentUpdateReport.shouldUpdate) {
- try {
- if (this.isLoaded()) this.treeComponentDidUpdate()
- } catch (err) {
- console.error(err)
- }
- }
- } else {
- try {
- if (this.isLoaded()) this.treeComponentDidMount()
- } catch (err) {
- console.error(err)
- }
- }
- let str = `${this.getWord(0) || this.constructor.name} ${isUpdateOp ? "update" : "mount"} ${treeComponentUpdateReport.shouldUpdate} ${treeComponentUpdateReport.reason}`
- childResults.forEach(child => (str += "\n" + child.toString(1)))
- return new jtree.TreeNode(str)
- }
- }
- AbstractTreeComponent._mountedTreeComponents = 0
- class TreeComponentFrameworkDebuggerComponent extends AbstractTreeComponent {
- toHakonCode() {
- return `.TreeComponentFrameworkDebuggerComponent
- position fixed
- top 5px
- left 5px
- z-index 1000
- background rgba(254,255,156, .95)
- box-shadow 1px 1px 1px rgba(0,0,0,.5)
- padding 12px
- overflow scroll
- max-height 500px
- .TreeComponentFrameworkDebuggerComponentCloseButton
- position absolute
- cursor pointer
- opacity .9
- top 2px
- right 2px
- &:hover
- opacity 1`
- }
- toStumpCode() {
- const app = this.getRootNode()
- return `div
- class TreeComponentFrameworkDebuggerComponent
- div x
- class TreeComponentFrameworkDebuggerComponentCloseButton
- clickCommand toggleTreeComponentFrameworkDebuggerCommand
- div
- span This app is powered by the
- a Tree Component Framework
- href https://github.com/treenotation/jtree/tree/master/treeComponentFramework
- p ${app.getNumberOfLines()} components loaded. ${WillowBrowser._stumpsOnPage} stumps on page.
- pre
- bern
- ${app.toString(3)}`
- }
- }
- class AbstractGithubTriangleComponent extends AbstractTreeComponent {
- constructor() {
- super(...arguments)
- this.githubLink = `https://github.com/treenotation/jtree`
- }
- toHakonCode() {
- return `.AbstractGithubTriangleComponent
- display block
- position absolute
- top 0
- right 0`
- }
- toStumpCode() {
- return `a
- class AbstractGithubTriangleComponent
- href ${this.githubLink}
- img
- src /github-fork.svg`
- }
- }
- window.AbstractGithubTriangleComponent = AbstractGithubTriangleComponent
- window.AbstractTreeComponent = AbstractTreeComponent
- window.WillowBrowser = WillowBrowser
- window.TreeComponentFrameworkDebuggerComponent = TreeComponentFrameworkDebuggerComponent
- ;
-
- {
- class tileBlankLineNode extends jtree.GrammarBackedNode {
- get emptyCell() {
- return this.getWord(0)
- }
- get visible() {
- return false
- }
- }
-
- class abstractTileTreeComponentNode extends AbstractTreeComponent {
- createParser() {
- return new jtree.TreeNode.Parser(
- catchAllErrorNode,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- "amazon.history": amazonHistoryNode,
- "fitbit.all": fitbitAllNode,
- "datawrapper.comingSoon": datawrapperComingSoonNode,
- "dcjs.comingSoon": dcjsComingSoonNode,
- "finos.perspective.comingSoon": finosPerspectiveComingSoonNode,
- "fivethirtyeight.comingSoon": fivethirtyeightComingSoonNode,
- "gov.comingSoon": GovNode,
- "highcharts.comingSoon": highchartsComingSoonNode,
- "re3data.comingSoon": re3dataComingSoonNode,
- "zing.comingSoon": zingComingSoonNode,
- "editor.helloWorld": editorHelloWorldNode,
- "challenge.list": challengeListNode,
- "samples.list": samplesListNode,
- "vega.data.list": vegaDataListNode,
- "vega.example.list": vegaExampleListNode,
- "doc.picker": PickerTileNode,
- "templates.list": templatesListNode,
- "asciichart.line": asciiChartNode,
- "calendar.heat": calendarHeatNode,
- "challenge.play": challengePlayNode,
- "debug.dump": debugDumpNode,
- "web.dump": webDumpNode,
- "debug.commands": debugCommandsNode,
- "debug.sleep": debugSleepNode,
- "debug.noop": debugNoOpNode,
- "debug.throw": debugThrowNode,
- "dtjs.basic": dtjsBasicNode,
- "editor.gallery": editorGalleryNode,
- "handsontable.basic": handsontableBasicNode,
- "html.text": htmlTextNode,
- "html.printAs": htmlPrintAsNode,
- "html.h1": htmlH1Node,
- "html.img": htmlImgNode,
- "html.iframe": htmlIframeNode,
- "html.custom": htmlCustomNode,
- "icons.human": iconsHumanNode,
- "icons.circle": iconsCircleNode,
- "list.basic": listBasicNode,
- "list.links": listLinksNode,
- "markdown.toHtml": markdownToHtmlNode,
- "roughjs.bar": roughJsBarNode,
- "roughjs.pie": roughJsPieNode,
- "roughjs.line": roughJsLineNode,
- "show.rowCount": showRowCountNode,
- "show.columnCount": showColumnCountNode,
- "show.static": showStaticNode,
- "show.value": showValueNode,
- "show.median": showMedianNode,
- "show.sum": showSumNode,
- "show.mean": showMeanNode,
- "show.min": showMinNode,
- "show.max": showMaxNode,
- "tables.basic": tablesBasicNode,
- "tables.interesting": tablesInterestingNode,
- "tables.dump": tablesDumpNode,
- "text.wordcloud": textWordcloudNode,
- "treenotation.3d": treenotation3dNode,
- "treenotation.outline": treenotationOutlineNode,
- "treenotation.dotline": treenotationDotlineNode,
- "vega.bar": vegaBarNode,
- "vega.line": vegaLineNode,
- "vega.area": vegaAreaNode,
- "vega.scatter": vegaScatterNode,
- "vega.bubble": vegaBubbleNode,
- "vega.emoji": vegaEmojiNode,
- "vega.histogram": vegaHistogramNode,
- "vega.example": vegaExampleNode,
- "tiles.didyoumean": DidYouMeanTileNode,
- "doc.title": docTitleNode,
- "doc.subtitle": docSubtitleNode,
- "doc.section": docSectionNode,
- "doc.ref": docReferenceNode,
- "doc.comment": docCommentNode,
- "doc.tooling": docToolingNode,
- "github.info": githubInfoNode,
- "disk.browse": diskBrowseNode,
- "disk.read": diskReadNode,
- "hackernews.top": hackernewsTopNode,
- "hackernews.submissions": hackernewsSubmissionsNode,
- "publicapis.entries": publicApisNode,
- "web.get": webGetNode,
- "web.post": webPostNode,
- "wikipedia.page": wikipediaContentNode,
- "cancer.cases": cancerCasesNode,
- "cdc.infants.weight": weightPercentilesNode,
- "cdc.infants.length": lengthPercentilesNode,
- "cdc.infants.headCircumference": headPercentilesNode,
- "kaggle.datasets.heart": kaggleDatasetsHeartNode,
- "moz.top500": mozTop500Node,
- "owid.lifeExpectancy": lifeExpectancyNode,
- "owid.list": owidListNode,
- "samples.telescopes": samplesTelescopesNode,
- "samples.mtcars": samplesMtcarsNode,
- "samples.iris": samplesIrisNode,
- "samples.flights14": samplesFlights14Node,
- "samples.si": samplesSiNode,
- "samples.portals": samplesPortalNode,
- "samples.starWars": samplesStarWarsNode,
- "samples.populations": samplesPopulationsNode,
- "samples.babyNames": samplesBabyNamesNode,
- "samples.declaration": samplesDeclarationNode,
- "samples.periodicTable": samplesPeriodicTableNode,
- "samples.letters": samplesLettersNode,
- "samples.presidents": samplesPresidentsNode,
- "ucimlr.datasets": ucimlrDatasetsNode,
- "vega.data": vegaDataNode,
- "reddit.all": redditAllNode,
- "reddit.subs": redditSubsNode,
- "reddit.sub": redditSubNode,
- "samples.patients": samplesPatientsNode,
- "samples.poem": samplesPoemNode,
- "samples.outerSpace": samplesOuterSpaceNode,
- "samples.treeProgram": samplesTreeProgramNode,
- "samples.waterBill": samplesWaterBillNode,
- "samples.gapMinder": samplesGapMinderNode,
- "date.addColumns": dateAddColumnsNode,
- "gen.constant": genConstantNode,
- "gen.growth": genGrowthNode,
- "math.log": mathLogNode,
- "rows.addIndexColumn": rowsAddIndexColumnNode,
- "rows.runningTotal": rowsRunningTotalNode,
- "text.length": textLengthNode,
- "text.split": textSplitNode,
- "text.reverseSplit": reverseTextSplitNode,
- "text.toLowerCase": textToLowerCaseNode,
- "text.template": textTemplateNode,
- "text.permalink": textPermalinkNode,
- "text.replace": textReplaceNode,
- "text.trim": textTrimNode,
- "text.substring": textSubstringNode,
- "text.firstLetter": testFirstLetterNode,
- "columns.describe": columnsDescribeNode,
- "columns.list": columnsListNode,
- "data.eval": dataEvalNode,
- "join.by": joinByNode,
- "match.columnsFuzzy": matchColumnsFuzzyNode,
- "schema.toTypescript": schemaTypeScriptNode,
- "schema.toSimple": schemaSimpleNode,
- "text.wordCount": textWordCountNode,
- "text.lineCount": textLineCountNode,
- "treenotation.wordTypes": treenotationWordTypesNode,
- "columns.first": columnsFirstNode,
- "columns.last": columnsLastNode,
- "columns.drop": columnsDropNode,
- "columns.dropConstants": columnsDropConstantsNode,
- "columns.keep": columnsKeepNode,
- "columns.keepNumerics": columnsKeepNumericsNode,
- "rows.shuffle": rowsShuffleNode,
- "rows.reverse": rowsReverseNode,
- "filter.where": filterWhereNode,
- "filter.with": filterWithNode,
- "filter.without": filterWithoutNode,
- "filter.withAny": filterAnyNode,
- "rows.first": rowsFirstNode,
- "rows.sample": rowsSampleNode,
- "rows.dropIfMissing": rowsDropIfMissingNode,
- "rows.last": rowsLastNode,
- "bitanath.pca": pcaNode,
- "columns.rename": columnsRenameNode,
- "columns.cleanNames": columnsCleanNamesNode,
- "columns.setType": columnsSetTypeNode,
- "data.synth": dataSynthNode,
- "data.about": dataAboutNode,
- "data.usabilityScore": dataUsabilityScoreNode,
- "fill.missing": fillMissingNode,
- "gen.range": genRangeNode,
- "group.by": groupByNode,
- "rows.sortBy": rowsSortByNode,
- "rows.sortByReverse": rowsSortByReverseNode,
- "rows.addOne": rowsAddOneNode,
- "text.matches": textMatchesNode,
- "text.combine": textCombineNode,
- "data.inline": dataInlineNode,
- "data.localStorage": dataLocalStorageNode,
- "debug.parserTest": debugParserTestNode,
- "debug.ohayoGrammar": debugGrammarNode,
- "debug.ohayoGrammarTree": debugGrammarTreeNode,
- "editor.files": editorFilesNode,
- "editor.commandHistory": editorCommandHistoryNode,
- "math.gen": mathGenNode,
- "random.float": randomFloatNode,
- "random.int": randomIntNode,
- "samples.tinyIris": samplesTinyIrisNode,
- "assert.rowCount": assertRowCountNode,
- "print.text": printNode,
- "print.csv": printCsvNode,
- hidden: hiddenNode,
- visible: visibleNode,
- maximized: maximizedNode,
- left: leftNode,
- top: topNode,
- width: widthNode,
- height: heightNode
- }),
- [{ regex: /^$/, nodeConstructor: tileBlankLineNode }]
- )
- }
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get inspectionStumpTemplate() {
- return `div TileConstructor: {constructorName} ParentConstructor: {parentConstructorName}
- div Messages:
- ol
- {messages}
- div Tree:
- pre
- bern
- {sourceCode}
- div All Tile Settings:
- pre
- bern
- {settings}
- div Input rows: {inputCount} Output rows: {outputCount}
- div Load time: {timeToLoad} Render time: {renderTime}
- div Input Columns:
- pre
- bern
- {inputColumnsAsTable}
- div Output Columns
- pre
- bern
- {outputColumnsAsTable}
- div Output Numeric Values:
- pre
- bern
- {outputNumericValues}
- div TypeScript Interface:
- pre
- bern
- {typeScriptInterface}
- div Input Numeric Values:
- pre
- bern
- {inputNumericValues}`
- }
- get tileStumpTemplate() {
- return `div
- class {classes}
- id {id}
- div
- style {bodyStyle}
- class TileBody
- {body}
- div
- class TileFooter
- {footer}`
- }
- get errorStateStumpTemplate() {
- return `div
- class {classes}
- id {id}
- div
- class TileBody
- div ERROR
- {content}
- div
- class TileFooter
- {footer}`
- }
- get pencilStumpTemplate() {
- return `span ➕
- class TileInsertBetweenButton
- clickCommand insertTileBetweenCommand
- span ▼
- class TileDropDownButton
- clickCommand toggleTileMenuCommand`
- }
- get errorLogMessageStumpTemplate() {
- return `div Error occurred. See console.
- class OhayoError`
- }
- get tileLoadingTemplate() {
- return `div
- class abstractTileTreeComponentNode
- id {id}
- div Loading {name}...
- class TileBody
- div
- class TileFooter
- {footer}`
- }
- get visibleKey() {
- return `visible`
- }
- get hiddenKey() {
- return `hidden`
- }
- get yearKey() {
- return `year`
- }
- get monthKey() {
- return `month`
- }
- get needsData() {
- return true
- }
- get dayKey() {
- return `day`
- }
- get ohayoFileExtensionKey() {
- return `.ohayo`
- }
- get columnPredictionHintsKey() {
- return `columnPredictionHints`
- }
- get sizeColumnKey() {
- return `sizeColumn`
- }
- get shapeColumnKey() {
- return `shapeColumn`
- }
- get colorColumnKey() {
- return `colorColumn`
- }
- get yColumnKey() {
- return `yColumn`
- }
- get xColumnKey() {
- return `xColumn`
- }
- get contentKey() {
- return `content`
- }
- get rowDisplayLimitKey() {
- return `rowDisplayLimit`
- }
- get settingKey() {
- return `setting`
- }
- // todo: ADD TYPINGS
- getPipishInput() {
- // todo: add placeholder property?
- return this.getSettingsStruct().content || this.getParentOrDummyTable().getFirstColumnAsString() || ""
- }
- getDependencies() {
- return [{ getLineModifiedTime: () => this.getParentOrDummyTable().getTableCTime() }] // todo: we removed this: this.getOutputOrInputTable().getTableCTime()...i think we had it because we want to return true to update children.
- }
- getRunTimeEnumOptions(cell) {
- // todo: only works if codemirror === tab
- try {
- // todo: handle at static time.
- if (cell.getCellTypeId() === "columnNameCell" && this.isLoaded()) {
- const mirrorNode = typeof app === "undefined" ? this : app.mountedProgram.nodeAtLine(this.getLineNumber() - 1)
- return mirrorNode.getParentOrDummyTable().getColumnNames()
- }
- } catch (err) {
- console.log(err)
- }
- }
- mapSettingNamesToColumnNames(settingNames) {
- const tileStruct = this.getSettingsStruct()
- return settingNames.map(name => tileStruct[name])
- }
- getOutputOrInputTable() {
- return this._outputTable || this.getParentOrDummyTable()
- }
- getOutputTable() {
- return this._outputTable
- }
- getParentOrDummyTable() {
- // Returns: non-empty input table || dummy table || empty input table.
- const parentTable = this.getParent().getOutputOrInputTable()
- if (!parentTable.isBlankTable()) return parentTable
- return this._getDummyTable() || parentTable
- }
- _getDummyTable() {
- const dataSet = DummyDataSets[this.dummyDataSetName]
- if (!this._dummyTable && dataSet) this._dummyTable = new Table(jtree.Utils.javascriptTableWithHeaderRowToObjects(dataSet))
- return this._dummyTable
- }
- getRequiredTableWithHeader(headerSettingNames) {
- const columnNames = this.mapSettingNamesToColumnNames(headerSettingNames)
- const table = this.getParentOrDummyTable()
- const columns = columnNames.map(name => table.getTableColumnByName(name))
- if (columns.some(col => !col)) return []
- return this.getRowsAsDataTableArrayWithHeader(table.getRows(), columnNames)
- }
- setIsDataLoaded(value) {
- this._isDataLoaded = value
- this.makeDirty() // todo: remove
- return this
- }
- getRowsAsDataTableArrayWithHeader(rows, header) {
- const data = rows.map(row => row.getAsArray(header))
- data.unshift(header)
- return data
- }
- getTileQualityCheck() {
- const definition = this.getDefinition()
- const name = this.getFirstWord()
- let score = 0
- return {
- name: name,
- namespace: name.split(".")[0],
- description: definition.getDescription() ? 1 : 0,
- dummyDataSetName: this.dummyDataSetName,
- runTimeErrors: Object.values(this.getRunTimePhaseErrors()).length,
- examples: definition.getExamples().length,
- edgeTests: 0,
- speedTests: 0,
- roadMap: 0,
- idealStyleUXDescription: 0,
- secPriTests: 0,
- userType: 0
- }
- }
- _getCachedSettings() {
- if (this._cache_settingsObject) return this._cache_settingsObject
- this._cache_settingsObject = {}
- this.filter(child => child.doesExtend("abstractTileSettingTerminalNode") || child.doesExtend("abstractTileSettingNonTerminalNode")).forEach(setting => {
- this._cache_settingsObject[setting.getFirstWord()] = setting.getSettingValue()
- })
- return this._cache_settingsObject
- }
- // todo: ADD TYPINGS
- getSettingsStruct() {
- const settingsFromCache = this._getCachedSettings()
- // todo: this wont work anymore
- const hintsNode = this.getDefinition().getConstantsObject()[this.columnPredictionHintsKey]
- if (hintsNode)
- Object.assign(
- settingsFromCache,
- this.getParentOrDummyTable().getPredictionsForAPropertyNameToColumnNameMapGivenHintsNode(new jtree.TreeNode(hintsNode), settingsFromCache)
- )
- return settingsFromCache
- }
- getProgramTemplate(id) {}
- getSnippetTemplate(id) {}
- getExampleTemplate(index) {
- // todo: right now we only have 1 example per tile.
- const exampleNode = this.getDefinition().getNode(jtree.GrammarConstants.example)
- return exampleNode ? exampleNode.childrenToString() : ""
- }
- toStumpLoadingCode() {
- return this.qFormat(this.tileLoadingTemplate, {
- classes: this.getCssClassNames().join(" "),
- id: this.getTreeComponentId(),
- name: this.getWord(0),
- footer: this.getTileMenuButtonStumpCode()
- })
- }
- emitLogMessage(message) {
- const tab = this.getTab()
- if (tab) tab.addStumpCodeMessageToLog(message)
- else if (this.isNodeJs()) console.log(message)
- }
- getTheme() {
- return this.getTab().getTheme()
- }
- qFormat(str, obj) {
- return new jtree.TreeNode(str).templateToString(obj)
- }
- scrollIntoView() {
- const el = this.getStumpNode()
- .getShadow()
- .getShadowElement()
- if (el) el.scrollIntoView()
- }
- async loadBrowserRequirements() {
- const loadingMap = this.getTab()
- .getRootNode()
- .getDefinitionLoadingPromiseMap()
- if (!loadingMap.has(this.constructor)) loadingMap.set(this.constructor, this._makeBrowserLoadRequirementsPromise(loadingMap))
- await loadingMap.get(this.constructor)
- }
- async _makeBrowserLoadRequirementsPromise(loadingMap) {
- const app = this.getWebApp()
- const cssScript = this[OhayoConstants.tileCssScript]
- if (cssScript) this._loadTileCss(cssScript)
- const def = this.getDefinition()
- const scriptPaths = def.nodesThatStartWith("string " + OhayoConstants.tileScript).map(node => node.getWord(2))
- const thisScript = this[OhayoConstants.tileScript]
- if (thisScript && !scriptPaths.includes(thisScript)) scriptPaths.push(thisScript)
- if (scriptPaths.length) await Promise.all(scriptPaths.map(scriptPath => app.getWillowBrowser().appendScript(scriptPath)))
- loadingMap.set(this.constructor, true)
- }
- _loadTileCss(css) {
- const app = this.getWebApp()
- app
- .getWillowBrowser()
- .getBodyStumpNode()
- .insertChildNode(
- css
- .split(" ")
- .map(
- url => `link
- rel stylesheet
- media screen
- href ${url}`
- )
- .join("\n")
- )
- }
- _hasBrowserRequirements() {
- return this.tileScript
- }
- _areBrowserRequirementsLoaded() {
- if (this.isNodeJs()) return true
- // todo: cleanup. assumes app is here in browser.
- const loadingMap = app.getDefinitionLoadingPromiseMap()
- return !this._hasBrowserRequirements() || loadingMap.get(this.constructor) === true
- }
- isLoaded() {
- return this._areBrowserRequirementsLoaded() && (!this.needsData || this._isDataLoaded)
- }
- getErrorMessageHtml() {
- const errors = Object.values(this.getRunTimePhaseErrors())
- return errors.length ? ` ${errors.join(" ")} ` : "" //todo: cleanup
- }
- toStumpErrorStateCode(err) {
- return this.qFormat(this.errorStateStumpTemplate, {
- classes: this.getCssClassNames().join(" "),
- id: this.getTreeComponentId(),
- content: `div ` + err,
- footer: this.getTileMenuButtonStumpCode()
- })
- }
- // todo: delete this
- makeDirty() {
- delete this._cache_settingsObject
- delete this._bodyStumpCodeCache // todo: cleanup
- this._setLastRenderedTime(0)
- }
- getAllTileSettingsDefinitions() {
- const def = this.getDefinition()
- return Object.values(def.getFirstWordMapWithDefinitions()).filter(def => def.isOrExtendsANodeTypeInScope([OhayoConstants.abstractTileSetting]))
- }
- getTab() {
- return this.getRootNode().getTab()
- }
- getChildTiles() {
- return this.getChildInstancesOfNodeTypeId("abstractTileTreeComponentNode")
- }
- selectTile() {
- this.selectNode()
- if (this.isMounted()) this.getStumpNode().addClassToStumpNode(OhayoConstants.selectedClass)
- }
- unselectNode() {
- super.unselectNode()
- if (this.isMounted()) this.getStumpNode().removeClassFromStumpNode(OhayoConstants.selectedClass)
- }
- getCssClassNames() {
- const classNames = super.getCssClassNames()
- if (this._isMaximized()) classNames.push("TileMaximized")
- return classNames
- }
- toStumpCode() {
- return this.qFormat(this.tileStumpTemplate, {
- classes: this.getCssClassNames().join(" "),
- id: this.getTreeComponentId(),
- bodyStyle: this.customBodyStyle || "",
- body: this._getBodyStumpCodeCache() || "",
- footer: this.getTileFooterStumpCode()
- })
- }
- _getBodyStumpCodeCache() {
- if (!this._bodyStumpCodeCache) this._bodyStumpCodeCache = this.getTileBodyStumpCode()
- return this._bodyStumpCodeCache
- }
- getTileBodyStumpCode() {
- return ``
- }
- _getCss() {
- const selector = "#" + this.getTreeComponentId()
- const theme = this.getTheme()
- const visibleCss = this.isVisible() ? "" : "display: none"
- const hakonCode = this.hakonTemplate ? new jtree.TreeNode(theme).evalTemplateString(this.hakonTemplate) : this.toHakonCode()
- return `${selector} { ${visibleCss} }
- ${theme.hakonToCss(hakonCode)}`
- }
- handleTileError(err) {
- if (!this._errorCount) this._errorCount = 0
- this._errorCount++
- this.getRootNode().goRed(err)
- }
- async insertTileBetweenCommand() {
- const tab = this.getTab()
- const newNode = this.appendLine("doc.picker")
- this.getChildTiles().forEach(tile => {
- if (tile === newNode) return true
- newNode.appendNode(tile)
- tile.unmountAndDestroy()
- })
- tab.autosaveTab()
- await this.getRootNode().loadAndIncrementalRender()
- }
- getWall() {
- return this.getWebApp().getAppWall()
- }
- getWebApp() {
- return this.getTab().getRootNode()
- }
- async runAndrenderAndGetRenderReport() {
- await this.execute()
- return this.renderAndGetRenderReport()
- }
- getTimeToLoad() {
- return this._timeToLoad || 0
- }
- toHakonCode() {
- return ""
- }
- getTileFooterStumpCode() {
- return this.getTileMenuButtonStumpCode()
- }
- getTileMenuButtonStumpCode() {
- return this.qFormat(this.pencilStumpTemplate)
- }
- // Tile child rendering is done at the wall level.
- _getChildTreeComponents() {
- return []
- }
- getStumpNodeForChildren() {
- // We render all Tiles on the Wall.
- return this.getStumpNode().getParent()
- }
- toInspectionStumpCode() {
- const messages = this.getMessageBuffer().map(message => `li ${moment(message.getLineModifiedTime()).fromNow()} - ${message.childrenToString()}`)
- // const settings = this.getAllTileSettingsDefinitions()
- // .map(setting => `${setting.getFirstWord()} ${setting.getDescription()}`)
- // .join("\n")
- const settings = JSON.stringify(this.getSettingsStruct(), null, 2)
- const parentConstructorName = this.getParent().constructor.name
- const constructorName = this.constructor.name
- const sourceCode = this.toString()
- const inputTable = this.getParentOrDummyTable()
- const outputTable = this.getOutputOrInputTable()
- const outputColumns = outputTable.getColumnsArrayOfObjects()
- const inputCols = inputTable.getColumnsArrayOfObjects()
- const inputCount = inputTable.getRowCount()
- const outputCount = outputTable.getRowCount()
- const timeToLoad = this.getTimeToLoad()
- const renderTime = this.getNewestTimeToRender()
- const inputColumnsAsTable = new jtree.TreeNode(inputCols).toTable()
- const outputColumnsAsTable = new jtree.TreeNode(outputColumns).toTable()
- const outputNumericValues = new jtree.TreeNode(outputTable.getJavascriptNativeTypedValues()).toTable()
- const typeScriptInterface = outputTable.toTypeScriptInterface()
- const inputNumericValues = new jtree.TreeNode(inputTable.getJavascriptNativeTypedValues()).toTable()
- return this.qFormat(this.inspectionStumpTemplate, {
- settings,
- inputCount,
- outputCount,
- timeToLoad,
- renderTime,
- inputColumnsAsTable,
- outputColumnsAsTable,
- outputNumericValues,
- typeScriptInterface,
- inputNumericValues,
- constructorName,
- parentConstructorName,
- sourceCode,
- messages
- })
- }
- isVisible() {
- if (this.has(this.visibleKey)) return true
- if (this.visible === false) return false
- if (this.has(this.hiddenKey)) return false
- return true
- }
- _isMaximized() {
- return this.has(OhayoConstants.maximized)
- }
- async _executeChildNodes() {
- await Promise.all(this.getChildTiles().map(tile => tile.execute()))
- }
- async _execute() {
- this.setIsDataLoaded(true)
- await this._executeChildNodes()
- }
- async execute() {
- try {
- this.setRunTimePhaseError("execute")
- await this._execute()
- } catch (err) {
- this.setRunTimePhaseError("execute", err)
- console.error(err)
- this.emitLogMessage(this.errorLogMessageStumpTemplate)
- }
- return this
- }
- cloneTileCommand() {
- this.duplicateLineCommand()
- return this.getTab().autosaveAndRender()
- }
- duplicateLineCommand() {
- return this.getParent().insertLineAndChildren(this.getLine(), undefined, this.getIndex() + 1)
- }
- async toggleTileMaximizeCommand() {
- if (this.has(OhayoConstants.maximized)) this.delete(OhayoConstants.maximized)
- else this.touchNode(OhayoConstants.maximized)
- await this._runAfterTileUpdate(this)
- }
- async triggerTileMethodCommand(value, methodName) {
- await thisethodName](value)
- await this._runAfterTileUpdate(this)
- }
- // todo: refactor.
- async changeTileTypeCommand(newValue) {
- const tab = this.getTab()
- this.setFirstWord(newValue)
- const newNode = this.duplicate()
- // todo: destroy or something? how do we reparse.
- this.getChildTiles().forEach(tile => tile.unmountAndDestroy())
- this.unmountAndDestroy()
- tab.autosaveTab()
- this.getRootNode().loadAndIncrementalRender()
- }
- changeParentCommand(pathVector) {
- // if (tile.getFirstWordPath() === value) return; // todo: do we need this line?
- const program = this.getRootNode()
- const indexPath = pathVector ? pathVector.split(" ").map(num => parseInt(num)) : ""
- const destinationTree = indexPath ? program.nodeAt(indexPath) : program
- // todo: on jtree should we make copyTo second param optional?
- this.copyTo(destinationTree, destinationTree.length)
- this.unmountAndDestroy()
- return this.getTab().autosaveAndRender()
- }
- async removeTileCommand() {
- const tab = this.getTab()
- this.getChildTiles().forEach(tile => {
- tile.unmount()
- tile.shiftLeft()
- })
- this.unmountAndDestroy()
- tab.autosaveTab()
- this.getRootNode().loadAndIncrementalRender()
- }
- getNewDataCommand() {
- // todo: have some type of paging system to fetch new data.
- }
- async changeTileSettingAndRenderCommand(value, settingName) {
- // note the unusual ordering of params.
- this.touchNode(settingName).setContent(value.toString())
- // todo: sometimes size needs to be redone (maximize, for example)
- await this._runAfterTileUpdate(this)
- }
- // todo: remove
- async changeTileSettingMultilineCommand(val, settingName) {
- this.touchNode(settingName).setChildren(val)
- await this._runAfterTileUpdate(this)
- }
- async changeTileSettingCommand(settingName, value) {
- this.touchNode(settingName).setContent(value)
- }
- async changeWordAndRenderCommand(value, index) {
- this.setWord(parseInt(index), value)
- await this._runAfterTileUpdate(this)
- }
- async changeWordsAndRenderCommand(value, index) {
- index = parseInt(index)
- const edgeSymbol = this.getEdgeSymbol()
- const words = this.getWords().slice(0, index)
- this.setLine(words.concat(value.split(edgeSymbol)).join(edgeSymbol))
- await this._runAfterTileUpdate(this)
- }
- async updateChildrenCommand(val) {
- this.setChildren(val)
- // reload the whole doc for now.
- await this._runAfterTileUpdate(this)
- }
- async _runAfterTileUpdate(tile) {
- tile.makeDirty() // ugly!
- tile.getChildTiles().forEach(tile => {
- tile.makeDirty() // todo: ugly!
- })
- // todo: what if you have a tile that has a contextare that allows editing of its children/
- // if you edit a child, then that parent tile needs to update to...should we allow that or ban that?
- await tile.getTab().autosaveTab()
- await tile.runAndrenderAndGetRenderReport()
- tile
- .getTab()
- .getRootNode()
- .renderApp() // Need to render full app because of code editor
- }
- // todo: downstream data changes?
- async changeTileContentAndRenderCommand(value) {
- this.setContent(value)
- await this._runAfterTileUpdate(this)
- }
- async copyTileCommand() {
- // todo: remove cousin tiles?
- this.getRootNode()
- .getWillowBrowser()
- .copyTextToClipboard(this.getFirstAncestor().toString())
- }
- async createProgramFromTileExampleCommand(index) {
- const template = this.getExampleTemplate(index)
- if (!template) return undefined
- const fileExtension = "ohayo" // todo: generalize
- const tab = await this.getTab()
- .getRootNode()
- ._createAndOpen(template, `help-for-${this.getFirstWord()}.${fileExtension}`)
- tab.addStumpCodeMessageToLog(`div Created '${tab.getFullTabFilePath()}'`)
- }
- async inspectTileCommand() {
- if (!this.isNodeJs()) {
- console.log("Tile available at window.tile")
- window.tile = this
- console.log(this)
- }
- this.getTab().addStumpCodeMessageToLog(this.toInspectionStumpCode())
- this.getTab()
- .getRootNode()
- .renderApp()
- }
- async toggleTileMenuCommand() {
- const app = this.getTab().getRootNode()
- app.setTargetTile(this)
- app.toggleAndRender(`${StudioConstants.tileMenu}`)
- }
- async createProgramFromTemplateCommand(id) {
- const programTemplate = this.getProgramTemplate(id)
- if (!programTemplate) return undefined
- const tab = await this.getTab()
- .getRootNode()
- ._createAndOpen(programTemplate.template, programTemplate.name)
- tab.addStumpCodeMessageToLog(`div Created '${tab.getFullTabFilePath()}'`)
- }
- async appendSnippetTemplateCommand(id) {
- const snippet = this.getSnippetTemplate(id)
- if (!snippet) return undefined
- const tab = this.getTab()
- const tabProgram = tab.getTabProgram()
- const newNodes = tabProgram.concat(snippet)
- const newTiles = newNodes.filter(tile => tile.doesExtend && tile.doesExtend("abstractTileTreeComponentNode"))
- tab.autosaveTab()
- tabProgram.clearSelection()
- tab.getTabWall().unmount()
- await tabProgram.loadAndIncrementalRender()
- newTiles.forEach(tile => tile.selectTile())
- newTiles[0].scrollIntoView()
- }
- async copyDataCommand(delimiter) {
- this.getRootNode()
- .getWillowBrowser()
- .copyTextToClipboard(this.getOutputOrInputTable().toDelimited(delimiter))
- }
- async copyDataAsJavascriptCommand() {
- const table = this.getOutputOrInputTable()
- this.getRootNode()
- .getWillowBrowser()
- .copyTextToClipboard(JSON.stringify(table.toTree().toDataTable(table.getColumnNames()), null, 2))
- }
- async copyDataAsTreeCommand() {
- const text = this.getOutputOrInputTable()
- .toTree()
- .toString()
- this.getRootNode()
- .getWillowBrowser()
- .copyTextToClipboard(text)
- }
- async exportTileDataCommand(format = "csv") {
- // todo: figure this out. use the browsers filename? tile title? id?
- let extension = "csv"
- let type = "text/csv"
- let str = this.getOutputOrInputTable().toDelimited(",")
- if (format === "tree") {
- extension = "tree"
- type = "text"
- str = this.getOutputOrInputTable()
- .toTree()
- .toString()
- }
- this.getRootNode()
- .getWillowBrowser()
- .downloadFile(str, this.getTab().getFileName() + "." + extension, type)
- }
- }
-
- class abstractChartNode extends abstractTileTreeComponentNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { rowDisplayLimit: rowDisplayLimitNode }),
- undefined
- )
- }
- get tileFooterTemplate() {
- return `span Rows: {rowCount} Columns Out: {columnCount}
- {tileMenuButton}`
- }
- get rowDisplayLimit() {
- return 10000
- }
- getTileFooterStumpCode() {
- const table = this.getParentOrDummyTable()
- return this.qFormat(this.tileFooterTemplate, {
- rowCount: table.getRowCount(),
- columnCount: table.getColumnCount(),
- tileMenuButton: this.getTileMenuButtonStumpCode()
- })
- }
- getTileRunTimeWidth() {
- return this.isNodeJs() ? 456 : jQuery(".WallTreeComponent").width() - 100
- }
- getTileRunTimeHeight() {
- return 300
- }
- toDisplayString(value, columnName) {
- // todo: remove.
- if (value === undefined) return ""
- return this.getParentOrDummyTable()
- .getTableColumnByName(columnName)
- .toDisplayString(value)
- }
- _getRowDisplayLimit() {
- const limitStr = this.getSettingsStruct()[this.rowDisplayLimitKey] || this.rowDisplayLimit
- const limit = parseInt(limitStr)
- if (!limitStr || isNaN(limit)) return undefined
- return limit
- }
- getRowsWithRowDisplayLimit() {
- return this.getParentOrDummyTable()
- .getRows()
- .slice(0, this._getRowDisplayLimit())
- }
- }
-
- class abstractTextNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { content: contentNode }),
- undefined
- )
- }
- get stringCell() {
- return this.getWordsFrom(0)
- }
- get bodyStumpTemplate() {
- return `div
- class TileSelectable
- bern
- {content}`
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { content: this.content ? jtree.Utils.linkify(this.content) : "" })
- }
- }
-
- class abstractInstructionsNode extends abstractTextNode {
- get content() {
- return `Instructions go here.`
- }
- get tileSize() {
- return `600 240`
- }
- }
-
- class amazonHistoryNode extends abstractInstructionsNode {
- get dummyDataSetName() {
- return `amazonPurchases`
- }
- get content() {
- return `Step 1. Go to https://www.amazon.com/gp/b2b/reports to download your Amazon order history. Step 2. Add the data here.`
- }
- }
-
- class fitbitAllNode extends abstractInstructionsNode {
- get content() {
- }
- }
-
- class abstractComingSoonNode extends abstractTextNode {
- get content() {
- return `Instructions go here.`
- }
- get tileSize() {
- return `600 240`
- }
- }
-
- class datawrapperComingSoonNode extends abstractComingSoonNode {
- get content() {
- return `We don't have support yet for https://www.datawrapper.de/`
- }
- }
-
- class dcjsComingSoonNode extends abstractComingSoonNode {
- get content() {
- return `We don't have support yet for https://github.com/dc-js/dc.js`
- }
- }
-
- class finosPerspectiveComingSoonNode extends abstractComingSoonNode {
- get content() {
- return `We don't have support yet for https://perspective.finos.org/`
- }
- }
-
- class fivethirtyeightComingSoonNode extends abstractComingSoonNode {
- get content() {
- return `We don't have support yet for https://github.com/fivethirtyeight/data/`
- }
- }
-
- class GovNode extends abstractComingSoonNode {
- get content() {
- return `We don't have support yet for https://www.data.gov/`
- }
- }
-
- class highchartsComingSoonNode extends abstractComingSoonNode {
- get content() {
- return `We don't have support yet for https://www.highcharts.com/blog/snippets/3d-solar-system/`
- }
- }
-
- class re3dataComingSoonNode extends abstractComingSoonNode {
- get content() {
- return `We don't have support yet for https://www.re3data.org/`
- }
- }
-
- class zingComingSoonNode extends abstractComingSoonNode {
- get content() {
- return `We don't have support yet for https://www.zingchart.com/`
- }
- }
-
- class editorHelloWorldNode extends abstractTextNode {
- get content() {
- return `Ohayo world!`
- }
- }
-
- class abstractSnippetGalleryNode extends abstractChartNode {
- get optionStumpTemplate() {
- return `li
- a {title}
- value {value}
- class appendSnippetButton
- clickCommand appendSnippetTemplateCommand`
- }
- get bodyStumpTemplate() {
- return `h4 {title}
- ol
- class TileSelectable
- {options}`
- }
- get tileSize() {
- return `600 240`
- }
- getGalleryNodes() {}
- async _execute() {
- this._outputTable = new Table(
- this.getGalleryNodes()
- .toDataTable()
- .slice(1)
- )
- this.setIsDataLoaded(true)
- await this._executeChildNodes()
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, {
- title: this.title,
- options: new jtree.TreeNode(
- this.getGalleryNodes()
- .map(node => this.qFormat(this.optionStumpTemplate, { title: node.evalTemplateString(this.itemFormat), value: node.get("id") }))
- .join("\n")
- ).toString()
- })
- }
- }
-
- class abstractTemplateGalleryNode extends abstractSnippetGalleryNode {
- get optionStumpTemplate() {
- return `li
- a {title}
- value {value}
- class createProgramButton
- clickCommand createProgramFromTemplateCommand`
- }
- }
-
- class challengeListNode extends abstractSnippetGalleryNode {
- get itemFormat() {
- return `{question}`
- }
- get title() {
- return `Try a challenge:`
- }
- getGalleryNodes() {
- return typeof challengesTree === "undefined" ? jtree.TreeNode.fromDisk("ohayo/packages/challenge/challenges.tree") : new jtree.TreeNode(challengesTree)
- }
- getSnippetTemplate(id) {
- return `challenge.play ${id}`
- }
- }
-
- class samplesListNode extends abstractSnippetGalleryNode {
- get isDataPublicDomain() {
- return true
- }
- get itemFormat() {
- return `{id} - {description}`
- }
- get title() {
- return `All samples:`
- }
- getGalleryNodes() {
- // todo: cleanup.
- const ohayo = this.getWebApp().getOhayoGrammarAsTree()
- const hits = ohayo.getNodesByRegex(/^samples/).map(node => {
- return {
- id: node.get("crux"),
- description: node.get("description")
- }
- })
- return new jtree.TreeNode(hits)
- }
- getSnippetTemplate(id) {
- return id
- }
- }
-
- class vegaDataListNode extends abstractSnippetGalleryNode {
- get itemFormat() {
- return `{id}`
- }
- get title() {
- return `All Vega datasets:`
- }
- getGalleryNodes() {
- // todo: cleanup this line.
- const node = this.getWebApp()
- .getOhayoGrammarAsTree()
- .getNodesByRegex(/^vegaDataSetCell/)[0]
- return new jtree.TreeNode(
- node
- .get("enum")
- .split(" ")
- .map(item => {
- return {
- id: item
- }
- })
- )
- }
- getSnippetTemplate(id) {
- return `vega.data ${id}`
- }
- }
-
- class vegaExampleListNode extends abstractSnippetGalleryNode {
- get itemFormat() {
- return `{id}`
- }
- get title() {
- return `All Vega examples:`
- }
- getGalleryNodes() {
- // todo: cleanup this line.
- const node = this.getWebApp()
- .getOhayoGrammarAsTree()
- .getNodesByRegex(/^vegaExampleNameCell/)[0]
- return new jtree.TreeNode(
- node
- .get("enum")
- .split(" ")
- .map(item => {
- return {
- id: item
- }
- })
- )
- }
- getSnippetTemplate(id) {
- return `vega.example ${id}`
- }
- }
-
- class abstractPickerTileNode extends abstractChartNode {
- get categoryBreakStumpTemplate() {
- return `div {category}
- class PickerCategory`
- }
- get itemStumpTemplate() {
- return `{categoryBreak}
- a {name}
- br
- span {description}
- title {description}
- tabindex -1
- value {value}
- class pickerItemButton
- clickCommand {command}`
- }
- get hakonTemplate() {
- return `.abstractPickerTileNode
- .PickerCategory
- width 100%
- margin-top 20px
- text-align center
- .TileBody
- display flex
- flex-flow row wrap
- a
- &:hover
- background-color {borderColor}
- padding 10px
- margin 5px
- height 30px
- background-color {backgroundColor}
- border 1px solid {borderColor}
- overflow hidden
- text-align center
- text-overflow ellipsis
- font-size 14px
- width 120px
- span
- font-size 70%`
- }
- get needsData() {
- return false
- }
- get tileSize() {
- return `480 420`
- }
- async fetchTableInputs() {
- return { rows: this.getChoices() }
- }
- getTileBodyStumpCode() {
- let lastCat = ""
- return this.getChoices()
- .map(choice => {
- choice.categoryBreak = lastCat !== choice.category ? this.qFormat(this.categoryBreakStumpTemplate, { category: choice.category }) : ""
- lastCat = choice.category
- return this.qFormat(this.itemStumpTemplate, choice)
- })
- .join("\n")
- }
- }
-
- class PickerTileNode extends abstractPickerTileNode {
- getChoices() {
- const allChoices = this.getRootNode()
- .getHandGrammarProgram()
- .getTopNodeTypeDefinitions()
- const filteredChoices = allChoices.filter(nodeDef => !(nodeDef.get(jtree.GrammarConstants.tags) || "").includes(OhayoConstants.noPicker))
- const theChoices = filteredChoices.length ? filteredChoices : allChoices
- return theChoices.map(nodeDefinition => {
- const nodeId = nodeDefinition.get("crux") || nodeDefinition.getNodeTypeIdFromDefinition()
- const name = nodeId.split(".")[1] || ""
- const category = lodash.upperFirst(nodeId.split(".")[0])
- const description = nodeDefinition.getDescription()
- return { name, category, description, value: nodeId, command: "changeTileTypeCommand" }
- })
- }
- }
-
- class templatesListNode extends abstractPickerTileNode {
- getChoices() {
- // todo: cleanup.
- const choices = this.getWebApp()
- .getStandardTemplates()
- .map(node => {
- const id = node
- .getWord(1)
- .replace("templates/", "")
- .replace(this.ohayoFileExtensionKey, "")
- return {
- command: "createProgramFromTemplateCommand",
- name: node.get("data doc.title"),
- value: id,
- category: lodash.upperFirst(node.get("data doc.categories")),
- description: ""
- }
- })
- return lodash.sortBy(choices, "category")
- }
- getProgramTemplate(id) {
- const node = this.getWebApp()
- .getStandardTemplates()
- .filter(node => node.getContent() === `templates/${id}${this.ohayoFileExtensionKey}`)[0]
- return {
- template: node.getNode("data").childrenToString(),
- name: id + this.ohayoFileExtensionKey
- }
- }
- }
-
- class asciiChartNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { yColumn: yColumnNode }),
- undefined
- )
- }
- get titleCell() {
- return this.getWordsFrom(0)
- }
- get bodyStumpTemplate() {
- return `pre
- class TileSelectable
- style overflow: scroll; width: 100%; height: 100%; white-space: pre;
- bern
- {title}
- {chart}`
- }
- get columnPredictionHints() {
- return `yColumn isString=false`
- }
- get tileScript() {
- return `ohayo/packages/asciichart/asciichart.js`
- }
- getTileBodyStumpCode() {
- // todo: autodetect column
- const columName = this.mapSettingNamesToColumnNames(["yColumn"])[0]
- const column = this.getParentOrDummyTable().getColumnByName(columName)
- const values = column.getValues()
- const chart = asciichart.plot(values, { height: 15 })
- const title = this.getContent() || ""
- const leftPad = Math.max(0, Math.floor((chart.split("\n")[0].length - title.length) / 2))
- return this.qFormat(this.bodyStumpTemplate, { title: " ".repeat(leftPad) + title, chart })
- }
- }
-
- class calendarHeatNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { count: countNode, dayColumn: dayColumnNode }),
- undefined
- )
- }
- get hakonTemplate() {
- return `.heatCal
- rect
- fill {darkerBackground}
- shape-rendering crispedges
- text
- font-size 10px
- fill #ddd`
- }
- get bodyStumpTemplate() {
- return `div
- class heatCal
- bern
- {svg}`
- }
- get dummyDataSetName() {
- return `waterBill`
- }
- get columnPredictionHints() {
- return `count getPrimitiveTypeName=number
- dayColumn getPrimitiveTypeName=day`
- }
- get tileSize() {
- return `750 200`
- }
- _getLegend(quins, squareSideWithPadding, position) {
- const theme = this.getTheme()
- const quinSvgs = quins
- .map((quin, index) => {
- const left = position.left + squareSideWithPadding * index
- const style = "fill: " + theme.getHeatColor(1 - quin.percent)
- return ` `
- })
- .join("")
- return `
- Less
- ${quinSvgs}
- More
- `
- }
- getTileBodyStumpCode() {
- const svg = this._getSvg()
- return this.qFormat(this.bodyStumpTemplate, { svg })
- }
- _getDayMap(quins, rows, dayColumnName, countColumnName) {
- const getQuin = val => {
- for (let index = 0; index < quins.length; index++) {
- if (val <= quins[index].value) return quins[index].percent
- }
- }
- const dayMap = {}
- rows.forEach(row => {
- dayMapoment(row[dayColumnName]).format("MM/DD/YYYY")] = {
- Quin: getQuin(row[countColumnName]),
- count: row[countColumnName],
- row: row
- }
- })
- return dayMap
- }
- _getDaysArray(startDay, daysToShow) {
- const days = []
- const firstDay = parseInt(startDay.format("e"))
- for (let dayIndex = 0; dayIndex <= daysToShow; dayIndex++) {
- const day = startDay.clone().add(dayIndex, "days")
- days.push({
- day: day,
- row: parseInt(day.format("e")),
- col: Math.floor((firstDay + dayIndex) / 7)
- })
- }
- return days
- }
- _getDayNamesG(squareSideWithPadding) {
- const dayNames = [{ day: "Mon", row: 2 }, { day: "Wed", row: 4 }, { day: "Fri", row: 6 }]
- .map(day => {
- const _top = 20 + day.row * squareSideWithPadding - 3
- return `${day.day} `
- })
- .join("")
- return `${dayNames} `
- }
- _getMonthNamesG(daysArray, squareSideWithPadding) {
- const _usedMonths = {}
- const monthNames = daysArray
- .map(day => {
- const monthName = day.day.format("MMM")
- const monthYear = day.day.format("MM/YYYY")
- if (_usedMonthsonthYear]) return ""
- _usedMonthsonthYear] = true
- const left = 40 + day.col * squareSideWithPadding
- return `${monthName} `
- })
- .join("")
- return `${monthNames} `
- }
- _getDataSquaresG(daysArray, squareSideWithPadding, dayMap) {
- const dayFormat = "MM/DD/YYYY"
- const today = moment(Date.now()).format(dayFormat)
- const theme = this.getTheme()
- const dataSquares = daysArray
- .map(day => {
- const dayKey = day.day.format(dayFormat)
- const _top = 20 + day.row * squareSideWithPadding
- const left = 40 + day.col * squareSideWithPadding
- const value = dayMap[dayKey]
- const todayStyle = dayKey === today ? "stroke-width:2;stroke:rgb(0,0,0);" : ""
- const style = (value ? "fill: " + theme.getHeatColor(1 - value.Quin) : "") + ";" + todayStyle
- const title = `${dayKey}: ${value ? value.count : 0}`
- return `${title} `
- })
- .join("")
- return `${dataSquares} `
- }
- _getSvg() {
- const inputTable = this.getParentOrDummyTable()
- const rows = inputTable.getJavascriptNativeTypedValues()
- if (!rows.length) return ""
- const tileStruct = this.getSettingsStruct()
- const dayColumnName = tileStruct.dayColumn
- const countColumnName = tileStruct.count
- if (!dayColumnName || !countColumnName) return ""
- const dayCol = inputTable.getTableColumnByName(dayColumnName)
- const countCol = inputTable.getTableColumnByName(countColumnName)
- let daysToShow = 365 * 1 // todo: make configurable
- let endDay = moment(Date.now())
- let startDay = endDay.clone().subtract(daysToShow, "days")
- // todo: make configurable
- // reductions = dayCol.getReductions()
- // startDay = moment(reductions.min)
- // endDay = moment(reductions.max)
- // daysToShow = endDay.diff(startDay, "days")
- const squareSide = 10
- const squarePadding = 2
- const squareSideWithPadding = squareSide + squarePadding
- const width = squareSideWithPadding * (daysToShow / 6)
- const height = 7 * squareSideWithPadding + 100
- const quins = countCol.getQuins()
- const dayMap = this._getDayMap(quins, rows, dayColumnName, countColumnName)
- const daysArray = this._getDaysArray(startDay, daysToShow)
- const dayNamesG = this._getDayNamesG(squareSideWithPadding)
- const monthNamesG = this._getMonthNamesG(daysArray, squareSideWithPadding)
- const squaresG = this._getDataSquaresG(daysArray, squareSideWithPadding, dayMap)
- const keyG = this._getLegend(quins, squareSideWithPadding, { top: 110, left: 60 })
- return `${dayNamesG + squaresG + monthNamesG + keyG} `
- }
- }
-
- class challengePlayNode extends abstractChartNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get challengeIdCell() {
- return parseInt(this.getWord(1))
- }
- get challengeAnswerCell() {
- return this.getWordsFrom(2).map(val => parseFloat(val))
- }
- get tileSize() {
- return `640 240`
- }
- getProgramTemplate(id) {
- const challengeNode = this._getChallengeNode(parseInt(id))
- return {
- template: challengeNode.getNode("solution").childrenToString(),
- name: "challenge-" + id + "-solution.ohayo"
- }
- }
- _getChallengeNode(challengeId) {
- const challenges =
- typeof challengesTree === "undefined" ? jtree.TreeNode.fromDisk("ohayo/packages/challenge/challenges.tree") : new jtree.TreeNode(challengesTree)
- return challenges.nodeAt(challengeId - 1) || challenges.nodeAt(0)
- }
- getTileBodyStumpCode() {
- const challengeId = parseInt(this.getWord(1))
- const answer = this.getWord(2)
- const challengeNode = this._getChallengeNode(challengeId)
- const isCorrect = answer === challengeNode.get("answer")
- const theme = this.getTheme()
- const color = answer ? (isCorrect ? theme.successColor : theme.errorColor) : theme.warningColor
- const answerMessage = answer !== undefined ? (isCorrect ? "CORRECT!" : "Wrong.") : ""
- return `h3 Challenge #${challengeId}
- style color:${color}
- br
- div ${challengeNode.evalTemplateString(`Question: {question}`)}
- class TileSelectable
- br
- input
- placeholder Enter your answer here. All answers are a number.
- value ${answer !== undefined ? answer : ""}
- style width: 300px;
- name 2
- changeCommand changeWordAndRenderCommand
- span ${answerMessage}
- style color: ${color};
- br
- div
- a See a solution
- clickCommand createProgramFromTemplateCommand
- value ${challengeId}`
- }
- }
-
- class debugDumpNode extends abstractChartNode {
- get bodyStumpTemplate() {
- return `div
- style overflow: scroll; width: 100%; height: 100%; white-space: pre;
- bern
- {text}`
- }
- _getCharacterLimit() {
- // Todo: great example of a scale test. I found it to be slow with:
- /*
- vega.sample movies.json
- web.dump
- So some tiles will have characterLimit, rowDisplayLimit, et cetera. And have "speedTestExamples" .
- */
- return 20000
- }
- getTileBodyStumpCode() {
- const text = this._getTextToDump()
- const characterLimit = this._getCharacterLimit()
- let sub = text.substr(0, characterLimit)
- if (text.length > characterLimit)
- // todo: Show standardized truncation warning
- sub = `(Notice: Results truncated to ${characterLimit} characters) ` + sub
- return this.qFormat(this.bodyStumpTemplate, { text: sub || "No data to dump" })
- }
- _getTextToDump() {
- return this.getPipishInput()
- }
- }
-
- class webDumpNode extends debugDumpNode {
- _getTextToDump() {
- return this.getParent().getWillowHttpResponse ? this.getParent().getWillowHttpResponse().text : `${this.constructor.name} requires a parent web tile.`
- }
- }
-
- class debugCommandsNode extends abstractChartNode {
- get bodyStumpTemplate() {
- return `a Run Speed Test on all Templates
- clickCommand _runTemplateSpeedTestCommand
- br
- a Open all Templates Command
- clickCommand _openAllTemplatesCommand
- br
- a Run Tile Quality Check
- clickCommand _doTileQualityCheckCommand`
- }
- getTileBodyStumpCode() {
- return this.bodyStumpTemplate
- }
- _runTemplateSpeedTestCommand() {
- return this.getWebApp()._runTemplateSpeedTestCommand()
- }
- _openAllTemplatesCommand() {
- return this.getWebApp()._openAllTemplatesCommand()
- }
- _doTileQualityCheckCommand() {
- return this.getWebApp()._doTileQualityCheckCommand()
- }
- }
-
- class debugSleepNode extends abstractChartNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get millisecondsCell() {
- return parseInt(this.getWord(1))
- }
- get dummyDataSetIdCell() {
- return this.getWord(2)
- }
- get dummyDataSetName() {
- return `waterBill`
- }
- async fetchTableInputs() {
- const ms = parseInt(this.getWord(1) || 1)
- await this.getWebApp().sleepCommand(ms)
- return { rows: jtree.Utils.javascriptTableWithHeaderRowToObjects(DummyDataSets[this.getWord(2) || "stockPrice"]) }
- }
- }
-
- class debugNoOpNode extends abstractChartNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get anyCell() {
- return this.getWordsFrom(1)
- }
- get visible() {
- return false
- }
- }
-
- class debugThrowNode extends abstractChartNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get tileEventNameCell() {
- return this.getWord(1)
- }
- async fetchTableInputs() {
- this._throwIfMethodNameIs("fetchTableInputs")
- return {
- rows: []
- }
- }
- _throwIfMethodNameIs(name) {
- // Never throw if no word provided. That ensures it wont throw during testing.
- const lookingFor = this.getContent()
- if (lookingFor === name) throw new Error(`DebugTile threw an error on purpose on event: "${lookingFor}"`)
- }
- getTileBodyStumpCode() {
- this._throwIfMethodNameIs("getTileBodyStumpCode")
- }
- treeComponentDidMount() {
- this._throwIfMethodNameIs("treeComponentDidMount")
- }
- treeComponentDidUpdate() {
- this._throwIfMethodNameIs("treeComponentDidUpdate")
- }
- }
-
- class dtjsBasicNode extends abstractChartNode {
- get rowStumpTemplate() {
- return `tr
- {cols}`
- }
- get cellStumpTemplate() {
- return `td
- bern
- {box}`
- }
- get bodyStumpTemplate() {
- return `div
- table
- class DataTable
- thead
- tr
- {headerRows}
- tbody
- {rows}`
- }
- get tileScript() {
- return `ohayo/packages/dtjs/datatables.min.js`
- }
- get tileCssScript() {
- return `ohayo/packages/dtjs/datatables.min.css`
- }
- get tileSize() {
- return `1200 500`
- }
- getTileBodyStumpCode() {
- const columnDefs = this.getParentOrDummyTable()
- .getColumnsArray()
- .slice(0, 10)
- const headerRows = this._getHeaderRowsStumpCode(columnDefs.map(col => col.getColumnName()))
- const rows = this._getTableRowsStumpCode(columnDefs)
- return this.qFormat(this.bodyStumpTemplate, { headerRows, rows })
- }
- _getHeaderRowsStumpCode(columns) {
- return columns.map(colName => `th ${colName}`).join("\n")
- }
- _getTableRowsStumpCode(columns) {
- return this.getRowsWithRowDisplayLimit()
- .slice(0, 10)
- .map((row, index) => {
- const cols = columns
- .map(column => {
- const box = row.getRowHtmlSafeValue(column.getColumnName()) // todo: cache?
- return this.qFormat(this.cellStumpTemplate, { box })
- })
- .join("\n")
- return this.qFormat(this.cellStumpTemplate, { cols })
- })
- .join("\n")
- }
- treeComponentWillUnmount() {
- // cleanup
- }
- treeComponentDidUpdate() {
- super.treeComponentDidUpdate()
- const table = this.getParentOrDummyTable()
- const columnDefs = this.getParentOrDummyTable()
- .getColumnsArray()
- .slice(0, 10)
- const container = this.getStumpNode().findStumpNodeByChild("class DataTable")
- if (this.isNodeJs()) return undefined
- const width = this.getTileRunTimeWidth()
- const height = this.getTileRunTimeHeight()
- const shadow = container.getShadow()
- const el = shadow.getShadowElement()
- shadow.setShadowCss({ width, height })
- const rows = this.getRowsWithRowDisplayLimit()
- // todo: note, this is only works with jQuery
- jQuery.fn.dataTable.ext.errMode = "throw"
- this._dataTables = jQuery(el).DataTable({
- data: this.getRowsAsDataTableArrayWithHeader(rows, columnDefs.map(col => col.getColumnName())).slice(1),
- pageLength: 10,
- scrollY: height
- //"scrollCollapse": true,
- //"paging": false
- })
- }
- treeComponentDidMount() {
- this.treeComponentDidUpdate()
- }
- }
-
- class editorGalleryNode extends abstractChartNode {
- get miniStyleTemplate() {
- return `div`
- }
- get bodyStumpTemplate() {
- return `div
- class MiniMapTile
- {minis}`
- }
- get miniStumpTemplate() {
- return `a
- class miniMap
- {onClick}
- {value}
- {href}
- div
- class miniPreview
- {theTiles}
- div {filename}
- class miniFooter`
- }
- get hakonTemplate() {
- return `.MiniMapTile
- .miniMap
- background {backgroundColor}
- width 120px
- height 90px
- margin 6px
- position relative
- overflow hidden
- box-sizing border-box
- display inline-block
- &:hover
- border 1px solid {boxShadow}
- &:active
- border 2px solid {boxShadow}
- .miniFooter
- font-size 12px
- position absolute
- bottom 0
- width 100%
- height 15px
- line-height 15px
- white-space nowrap
- text-align center
- .miniPreview
- position absolute
- width 100%
- height calc(100% - 15px)
- top 0
- overflow hidden
- div
- background {linkColor}
- height 5px
- margin 1px`
- }
- get dummyDataSetName() {
- return `ohayoPrograms`
- }
- get tileSize() {
- return `1080 600`
- }
- async openFullPathInNewTabAndFocusCommand(url) {
- return this.getTab()
- .getRootNode()
- .openFullPathInNewTabAndFocusCommand(url)
- }
- _getMiniStumpCode(sourceCode, filename, permalink, width = 120, height = 75) {
- const ohayoProgram = new ohayoNode(sourceCode)
- const theTiles = ohayoProgram
- .getTiles()
- .filter(tile => tile.isVisible())
- .map(tile => this.qFormat(this.miniStyleTemplate, {}))
- .join("\n")
- const onClick = permalink ? "clickCommand openFullPathInNewTabAndFocusCommand" : ""
- const value = permalink ? `value ${permalink}` : ""
- const href = permalink ? `href ${permalink}` : ""
- return this.qFormat(this.miniStumpTemplate, { filename, theTiles, onClick, value, href })
- }
- getTileBodyStumpCode() {
- // todo: cache.
- const minis = this.getRowsWithRowDisplayLimit()
- .map(row => this._getMiniStumpCode(row.getRowOriginalValue("bytes"), row.getRowOriginalValue("filename"), row.getRowOriginalValue("link")))
- .join("\n")
- return this.qFormat(this.bodyStumpTemplate, { minis })
- }
- }
-
- class handsontableBasicNode extends abstractChartNode {
- get bodyStumpTemplate() {
- return `div
- class hot`
- }
- get hakonTemplate() {
- return `.hot
- color black`
- }
- get tileScript() {
- return `ohayo/packages/handsontable/handsontable.full.min.js`
- }
- get tileCssScript() {
- return `ohayo/packages/handsontable/handsontable.min.css`
- }
- get tileSize() {
- return `1200 500`
- }
- getTileBodyStumpCode() {
- return this.bodyStumpTemplate
- }
- // todo: allow editing
- treeComponentWillUnmount() {
- if (this._hot) this._hot.destroy()
- delete this._hot
- }
- treeComponentDidUpdate() {
- super.treeComponentDidUpdate()
- const table = this.getParentOrDummyTable()
- const columnDefs = table.getColumnsByImportance()
- const colNames = columnDefs.map(col => col.getColumnName())
- const rows = this.getRowsWithRowDisplayLimit()
- const data = this.getRowsAsDataTableArrayWithHeader(rows, colNames)
- const container = this.getStumpNode().findStumpNodeByChild("class hot")
- const app = this.getWebApp()
- if (this.isNodeJs()) return undefined
- const width = this.getTileRunTimeWidth()
- const height = this.getTileRunTimeHeight()
- this._hot = new Handsontable(container.getShadow().getShadowElement(), {
- data: data,
- rowHeaders: true,
- colHeaders: true,
- stretchH: "all",
- width,
- minSpareCols: 10,
- minSpareRows: 30,
- afterSelection: () => app.pauseShortcutListener(),
- afterDeselect: () => app.startShortcutListener(),
- height
- })
- return this._hot
- }
- treeComponentDidMount() {
- this.treeComponentDidUpdate()
- }
- }
-
- class abstractHtmlNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { style: styleNode, content: contentNode }),
- undefined
- )
- }
- get htmlCell() {
- return this.getWordsFrom(0)
- }
- get bodyStumpTemplate() {
- return `{tag}
- {style}
- {src}
- bern
- {content}`
- }
- get hakonTemplate() {
- return `.abstractHtmlNode
- code
- user-select text`
- }
- getTileFooterStumpCode() {
- return this.getTileMenuButtonStumpCode()
- }
- async fetchTableInputs() {
- return { rows: [{ text: this.getHtmlContent() }] }
- }
- getHtmlContent() {
- return this.getWordsFrom(2).join(" ") || "No html content to show."
- }
- getTag() {
- return this.getWord(1) || "div" // todo: verify this is legal tag.
- }
- getSrc() {
- return this.getSettingsStruct().src
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, {
- tag: this.getTag(),
- style: this.style ? `style ${this.style}` : "",
- src: this.getSrc() ? `src ${this.getSrc()}` : "",
- content: this.getHtmlContent() || ""
- })
- }
- }
-
- class htmlTextNode extends abstractHtmlNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get htmlTextTagCell() {
- return this.getWord(1)
- }
- get htmlCell() {
- return this.getWordsFrom(2)
- }
- }
-
- class htmlPrintAsNode extends abstractHtmlNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get htmlTextTagCell() {
- return this.getWord(1)
- }
- getHtmlContent() {
- return this.getPipishInput()
- }
- }
-
- class abstractHTMLFixedTagTileNode extends abstractHtmlNode {
- getHtmlContent() {
- return this.getContent()
- }
- getTag() {
- return this.htmlTagName
- }
- }
-
- class htmlH1Node extends abstractHTMLFixedTagTileNode {
- get htmlCell() {
- return this.getWordsFrom(0)
- }
- get style() {
- return `text-align:center;`
- }
- get htmlTagName() {
- return `h1`
- }
- get tileSize() {
- return `600 75`
- }
- }
-
- class abstractHTMLContentIsSrcTileNode extends abstractHTMLFixedTagTileNode {
- getHtmlContent() {
- return ""
- }
- getSrc() {
- return this.getContent() || super.getSrc()
- }
- }
-
- class htmlImgNode extends abstractHTMLContentIsSrcTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get urlCell() {
- return this.getWord(1)
- }
- get style() {
- return `width:100%;`
- }
- get htmlTagName() {
- return `img`
- }
- }
-
- class htmlIframeNode extends abstractHTMLContentIsSrcTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get urlCell() {
- return this.getWord(1)
- }
- get htmlTagName() {
- return `iframe`
- }
- }
-
- class htmlCustomNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { content: contentNode }),
- undefined
- )
- }
- get bodyStumpTemplate() {
- return `div
- bern
- {content}`
- }
- getTileBodyStumpCode() {
- // https://meta.stackexchange.com/questions/1777/what-html-tags-are-allowed-on-stack-exchange-sites
- // todo: sanitize tags
- const contentNode = this.getNode("content")
- const content = contentNode ? contentNode.childrenToString() : "No HTML content to show"
- return this.qFormat(this.bodyStumpTemplate, { content })
- }
- }
-
- class iconsIconNode extends abstractChartNode {}
-
- class iconsHumanNode extends iconsIconNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { genderColumn: genderColumnNode, headSize: headSizeNode }),
- undefined
- )
- }
- get dummyDataSetName() {
- return `patients`
- }
- get bodyStumpTemplate() {
- return `div
- bern
- {bern}`
- }
- get columnPredictionHints() {
- return `headSize isString=false
- genderColumn isString=true`
- }
- getTileBodyStumpCode() {
- // Now, what if there is no input table?
- const table = this.getParentOrDummyTable()
- const rows = table.getRows()
- // Now, what if we are using dummy input table?
- const headSizeColumn = this.getSettingsStruct().headSize
- const genderColumn = this.getSettingsStruct().genderColumn
- const reducts = table.getColumnByName(headSizeColumn).getReductions()
- const headColMax = reducts.max
- const bern = rows
- .map(row => {
- const typedRow = row.rowToObjectWithOnlyNativeJavascriptTypes()
- const value = typedRow[headSizeColumn]
- // TODO: ADD TYPINGS
- const genderVal = typedRow[genderColumn].toLowerCase()
- const gender = genderVal === "male" ? "blue" : "pink"
- let character = "O"
- let percent = value / headColMax
- if (isNaN(value)) {
- character = "x"
- percent = reducts.median / headColMax
- }
- const title = row.getHoverTitle()
- percent = Math.round(18 * percent)
- return `${character} `
- })
- .join(" ")
- return this.qFormat(this.bodyStumpTemplate, { bern: bern })
- }
- }
-
- class iconsCircleNode extends iconsIconNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { radius: radiusNode }),
- undefined
- )
- }
- get bodyStumpTemplate() {
- return `div
- bern
- {bern}`
- }
- get dummyDataSetName() {
- return `playerGoals`
- }
- get columnPredictionHints() {
- return `radius isString=false`
- }
- getTileBodyStumpCode() {
- const column = this.getSettingsStruct().radius
- const bern = this.getParentOrDummyTable()
- .getRows()
- .map(row => `O `)
- .join(" ")
- return this.qFormat(this.bodyStumpTemplate, { bern: bern })
- }
- }
-
- class listBasicNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { label: labelNode }),
- undefined
- )
- }
- get columnNameCell() {
- return this.getWordsFrom(0)
- }
- get columnPredictionHints() {
- return `label getTitlePotential`
- }
- get dummyDataSetName() {
- return `telescopes`
- }
- get tileSize() {
- return `400 400`
- }
- get listItemStumpTemplate() {
- return `li
- span {label}`
- }
- get bodyStumpTemplate() {
- return `ol
- {items}`
- }
- _getListItem(label) {
- return this.qFormat(this.listItemStumpTemplate, { label })
- }
- _getLabelColumnName() {
- // todo: more automatic! Need to fix our columns/keywords issues
- return this.getWord(1) || this.getSettingsStruct().label
- }
- getTileBodyStumpCode() {
- const labelColumnName = this._getLabelColumnName()
- const items = this.getRowsWithRowDisplayLimit()
- .map(row => this._getListItem(jtree.Utils.stripHtml(row.getRowOriginalValue(labelColumnName)), row))
- .join("\n")
- return this.qFormat(this.bodyStumpTemplate, { items })
- }
- }
-
- class listLinksNode extends listBasicNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { label: labelNode, link: linkNode }),
- undefined
- )
- }
- get columnNameCell() {
- return this.getWordsFrom(0)
- }
- get columnPredictionHints() {
- return `label getTitlePotential
- link isLink`
- }
- get listItemHakonTemplate() {
- return `li
- a {label}
- href {link}`
- }
- get dummyDataSetName() {
- return `telescopes`
- }
- _getUrlColumnName() {
- // todo: more automatic! Need to fix our columns/keywords issues
- return this.getWord(2) || this.getSettingsStruct().link
- }
- _getListItem(label, row) {
- const urlColumnName = this._getUrlColumnName()
- if (!urlColumnName) return super._getListItem(label, row)
- return this.qFormat(this.listItemHakonTemplate, { label, link: jtree.Utils.stripHtml(row.getRowOriginalValue(urlColumnName)) })
- }
- }
-
- class markdownToHtmlNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { content: contentNode }),
- undefined
- )
- }
- get dummyDataSetName() {
- return `markdown`
- }
- get tileSize() {
- return `400 400`
- }
- get bodyStumpTemplate() {
- return `div
- class TileSelectable
- bern
- {md}`
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { md: marked(this.getPipishInput()) })
- }
- }
-
- class abstractRoughJsChartNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { roughness: roughnessNode }),
- undefined
- )
- }
- get titleCell() {
- return this.getWordsFrom(0)
- }
- get bodyStumpTemplate() {
- return `div
- id {id}`
- }
- get tileScript() {
- return `ohayo/packages/roughjs/roughviz.min.js`
- }
- _getRoughId() {
- return `rough${this._getUid()}`
- }
- treeComponentDidUpdate() {
- super.treeComponentDidUpdate()
- if (this.isNodeJs()) return undefined
- this._drawRough()
- }
- treeComponentDidMount() {
- this.treeComponentDidUpdate()
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { id: this._getRoughId() })
- }
- get _roughness() {
- const value = this.get("roughness")
- return value ? parseInt(value) : 1
- }
- _getOptions() {
- return {}
- }
- _drawRough() {
- const colors = this.get("colors") ? this.get("colors").split(" ") : undefined
- const options = Object.assign(this._getOptions(), {
- title: this.getContent() || "",
- element: "#" + this._getRoughId(),
- roughness: this._roughness,
- width: this.getTileRunTimeWidth(),
- height: this.getTileRunTimeHeight(),
- colors,
- data: this._getRoughData()
- })
- const roughEl = new roughViz[this.roughChartType](options)
- }
- _getValues(settingName) {
- const columName = this.mapSettingNamesToColumnNames([settingName])[0]
- return this.getParentOrDummyTable()
- .getColumnByName(columName)
- .getValues()
- }
- }
-
- class abstractRoughJsLabelValueNode extends abstractRoughJsChartNode {
- get columnPredictionHints() {
- return `value getPrimitiveTypeName=number`
- }
- get dummyDataSetName() {
- return `stockPrice`
- }
- _getRoughData() {
- const data = { labels: this._getValues("label"), values: this._getValues("value") }
- console.log(data)
- return data
- }
- }
-
- class roughJsBarNode extends abstractRoughJsLabelValueNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { label: labelNode, value: valueNode }),
- undefined
- )
- }
- get roughChartType() {
- return `Bar`
- }
- }
-
- class roughJsPieNode extends abstractRoughJsLabelValueNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { label: labelNode, value: valueNode }),
- undefined
- )
- }
- get roughChartType() {
- return `Pie`
- }
- }
-
- class roughJsLineNode extends abstractRoughJsChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { colors: colorsNode }),
- undefined
- )
- }
- get roughChartType() {
- return `Line`
- }
- _getNumericColumns() {
- return Object.values(this.getParentOrDummyTable().getColumnsMap()).filter(col => col.isNumeric())
- }
- _getRoughData() {
- const data = {}
- const numerics = this._getNumericColumns()
- numerics.forEach(col => {
- data[col.getColumnName()] = col.getValues()
- })
- return data
- }
- _getOptions() {
- const options = {}
- const numerics = this._getNumericColumns()
- numerics.forEach((col, index) => {
- options["y" + index] = col.getColumnName()
- })
- return options
- }
- }
-
- class abstractShowTileNode extends abstractChartNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get titleCell() {
- return this.getWordsFrom(2)
- }
- get bodyStumpTemplate() {
- return `h6 {title}
- h3 {number}`
- }
- get hakonTemplate() {
- return `.abstractShowTileNode
- h3
- text-align center
- h6
- text-align center
- height 40px
- overflow hidden`
- }
- get dummyDataSetName() {
- return `stockPrice`
- }
- get tileSize() {
- return `140 120`
- }
- getTileBodyStumpCode() {
- const columnName = this.getWord(1)
- if (!columnName) return `No data for ${this.getFirstWord()}`
- const table = this.getParentOrDummyTable()
- const col = table.getTableColumnByName(columnName)
- if (!col) {
- console.log(`No column named ${columnName}`)
- return ""
- }
- const reductionName = this.reductionName || this.getWord(0).split(".")[1]
- const title = this.getWordsFrom(2).join(" ") || [columnName, reductionName].join(" ")
- const number = this.toDisplayString(col.getReductions()[reductionName], columnName)
- return this.qFormat(this.bodyStumpTemplate, { title, number })
- }
- }
-
- class showRowCountNode extends abstractShowTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get titleCell() {
- return this.getWordsFrom(1)
- }
- get defaultTitle() {
- return `Total rows`
- }
- get dummyDataSetName() {
- return `stockPrice`
- }
- get tileSize() {
- return `140 120`
- }
- getTileBodyStumpCode() {
- const title = this.getWordsFrom(1).join(" ") || this.defaultTitle
- return this.qFormat(this.bodyStumpTemplate, { title, number: this._getNumber() })
- }
- _getNumber() {
- return this.getParentOrDummyTable().getRowCount()
- }
- }
-
- class showColumnCountNode extends showRowCountNode {
- get defaultTitle() {
- return `Total columns`
- }
- _getNumber() {
- return this.getParentOrDummyTable().getColumnNames().length
- }
- }
-
- class showStaticNode extends abstractShowTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get numberCell() {
- return parseFloat(this.getWord(1))
- }
- get titleCell() {
- return this.getWordsFrom(2)
- }
- getTileBodyStumpCode() {
- const title = this.getWordsFrom(2).join(" ")
- return this.qFormat(this.bodyStumpTemplate, { title, number: this.getWord(1) || "" })
- }
- }
-
- class showValueNode extends abstractShowTileNode {
- get reductionName() {
- return `median`
- }
- }
-
- class showMedianNode extends abstractShowTileNode {}
-
- class showSumNode extends abstractShowTileNode {}
-
- class showMeanNode extends abstractShowTileNode {}
-
- class showMinNode extends abstractShowTileNode {}
-
- class showMaxNode extends abstractShowTileNode {}
-
- class tablesBasicNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { columnLimit: columnLimitNode }),
- undefined
- )
- }
- get bodyStumpTemplate() {
- return `div
- class tablesBasicNode
- table
- thead
- {headerRows}
- tbody
- {bodyRows}`
- }
- get headerRowStumpTemplate() {
- return `th
- value {colName}
- span {colName}
- value {colName}`
- }
- get rowStumpTemplate() {
- return `tr
- class tableRow
- value {value}
- td {number}
- {cols}`
- }
- get cellLinkStumpTemplate() {
- return `td
- a
- href {content}
- bern
- {content}`
- }
- get cellStumpTemplate() {
- return `td
- bern
- {content}`
- }
- get hakonTemplate() {
- return `.tablesBasicNode
- font-size 14px
- box-sizing border-box
- {enableTextSelect1}
- table
- width 100%
- tr
- white-space nowrap
- padding 0
- td
- border 1px solid {lineColor}
- tr:nth-child(even)
- background-color {veryLightGrey}
- td,th
- padding 2px 3px
- text-align left
- overflow hidden
- text-overflow ellipsis
- max-width 250px
- td:hover,th:hover
- overflow visible
- td:first-child,th:first-child
- padding-left 5px
- color {greyish}
- width 60px
- th
- cursor pointer
- background-color {lightGrey}
- border 1px solid {lineColor}
- border-bottom-color {greyish}`
- }
- get customBodyStyle() {
- return `padding:0px;`
- }
- get tileSize() {
- return `750 300`
- }
- get columnLimit() {
- return 20
- }
- get rowDisplayLimit() {
- return 100
- }
- _getTableRowsStumpCode(columns) {
- return this.getRowsWithRowDisplayLimit()
- .map((row, index) => {
- const cols = columns
- .map(column => {
- return this.qFormat(column.isLink() ? this.cellLinkStumpTemplate : this.cellStumpTemplate, {
- content: row.getRowHtmlSafeValue(column.getColumnName())
- })
- })
- .join("\n")
- return this.qFormat(this.rowStumpTemplate, { number: index + 1, value: row.getPuid(), cols })
- })
- .join("\n")
- }
- _getHeaderRowsStumpCode(columns) {
- // todo: can we get a copy column command?
- return ["Row"]
- .concat(columns)
- .map(colName => this.qFormat(this.headerRowStumpTemplate, { colName }))
- .join("\n")
- }
- getTileBodyStumpCode() {
- const tileStruct = this.getSettingsStruct()
- const table = this.getParentOrDummyTable()
- if (table.isBlankTable()) return `div No data to show`
- let columnDefs = tileStruct.columnOrder === "importance" ? table.getColumnsByImportance() : table.getColumnsArray()
- columnDefs = columnDefs.slice(0, tileStruct.columnLimit || this.columnLimit)
- const columnNames = columnDefs.map(col => col.getColumnName())
- // todo: if the types for a column are all equal, add a total row to the bottom.
- // todo: if the types for a row are all equal, add a total column to the right.
- const headerRows = this._getHeaderRowsStumpCode(columnNames)
- const bodyRows = this._getTableRowsStumpCode(columnDefs)
- return this.qFormat(this.bodyStumpTemplate, { headerRows, bodyRows })
- }
- }
-
- class tablesInterestingNode extends tablesBasicNode {
- get columnOrder() {
- return `importance`
- }
- }
-
- class tablesDumpNode extends tablesBasicNode {
- get columnOrder() {
- return `default`
- }
- }
-
- class textWordcloudNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { column: columnNode, count: countNode }),
- undefined
- )
- }
- get columnPredictionHints() {
- return `name isString=true
- count isString=false`
- }
- get dummyDataSetName() {
- return `wordCounts`
- }
- get tileScript() {
- return `ohayo/packages/text/wordcloud2.min.js`
- }
- get bodyStumpTemplate() {
- return `div
- class divWhereWordCloudWillGo
- style height: 300px;`
- }
- getTileBodyStumpCode() {
- return this.bodyStumpTemplate
- }
- _getAllWords() {
- return this.getRequiredTableWithHeader(["name", "count"])
- }
- treeComponentDidUpdate() {
- this._draw()
- }
- treeComponentDidMount() {
- this._draw()
- }
- _draw() {
- if (this.isNodeJs()) return undefined
- const tileStruct = this.getSettingsStruct()
- const words = this._getAllWords()
- if (!words.length) return
- words.shift() // drop header
- const shadow = this.getStumpNode().getShadow()
- const width = shadow.getShadowOuterWidth()
- const powConstant = 10 / Math.log(words.length) // breaks if too hgih.
- const options = {
- list: words.map(word => [word[0], word[1]]),
- shuffle: false,
- gridSize: Math.round((16 * width) / 1024),
- weightFactor: size => (Math.pow(size, powConstant) * width) / 1024,
- backgroundColor: "transparent",
- random: jtree.Utils.makeSemiRandomFn(),
- wait: 0
- }
- Object.assign(options, tileStruct)
- const element = this.getStumpNode()
- .findStumpNodeByChild("class divWhereWordCloudWillGo")
- .getShadow()
- .getShadowElement()
- WordCloud(element, options)
- }
- }
-
- class treenotation3dNode extends abstractChartNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- size: sizeNode,
- cameraPosition: cameraPositionNode,
- content: contentNode
- }),
- undefined
- )
- }
- get dummyDataSetName() {
- return `treeProgram`
- }
- get tileScript() {
- return `ohayo/packages/treenotation/vis.min.js`
- }
- get tileSize() {
- return `800 500`
- }
- getTileBodyStumpCode() {
- return `div
- class visjs`
- }
- treeComponentDidMount() {
- super.treeComponentDidMount()
- this.treeComponentDidUpdate()
- }
- // Called when the Visualization API is loaded.
- treeComponentDidUpdate() {
- super.treeComponentDidUpdate()
- if (this.isNodeJs()) return undefined
- try {
- this._tryVis()
- } catch (err) {
- // log error
- console.error(err)
- }
- }
- _tryVis() {
- const tileStruct = this.getSettingsStruct()
- const source = this.getPipishInput()
- const app = this.getWebApp()
- const program = new ohayoNode(source)
- const rows = this._treeTo3D(program)
- // Create and populate a data table.
- const data = new vis.DataSet()
- rows.forEach(row => data.add(row))
- const dotSize = tileStruct.size
- const showGrid = tileStruct.showGrid
- // specify options
- // docs: http://visjs.org/docs/graph3d/
- const cameraPositionNode = this.getNode("cameraPosition") || new jtree.TreeNode("cameraPosition 4 .1 1.5").getNode("cameraPosition")
- const distance = parseFloat(cameraPositionNode.getWord(1))
- const horizontal = parseFloat(cameraPositionNode.getWord(2))
- const vertical = parseFloat(cameraPositionNode.getWord(3))
- const options = {
- width: this.getTileRunTimeWidth() + "px",
- height: this.getTileRunTimeHeight() - 80 + "px",
- style: "dot-color", // dot?
- showPerspective: false,
- showLegend: false,
- showShadow: false,
- keepAspectRatio: true,
- xStep: 1,
- yStep: 1,
- zStep: 1,
- zMax: 5,
- showGrid: true,
- showZAxis: false,
- showXAxis: false,
- showYAxis: false,
- dotSizeRatio: dotSize,
- dotSizeMinFraction: 1,
- cameraPosition: {
- distance: distance,
- horizontal: horizontal,
- vertical: vertical
- },
- verticalRatio: 1.0,
- // parameter point contains properties x, y, z, and data
- // data is the original object passed to the point constructor
- tooltip: point => point.data.line
- }
- // create a graph3d
- const element = this.getStumpNode()
- .findStumpNodeByChild("class visjs")
- .getShadow()
- .getShadowElement()
- this._graph3d = new vis.Graph3d(element, data, options)
- const throttled = lodash.throttle(evt => this._onCameraPositionChange(evt), 100)
- this._graph3d.on("cameraPositionChange", throttled)
- }
- async _onCameraPositionChange(evt) {
- // todo: throttle
- const pos = this._graph3d.getCameraPosition()
- const str = `${pos.distance} ${pos.horizontal} ${pos.vertical}`
- this.touchNode("cameraPosition").setContent(str)
- await this.getTab().autosaveTab()
- }
- _treeTo3D(program) {
- // getCameraPosition
- // setCameraPosition
- // onCameraPositionChange
- const theme = this.getWebApp().getTheme()
- // todo: use node type for color.
- const tagMap = {}
- const tagTree = new jtree.TreeNode(program.toCellTypeTree())
- // const outlineFn = node => node.getIndex()
- // use language to get dict, use dict to get type overlay to get tag types.
- const randomFn = jtree.Utils.makeSemiRandomFn()
- const makeColor = word => {
- if (!tagMap[word]) tagMap[word] = randomFn() // todo: give word types certain colors. green for keword, red for error, etc
- const color = tagMap[word]
- //console.log(color)
- return color
- }
- const points = []
- const nodeToPoint = (node, index) => {
- const nodePath = node.getPathVector(program)
- const tagNode = tagTree.nodeAt(nodePath)
- node.getWords().forEach((word, wordIndex) => {
- const wordType = tagNode.getWord(wordIndex)
- const colorNumber = makeColor(wordType)
- const xcc = node.getIndentLevel(program) + wordIndex
- const ycc = -node.getLineNumber()
- const zcc = 0
- points.push({
- x: xcc,
- y: ycc,
- z: zcc,
- line: `cellType: ${wordType} | word: ${word}`,
- style: colorNumber
- })
- })
- }
- program.getTopDownArray().forEach(nodeToPoint)
- return points
- }
- }
-
- class treenotationOutlineNode extends abstractChartNode {
- get bodyStumpTemplate() {
- return `pre
- style overflow: scroll; width: 100%; height: 100%; margin: 0; box-sizing: border-box; font-family: monospace; line-height: 13px;
- bern
- {bern}`
- }
- get tileSize() {
- return `800 500`
- }
- get dummyDataSetName() {
- return `outerSpace`
- }
- _getTheBern() {
- return new jtree.TreeNode(this.getPipishInput()).toOutline()
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { bern: this._getTheBern() })
- }
- }
-
- class treenotationDotlineNode extends treenotationOutlineNode {
- get dummyDataSetName() {
- return `outerSpace`
- }
- get dots() {
- return true
- }
- _getTheBern() {
- return new jtree.TreeNode(this.getPipishInput()).toMappedOutline(
- node =>
- "o" +
- node
- .getLine()
- .split(" ")
- .map(word => "º")
- .join("")
- )
- }
- }
-
- class abstractVegaNode extends abstractChartNode {
- get titleCell() {
- return this.getWordsFrom(0)
- }
- get bodyStumpTemplate() {
- return `div
- class divForExternalLibrary`
- }
- get markName() {
- return `bar`
- }
- get dummyDataSetName() {
- return `stockPrice`
- }
- get tileScript() {
- return `ohayo/packages/vega/vega.combined.min.js`
- }
- get tileSize() {
- return `800 300`
- }
- // todo: I don't think vega handles . in column names.
- getTileBodyStumpCode() {
- return this.bodyStumpTemplate
- }
- _getColumnToField(columnName) {
- if (!columnName) return undefined
- const columnsMap = this.getParentOrDummyTable().getColumnsMap()
- const col = columnsMap[columnName]
- const obj = { field: columnName, type: col.getVegaType() }
- if (col.isTemporal()) {
- const timeUnit = col.getVegaTimeUnit()
- if (timeUnit) obj.timeUnit = timeUnit
- }
- return obj
- }
- _getElementForVega() {
- return this.getStumpNode()
- .findStumpNodeByChild("class divForExternalLibrary")
- .getShadow()
- .getShadowElement()
- }
- async _drawVega() {
- // todo: don't rerun this if we dont need to.
- await vegaEmbed(this._getElementForVega(), this._getVegaSpec())
- }
- treeComponentDidUpdate() {
- super.treeComponentDidUpdate()
- if (this.isNodeJs()) return undefined
- this._drawVega()
- }
- treeComponentDidMount() {
- this.treeComponentDidUpdate()
- }
- _getVegaData() {
- return {
- values: this.getParentOrDummyTable()
- .cloneNativeJavascriptTypedRows()
- .slice(0, this._getRowDisplayLimit())
- }
- }
- _getVegaTitle() {
- return this.getContent()
- }
- _getVegaSpec() {
- return {
- description: "A simple bar chart with embedded data.",
- data: this._getVegaData(),
- width: this.getTileRunTimeWidth(),
- height: this.getTileRunTimeHeight(),
- mark: this._getVegaMarkObj(),
- encoding: this._getEncodingMap(),
- transform: this._getVegaTransform(),
- title: this._getVegaTitle(),
- config: this._getVegaConfig()
- }
- }
- _getVegaTransform() {
- return undefined
- }
- _getVegaConfig() {
- return undefined
- }
- _getEncodingMap() {
- return {}
- }
- // todo: add type
- _getVegaMarkObj() {
- return { type: this._getVegaMark(), tooltip: { content: "data" } }
- }
- _getVegaMark() {
- return this.markName
- }
- }
-
- class vegaBarNode extends abstractVegaNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- colorColumn: colorColumnNode,
- shapeColumn: shapeColumnNode,
- xColumn: xColumnNode,
- yColumn: yColumnNode
- }),
- undefined
- )
- }
- get columnPredictionHints() {
- return `xColumn
- yColumn isString=false,!xColumn`
- }
- _getEncodingMap() {
- const columnNames = this.mapSettingNamesToColumnNames([this.xColumnKey, this.yColumnKey, this.colorColumnKey])
- return {
- x: this._getColumnToField(columnNames[0]),
- y: this._getColumnToField(columnNames[1]),
- color: this._getColumnToField(columnNames[2])
- }
- }
- }
-
- class vegaLineNode extends vegaBarNode {
- get markName() {
- return `line`
- }
- }
-
- class vegaAreaNode extends vegaLineNode {
- get markName() {
- return `area`
- }
- }
-
- class vegaScatterNode extends vegaBarNode {
- get markName() {
- return `point`
- }
- _getEncodingMap() {
- const columnNames = this.mapSettingNamesToColumnNames([this.xColumnKey, this.yColumnKey, this.colorColumnKey, this.shapeColumnKey])
- return {
- x: this._getColumnToField(columnNames[0]),
- y: this._getColumnToField(columnNames[1]),
- color: this._getColumnToField(columnNames[2]),
- shape: this._getColumnToField(columnNames[3])
- }
- }
- }
-
- class vegaBubbleNode extends vegaScatterNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { sizeColumn: sizeColumnNode, colorColumn: colorColumnNode }),
- undefined
- )
- }
- get columnPredictionHints() {
- return `sizeColumn isString=false
- xColumn isString=false`
- }
- get dummyDataSetName() {
- return `gapMinder`
- }
- get markName() {
- return `circle`
- }
- _getEncodingMap() {
- const columnNames = this.mapSettingNamesToColumnNames([this.xColumnKey, this.yColumnKey, this.sizeColumnKey, this.colorColumnKey])
- return {
- y: {
- field: columnNames[1],
- type: "quantitative",
- scale: { zero: false },
- axis: { minExtent: 30 }
- },
- x: this._getColumnToField(columnNames[0]),
- size: { field: columnNames[2], type: "quantitative" },
- color: { value: "#000" }
- }
- }
- }
-
- class vegaEmojiNode extends vegaBarNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { yColumn: yColumnNode, emojiColumn: emojiColumnNode }),
- undefined
- )
- }
- get dummyDataSetName() {
- return `emojis`
- }
- get columnPredictionHints() {
- return `emojiColumn isString=true
- yColumn isString=false`
- }
- _getVegaConfig() {
- return { view: { stroke: "" } }
- }
- _getVegaMark() {
- return { type: "text", baseline: "middle" }
- }
- _getEncodingMap() {
- const columnNames = this.mapSettingNamesToColumnNames([this.yColumnKey, "emoji"])
- return {
- x: { field: columnNames[1], type: "nominal", axis: null },
- y: { field: columnNames[0], type: "quantitative", axis: null, sort: null },
- text: { field: columnNames[1], type: "nominal" },
- size: { value: 65 }
- }
- }
- }
-
- class vegaHistogramNode extends abstractVegaNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { xColumn: xColumnNode }),
- undefined
- )
- }
- get dummyDataSetName() {
- return `wordCounts`
- }
- get columnPredictionHints() {
- return `xColumn isString=false`
- }
- _getEncodingMap() {
- const columnName = this.getContent() || this.mapSettingNamesToColumnNames([this.xColumnKey])[0]
- return {
- x: {
- bin: true,
- field: columnName,
- type: "quantitative"
- },
- y: {
- aggregate: "count",
- type: "quantitative"
- }
- }
- }
- }
-
- class vegaExampleNode extends abstractVegaNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get vegaExampleNameCell() {
- return this.getWord(1)
- }
- _getVegaSpec() {
- return this._spec
- }
- async _fetchSpec() {
- // todo: localtesting.
- if (this.isNodeJs()) return undefined
- const exampleName = this.getContent() || "area" // todo: pull this default from the gram?
- const url = `ohayo/packages/vega/ignore/vega-lite/examples/compiled/${exampleName}.vg.json`
- const res = await this.getWebApp()
- .getWillowBrowser()
- .httpGetUrl(url)
- const spec = JSON.parse(res.text)
- // rewrite data urls
- spec.data.forEach(row => {
- if (row.url) row.url = row.url.replace("data/", "packages/vega/datasets/")
- })
- this._spec = spec
- return spec
- }
- // todo: clean this up.
- async fetchTableInputs() {
- const spec = await this._fetchSpec()
- if (this.isNodeJs()) return { rows: [] }
- const el = jQuery("
")[0]
- const embedded = await vegaEmbed(el, spec)
- const rows = await this._getVegaPostTransformOutputRows(spec, embedded)
- return { rows: rows }
- }
- async _getVegaPostTransformOutputRows(spec, embedded) {
- const tableName = spec.data[0] && spec.data[0].name
- if (tableName) return embedded.view.data(tableName)
- // const values = spec.data.values
- // if (values && values.entries) return Array.from(values.entries())
- // if (typeof values === "function") return []
- // else if (values) return values
- return []
- }
- }
-
- class DidYouMeanTileNode extends abstractTileTreeComponentNode {
- get bodyStumpTemplate() {
- return `div
- span No tile '{input}' found. Line {lineNo}. Did you mean
- a {closestTile}
- collapse
- tabindex -1
- value {closestTile}
- clickCommand changeTileTypeCommand
- span ?`
- }
- getTileBodyStumpCode() {
- const input = this.getFirstWord()
- const lineNo = this.getLineNumber()
- const closestTile = jtree.Utils.didYouMean(
- input,
- this.getRootNode()
- .getHandGrammarProgram()
- .getTopNodeTypeDefinitions()
- .map(def => def.get("crux"))
- )
- if (!closestTile) {
- if (!input) return `div Your program has a blank line on line ${lineNo}.`
- return `div No tile '${input}' found.`
- }
- return this.qFormat(this.bodyStumpTemplate, { input, lineNo, closestTile })
- }
- getErrors() {
- return [new jtree.UnknownNodeTypeError(this)]
- }
- }
-
- class abstractDocTileNode extends abstractTileTreeComponentNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get tileStumpTemplate() {
- return `div
- class {classes}
- id {id}
- div
- class TileBody
- {body}
- div
- class TileFooter
- {footer}`
- }
- get bodyStumpTemplate() {
- return `{tagName}
- bern
- {content}`
- }
- _getBody() {
- return this.qFormat(this.bodyStumpTemplate, { content: this.getContent() || "", tagName: this.tagName })
- }
- toStumpCode() {
- return this.qFormat(this.tileStumpTemplate, {
- classes: this.getCssClassNames().join(" "),
- footer: this.getTileMenuButtonStumpCode(),
- id: this.getTreeComponentId(),
- body: this._getBody()
- })
- }
- }
-
- class docTitleNode extends abstractDocTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get stringCell() {
- return this.getWordsFrom(1)
- }
- get tagName() {
- return `h1`
- }
- get tileSize() {
- return `600 75`
- }
- }
-
- class docSubtitleNode extends docTitleNode {
- get tagName() {
- return `h2`
- }
- }
-
- class docSectionNode extends abstractDocTileNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- subtitle: docSectionSubtitleNode,
- paragraph: docSectionParagraphNode,
- link: docSectionLinkNode,
- code: docSectionCodeNode
- }),
- undefined
- )
- }
- _getBody() {
- return this.compile()
- }
- _getCompiledLine() {
- return ""
- }
- }
-
- class docReferenceNode extends abstractDocTileNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { url: docReferenceUrlNode }),
- undefined
- )
- }
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get referenceIdCell() {
- return this.getWord(1)
- }
- get tagName() {
- return `p`
- }
- }
-
- class docCommentNode extends abstractTileTreeComponentNode {
- createParser() {
- return new jtree.TreeNode.Parser(commentLineNode, undefined, undefined)
- }
- get commentKeywordCell() {
- return this.getWord(0)
- }
- get commentCell() {
- return this.getWordsFrom(1)
- }
- get visible() {
- return false
- }
- }
-
- class docToolingNode extends docCommentNode {}
-
- class abstractProviderNode extends abstractTileTreeComponentNode {
- get tileFooterTemplate() {
- return `span Rows Out: {outputCount} Columns Out: {columnCount} Time: {time}s Parser: {parserId} {errorMessageHtml}
- {tileMenuButton}`
- }
- get tileStumpTemplate() {
- return `div
- class {classes}
- id {id}
- div
- class TileBody
- {body}
- div
- class TileFooter
- {footer}`
- }
- get tileSize() {
- return `140 60`
- }
- getTileFooterStumpCode() {
- const table = this.getOutputOrInputTable()
- const time = (this.getTimeToLoad() / 1000).toFixed(1)
- const parserId = this.getParserId() || "?"
- return this.qFormat(this.tileFooterTemplate, {
- parserId,
- errorMessageHtml: this.getErrorMessageHtml() || "",
- time,
- outputCount: table.getRowCount(),
- columnCount: table.getColumnCount(),
- tileMenuButton: this.getTileMenuButtonStumpCode()
- })
- }
- getRowClass() {
- return Row
- }
- getTileBodyStumpCode() {
- const description = this._getDescription()
- return "div " + (description ? jtree.Utils.linkify(description) : "")
- }
- _getDescription() {
- return this.getDefinition().get("description")
- }
- toStumpCode() {
- return this.qFormat(this.tileStumpTemplate, {
- classes: this.getCssClassNames().join(" "),
- id: this.getTreeComponentId(),
- body: this._getBodyStumpCodeCache(),
- footer: this.getTileFooterStumpCode()
- })
- }
- getParserId() {
- return this.getSettingsStruct().parser
- }
- async fetchTableInputs() {
- return {
- rows: []
- }
- }
- async _execute() {
- const timeLoadStarted = this._getProcessTimeInMilliseconds()
- this._timeLastLoadStarted = timeLoadStarted
- const fetchedTableInputs = await this.fetchTableInputs()
- // If a new request happened after this one, abort this one.
- // todo: what happens to children?
- // todo: add testing for this.
- if (this._timeLastLoadStarted !== timeLoadStarted) {
- console.log("superceded")
- return null
- }
- this._outputTable = new Table(fetchedTableInputs.rows, fetchedTableInputs.columnDefinitions, this.getRowClass())
- this._timeToLoad = this._getProcessTimeInMilliseconds() - timeLoadStarted
- this.setIsDataLoaded(true)
- await this._executeChildNodes()
- }
- }
-
- class abstractUrlNoCellsNode extends abstractProviderNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { parser: parserNode, useCache: useCacheNode }),
- undefined
- )
- }
- get tileSize() {
- return `300 150`
- }
- get useCache() {
- return true
- }
- getUrl() {
- const struct = Object.assign(this.getSettingsStruct(), this.getDefinition().getConstantsObject())
- if (struct.urlTemplate && this.getContent()) return new jtree.TreeNode({ content: this.getContent() }).evalTemplateString(struct.urlTemplate)
- if (struct.urlPrefix && this.getContent()) return struct.urlPrefix + this.getContent()
- return struct.urlCell || this.getContent() || this.url || ""
- }
- getParserId() {
- if (this.parser) return this.parser
- const url = this.getUrl()
- if (super.getParserId()) return super.getParserId()
- const extension = jtree.Utils.getFileExtension(url)
- if (new TableParser().getAllTableParserIds().includes(extension)) return extension
- }
- getWillowHttpResponse() {
- return this._willowHttpResponse
- }
- _setWillowHttpResponse(willowHttpResponse) {
- this._willowHttpResponse = willowHttpResponse
- return this
- }
- // todo: add support for Arrow.
- // todo: remove this cache. use higher level.
- async _getData(url) {
- const useCache = this.getSettingsStruct().useCache !== "false" || this.useCache
- const willowBrowser = this.getWebApp().getWillowBrowser()
- let response
- if (useCache) response = await willowBrowser.httpGetUrlFromCache(url)
- else response = await willowBrowser.httpGetUrl(url)
- if (response.fromCache)
- this.emitLogMessage(`div
- bern
- Loading from cache: ${url}`)
- this._setWillowHttpResponse(response)
- return response.getParsedDataOrText()
- }
- async fetchTableInputs() {
- let url = this.getUrl()
- if (!url) return { rows: [] }
- url = encodeURI(url)
- const parserId = this.getParserId()
- this.setRunTimePhaseError("fetchUrl")
- try {
- const data = await this._getData(url)
- const parser = new TableParser()
- if (typeof data === "string") return parser.parseTableInputsFromString(data, parserId)
- if (this.jsonPath) return parser.parseTableInputsFromObject(data[this.jsonPath], parserId)
- return parser.parseTableInputsFromObject(data, parserId)
- } catch (err) {
- // todo: solve the superagent not throwing response message thing.
- const txt = (err.text || err.toString()).substr(0, 280)
- this.emitLogMessage(`Error getting url: ${url}
- ${txt}`)
- this.setRunTimePhaseError("fetchUrl", txt)
- return { rows: [] }
- }
- }
- }
-
- class abstractUrlNode extends abstractUrlNoCellsNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get urlCell() {
- return this.getWord(1)
- }
- get tileSize() {
- return `300 100`
- }
- }
-
- class abstractUrlsNode extends abstractUrlNode {
- getUrls() {
- return this.getWordsFrom(1).map(url => (this.urlPrefix || "") + url)
- }
- async fetchTableInputs() {
- // todo: allow cache breaking.
- const app = this.getWebApp()
- const willowBrowser = app.getWillowBrowser()
- let allResults = []
- const urls = this.getUrls()
- const fetchMethod = async url => (app.isUrlGetProxyAvailable() ? willowBrowser.httpGetUrlFromProxyCache(url) : willowBrowser.httpGetUrlFromCache(url))
- for (let url of urls) {
- const response = await fetchMethod(url)
- allResults.push(response)
- }
- return { rows: allResults.map(res => res.asJson) }
- }
- }
-
- class githubInfoNode extends abstractUrlsNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get githubRepoCell() {
- return this.getWordsFrom(1)
- }
- get urlPrefix() {
- return `https://api.github.com/repos/`
- }
- get dataDomain() {
- return `github.com`
- }
- }
-
- class diskBrowseNode extends abstractUrlNode {
- get pathCell() {
- return this.getWordsFrom(0)
- }
- get tileSize() {
- return `500 500`
- }
- get hakonTemplate() {
- return `.DiskTile
- table
- width 100%
- td,th
- overflow hidden
- text-overflow ellipsis
- tr
- white-space nowrap`
- }
- getUrl() {
- return this.getContent() ? "/disk?path=" + this.getContent() : "/disk"
- }
- getTileBodyStumpCode() {
- const labelCol = "name"
- const path = this.getContent() || ""
- const parentPath = path.replace(/\/[^\/]*$/, "")
- const rowDisplayLimit = 1000 // todo: adjustable?
- let rows = this.getOutputTable()
- .getRows()
- .slice(0, rowDisplayLimit)
- rows = lodash.sortBy(rows, row => row.getRowOriginalValue("isDirectory") === "false")
- return `input
- placeholder Filepath
- value ${path}
- changeCommand changeTileContentAndRenderCommand
- class LargeTileInput
- table
- tr
- td
- a ..
- clickCommand changeTileContentAndRenderCommand
- value ${parentPath}
- ${rows
- .map(row => {
- const label = jtree.Utils.stripHtml(row.getRowOriginalValue(labelCol))
- const isDir = row.getRowOriginalValue("isDirectory") === "true"
- const size = row.getRowOriginalValue("bytes")
- const mtime = row.getRowOriginalValue("mtime")
- if (!isDir)
- return ` tr
- td ${label}
- td ${numeral(size).format("0.0 b")}
- td ${moment(parseFloat(mtime)).fromNow()}`
- return ` tr
- td
- a ${label}
- clickCommand changeTileContentAndRenderCommand
- value ${path.replace(/\/$/, "") + "/" + label}`
- })
- .join("\n")}`
- }
- }
-
- class diskReadNode extends abstractUrlNode {
- get urlPrefix() {
- return `/disk.read?path=`
- }
- }
-
- class abstractHackernewsNode extends abstractUrlNode {
- get dataDomain() {
- return `news.ycombinator.com`
- }
- }
-
- class hackernewsTopNode extends abstractHackernewsNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get quantityCell() {
- return parseInt(this.getWord(1))
- }
- async fetchTableInputs() {
- // todo: allow cache breaking.
- const willowBrowser = this.getWebApp().getWillowBrowser()
- const firstUrls = this._getFirstUrls()
- if (!firstUrls.length || this.isNodeJs()) return []
- let allResults = []
- const fetchMethod = async url =>
- this.getWebApp().isUrlGetProxyAvailable() ? willowBrowser.httpGetUrlFromProxyCache(url) : willowBrowser.httpGetUrlFromCache(url)
- for (let mainUrl of firstUrls) {
- const response = await fetchMethod(mainUrl)
- const nextUrls = this._parseNextUrls(response)
- const batchResults = await Promise.all(nextUrls.slice(0, this._getLimit()).map(url => fetchMethod(url)))
- allResults = allResults.concat(batchResults)
- }
- return { rows: allResults.map(res => res.asJson) }
- }
- _getLimit() {
- return parseInt(this.getContent() || 10)
- }
- _parseNextUrls(response) {
- return response.asJson.map(id => `https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`)
- }
- _getFirstUrls() {
- return ["https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty"]
- }
- }
-
- class hackernewsSubmissionsNode extends hackernewsTopNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get quantityCell() {
- return parseInt(this.getWord(1))
- }
- get hackerNewsUserNameCell() {
- return this.getWordsFrom(2)
- }
- _getFirstUrls() {
- return this.getWordsFrom(2).map(username => `https://hacker-news.firebaseio.com/v0/user/${username}.json?print=pretty`)
- }
- _getLimit() {
- return parseInt(this.getWord(1) || 10)
- }
- _parseNextUrls(response) {
- return response.asJson.submitted.map(id => `https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`)
- }
- }
-
- class publicApisNode extends abstractUrlNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get jsonPath() {
- return `entries`
- }
- get parser() {
- return `json`
- }
- get url() {
- return `https://api.publicapis.org/entries`
- }
- get dataDomain() {
- return `publicapis.org`
- }
- }
-
- class webGetNode extends abstractUrlNode {
- get tileSize() {
- return `400 100`
- }
- get bodyStumpTemplate() {
- return `span {kind}
- class LargeLabel
- input
- value {content}
- placeholder {placeholderMessage}
- changeCommand changeTileContentAndRenderCommand
- class LargeTileInput`
- }
- get placeholderMessage() {
- return `Enter a url.`
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { kind: this.getFirstWord(), content: this.getContent() || "", placeholderMessage: this.placeholderMessage })
- }
- }
-
- class webPostNode extends abstractUrlNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { post: postNode }),
- undefined
- )
- }
- get tileSize() {
- return `400 130`
- }
- get webPostBodyStumpTemplate() {
- return `textarea
- bern
- {post}
- placeholder This data will be sent as the value of the 'q' param
- name post
- changeCommand changeTileSettingMultilineCommand
- class TileTextArea`
- }
- getTileBodyStumpCode() {
- return super.getTileBodyStumpCode() + this.qFormat(this.webPostBodyStumpTemplate, { post: jtree.Utils.stripHtml(this.getSettingsStruct().post || "") })
- }
- async _getData(url) {
- const settings = this.getSettingsStruct()
- // todo, but make a separate tile
- // if (settings.pushButton) {
- // if (!settings.pushed) return ""
- // settings.pushed = false
- // }
- const postData = settings.post || ""
- const res = await this.getWebApp()
- .getWillowBrowser()
- .httpPostUrl(url, { q: postData.trim() })
- this._setWillowHttpResponse(res)
- return res.getParsedDataOrText()
- }
- }
-
- class wikipediaContentNode extends abstractUrlNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get wikipediaPermalinkCell() {
- return this.getWordsFrom(1)
- }
- get urlPrefix() {
- return `https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&explaintext&redirects=1&origin=*&titles=`
- }
- get dataDomain() {
- return `wikipedia.org`
- }
- getUrl() {
- return this.urlPrefix + this.wikipediaPermalinkCell.join("|")
- }
- async fetchTableInputs() {
- const inputs = await super.fetchTableInputs()
- // todo: cleanup
- return { rows: Object.values(inputs.rows[0].query.pages) }
- }
- }
-
- class abstractFixedDatasetFromUrlNode extends abstractUrlNoCellsNode {
- _getDescription() {
- const desc = super._getDescription()
- if (this.dataUrl) return (desc ? desc + " from " : "") + this.dataUrl
- return desc
- }
- }
-
- class abstractFixedDatasetFromOhayoCollectionNode extends abstractFixedDatasetFromUrlNode {
- get tileSize() {
- return `300 150`
- }
- async _getData(url) {
- if (!this.isNodeJs()) return super._getData(url)
- const fs = require("fs")
- const filepath = __dirname + "/../" + url
- return fs.readFileSync(filepath, "utf8")
- }
- }
-
- class cancerCasesNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/cancer/cases.csv`
- }
- }
-
- class abstractCdcInfantPercentileNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get dataUrl() {
- return `https://www.cdc.gov/growthcharts/percentile_data_files.htm`
- }
- get isDataPublicDomain() {
- return true
- }
- }
-
- class weightPercentilesNode extends abstractCdcInfantPercentileNode {
- get url() {
- return `ohayo/packages/cdc/wtageinf.csv`
- }
- }
-
- class lengthPercentilesNode extends abstractCdcInfantPercentileNode {
- get url() {
- return `ohayo/packages/cdc/lenageinf.csv`
- }
- }
-
- class headPercentilesNode extends abstractCdcInfantPercentileNode {
- get url() {
- return `ohayo/packages/cdc/hcageinf.csv`
- }
- }
-
- class kaggleDatasetsHeartNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/kaggle/heart.csv`
- }
- }
-
- class mozTop500Node extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/moz/top500Domains.csv`
- }
- get isDataPublicDomain() {
- return true
- }
- get dataUrl() {
- return `https://moz.com/top500`
- }
- }
-
- class lifeExpectancyNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/owid/life-expectancy.csv`
- }
- }
-
- class owidListNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/owid/owid.tree`
- }
- get parser() {
- return `treeRows`
- }
- }
-
- class samplesTelescopesNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/telescopes.tsv`
- }
- get dataUrl() {
- return `https://github.com/treenotation/ohayo/blob/master/ohayo/packages/samples/telescopes.tsv`
- }
- get isDataPublicDomain() {
- return true
- }
- get dataDescription() {
- return `## Provenance
- This list was put together by a group of remote workers in a Google spreadsheet in 2017 and hasn't been updated in a while.`
- }
- }
-
- class samplesMtcarsNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/mtcars.tsv`
- }
- get dataUrl() {
- return `https://stat.ethz.ch/R-manual/R-devel/library/datasets/html/mtcars.html`
- }
- get isDataPublicDomain() {
- return true
- }
- }
-
- class samplesIrisNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/iris.tsv`
- }
- get isDataPublicDomain() {
- return true
- }
- get dataUrl() {
- return `https://archive.ics.uci.edu/ml/datasets/iris`
- }
- }
-
- class samplesFlights14Node extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/flights14-sample.csv`
- }
- get isDataPublicDomain() {
- return true
- }
- get dataUrl() {
- return `https://github.com/Rdatatable/data.table/blob/master/vignettes/flights14.csv`
- }
- }
-
- class samplesSiNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get parser() {
- return `text`
- }
- get url() {
- return `ohayo/packages/samples/si.tree`
- }
- get dataUrl() {
- return `https://github.com/treenotation/ohayo/blob/master/ohayo/packages/samples/si.tree`
- }
- get isDataPublicDomain() {
- return true
- }
- }
-
- class samplesPortalNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/portals.ssv`
- }
- get dataUrl() {
- return `https://github.com/treenotation/ohayo/blob/master/ohayo/packages/samples/portals.ssv`
- }
- get isDataPublicDomain() {
- return true
- }
- }
-
- class samplesStarWarsNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/starwars.json`
- }
- get dataLicense() {
- return `BSD`
- }
- get isDataPublicDomain() {
- return false
- }
- }
-
- class samplesPopulationsNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/populations.tsv`
- }
- }
-
- class samplesBabyNamesNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/baby-names-sample.csv`
- }
- }
-
- class samplesDeclarationNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/declaration-of-independence.text`
- }
- get isDataPublicDomain() {
- return true
- }
- }
-
- class samplesPeriodicTableNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/periodic-table.csv`
- }
- get dataUrl() {
- return `https://gist.github.com/GoodmanSciences/c2dd862cd38f21b0ad36b8f96b4bf1ee`
- }
- }
-
- class samplesLettersNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/letters.tsv`
- }
- }
-
- class samplesPresidentsNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/samples/presidents.csv`
- }
- get isDataPublicDomain() {
- return true
- }
- }
-
- class ucimlrDatasetsNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get url() {
- return `ohayo/packages/ucimlr/datasets.tsv`
- }
- }
-
- class vegaDataNode extends abstractFixedDatasetFromOhayoCollectionNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get vegaDataSetCell() {
- return this.getWord(1)
- }
- get urlPrefix() {
- return `ohayo/packages/vega/datasets/`
- }
- }
-
- class redditAllNode extends abstractUrlNoCellsNode {
- get offlineDataSet() {
- return `ohayo/packages/reddit/all.json`
- }
- get url() {
- return `https://www.reddit.com/r/all/top/.json?sort=top`
- }
- get dataDomain() {
- return `reddit.com`
- }
- async fetchTableInputs() {
- const inputs = await super.fetchTableInputs()
- // Todo: add tests/external dependency, as reddit API changes.
- // Here it looks like we have the equivalent of a custom parser just for a Reddit Data source.
- // todo: explore/define/typescriptAPI this custom parser pattern more. probably will be common.
- return inputs.rows.length ? { rows: inputs.rows[0].data.children.map(obj => obj.data) } : inputs
- }
- getParserId() {
- return "json"
- }
- }
-
- class redditSubsNode extends redditAllNode {
- get url() {
- return `https://www.reddit.com/reddits.json`
- }
- }
-
- class redditSubNode extends redditAllNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get subredditNameCell() {
- return this.getWord(1)
- }
- getUrl() {
- const subreddit = this.getContent() || "all"
- return `https://www.reddit.com/r/${subreddit}/top/.json?sort=top`
- }
- }
-
- class abstractDummyNode extends abstractProviderNode {
- get tileSize() {
- return `300 150`
- }
- async _execute() {
- this.setIsDataLoaded(true)
- await this._executeChildNodes()
- }
- }
-
- class samplesPatientsNode extends abstractDummyNode {
- get isDataPublicDomain() {
- return true
- }
- get dummyDataSetName() {
- return `patients`
- }
- }
-
- class samplesPoemNode extends abstractDummyNode {
- get isDataPublicDomain() {
- return true
- }
- get dummyDataSetName() {
- return `poem`
- }
- }
-
- class samplesOuterSpaceNode extends abstractDummyNode {
- get dummyDataSetName() {
- return `outerSpace`
- }
- }
-
- class samplesTreeProgramNode extends abstractDummyNode {
- get isDataPublicDomain() {
- return true
- }
- get dummyDataSetName() {
- return `treeProgram`
- }
- }
-
- class samplesWaterBillNode extends abstractDummyNode {
- get isDataPublicDomain() {
- return true
- }
- get dummyDataSetName() {
- return `waterBill`
- }
- }
-
- class samplesGapMinderNode extends abstractDummyNode {
- get dummyDataSetName() {
- return `gapMinder`
- }
- }
-
- class abstractTransformerNode extends abstractProviderNode {
- get tileFooterTemplate() {
- return `span Rows In: {inputCount} Rows Out: {outputCount} Columns Out: {columnCount}
- {tileMenuButton}`
- }
- get bodyStumpTemplate() {
- return `span {kind}
- class LargeLabel
- input
- value {content}
- placeholder {placeholderMessage}
- changeCommand changeTileContentAndRenderCommand
- class LargeTileInput`
- }
- get placeholderMessage() {
- return ``
- }
- get tileSize() {
- return `160 100`
- }
- getTileFooterStumpCode() {
- const table = this.getParentOrDummyTable()
- const inputCount = table.getRowCount()
- const outputTable = this.getOutputOrInputTable()
- return this.qFormat(this.tileFooterTemplate, {
- inputCount,
- outputCount: table.getRowCount(),
- columnCount: outputTable.getColumnCount(),
- tileMenuButton: this.getTileMenuButtonStumpCode()
- })
- }
- async _execute() {
- this._outputTable = this._createOutputTable()
- this.setIsDataLoaded(true)
- await this._executeChildNodes()
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { kind: this.getFirstWord(), content: this.getContent() || "", placeholderMessage: this.placeholderMessage })
- }
- }
-
- class abstractColumnAdderTileNode extends abstractTransformerNode {
- _createOutputTable() {
- return this.getParentOrDummyTable().addColumns(this.getNewColumns())
- }
- }
-
- class dateAddColumnsNode extends abstractColumnAdderTileNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { sourceColumn: sourceColumnNode }),
- undefined
- )
- }
- get dateColumnTypeCell() {
- return this.getWordsFrom(0)
- }
- get placeholderMessage() {
- return `Enter the source column and new date columns you want, or leave blank to get 'day month year'.`
- }
- get columnPredictionHints() {
- return `sourceColumn isTemporal=true`
- }
- getNewColumns() {
- const inputColumnName = this.getSettingsStruct().sourceColumn // todo: this is probably broken. need to fix settings timing issues.
- if (!inputColumnName) return []
- const addColumns = this.getContent() ? this.getWordsFrom(1) : ["day", "week", "month"]
- // what happened to dayName? timeOfDay?
- return addColumns.map(outputCol => {
- return {
- source: inputColumnName,
- name: outputCol,
- type: outputCol
- }
- })
- }
- }
-
- class genConstantNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get primitiveTypeCell() {
- return this.getWord(2)
- }
- get anyCell() {
- return this.getWord(3)
- }
- getNewColumns() {
- return [
- {
- name: this.columnNameCell,
- type: this.primitiveTypeCell,
- accessorFn: row => this.anyCell
- }
- ]
- }
- }
-
- class genGrowthNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get minCell() {
- return parseFloat(this.getWord(2))
- }
- get growthRateCell() {
- return parseFloat(this.getWord(3))
- }
- getNewColumns() {
- let total = this.minCell
- return [
- {
- name: this.columnNameCell,
- accessorFn: (row, rowIndex) => {
- total = total * (1 + this.growthRateCell)
- return total
- }
- }
- ]
- }
- }
-
- class mathLogNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- getNewColumns() {
- const inputColumnName = this.getWord(1)
- if (!inputColumnName) return []
- const inputCol = this.getParentOrDummyTable().getColumnByName(inputColumnName)
- return [
- {
- source: inputColumnName,
- name: inputColumnName + "Log",
- type: inputCol.getPrimitiveTypeName(),
- mathFn: Math.log
- }
- ]
- }
- }
-
- class rowsAddIndexColumnNode extends abstractColumnAdderTileNode {
- getNewColumns() {
- let index = 0
- return [
- {
- name: "index",
- accessorFn: row => index++
- }
- ]
- }
- }
-
- class rowsRunningTotalNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- getNewColumns() {
- const sourceColumnName = this.getWord(1)
- let total = 0
- return [
- {
- source: sourceColumnName,
- name: "total",
- accessorFn: row => {
- total += row[sourceColumnName]
- return total
- }
- }
- ]
- }
- }
-
- class textLengthNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- getNewColumns() {
- const sourceColumnName = this.getWord(1)
- const destinationColumnName = sourceColumnName + "Length"
- return [
- {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => row[sourceColumnName].length
- }
- ]
- }
- }
-
- class textSplitNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get delimiterCell() {
- return this.getWord(2)
- }
- get newColumnNamesCell() {
- return this.getWordsFrom(3)
- }
- get dummyDataSetName() {
- return `poem`
- }
- // note: delimiter can probably be ""
- // todo: how would we split on a space???
- // perhaps its better to use getContent() as delimiter, and if you want to name the columns, you can do that later?
- getNewColumns() {
- const sourceColumnName = this.getWord(1)
- const delimiter = this.getWord(2)
- const destinationColumns = this.getWordsFrom(3)
- return destinationColumns.map((destinationColumnName, index) => {
- return {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => {
- const words = row[sourceColumnName].split(delimiter)
- return this.reverseSplit ? words.reverse()[index] : words[index]
- }
- }
- })
- return [
- {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => row[sourceColumnName].length
- }
- ]
- }
- }
-
- class reverseTextSplitNode extends textSplitNode {
- get reverseSplit() {
- return [true]
- }
- }
-
- class textToLowerCaseNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get dummyDataSetName() {
- return `poem`
- }
- getNewColumns() {
- const sourceColumnName = this.getWord(1) || "text"
- return [
- {
- source: sourceColumnName,
- name: sourceColumnName,
- accessorFn: row => row[sourceColumnName].toLowerCase()
- }
- ]
- }
- }
-
- class textTemplateNode extends abstractColumnAdderTileNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { content: contentNode }),
- undefined
- )
- }
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get newColumnNameCell() {
- return this.getWordsFrom(1)
- }
- get bodyStumpTemplate() {
- return `textarea
- name content
- changeCommand changeTileSettingMultilineCommand
- placeholder Enter template here.
- class TileTextArea savable
- bern
- {text}`
- }
- getNewColumns() {
- const contentNode = this.getNode("content")
- const templateString = contentNode ? contentNode.childrenToString() : ""
- const destColumnName = this.getWord(1) || "Output"
- return [
- {
- name: destColumnName,
- accessorFn: row => new jtree.TreeNode(templateString).templateToString(row)
- }
- ]
- }
- getDataContent() {
- const node = this.getNode("content")
- return node ? node.childrenToString() : ""
- }
- getTileBodyStumpCode() {
- const text = lodash.escape(this.getDataContent())
- return this.qFormat(this.bodyStumpTemplate, { text })
- }
- }
-
- class textPermalinkNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get newColumnNameCell() {
- return this.getWord(2)
- }
- getNewColumns() {
- const sourceColumnName = this.getWord(1) || "text"
- const destinationColumnName = this.getWord(2) || "Permalink"
- return [
- {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => jtree.Utils.stringToPermalink(row[sourceColumnName])
- }
- ]
- }
- }
-
- class textReplaceNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get anyCell() {
- return this.getWordsFrom(2)
- }
- getNewColumns() {
- const sourceColumnName = this.getWord(1) || "text"
- const simpleSearch = this.getWord(2)
- const simpleReplace = this.getWord(3)
- const destinationColumnName = sourceColumnName
- return [
- {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => row[sourceColumnName].replace(new RegExp(simpleSearch, "g"), simpleReplace)
- }
- ]
- }
- }
-
- class textTrimNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get anyCell() {
- return this.getWordsFrom(2)
- }
- getNewColumns() {
- const sourceColumnName = this.getWord(1) || "text"
- const trimChar = this.getWord(2)
- const destinationColumnName = sourceColumnName
- return [
- {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => (trimChar ? row[sourceColumnName].replace(new RegExp(`(^${trimChar}|${trimChar}$)`, "g"), "") : row[sourceColumnName].trim())
- }
- ]
- }
- }
-
- class textSubstringNode extends abstractColumnAdderTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get startIndexCell() {
- return parseInt(this.getWord(2))
- }
- get lengthCell() {
- return parseInt(this.getWord(3))
- }
- get destinationColumnName() {
- return `substring`
- }
- get dummyDataSetName() {
- return `poem`
- }
- getNewColumns() {
- const sourceColumnName = this.getWord(1)
- const startPosition = typeof this.startIndex !== undefined ? this.startIndex : parseInt(this.getWord(2))
- const endPosition = typeof this.endIndex !== undefined ? this.endIndex : this.getWord(3) === undefined ? undefined : parseInt(this.getWord(3))
- return [
- {
- source: sourceColumnName,
- name: this.destinationColumnName,
- accessorFn: row => (row[sourceColumnName] ? row[sourceColumnName].toString().substr(startPosition, endPosition) : "")
- }
- ]
- }
- }
-
- class testFirstLetterNode extends textSubstringNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get endIndex() {
- return 1
- }
- get startIndex() {
- return 0
- }
- get destinationColumnName() {
- return `firstLetter`
- }
- }
-
- class abstractNewRowsTransformerTileNode extends abstractTransformerNode {
- _createOutputTable() {
- // todo: remove this
- return new Table(this.makeNewRows())
- }
- }
-
- class columnsDescribeNode extends abstractNewRowsTransformerTileNode {
- makeNewRows() {
- return this.getParentOrDummyTable().getColumnNamesAndTypesAndReductions()
- }
- }
-
- class columnsListNode extends columnsDescribeNode {
- makeNewRows() {
- return this.getParentOrDummyTable().getColumnNamesAndTypes()
- }
- }
-
- class dataEvalNode extends abstractNewRowsTransformerTileNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { content: contentNode }),
- undefined
- )
- }
- makeNewRows() {
- const node = this.getNode(this.contentKey)
- const code = node && node.childrenToString() // "rows => { return []}"
- let fn
- try {
- fn = code && eval(code)
- } catch (err) {
- // todo: warn user
- console.error(err)
- }
- const inputRows = this.getParentOrDummyTable().cloneNativeJavascriptTypedRows()
- return fn ? fn(inputRows) : inputRows
- }
- }
-
- class joinByNode extends abstractNewRowsTransformerTileNode {
- get columnNameCell() {
- return this.getWordsFrom(0)
- }
- makeNewRows() {
- // Todo: move to table project
- const parentTile = this.getParent()
- if (parentTile.isRoot()) return []
- const grandParentTile = parentTile.getParent()
- if (grandParentTile.isRoot()) return parentTile.getOutputOrInputTable().cloneNativeJavascriptTypedRows()
- const tiles = [parentTile, grandParentTile]
- const arrays = tiles.map(tile => tile.getOutputOrInputTable().cloneNativeJavascriptTypedRows())
- const joinOn = this.getContent()
- if (!joinOn) return jtree.Utils.flatten(arrays)
- const cols = tiles.map(tile => tile.getOutputOrInputTable().getColumnNames())
- return jtree.Utils.joinArraysOn(joinOn, arrays, cols)
- }
- }
-
- class matchColumnsFuzzyNode extends abstractNewRowsTransformerTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get needleColumnNameCell() {
- return this.getWord(1)
- }
- get haystackColumnNameCell() {
- return this.getWord(2)
- }
- get tileScript() {
- return `ohayo/packages/match/fuse.min.js`
- }
- makeNewRows() {
- // Todo: move some of this logic to table project?
- const parentTile = this.getParent()
- if (parentTile.isRoot()) return []
- const grandParentTile = parentTile.getParent()
- if (grandParentTile.isRoot()) return parentTile.getOutputOrInputTable().cloneNativeJavascriptTypedRows()
- const tiles = [parentTile, grandParentTile]
- const arrays = tiles.map(tile => tile.getOutputOrInputTable().cloneNativeJavascriptTypedRows())
- return this._addFuzz(arrays[0], arrays[1])
- }
- get fuse() {
- return this.isNodeJs() ? require("fuse.js") : Fuse
- }
- _addFuzz(needles, haystacks) {
- const needleColumnName = this.getWord(1) || "name"
- const haystackColumnName = this.getWord(2) || "name"
- const options = {
- shouldSort: true,
- includeScore: true,
- threshold: 0.6,
- location: 0,
- distance: 100,
- maxPatternLength: 32,
- minMatchCharLength: 1,
- keys: [haystackColumnName]
- }
- const fuse = new this.fuse(haystacks, options) // "list" is the item array
- return needles.map(needle => {
- const searchValue = needle[needleColumnName]
- const result = fuse.search(searchValue)
- if (!result.length)
- return {
- search: searchValue,
- match: ""
- }
- const match = result[0]
- return {
- search: searchValue,
- match: match.item[haystackColumnName],
- confidence: parseFloat((1 - match.score).toFixed(3))
- }
- })
- }
- }
-
- class schemaTypeScriptNode extends abstractNewRowsTransformerTileNode {
- makeNewRows() {
- return [{ text: this.getParentOrDummyTable().toTypeScriptInterface() }]
- }
- }
-
- class schemaSimpleNode extends abstractNewRowsTransformerTileNode {
- makeNewRows() {
- const schema = this.getParentOrDummyTable().toSimpleSchema()
- const oneLiner = schema.replace(/ /g, ":").replace(/\n/g, " ")
- return [{ text: oneLiner + "\n\n" + schema }]
- }
- }
-
- class textWordCountNode extends abstractNewRowsTransformerTileNode {
- get dummyDataSetName() {
- return `poem`
- }
- makeNewRows() {
- return this._getAllWords(this.getPipishInput())
- }
- _getAllWords(text) {
- const rows = []
- if (!text) return rows
- const words = text
- .split(/\s/g)
- .map(word => word.replace(/[^a-z0-9\-]/gi, ""))
- .filter(word => word)
- const index = {}
- words.forEach(word => {
- if (!index[word]) index[word] = 1
- else index[word]++
- })
- Object.keys(index).forEach(word => {
- const trimmedWord = word.trim()
- if (trimmedWord)
- rows.push({
- word: trimmedWord,
- count: index[trimmedWord]
- })
- })
- return rows
- }
- }
-
- class textLineCountNode extends abstractNewRowsTransformerTileNode {
- get dummyDataSetName() {
- return `poem`
- }
- makeNewRows() {
- return [{ lines: this.getPipishInput().split(/\n/g).length }]
- }
- }
-
- class treenotationWordTypesNode extends abstractNewRowsTransformerTileNode {
- get dummyDataSetName() {
- return `treeProgram`
- }
- makeNewRows() {
- return [{ text: new ohayoNode(this.getPipishInput()).toCellTypeTree() }]
- }
- }
-
- class abstractColumnFilterTileNode extends abstractTransformerNode {
- _createOutputTable() {
- return this.getParentOrDummyTable().dropAllColumnsExcept(this.getColumnNamesToKeep())
- }
- }
-
- class columnsFirstNode extends abstractColumnFilterTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get intCell() {
- return parseInt(this.getWord(1))
- }
- get placeholderMessage() {
- return `Enter the number of columns you want to keep`
- }
- getColumnNamesToKeep() {
- return this.getParentOrDummyTable()
- .getColumnsArrayOfObjects()
- .slice(0, parseInt(this.getContent()))
- .map(col => col.name)
- }
- }
-
- class columnsLastNode extends abstractColumnFilterTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get intCell() {
- return parseInt(this.getWord(1))
- }
- get placeholderMessage() {
- return `Enter the number of columns you want to keep`
- }
- getColumnNamesToKeep() {
- const cols = this.getParentOrDummyTable().getColumnsArrayOfObjects()
- return cols.slice(cols.length - parseInt(this.getContent())).map(col => col.name)
- }
- }
-
- class columnsDropNode extends abstractColumnFilterTileNode {
- get columnNameCell() {
- return this.getWordsFrom(0)
- }
- getColumnNamesToKeep() {
- const colsToDrop = this.getWordsFrom(1)
- return this.getParentOrDummyTable()
- .getColumnsArrayOfObjects()
- .filter(col => !colsToDrop.includes(col.name))
- .map(col => col.name)
- }
- }
-
- class columnsDropConstantsNode extends abstractColumnFilterTileNode {
- getColumnNamesToKeep() {
- return this.getParentOrDummyTable()
- .getColumnsArray()
- .filter(col => col.getReductions().uniqueValues > 1)
- .map(col => col.getColumnName())
- }
- }
-
- class columnsKeepNode extends abstractColumnFilterTileNode {
- get columnNameCell() {
- return this.getWordsFrom(0)
- }
- get placeholderMessage() {
- return `Enter the column names to keep.`
- }
- getColumnNamesToKeep() {
- const colsToKeep = this.getWordsFrom(1)
- return this.getParentOrDummyTable()
- .getColumnsArrayOfObjects()
- .filter(col => colsToKeep.includes(col.name))
- .map(col => col.name)
- }
- }
-
- class columnsKeepNumericsNode extends abstractColumnFilterTileNode {
- getColumnNamesToKeep() {
- return Object.values(this.getParentOrDummyTable().getColumnsMap())
- .filter(col => col.isNumeric())
- .map(col => col.getColumnName())
- }
- }
-
- class abstractTransformerNoParamsTileNode extends abstractTransformerNode {
- getTileBodyStumpCode() {
- return `span ${this.getFirstWord()}
- class LargeLabel`
- }
- }
-
- class rowsShuffleNode extends abstractTransformerNoParamsTileNode {
- _createOutputTable() {
- return this.getParentOrDummyTable().shuffleRows()
- }
- }
-
- class rowsReverseNode extends abstractTransformerNoParamsTileNode {
- _createOutputTable() {
- return this.getParentOrDummyTable().reverseRows()
- }
- }
-
- class abstractRowFilterTileNode extends abstractTransformerNode {
- get placeholderMessage() {
- return `Enter a string to filter by.`
- }
- // todo: pass thru.
- // todo: remove this?
- _createOutputTable() {
- const fn = this.getRowFilterFn()
- if (!fn) return this.getParentOrDummyTable().clone()
- return this.getParentOrDummyTable().filterRowsByFn(fn)
- }
- }
-
- class filterWhereNode extends abstractRowFilterTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get comparisonCell() {
- return this.getWord(2)
- }
- get scalarValueCell() {
- return this.getWord(3)
- }
- get tileSize() {
- return `250 100`
- }
- _createOutputTable() {
- // todo: use cells here.
- const columnName = this.getWord(1)
- const comparison = this.getWord(2)
- let untypedScalarValue = this.getWord(3)
- const table = this.getParentOrDummyTable()
- if (!columnName || !comparison || untypedScalarValue === undefined) return table.clone()
- const column = table.getColumnByName(columnName)
- if (!column) return table
- return table.filterClonedRowsByScalar(columnName, comparison, untypedScalarValue)
- }
- }
-
- class filterWithNode extends abstractRowFilterTileNode {
- get stringCell() {
- return this.getWordsFrom(0)
- }
- get expectedBooleanValue() {
- return true
- }
- get tileSize() {
- return `250 100`
- }
- getRowFilterFn() {
- const words = this.getWordsFrom(1)
- // todo: problem here is, getRows has too many columns if after a transformed column.
- if (!words.length) return undefined
- const len = words.length
- const expectedValue = this.expectedBooleanValue
- return row => {
- const str = JSON.stringify(row)
- for (let index = 0; index < len; index++) {
- if (str.includes(words[index]) !== expectedValue) return false
- }
- return true
- }
- }
- }
-
- class filterWithoutNode extends filterWithNode {
- get expectedBooleanValue() {
- return false
- }
- }
-
- class filterAnyNode extends filterWithNode {
- getRowFilterFn() {
- const words = this.getWordsFrom(1)
- if (!words.length) return undefined
- const len = words.length
- // todo: problem here is, getRows has too many columns if after a transformed column.
- return row => {
- const str = JSON.stringify(row)
- for (let index = 0; index < len; index++) {
- if (str.includes(words[index])) return true
- }
- return false
- }
- }
- }
-
- class rowsFirstNode extends abstractRowFilterTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get intCell() {
- return parseInt(this.getWord(1))
- }
- getRowFilterFn() {
- const limit = parseInt(this.getContent())
- if (isNaN(limit)) return undefined
- return (row, rowIndex) => rowIndex < limit
- }
- }
-
- class rowsSampleNode extends abstractRowFilterTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get intCell() {
- return parseInt(this.getWord(1))
- }
- getRowFilterFn() {
- // todo: move to jtable?
- const sampleCount = parseInt(this.getContent())
- if (isNaN(sampleCount)) return undefined
- const totalCount = this.getParentOrDummyTable().getRowCount()
- if (totalCount <= sampleCount) return undefined
- const every = Math.floor(totalCount / sampleCount)
- let total = 0
- return (row, rowIndex) => {
- if (total === totalCount) return false
- if (rowIndex % every !== 0) return false
- total++
- return true
- }
- }
- }
-
- class rowsDropIfMissingNode extends abstractRowFilterTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWordsFrom(1)
- }
- get placeholderMessage() {
- return `Leave blank to filter a row if it is missing any column, or specifiy column name(s).`
- }
- getRowFilterFn() {
- const column = this.getContent()
- if (column) return row => !jtree.Utils.isValueEmpty(row[column])
- return row => !Object.values(row).some(jtree.Utils.isValueEmpty)
- }
- }
-
- class rowsLastNode extends abstractRowFilterTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get intCell() {
- return parseInt(this.getWord(1))
- }
- getRowFilterFn() {
- const limit = parseInt(this.getContent())
- if (isNaN(limit)) return undefined
- const start = this.getParentOrDummyTable().getRowCount() - limit
- return (row, rowIndex) => rowIndex >= start
- }
- }
-
- class pcaNode extends abstractTransformerNode {
- get tileScript() {
- return `ohayo/packages/bitanath/pca.js`
- }
- get github() {
- return `https://github.com/bitanath/pca`
- }
- get pcaLib() {
- return this.isNodeJs() ? require(__dirname + "/packages/bitanath/pca.js") : PCA
- }
- get mathLib() {
- return this.isNodeJs() ? require(__dirname + "/packages/mathjs/math.min.js") : math
- }
- _createOutputTable() {
- const table = this.getParentOrDummyTable()
- const matrix = table.toNumericMatrix()
- const vectors = this.pcaLib.getEigenVectors(matrix)
- const pcaRows = vectors.map(vec => vec.vector)
- const rows = table.getRows().map((row, index) => {
- const obj = row.rowToObjectWithOnlyNativeJavascriptTypes()
- const vec = matrix[index]
- obj.pc1 = this.mathLib.dot(vec, pcaRows[0])
- obj.pc2 = this.mathLib.dot(vec, pcaRows[1])
- obj.pc3 = this.mathLib.dot(vec, pcaRows[2])
- return obj
- })
- return new Table(rows)
- }
- }
-
- class columnsRenameNode extends abstractTransformerNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get newColumnNameCell() {
- return this.getWord(2)
- }
- _createOutputTable() {
- const renameMap = {}
- renameMap[this.getWord(1)] = this.getWord(2)
- return this.getParentOrDummyTable().renameColumns(renameMap)
- }
- }
-
- class columnsCleanNamesNode extends abstractTransformerNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- _createOutputTable() {
- return this.getParentOrDummyTable().cloneWithCleanColumnNames()
- }
- }
-
- class columnsSetTypeNode extends abstractTransformerNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get primitiveTypeCell() {
- return this.getWord(2)
- }
- _createOutputTable() {
- const colToChange = this.getWord(1)
- const newType = this.getWord(2)
- return this.getParentOrDummyTable().changeColumnType(colToChange, newType)
- }
- }
-
- class dataSynthNode extends abstractTransformerNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { schema: schemaNode }),
- undefined
- )
- }
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get intCell() {
- return parseInt(this.getWord(1))
- }
- get schemaSimpleCell() {
- return this.getWordsFrom(2)
- }
- _createOutputTable() {
- const schema = this._getSchema()
- const table = !schema ? this.getParentOrDummyTable() : new Table([], schema)
- return table.synthesizeTable(this.intCell || 30, Date.now())
- }
- _getSchema() {
- const schema = this.getNode("schema")
- if (schema) return schema.toJTableColumnDefinitionMap()
- const words = this.getWordsFrom(2)
- if (words.length)
- return words.map(word => {
- const parts = word.split(":")
- return {
- name: parts[0],
- type: parts[1]
- }
- })
- }
- }
-
- class dataAboutNode extends abstractTransformerNode {
- _getDataSetInfo() {
- const parentTile = this.getParent()
- const def = parentTile.getDefinition()
- return {
- licenseSpecified: parentTile.isDataPublicDomain,
- tags: def.get("tags"),
- overviewDescription: parentTile.dataDescription || def.get("description"),
- dataUrl: parentTile.dataUrl
- }
- }
- _createOutputTable() {
- return new Table([this._getDataSetInfo()])
- }
- }
-
- class dataUsabilityScoreNode extends dataAboutNode {
- _createOutputTable() {
- const row = this._getDataSetInfo()
- // todo: a very simplistic approximation of Kaggle's data usability score
- row.score = Object.values(row).filter(item => !!item).length * 2.5
- return new Table([row])
- }
- }
-
- class fillMissingNode extends abstractTransformerNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get anyCell() {
- return this.getWord(2)
- }
- _createOutputTable() {
- return this.getParentOrDummyTable().fillMissing(this.getWord(1), this.getWord(2))
- }
- }
-
- class genRangeNode extends abstractTransformerNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get newColumnNameCell() {
- return this.getWord(1)
- }
- get minCell() {
- return parseFloat(this.getWord(2))
- }
- get maxCell() {
- return parseFloat(this.getWord(3))
- }
- get stepCell() {
- return parseFloat(this.getWord(4))
- }
- _createOutputTable() {
- const rows = []
- // todo: protect against infinite loops
- let currentValue = this.minCell
- if (!this.stepCell) throw new Error("Step cannot be zero.")
- while (currentValue <= this.maxCell) {
- const row = []
- row[this.newColumnNameCell] = currentValue
- rows.push(row)
- currentValue += this.stepCell
- }
- return new Table(rows)
- }
- }
-
- class groupByNode extends abstractTransformerNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), { reduce: reduceNode }),
- undefined
- )
- }
- get columnNameCell() {
- return this.getWordsFrom(0)
- }
- get placeholderMessage() {
- return `Enter the column to groupby.`
- }
- _createOutputTable() {
- const groupByColNames = this.getWordsFrom(1)
- if (!groupByColNames.length) return this.getParentOrDummyTable().clone()
- const newCols = this.findNodes("reduce").map(reduceNode => {
- return {
- source: reduceNode.getWord(1),
- reduction: reduceNode.getWord(2),
- name: reduceNode.getWord(3) || reduceNode.getWordsFrom(1).join("_")
- }
- })
- return this.getParentOrDummyTable().makePivotTable(groupByColNames, newCols)
- }
- }
-
- class rowsSortByNode extends abstractTransformerNode {
- get columnNameCell() {
- return this.getWordsFrom(0)
- }
- get placeholderMessage() {
- return `Columns you want to sort by`
- }
- _createOutputTable() {
- const table = this.getParentOrDummyTable().sortBy(this.getWordsFrom(1))
- if (this.getFirstWord().includes("Reverse")) return table.reverseRows()
- return table
- }
- }
-
- class rowsSortByReverseNode extends rowsSortByNode {}
-
- class rowsAddOneNode extends abstractTransformerNode {
- get anyCell() {
- return this.getWordsFrom(0)
- }
- _createOutputTable() {
- return this.getParentOrDummyTable().addRow(this.getWordsFrom(1))
- }
- }
-
- class textMatchesNode extends abstractTransformerNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get anyCell() {
- return this.getWordsFrom(1)
- }
- _createOutputTable() {
- return new Table([{ count: this.getPipishInput().match(new RegExp(this.getContent(), "g")).length }])
- }
- }
-
- class textCombineNode extends abstractTransformerNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- _createOutputTable() {
- // todo: cleanup
- const text = this.getParentOrDummyTable()
- .getRows()
- .map(row => row.getRowOriginalValue(this.columnNameCell))
- .join("\n")
- return new Table([{ text }])
- }
- }
-
- class dataInlineNode extends abstractProviderNode {
- createParser() {
- return new jtree.TreeNode.Parser(
- undefined,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- parser: parserNode,
- treeLanguage: treeLanguageNode,
- content: contentNode
- }),
- undefined
- )
- }
- get bodyStumpTemplate() {
- return `textarea
- name content
- changeCommand changeTileSettingMultilineCommand
- placeholder Enter data in any format here. It will be saved directly in your document.
- class TileTextArea savable
- bern
- {text}`
- }
- getDataContent() {
- const node = this.getNode("content")
- return node ? node.childrenToString() : ""
- }
- getTileBodyStumpCode() {
- const text = lodash.escape(this.getDataContent())
- return this.qFormat(this.bodyStumpTemplate, { text })
- }
- getRowClass() {
- class InlineDataTileRow extends Row {}
- return InlineDataTileRow
- }
- getParserId() {
- return super.getParserId() || new TableParser().guessTableParserId(this.getDataContent())
- }
- async fetchTableInputs() {
- return new TableParser().parseTableInputsFromString(this.getDataContent(), this.getParserId())
- }
- }
-
- class dataLocalStorageNode extends dataInlineNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get localStorageKeyCell() {
- return this.getWord(1)
- }
- get bodyStumpTemplate() {
- return `textarea
- changeCommand triggerTileMethodCommand
- placeholder Enter data in any format here. It will be saved in your browser's localStorage.
- name storeValueCommand
- class TileTextArea savable
- bern
- {text}`
- }
- // Note: for now, only way to clear a key is to do it manually through UI (select all delete) or console. That might be good enough.
- _getStoreKey() {
- return this.getContent()
- }
- getDataContent() {
- const key = this._getStoreKey()
- return key ? this.getWebApp().getFromStore(key) || "" : ""
- }
- storeValueCommand(value) {
- let key = this._getStoreKey()
- if (key) this.getWebApp().storeValue(key, value)
- else this.setContent(this.getWebApp().initLocalDataStorage(this.constructor.name + ".data", value))
- }
- getTileBodyStumpCode() {
- const text = lodash.escape(this.getDataContent())
- return this.qFormat(this.bodyStumpTemplate, { text })
- }
- }
-
- class debugParserTestNode extends abstractProviderNode {
- async fetchTableInputs() {
- const parentTile = this.getParent()
- if (parentTile.getWillowHttpResponse) {
- const probs = new TableParser().guessProbabilitiesForAllTableParsers(parentTile.getWillowHttpResponse().text)
- return {
- rows: Object.keys(probs).map(key => {
- return {
- parser: key,
- probability: probs[key]
- }
- })
- }
- }
- return [{ rows: [] }]
- }
- }
-
- class debugGrammarNode extends abstractProviderNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- async fetchTableInputs() {
- return { rows: [{ text: new ohayoNode("").getHandGrammarProgram().toString() }] }
- }
- }
-
- class debugGrammarTreeNode extends abstractProviderNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- async fetchTableInputs() {
- return {
- rows: [
- {
- text: new ohayoNode("")
- .getHandGrammarProgram()
- .getNodeTypeFamilyTree()
- .toString()
- }
- ]
- }
- }
- }
-
- class editorFilesNode extends abstractProviderNode {
- get tileSize() {
- return `140 120`
- }
- getRowClass() {
- // todo: remove?
- class FileRow extends Row {
- destroyRow(app) {
- return app.deleteFileCommand(this.getRowOriginalValue("link"))
- }
- }
- return FileRow
- }
- async fetchTableInputs() {
- const files = await this.getWebApp()
- .getDefaultDisk()
- .readFiles()
- return { rows: files.map(file => file.toFileObject()) }
- }
- }
-
- class editorCommandHistoryNode extends abstractProviderNode {
- get methodName() {
- return `getCommandsBuffer`
- }
- async fetchTableInputs() {
- return { rows: this.getWebApp()[this.methodName]() }
- }
- }
-
- class mathGenNode extends abstractProviderNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get mathFunctionNameCell() {
- return this.getWord(1)
- }
- get fromCell() {
- return parseFloat(this.getWord(2))
- }
- get toCell() {
- return parseFloat(this.getWord(3))
- }
- get incrementCell() {
- return parseFloat(this.getWord(4))
- }
- async fetchTableInputs() {
- const rows = []
- const fn = Math[this.getWord(1)]
- for (let input = parseFloat(this.fromCell); input < parseFloat(this.toCell); input += parseFloat(this.incrementCell)) {
- rows.push({ input, output: fn(input) })
- }
- return {
- rows
- }
- }
- }
-
- class abstractRandomTileNode extends abstractProviderNode {
- async fetchTableInputs() {
- let howMany = this.quantityCell || 30
- const rows = []
- for (let index = 1; index <= howMany; index++) {
- rows.push(this._genRow(index))
- }
- return { rows }
- }
- }
-
- class randomFloatNode extends abstractRandomTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get quantityCell() {
- return parseInt(this.getWord(1))
- }
- get minCell() {
- return parseFloat(this.getWord(2))
- }
- get maxCell() {
- return parseFloat(this.getWord(3))
- }
- get max() {
- return 1
- }
- get min() {
- return 0
- }
- _genRow(index) {
- return { index, number: jtree.Utils.randomUniformFloat(this.minCell, this.maxCell, Math.random()) }
- }
- }
-
- class randomIntNode extends abstractRandomTileNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get quantityCell() {
- return parseInt(this.getWord(1))
- }
- get minCell() {
- return parseFloat(this.getWord(2))
- }
- get maxCell() {
- return parseFloat(this.getWord(3))
- }
- get max() {
- return 100
- }
- get min() {
- return 0
- }
- _genRow(index) {
- return { index, number: jtree.Utils.randomUniformInt(this.minCell, this.maxCell, Math.random()) }
- }
- }
-
- class samplesTinyIrisNode extends abstractProviderNode {
- get bodyStumpTemplate() {
- return `pre
- class TileSelectable
- style overflow: scroll; max-height: 100%;
- bern
- {text}`
- }
- get data() {
- return `petal_length,petal_width,species
- 4.9,1.8,virginica
- 4.2,1.3,versicolor
- 4.9,2,virginica
- 1.5,0.2,setosa`
- }
- get isDataPublicDomain() {
- return true
- }
- getDataContent() {
- return this.data
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { text: this.getDataContent() })
- }
- getParserId() {
- return super.getParserId() || new TableParser().guessTableParserId(this.getDataContent())
- }
- async fetchTableInputs() {
- return new TableParser().parseTableInputsFromString(this.getDataContent(), this.getParserId())
- }
- }
-
- class assertRowCountNode extends abstractTileTreeComponentNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get intCell() {
- return parseInt(this.getWord(1))
- }
- get visible() {
- return false
- }
- async execute() {
- const num = this.getWord(1)
- if (!num) return super.execute()
- const expected = parseInt(num)
- const actual = this.getParentOrDummyTable().getRowCount()
- if (actual !== expected) throw new Error(`Expected ${expected} but got ${actual}`)
- return super.execute()
- }
- }
-
- class printNode extends abstractTileTreeComponentNode {
- execute() {
- console.log(this._getMessage())
- }
- _getMessage() {
- return this.getPipishInput()
- }
- }
-
- class printCsvNode extends printNode {
- _getMessage() {
- return this.getParentOrDummyTable().toDelimited(",")
- }
- }
-
- class abstractTileSettingNode extends jtree.GrammarBackedNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- }
-
- class abstractTileSettingTerminalNode extends abstractTileSettingNode {
- getSettingValue() {
- return this.getContent()
- }
- }
-
- class abstractColumnNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- getRunTimeEnumOptions(cell) {
- // todo: only works if codemirror === tab
- try {
- // todo: handle at static time.
- const mirrorNode = typeof app === "undefined" ? this : app.mountedProgram.nodeAtLine(this.getLineNumber() - 1)
- const mirrorParent = mirrorNode && mirrorNode.getParent()
- if (cell.getCellTypeId() === "columnNameCell" && mirrorParent && mirrorParent.isLoaded()) {
- const options = mirrorParent.getParentOrDummyTable().getColumnNames()
- return options
- }
- } catch (err) {
- console.log(err)
- }
- }
- }
-
- class columnNode extends abstractColumnNode {}
-
- class sourceColumnNode extends abstractColumnNode {}
-
- class labelNode extends abstractColumnNode {}
-
- class linkNode extends abstractColumnNode {}
-
- class sizeColumnNode extends abstractColumnNode {}
-
- class colorColumnNode extends abstractColumnNode {}
-
- class shapeColumnNode extends abstractColumnNode {}
-
- class valueNode extends abstractColumnNode {}
-
- class countNode extends abstractColumnNode {}
-
- class dayColumnNode extends abstractColumnNode {}
-
- class xColumnNode extends abstractColumnNode {}
-
- class yColumnNode extends abstractColumnNode {}
-
- class genderColumnNode extends abstractColumnNode {}
-
- class headSizeNode extends abstractColumnNode {}
-
- class radiusNode extends abstractColumnNode {}
-
- class emojiColumnNode extends abstractColumnNode {}
-
- class parserNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get parserIdsCell() {
- return this.getWord(1)
- }
- }
-
- class useCacheNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get booleanCell() {
- return this.getWord(1)
- }
- }
-
- class reductionNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get reductionTypeCell() {
- return this.getWord(1)
- }
- }
-
- class abstractCoreTileSettingTerminalNode extends abstractTileSettingTerminalNode {}
-
- class hiddenNode extends abstractCoreTileSettingTerminalNode {}
-
- class visibleNode extends abstractCoreTileSettingTerminalNode {}
-
- class maximizedNode extends abstractCoreTileSettingTerminalNode {}
-
- class abstractPagePositionNode extends abstractCoreTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get intCell() {
- return parseInt(this.getWord(1))
- }
- }
-
- class leftNode extends abstractPagePositionNode {}
-
- class topNode extends abstractPagePositionNode {}
-
- class widthNode extends abstractPagePositionNode {}
-
- class heightNode extends abstractPagePositionNode {}
-
- class reduceNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get columnNameCell() {
- return this.getWord(1)
- }
- get reductionTypeCell() {
- return this.getWord(2)
- }
- get newColumnNameCell() {
- return this.getWord(3)
- }
- }
-
- class styleNode extends abstractTileSettingTerminalNode {}
-
- class columnLimitNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get intCell() {
- return parseInt(this.getWord(1))
- }
- }
-
- class howManyNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get quantityCell() {
- return parseInt(this.getWord(1))
- }
- }
-
- class sizeNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get numberCell() {
- return parseFloat(this.getWord(1))
- }
- }
-
- class rowDisplayLimitNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get intCell() {
- return parseInt(this.getWord(1))
- }
- }
-
- class srcNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get urlCell() {
- return this.getWord(1)
- }
- }
-
- class roughnessNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get roughnessCell() {
- return parseInt(this.getWord(1))
- }
- }
-
- class colorsNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get anyCell() {
- return this.getWordsFrom(1)
- }
- }
-
- class cameraPositionNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get cameraDistanceNumberCell() {
- return parseFloat(this.getWord(1))
- }
- get horizontalNumberCell() {
- return parseFloat(this.getWord(2))
- }
- get verticalNumberCell() {
- return parseFloat(this.getWord(3))
- }
- }
-
- class treeLanguageNode extends abstractTileSettingTerminalNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get supportedTreeLanguageCell() {
- return this.getWord(1)
- }
- }
-
- class abstractTileSettingNonTerminalNode extends abstractTileSettingNode {
- createParser() {
- return new jtree.TreeNode.Parser(tileSettingNonTerminalContentNode, undefined, undefined)
- }
- getSettingValue() {
- return this.childrenToString()
- }
- }
-
- class contentNode extends abstractTileSettingNonTerminalNode {
- createParser() {
- return new jtree.TreeNode.Parser(lineOfContentNode, undefined, undefined)
- }
- }
-
- class catchAllNodesPostContentNode extends abstractTileSettingNonTerminalNode {
- get anyCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class postNode extends abstractTileSettingNonTerminalNode {
- createParser() {
- return new jtree.TreeNode.Parser(catchAllNodesPostContentNode, undefined, undefined)
- }
- }
-
- class abstractDocSettingNode extends jtree.GrammarBackedNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get visible() {
- return false
- }
- }
-
- class docCategoriesNode extends abstractDocSettingNode {
- get documentCategoryCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class docAuthorNode extends abstractDocSettingNode {
- get stringCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class docDateNode extends abstractDocSettingNode {
- get dateCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class abstractDocSectionComponentNode extends jtree.GrammarBackedNode {}
-
- class docSectionSubtitleNode extends abstractDocSectionComponentNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get stringCell() {
- return this.getWordsFrom(1)
- }
- compile() {
- return `h2 ${this.getContent()}`
- }
- }
-
- class docSectionParagraphNode extends abstractDocSectionComponentNode {
- createParser() {
- return new jtree.TreeNode.Parser(docParagraphLineNode, undefined, undefined)
- }
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get stringCell() {
- return this.getWordsFrom(1)
- }
- get stumpTemplate() {
- return `p
- bern
- {content}`
- }
- compile() {
- return new jtree.TreeNode(this.stumpTemplate).templateToString({ content: this.getContentWithChildren() })
- }
- }
-
- class docSectionLinkNode extends abstractDocSectionComponentNode {
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get urlCell() {
- return this.getWord(1)
- }
- get stringCell() {
- return this.getWordsFrom(2)
- }
- get stumpTemplate() {
- return `a {content}
- href {url}`
- }
- compile() {
- return new jtree.TreeNode(this.stumpTemplate).templateToString({ content: this.getWordsFrom(2).join(" "), url: this.getWord(1) })
- }
- }
-
- class docSectionCodeNode extends abstractDocSectionComponentNode {
- createParser() {
- return new jtree.TreeNode.Parser(docLineOfCodeNode, undefined, undefined)
- }
- get tileKeywordCell() {
- return this.getWord(0)
- }
- get programmingLanguageNameCell() {
- return this.getWord(1)
- }
- get stumpTemplate() {
- return `code
- bern
- {content}`
- }
- compile() {
- return new jtree.TreeNode(this.stumpTemplate).templateToString({ content: this.childrenToString().replace(/
- }
- }
-
- class docLineOfCodeNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(docLineOfCodeNode, undefined, undefined)
- }
- get codeCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class docParagraphLineNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(docParagraphLineNode, undefined, undefined)
- }
- get stringCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class commentLineNode extends jtree.GrammarBackedNode {
- createParser() {
- return new jtree.TreeNode.Parser(commentLineNode, undefined, undefined)
- }
- get commentCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class docReferenceUrlNode extends jtree.GrammarBackedNode {
- get tileSettingKeywordCell() {
- return this.getWord(0)
- }
- get urlCell() {
- return this.getWord(1)
- }
- }
-
- class catchAllErrorNode extends jtree.GrammarBackedNode {
- getErrors() {
- return this._getErrorNodeErrors()
- }
- get errorCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class hashBangNode extends jtree.GrammarBackedNode {
- get hashBangWordCell() {
- return this.getWordsFrom(0)
- }
- }
-
- class ohayoNode extends AbstractTreeComponent {
- createParser() {
- return new jtree.TreeNode.Parser(
- DidYouMeanTileNode,
- Object.assign(Object.assign({}, super.createParser()._getFirstWordMapAsObject()), {
- "amazon.history": amazonHistoryNode,
- "fitbit.all": fitbitAllNode,
- "datawrapper.comingSoon": datawrapperComingSoonNode,
- "dcjs.comingSoon": dcjsComingSoonNode,
- "finos.perspective.comingSoon": finosPerspectiveComingSoonNode,
- "fivethirtyeight.comingSoon": fivethirtyeightComingSoonNode,
- "gov.comingSoon": GovNode,
- "highcharts.comingSoon": highchartsComingSoonNode,
- "re3data.comingSoon": re3dataComingSoonNode,
- "zing.comingSoon": zingComingSoonNode,
- "editor.helloWorld": editorHelloWorldNode,
- "challenge.list": challengeListNode,
- "samples.list": samplesListNode,
- "vega.data.list": vegaDataListNode,
- "vega.example.list": vegaExampleListNode,
- "doc.picker": PickerTileNode,
- "templates.list": templatesListNode,
- "asciichart.line": asciiChartNode,
- "calendar.heat": calendarHeatNode,
- "challenge.play": challengePlayNode,
- "debug.dump": debugDumpNode,
- "web.dump": webDumpNode,
- "debug.commands": debugCommandsNode,
- "debug.sleep": debugSleepNode,
- "debug.noop": debugNoOpNode,
- "debug.throw": debugThrowNode,
- "dtjs.basic": dtjsBasicNode,
- "editor.gallery": editorGalleryNode,
- "handsontable.basic": handsontableBasicNode,
- "html.text": htmlTextNode,
- "html.printAs": htmlPrintAsNode,
- "html.h1": htmlH1Node,
- "html.img": htmlImgNode,
- "html.iframe": htmlIframeNode,
- "html.custom": htmlCustomNode,
- "icons.human": iconsHumanNode,
- "icons.circle": iconsCircleNode,
- "list.basic": listBasicNode,
- "list.links": listLinksNode,
- "markdown.toHtml": markdownToHtmlNode,
- "roughjs.bar": roughJsBarNode,
- "roughjs.pie": roughJsPieNode,
- "roughjs.line": roughJsLineNode,
- "show.rowCount": showRowCountNode,
- "show.columnCount": showColumnCountNode,
- "show.static": showStaticNode,
- "show.value": showValueNode,
- "show.median": showMedianNode,
- "show.sum": showSumNode,
- "show.mean": showMeanNode,
- "show.min": showMinNode,
- "show.max": showMaxNode,
- "tables.basic": tablesBasicNode,
- "tables.interesting": tablesInterestingNode,
- "tables.dump": tablesDumpNode,
- "text.wordcloud": textWordcloudNode,
- "treenotation.3d": treenotation3dNode,
- "treenotation.outline": treenotationOutlineNode,
- "treenotation.dotline": treenotationDotlineNode,
- "vega.bar": vegaBarNode,
- "vega.line": vegaLineNode,
- "vega.area": vegaAreaNode,
- "vega.scatter": vegaScatterNode,
- "vega.bubble": vegaBubbleNode,
- "vega.emoji": vegaEmojiNode,
- "vega.histogram": vegaHistogramNode,
- "vega.example": vegaExampleNode,
- "tiles.didyoumean": DidYouMeanTileNode,
- "doc.title": docTitleNode,
- "doc.subtitle": docSubtitleNode,
- "doc.section": docSectionNode,
- "doc.ref": docReferenceNode,
- "doc.comment": docCommentNode,
- "doc.tooling": docToolingNode,
- "github.info": githubInfoNode,
- "disk.browse": diskBrowseNode,
- "disk.read": diskReadNode,
- "hackernews.top": hackernewsTopNode,
- "hackernews.submissions": hackernewsSubmissionsNode,
- "publicapis.entries": publicApisNode,
- "web.get": webGetNode,
- "web.post": webPostNode,
- "wikipedia.page": wikipediaContentNode,
- "cancer.cases": cancerCasesNode,
- "cdc.infants.weight": weightPercentilesNode,
- "cdc.infants.length": lengthPercentilesNode,
- "cdc.infants.headCircumference": headPercentilesNode,
- "kaggle.datasets.heart": kaggleDatasetsHeartNode,
- "moz.top500": mozTop500Node,
- "owid.lifeExpectancy": lifeExpectancyNode,
- "owid.list": owidListNode,
- "samples.telescopes": samplesTelescopesNode,
- "samples.mtcars": samplesMtcarsNode,
- "samples.iris": samplesIrisNode,
- "samples.flights14": samplesFlights14Node,
- "samples.si": samplesSiNode,
- "samples.portals": samplesPortalNode,
- "samples.starWars": samplesStarWarsNode,
- "samples.populations": samplesPopulationsNode,
- "samples.babyNames": samplesBabyNamesNode,
- "samples.declaration": samplesDeclarationNode,
- "samples.periodicTable": samplesPeriodicTableNode,
- "samples.letters": samplesLettersNode,
- "samples.presidents": samplesPresidentsNode,
- "ucimlr.datasets": ucimlrDatasetsNode,
- "vega.data": vegaDataNode,
- "reddit.all": redditAllNode,
- "reddit.subs": redditSubsNode,
- "reddit.sub": redditSubNode,
- "samples.patients": samplesPatientsNode,
- "samples.poem": samplesPoemNode,
- "samples.outerSpace": samplesOuterSpaceNode,
- "samples.treeProgram": samplesTreeProgramNode,
- "samples.waterBill": samplesWaterBillNode,
- "samples.gapMinder": samplesGapMinderNode,
- "date.addColumns": dateAddColumnsNode,
- "gen.constant": genConstantNode,
- "gen.growth": genGrowthNode,
- "math.log": mathLogNode,
- "rows.addIndexColumn": rowsAddIndexColumnNode,
- "rows.runningTotal": rowsRunningTotalNode,
- "text.length": textLengthNode,
- "text.split": textSplitNode,
- "text.reverseSplit": reverseTextSplitNode,
- "text.toLowerCase": textToLowerCaseNode,
- "text.template": textTemplateNode,
- "text.permalink": textPermalinkNode,
- "text.replace": textReplaceNode,
- "text.trim": textTrimNode,
- "text.substring": textSubstringNode,
- "text.firstLetter": testFirstLetterNode,
- "columns.describe": columnsDescribeNode,
- "columns.list": columnsListNode,
- "data.eval": dataEvalNode,
- "join.by": joinByNode,
- "match.columnsFuzzy": matchColumnsFuzzyNode,
- "schema.toTypescript": schemaTypeScriptNode,
- "schema.toSimple": schemaSimpleNode,
- "text.wordCount": textWordCountNode,
- "text.lineCount": textLineCountNode,
- "treenotation.wordTypes": treenotationWordTypesNode,
- "columns.first": columnsFirstNode,
- "columns.last": columnsLastNode,
- "columns.drop": columnsDropNode,
- "columns.dropConstants": columnsDropConstantsNode,
- "columns.keep": columnsKeepNode,
- "columns.keepNumerics": columnsKeepNumericsNode,
- "rows.shuffle": rowsShuffleNode,
- "rows.reverse": rowsReverseNode,
- "filter.where": filterWhereNode,
- "filter.with": filterWithNode,
- "filter.without": filterWithoutNode,
- "filter.withAny": filterAnyNode,
- "rows.first": rowsFirstNode,
- "rows.sample": rowsSampleNode,
- "rows.dropIfMissing": rowsDropIfMissingNode,
- "rows.last": rowsLastNode,
- "bitanath.pca": pcaNode,
- "columns.rename": columnsRenameNode,
- "columns.cleanNames": columnsCleanNamesNode,
- "columns.setType": columnsSetTypeNode,
- "data.synth": dataSynthNode,
- "data.about": dataAboutNode,
- "data.usabilityScore": dataUsabilityScoreNode,
- "fill.missing": fillMissingNode,
- "gen.range": genRangeNode,
- "group.by": groupByNode,
- "rows.sortBy": rowsSortByNode,
- "rows.sortByReverse": rowsSortByReverseNode,
- "rows.addOne": rowsAddOneNode,
- "text.matches": textMatchesNode,
- "text.combine": textCombineNode,
- "data.inline": dataInlineNode,
- "data.localStorage": dataLocalStorageNode,
- "debug.parserTest": debugParserTestNode,
- "debug.ohayoGrammar": debugGrammarNode,
- "debug.ohayoGrammarTree": debugGrammarTreeNode,
- "editor.files": editorFilesNode,
- "editor.commandHistory": editorCommandHistoryNode,
- "math.gen": mathGenNode,
- "random.float": randomFloatNode,
- "random.int": randomIntNode,
- "samples.tinyIris": samplesTinyIrisNode,
- "assert.rowCount": assertRowCountNode,
- "print.text": printNode,
- "print.csv": printCsvNode,
- "doc.categories": docCategoriesNode,
- "doc.author": docAuthorNode,
- "doc.date": docDateNode,
- "#!": hashBangNode
- }),
- [{ regex: /^$/, nodeConstructor: tileBlankLineNode }]
- )
- }
- getTileClosestToLine(lineIndex) {
- let current = this.nodeAtLine(lineIndex)
- while (current) {
- if (current.doesExtend("abstractTileTreeComponentNode")) return current
- current = current.getParent()
- }
- }
- setTab(tab) {
- this._tab = tab
- }
- getTheme() {
- const tab = this.getTab()
- return tab ? tab.getTheme() : super.getTheme()
- }
- getTab() {
- return this._tab
- }
- async loadAndIncrementalRender() {
- const app = this.getTab().getRootNode()
- await Promise.all(this.getTiles().map(tile => tile.loadBrowserRequirements()))
- await Promise.all(
- this.getRootLevelTiles().map(async tile => {
- await tile.execute()
- app.renderApp()
- })
- )
- app.renderApp() // this one might be superfluous
- return this
- }
- getTiles() {
- return this.getTopDownArray().filter(node => node.doesExtend("abstractTileTreeComponentNode"))
- }
- getRootLevelTiles() {
- return this.filter(node => node.doesExtend("abstractTileTreeComponentNode"))
- }
- _getProjectRootDir() {
- return this.isNodeJs() ? jtree.Utils.findProjectRoot(__dirname, "ohayo") : ""
- }
- toRunTimeStats() {
- const tiles = this.getTiles()
- const stats = {
- tiles: tiles.length,
- treeLanguage: this.getHandGrammarProgram().getExtensionName(),
- url: this.getTab().getFileName()
- }
- stats.timeToLoad = this.getTiles()
- .map(tile => tile.getTimeToLoad())
- .sort()
- .reverse()[0]
- stats.timeToRender = this.getTiles()
- .map(tile => tile.getNewestTimeToRender())
- .sort()
- .reverse()[0]
- return stats
- }
- async execute() {
- await Promise.all(this.map(node => node.execute()))
- // Use shell tiles to do any outputs
- }
- _getProgramRowCount() {
- return this.getAllRowsFromAllOutputTables().reduce((acc, curr) => acc + curr.length, 0)
- }
- getOutputOrInputTable() {
- // todo: remove this?
- if (!this._outputTable) this._outputTable = new Table()
- return this._outputTable
- }
- getRowsFromLastTable() {
- const tiles = this.getTopDownArray()
- return tiles[tiles.length - 1].getOutputOrInputTable().getRows()
- }
- getAllRowsFromAllOutputTables() {
- return jtree.Utils.flatten(
- this.getTiles()
- .map(tile => tile.getOutputTable())
- .filter(table => table)
- .map(table => table.getRows())
- )
- }
- getHandGrammarProgram() {
- if (!this._cachedHandGrammarProgramRoot)
- this._cachedHandGrammarProgramRoot = new jtree.HandGrammarProgram(`emptyCell
- quantityCell
- extends intCell
- minCell
- extends numberCell
- maxCell
- extends numberCell
- stepCell
- extends numberCell
- millisecondsCell
- extends intCell
- titleCell
- extends stringCell
- anyCell
- todo remove
- columnNameCell
- highlightScope entity.other.attribute-name
- newColumnNameCell
- highlightScope entity.other.attribute-name
- newColumnNamesCell
- description When you are creating new columns.
- highlightScope entity.other.attribute-name
- primitiveTypeCell
- highlightScope constant.numeric
- description In Ohayo, all columns have a primitive type chosen from one of these. The type affects how the values in the column are understood and displayed. For example, a 0 could be interpretted as a "false", the number 0, or a string "0". Ohayo attempts to choose the correct type, but you can override the default with the columns.setType tile.
- enum boolean code date day dir feet hour hourMinute html int millisecond minute month monthDay number numberString object path second string text url usd week year
- programmingLanguageNameCell
- enum javascript latex css html ruby rust python csv tsv xml php typescript lisp swift java c cpp markdown bash
- highlightScope constant
- codeCell
- highlightScope string
- documentCategoryCell
- highlightScope constant
- enum shopping biology chemistry programming socialMedia math parenting writing dataScience ohayo geography web history wikipedia
- referenceIdCell
- highlightScope string
- commentCell
- highlightScope comment
- commentKeywordCell
- highlightScope comment
- errorCell
- highlightScope invalid
- hashBangWordCell
- highlightScope comment
- parserIdsCell
- description Ohayo has these parsers which convert your raw data to tables.
- enum csv tsv json treeRows tree ssv xml psv text spaced sections json list txt jsonMap jsonVector jsonCounts jsonDataTableWithHeader
- highlightScope constant
- booleanCell
- enum false true
- highlightScope constant.numeric
- pathCell
- highlightScope constant
- description A filepath
- alphanumericCell
- regex [a-zA-Z0-9]+
- extraWordCell
- todo Remove this? It looks like its a standard cell type.
- highlightScope invalid
- stringCell
- highlightScope string
- urlCell
- highlightScope constant
- dateCell
- highlightScope string
- intCell
- regex \\-?[0-9]+
- numberCell
- regex \\-?[0-9]*\\.?[0-9]*
- reductionTypeCell
- enum count sum mean min max median
- highlightScope constant
- tileKeywordCell
- highlightScope keyword
- intCell
- tileSettingKeywordCell
- highlightScope variable.language
- challengeIdCell
- extends intCell
- challengeAnswerCell
- extends numberCell
- localStorageKeyCell
- schemaSimpleCell
- highlightScope string
- examples name:string score:int
- dateColumnTypeCell
- enum day month year monthDay
- highlightScope constant
- dummyDataSetIdCell
- enum ohayoPrograms waterBill gapMinder markdown webPages outerSpace wordCounts treeProgram poem playerGoals patients regionalMarkets stockPrice
- tileEventNameCell
- enum fetchTableInputs getTileBodyStumpCode treeComponentDidMount treeComponentDidUpdate
- scalarValueCell
- highlightScope constant.numeric
- comparisonCell
- enum < > <= >= = !=
- highlightScope constant
- growthRateCell
- extends numberCell
- githubRepoCell
- extends anyCell
- hackerNewsUserNameCell
- highlightScope string
- htmlTextTagCell
- enum div pre p h1 h2 h3 h4 h5 h6 code
- htmlCell
- highlightScope string
- needleColumnNameCell
- extends columnNameCell
- haystackColumnNameCell
- extends columnNameCell
- mathFunctionNameCell
- enum sin cos tan log exp
- fromCell
- extends numberCell
- toCell
- extends numberCell
- incrementCell
- extends numberCell
- subredditNameCell
- roughnessCell
- extends intCell
- todo add min of 0 and max of 20
- delimiterCell
- startIndexCell
- extends intCell
- lengthCell
- extends intCell
- supportedTreeLanguageCell
- enum ohayo
- cameraDistanceNumberCell
- extends numberCell
- horizontalNumberCell
- extends numberCell
- verticalNumberCell
- extends numberCell
- vegaDataSetCell
- highlightScope constant.numeric
- enum 7zip.png airports.csv anscombe.json barley.json birdstrikes.json budget.json budgets.json burtin.json cars.json climate.json co2-concentration.csv countries.json crimea.json descriptions.json disasters.csv driving.json earthquakes.json ffox.png flare-dependencies.json flare.json flights-10k.json flights-200k.json flights-20k.json flights-2k.json flights-3m.csv flights-5k.json flights-airport.csv gapminder-health-income.csv gapminder.json gimp.png github.csv graticule.json income.json iowa-electricity.csv iris.json jobs.json la-riots.csv londonBoroughs.json londonCentroids.json londonTubeLines.json lookup_groups.csv lookup_people.csv miserables.json monarchs.json movies.json normal-2d.json obesity.json points.json population.json population_engineers_hurricanes.csv seattle-temps.csv seattle-weather.csv sf-temps.csv sp500.csv stocks.csv udistrict.json unemployment-across-industries.json unemployment.tsv us-10m.json us-employment.csv us-state-capitals.json weather.csv weather.json weball26.json wheat.json windvectors.csv world-110m.json zipcodes.csv
- vegaExampleNameCell
- highlightScope constant.numeric
- enum airport_connections area area_cumulative_freq area_horizon area_overlay area_temperature_range area_vertical bar bar_1d bar_1d_rangestep_config bar_aggregate bar_aggregate_count bar_aggregate_format bar_aggregate_size bar_aggregate_sort_by_encoding bar_aggregate_sort_mean bar_aggregate_transform bar_aggregate_vertical bar_argmax bar_argmax_transform bar_array_aggregate bar_binned_data bar_color_disabled_scale bar_column_fold bar_custom_sort_full bar_custom_sort_partial bar_distinct bar_diverging_stack_transform bar_filter_calc bar_fit bar_gantt bar_grouped bar_grouped_horizontal bar_layered_transparent bar_layered_weather bar_month bar_month_temporal bar_size_default bar_size_explicit bar_size_explicit_bad bar_size_fit bar_size_rangestep_small bar_sort_by_count bar_swap_axes bar_swap_custom bar_title bar_title_start bar_tooltip bar_tooltip_multi bar_yearmonth bar_yearmonth_custom_format boxplot_1D_horizontal boxplot_1D_horizontal_custom_mark boxplot_1D_horizontal_explicit boxplot_1D_vertical boxplot_2D_horizontal boxplot_2D_horizontal_color_size boxplot_2D_vertical boxplot_minmax_2D_horizontal boxplot_minmax_2D_horizontal_custom_midtick_color boxplot_minmax_2D_vertical boxplot_tooltip_aggregate boxplot_tooltip_not_aggregate brush_table circle circle_binned circle_binned_maxbins_2 circle_binned_maxbins_20 circle_binned_maxbins_5 circle_bubble_health_income circle_flatten circle_github_punchcard circle_natural_disasters circle_opacity circle_scale_quantile circle_scale_quantize circle_scale_threshold concat_bar_layer_circle concat_bar_scales_discretize concat_bar_scales_discretize_2_cols concat_hover concat_hover_filter concat_layer_voyager_result_future concat_marginal_histograms concat_population_pyramid concat_weather connected_scatterplot embedded_csv errorband_2d_horizontal_color_encoding errorband_2d_vertical_borders errorbar_2d_vertical_ticks errorbar_aggregate errorbar_horizontal_aggregate facet_bullet facet_column_facet_column_point_future facet_column_facet_row_point_future facet_cross_independent_scale facet_custom facet_custom_header facet_independent_scale facet_independent_scale_layer_broken facet_row_facet_row_point_future geo_choropleth geo_circle geo_constant_value geo_custom_projection geo_graticule geo_graticule_object geo_layer geo_layer_line_london geo_layer_multi geo_line geo_point geo_repeat geo_rule geo_sphere geo_text geo_trellis hconcat_weather histogram histogram_bin_change histogram_bin_transform histogram_log histogram_no_spacing histogram_ordinal histogram_ordinal_sort interactive_area_brush interactive_bar_select_highlight interactive_brush interactive_concat_layer interactive_dashboard_europe_pop interactive_layered_crossfilter interactive_layered_crossfilter_discrete interactive_multi_line_label interactive_multi_line_tooltip interactive_overview_detail interactive_paintbrush interactive_paintbrush_color interactive_paintbrush_color_nearest interactive_paintbrush_interval interactive_paintbrush_simple_all interactive_paintbrush_simple_none interactive_panzoom_splom interactive_panzoom_vconcat_shared interactive_query_widgets interactive_seattle_weather interactive_splom interactive_stocks_nearest_index isotype_bar_chart isotype_bar_chart_emoji isotype_grid joinaggregate_mean_difference joinaggregate_mean_difference_by_year joinaggregate_percent_of_total joinaggregate_residual_graph layer_bar_annotations layer_bar_labels layer_bar_labels_style layer_bar_line layer_bar_line_union layer_bar_month layer_boxplot_circle layer_candlestick layer_circle_independent_color layer_color_legend_left layer_cumulative_histogram layer_dual_axis layer_falkensee layer_histogram layer_histogram_global_mean layer_line_co2_concentration layer_line_color_rule layer_line_errorband_2d_horizontal_borders_strokedash layer_line_errorband_ci layer_line_errorband_pre_aggregated layer_line_mean_point_raw layer_overlay layer_point_errorbar_1d_horizontal layer_point_errorbar_1d_vertical layer_point_errorbar_2d_horizontal layer_point_errorbar_2d_horizontal_ci layer_point_errorbar_2d_horizontal_color_encoding layer_point_errorbar_2d_horizontal_custom_ticks layer_point_errorbar_2d_horizontal_iqr layer_point_errorbar_2d_horizontal_stdev layer_point_errorbar_2d_vertical layer_point_errorbar_ci layer_point_errorbar_pre_aggregated_asymmetric_error layer_point_errorbar_pre_aggregated_symmetric_error layer_point_errorbar_pre_aggregated_upper_lower layer_point_errorbar_stdev layer_precipitation_mean layer_ranged_dot layer_rect_extent layer_scatter_errorband_1D_stdev_global_mean layer_scatter_errorband_1d_stdev layer_single_color layer_text_heatmap line line_calculate line_color line_color_binned line_detail line_encoding_impute_keyvals line_encoding_impute_keyvals_sequence line_impute_frame line_impute_keyvals line_impute_method line_impute_transform_frame line_impute_transform_value line_impute_value line_inside_domain_using_clip line_inside_domain_using_transform line_max_year line_mean_month line_mean_year line_monotone line_month line_outside_domain line_overlay line_overlay_stroked line_quarter_legend line_shape_overlay line_skip_invalid line_skip_invalid_mid line_skip_invalid_mid_cap_square line_skip_invalid_mid_overlay line_slope line_step line_timeunit_transform lookup parallel_coordinate point_1d point_1d_array point_2d point_2d_aggregate point_2d_array point_2d_array_named point_2d_tooltip_data point_aggregate_detail point_background point_binned_color point_binned_opacity point_binned_size point_bubble point_color point_color_custom point_color_ordinal point_color_quantitative point_color_shape_constant point_color_with_shape point_colorramp_size point_diverging_color point_dot_timeunit_color point_filled point_href point_invalid_color point_log point_no_axis_domain_grid point_ordinal_color point_overlap point_shape_custom point_tooltip rect_binned_heatmap rect_heatmap rect_heatmap_weather rect_lasagna_future rect_mosaic_labelled rect_mosaic_labelled_with_offset rect_mosaic_simple repeat_histogram repeat_histogram_flights repeat_independent_colors repeat_layer repeat_line_weather repeat_splom_cars repeat_splom_iris rule_color_mean rule_extent sample_scatterplot selection_bind_cylyr selection_bind_origin selection_brush_timeunit selection_clear_brush selection_composition_and selection_composition_or selection_concat selection_filter selection_filter_composition selection_heatmap selection_insert selection_interval_mark_style selection_layer_bar_month selection_multi_condition selection_project_binned_interval selection_project_interval selection_project_interval_x selection_project_interval_x_y selection_project_interval_y selection_project_multi selection_project_multi_cylinders selection_project_multi_cylinders_origin selection_project_multi_origin selection_project_single selection_project_single_cylinders selection_project_single_cylinders_origin selection_project_single_origin selection_resolution_global selection_resolution_intersect selection_resolution_union selection_toggle_altKey selection_toggle_altKey_shiftKey selection_toggle_shiftKey selection_translate_brush_drag selection_translate_brush_shift-drag selection_translate_scatterplot_drag selection_translate_scatterplot_shift-drag selection_type_interval selection_type_interval_invert selection_type_multi selection_type_single selection_type_single_dblclick selection_type_single_mouseover selection_zoom_brush_shift-wheel selection_zoom_brush_wheel selection_zoom_scatterplot_shift-wheel selection_zoom_scatterplot_wheel sequence_line square stacked_area stacked_area_normalize stacked_area_ordinal stacked_area_overlay stacked_area_stream stacked_bar_1d stacked_bar_count stacked_bar_h stacked_bar_h_order stacked_bar_h_order_custom stacked_bar_normalize stacked_bar_population stacked_bar_population_transform stacked_bar_size stacked_bar_sum_opacity stacked_bar_unaggregate stacked_bar_v stacked_bar_weather test_aggregate_nested test_field_with_spaces test_single_point_color test_subobject test_subobject_missing test_subobject_nested text_format text_scatterplot_colored tick_dot tick_dot_thickness tick_sort tick_strip time_output_utc_scale time_output_utc_timeunit time_parse_local time_parse_utc time_parse_utc_format trail_color trellis_anscombe trellis_area trellis_area_sort_array trellis_bar trellis_bar_histogram trellis_bar_histogram_label_rotated trellis_barley trellis_barley_independent trellis_barley_layer_median trellis_column_year trellis_cross_sort trellis_cross_sort_array trellis_line_quarter trellis_row_column trellis_scatter trellis_scatter_binned_row trellis_scatter_small trellis_selections trellis_stacked_bar vconcat_flatten vconcat_weather waterfall_chart wheat_wages window_cumulative_running_average window_percent_of_total window_rank window_top_k window_top_k_others
- wikipediaPermalinkCell
- extends anyCell
- tileBlankLineNode
- boolean visible false
- pattern ^$
- tags doNotSynthesize
- cells emptyCell
- abstractTileTreeComponentNode
- abstract
- cells tileKeywordCell
- _extendsJsClass AbstractTreeComponent
- inScope tileBlankLineNode abstractTileTreeComponentNode abstractCoreTileSettingTerminalNode
- string settingKey setting
- string rowDisplayLimitKey rowDisplayLimit
- string contentKey content
- string xColumnKey xColumn
- string yColumnKey yColumn
- string colorColumnKey colorColumn
- string shapeColumnKey shapeColumn
- string sizeColumnKey sizeColumn
- string columnPredictionHintsKey columnPredictionHints
- string ohayoFileExtensionKey .ohayo
- string dayKey day
- boolean needsData true
- string monthKey month
- string yearKey year
- catchAllNodeType catchAllErrorNode
- string hiddenKey hidden
- string visibleKey visible
- string tileLoadingTemplate
- div
- class abstractTileTreeComponentNode
- id {id}
- div Loading {name}...
- class TileBody
- div
- class TileFooter
- {footer}
- string errorLogMessageStumpTemplate
- div Error occurred. See console.
- class OhayoError
- string pencilStumpTemplate
- span ➕
- class TileInsertBetweenButton
- clickCommand insertTileBetweenCommand
- span ▼
- class TileDropDownButton
- clickCommand toggleTileMenuCommand
- string errorStateStumpTemplate
- div
- class {classes}
- id {id}
- div
- class TileBody
- div ERROR
- {content}
- div
- class TileFooter
- {footer}
- string tileStumpTemplate
- div
- class {classes}
- id {id}
- div
- style {bodyStyle}
- class TileBody
- {body}
- div
- class TileFooter
- {footer}
- string inspectionStumpTemplate
- div TileConstructor: {constructorName} ParentConstructor: {parentConstructorName}
- div Messages:
- ol
- {messages}
- div Tree:
- pre
- bern
- {sourceCode}
- div All Tile Settings:
- pre
- bern
- {settings}
- div Input rows: {inputCount} Output rows: {outputCount}
- div Load time: {timeToLoad} Render time: {renderTime}
- div Input Columns:
- pre
- bern
- {inputColumnsAsTable}
- div Output Columns
- pre
- bern
- {outputColumnsAsTable}
- div Output Numeric Values:
- pre
- bern
- {outputNumericValues}
- div TypeScript Interface:
- pre
- bern
- {typeScriptInterface}
- div Input Numeric Values:
- pre
- bern
- {inputNumericValues}
- javascript
- // todo: ADD TYPINGS
- getPipishInput() {
- // todo: add placeholder property?
- return this.getSettingsStruct().content || this.getParentOrDummyTable().getFirstColumnAsString() || ""
- }
- getDependencies() {
- return [{ getLineModifiedTime: () => this.getParentOrDummyTable().getTableCTime() }] // todo: we removed this: this.getOutputOrInputTable().getTableCTime()...i think we had it because we want to return true to update children.
- }
- getRunTimeEnumOptions(cell) {
- // todo: only works if codemirror === tab
- try {
- // todo: handle at static time.
- if (cell.getCellTypeId() === "columnNameCell" && this.isLoaded()) {
- const mirrorNode = typeof app === "undefined" ? this : app.mountedProgram.nodeAtLine(this.getLineNumber() - 1)
- return mirrorNode.getParentOrDummyTable().getColumnNames()
- }
- } catch (err) {
- console.log(err)
- }
- }
- mapSettingNamesToColumnNames(settingNames) {
- const tileStruct = this.getSettingsStruct()
- return settingNames.map(name => tileStruct[name])
- }
- getOutputOrInputTable() {
- return this._outputTable || this.getParentOrDummyTable()
- }
- getOutputTable() {
- return this._outputTable
- }
- getParentOrDummyTable() {
- // Returns: non-empty input table || dummy table || empty input table.
- const parentTable = this.getParent().getOutputOrInputTable()
- if (!parentTable.isBlankTable()) return parentTable
- return this._getDummyTable() || parentTable
- }
- _getDummyTable() {
- const dataSet = DummyDataSets[this.dummyDataSetName]
- if (!this._dummyTable && dataSet) this._dummyTable = new Table(jtree.Utils.javascriptTableWithHeaderRowToObjects(dataSet))
- return this._dummyTable
- }
- getRequiredTableWithHeader(headerSettingNames) {
- const columnNames = this.mapSettingNamesToColumnNames(headerSettingNames)
- const table = this.getParentOrDummyTable()
- const columns = columnNames.map(name => table.getTableColumnByName(name))
- if (columns.some(col => !col)) return []
- return this.getRowsAsDataTableArrayWithHeader(table.getRows(), columnNames)
- }
- setIsDataLoaded(value) {
- this._isDataLoaded = value
- this.makeDirty() // todo: remove
- return this
- }
- getRowsAsDataTableArrayWithHeader(rows, header) {
- const data = rows.map(row => row.getAsArray(header))
- data.unshift(header)
- return data
- }
- getTileQualityCheck() {
- const definition = this.getDefinition()
- const name = this.getFirstWord()
- let score = 0
- return {
- name: name,
- namespace: name.split(".")[0],
- description: definition.getDescription() ? 1 : 0,
- dummyDataSetName: this.dummyDataSetName,
- runTimeErrors: Object.values(this.getRunTimePhaseErrors()).length,
- examples: definition.getExamples().length,
- edgeTests: 0,
- speedTests: 0,
- roadMap: 0,
- idealStyleUXDescription: 0,
- secPriTests: 0,
- userType: 0
- }
- }
- _getCachedSettings() {
- if (this._cache_settingsObject) return this._cache_settingsObject
- this._cache_settingsObject = {}
- this.filter(child => child.doesExtend("abstractTileSettingTerminalNode") || child.doesExtend("abstractTileSettingNonTerminalNode")).forEach(setting => {
- this._cache_settingsObject[setting.getFirstWord()] = setting.getSettingValue()
- })
- return this._cache_settingsObject
- }
- // todo: ADD TYPINGS
- getSettingsStruct() {
- const settingsFromCache = this._getCachedSettings()
- // todo: this wont work anymore
- const hintsNode = this.getDefinition().getConstantsObject()[this.columnPredictionHintsKey]
- if (hintsNode) Object.assign(settingsFromCache, this.getParentOrDummyTable().getPredictionsForAPropertyNameToColumnNameMapGivenHintsNode(new jtree.TreeNode(hintsNode), settingsFromCache))
- return settingsFromCache
- }
- getProgramTemplate(id) {}
- getSnippetTemplate(id) {}
- getExampleTemplate(index) {
- // todo: right now we only have 1 example per tile.
- const exampleNode = this.getDefinition().getNode(jtree.GrammarConstants.example)
- return exampleNode ? exampleNode.childrenToString() : ""
- }
- toStumpLoadingCode() {
- return this.qFormat(this.tileLoadingTemplate, { classes: this.getCssClassNames().join(" "), id: this.getTreeComponentId(), name: this.getWord(0), footer: this.getTileMenuButtonStumpCode() })
- }
- emitLogMessage(message) {
- const tab = this.getTab()
- if (tab) tab.addStumpCodeMessageToLog(message)
- else if (this.isNodeJs()) console.log(message)
- }
- getTheme() {
- return this.getTab().getTheme()
- }
- qFormat(str, obj) {
- return new jtree.TreeNode(str).templateToString(obj)
- }
- scrollIntoView() {
- const el = this.getStumpNode()
- .getShadow()
- .getShadowElement()
- if (el) el.scrollIntoView()
- }
- async loadBrowserRequirements() {
- const loadingMap = this.getTab()
- .getRootNode()
- .getDefinitionLoadingPromiseMap()
- if (!loadingMap.has(this.constructor)) loadingMap.set(this.constructor, this._makeBrowserLoadRequirementsPromise(loadingMap))
- await loadingMap.get(this.constructor)
- }
- async _makeBrowserLoadRequirementsPromise(loadingMap) {
- const app = this.getWebApp()
- const cssScript = this[OhayoConstants.tileCssScript]
- if (cssScript) this._loadTileCss(cssScript)
- const def = this.getDefinition()
- const scriptPaths = def.nodesThatStartWith("string " + OhayoConstants.tileScript).map(node => node.getWord(2))
- const thisScript = this[OhayoConstants.tileScript]
- if (thisScript && !scriptPaths.includes(thisScript)) scriptPaths.push(thisScript)
- if (scriptPaths.length) await Promise.all(scriptPaths.map(scriptPath => app.getWillowBrowser().appendScript(scriptPath)))
- loadingMap.set(this.constructor, true)
- }
- _loadTileCss(css) {
- const app = this.getWebApp()
- app
- .getWillowBrowser()
- .getBodyStumpNode()
- .insertChildNode(
- css
- .split(" ")
- .map(
- url => \`link
- rel stylesheet
- media screen
- href \${url}\`
- )
- .join("\\n")
- )
- }
- _hasBrowserRequirements() {
- return this.tileScript
- }
- _areBrowserRequirementsLoaded() {
- if (this.isNodeJs()) return true
- // todo: cleanup. assumes app is here in browser.
- const loadingMap = app.getDefinitionLoadingPromiseMap()
- return !this._hasBrowserRequirements() || loadingMap.get(this.constructor) === true
- }
- isLoaded() {
- return this._areBrowserRequirementsLoaded() && (!this.needsData || this._isDataLoaded)
- }
- getErrorMessageHtml() {
- const errors = Object.values(this.getRunTimePhaseErrors())
- return errors.length ? \` \${errors.join(" ")} \` : "" //todo: cleanup
- }
- toStumpErrorStateCode(err) {
- return this.qFormat(this.errorStateStumpTemplate, { classes: this.getCssClassNames().join(" "), id: this.getTreeComponentId(), content: \`div \` + err, footer: this.getTileMenuButtonStumpCode() })
- }
- // todo: delete this
- makeDirty() {
- delete this._cache_settingsObject
- delete this._bodyStumpCodeCache // todo: cleanup
- this._setLastRenderedTime(0)
- }
- getAllTileSettingsDefinitions() {
- const def = this.getDefinition()
- return Object.values(def.getFirstWordMapWithDefinitions()).filter(def => def.isOrExtendsANodeTypeInScope([OhayoConstants.abstractTileSetting]))
- }
- getTab() {
- return this.getRootNode().getTab()
- }
- getChildTiles() {
- return this.getChildInstancesOfNodeTypeId("abstractTileTreeComponentNode")
- }
- selectTile() {
- this.selectNode()
- if (this.isMounted()) this.getStumpNode().addClassToStumpNode(OhayoConstants.selectedClass)
- }
- unselectNode() {
- super.unselectNode()
- if (this.isMounted()) this.getStumpNode().removeClassFromStumpNode(OhayoConstants.selectedClass)
- }
- getCssClassNames() {
- const classNames = super.getCssClassNames()
- if (this._isMaximized()) classNames.push("TileMaximized")
- return classNames
- }
- toStumpCode() {
- return this.qFormat(this.tileStumpTemplate, {
- classes: this.getCssClassNames().join(" "),
- id: this.getTreeComponentId(),
- bodyStyle: this.customBodyStyle || "",
- body: this._getBodyStumpCodeCache() || "",
- footer: this.getTileFooterStumpCode()
- })
- }
- _getBodyStumpCodeCache() {
- if (!this._bodyStumpCodeCache) this._bodyStumpCodeCache = this.getTileBodyStumpCode()
- return this._bodyStumpCodeCache
- }
- getTileBodyStumpCode() {
- return \`\`
- }
- _getCss() {
- const selector = "#" + this.getTreeComponentId()
- const theme = this.getTheme()
- const visibleCss = this.isVisible() ? "" : "display: none"
- const hakonCode = this.hakonTemplate ? new jtree.TreeNode(theme).evalTemplateString(this.hakonTemplate) : this.toHakonCode()
- return \`\${selector} { \${visibleCss} }
- \${theme.hakonToCss(hakonCode)}\`
- }
- handleTileError(err) {
- if (!this._errorCount) this._errorCount = 0
- this._errorCount++
- this.getRootNode().goRed(err)
- }
- async insertTileBetweenCommand() {
- const tab = this.getTab()
- const newNode = this.appendLine("doc.picker")
- this.getChildTiles().forEach(tile => {
- if (tile === newNode) return true
- newNode.appendNode(tile)
- tile.unmountAndDestroy()
- })
- tab.autosaveTab()
- await this.getRootNode().loadAndIncrementalRender()
- }
- getWall() {
- return this.getWebApp().getAppWall()
- }
- getWebApp() {
- return this.getTab().getRootNode()
- }
- async runAndrenderAndGetRenderReport() {
- await this.execute()
- return this.renderAndGetRenderReport()
- }
- getTimeToLoad() {
- return this._timeToLoad || 0
- }
- toHakonCode() {
- return ""
- }
- getTileFooterStumpCode() {
- return this.getTileMenuButtonStumpCode()
- }
- getTileMenuButtonStumpCode() {
- return this.qFormat(this.pencilStumpTemplate)
- }
- // Tile child rendering is done at the wall level.
- _getChildTreeComponents() {
- return []
- }
- getStumpNodeForChildren() {
- // We render all Tiles on the Wall.
- return this.getStumpNode().getParent()
- }
- toInspectionStumpCode() {
- const messages = this.getMessageBuffer().map(message => \`li \${moment(message.getLineModifiedTime()).fromNow()} - \${message.childrenToString()}\`)
- // const settings = this.getAllTileSettingsDefinitions()
- // .map(setting => \`\${setting.getFirstWord()} \${setting.getDescription()}\`)
- // .join("\\n")
- const settings = JSON.stringify(this.getSettingsStruct(), null, 2)
- const parentConstructorName = this.getParent().constructor.name
- const constructorName = this.constructor.name
- const sourceCode = this.toString()
- const inputTable = this.getParentOrDummyTable()
- const outputTable = this.getOutputOrInputTable()
- const outputColumns = outputTable.getColumnsArrayOfObjects()
- const inputCols = inputTable.getColumnsArrayOfObjects()
- const inputCount = inputTable.getRowCount()
- const outputCount = outputTable.getRowCount()
- const timeToLoad = this.getTimeToLoad()
- const renderTime = this.getNewestTimeToRender()
- const inputColumnsAsTable = new jtree.TreeNode(inputCols).toTable()
- const outputColumnsAsTable = new jtree.TreeNode(outputColumns).toTable()
- const outputNumericValues = new jtree.TreeNode(outputTable.getJavascriptNativeTypedValues()).toTable()
- const typeScriptInterface = outputTable.toTypeScriptInterface()
- const inputNumericValues = new jtree.TreeNode(inputTable.getJavascriptNativeTypedValues()).toTable()
- return this.qFormat(this.inspectionStumpTemplate, {
- settings,
- inputCount,
- outputCount,
- timeToLoad,
- renderTime,
- inputColumnsAsTable,
- outputColumnsAsTable,
- outputNumericValues,
- typeScriptInterface,
- inputNumericValues,
- constructorName,
- parentConstructorName,
- sourceCode,
- messages
- })
- }
- isVisible() {
- if (this.has(this.visibleKey)) return true
- if (this.visible === false) return false
- if (this.has(this.hiddenKey)) return false
- return true
- }
- _isMaximized() {
- return this.has(OhayoConstants.maximized)
- }
- async _executeChildNodes() {
- await Promise.all(this.getChildTiles().map(tile => tile.execute()))
- }
- async _execute() {
- this.setIsDataLoaded(true)
- await this._executeChildNodes()
- }
- async execute() {
- try {
- this.setRunTimePhaseError("execute")
- await this._execute()
- } catch (err) {
- this.setRunTimePhaseError("execute", err)
- console.error(err)
- this.emitLogMessage(this.errorLogMessageStumpTemplate)
- }
- return this
- }
- cloneTileCommand() {
- this.duplicateLineCommand()
- return this.getTab().autosaveAndRender()
- }
- duplicateLineCommand() {
- return this.getParent().insertLineAndChildren(this.getLine(), undefined, this.getIndex() + 1)
- }
- async toggleTileMaximizeCommand() {
- if (this.has(OhayoConstants.maximized)) this.delete(OhayoConstants.maximized)
- else this.touchNode(OhayoConstants.maximized)
- await this._runAfterTileUpdate(this)
- }
- async triggerTileMethodCommand(value, methodName) {
- await thisethodName](value)
- await this._runAfterTileUpdate(this)
- }
- // todo: refactor.
- async changeTileTypeCommand(newValue) {
- const tab = this.getTab()
- this.setFirstWord(newValue)
- const newNode = this.duplicate()
- // todo: destroy or something? how do we reparse.
- this.getChildTiles().forEach(tile => tile.unmountAndDestroy())
- this.unmountAndDestroy()
- tab.autosaveTab()
- this.getRootNode().loadAndIncrementalRender()
- }
- changeParentCommand(pathVector) {
- // if (tile.getFirstWordPath() === value) return; // todo: do we need this line?
- const program = this.getRootNode()
- const indexPath = pathVector ? pathVector.split(" ").map(num => parseInt(num)) : ""
- const destinationTree = indexPath ? program.nodeAt(indexPath) : program
- // todo: on jtree should we make copyTo second param optional?
- this.copyTo(destinationTree, destinationTree.length)
- this.unmountAndDestroy()
- return this.getTab().autosaveAndRender()
- }
- async removeTileCommand() {
- const tab = this.getTab()
- this.getChildTiles().forEach(tile => {
- tile.unmount()
- tile.shiftLeft()
- })
- this.unmountAndDestroy()
- tab.autosaveTab()
- this.getRootNode().loadAndIncrementalRender()
- }
- getNewDataCommand() {
- // todo: have some type of paging system to fetch new data.
- }
- async changeTileSettingAndRenderCommand(value, settingName) {
- // note the unusual ordering of params.
- this.touchNode(settingName).setContent(value.toString())
- // todo: sometimes size needs to be redone (maximize, for example)
- await this._runAfterTileUpdate(this)
- }
- // todo: remove
- async changeTileSettingMultilineCommand(val, settingName) {
- this.touchNode(settingName).setChildren(val)
- await this._runAfterTileUpdate(this)
- }
- async changeTileSettingCommand(settingName, value) {
- this.touchNode(settingName).setContent(value)
- }
- async changeWordAndRenderCommand(value, index) {
- this.setWord(parseInt(index), value)
- await this._runAfterTileUpdate(this)
- }
- async changeWordsAndRenderCommand(value, index) {
- index = parseInt(index)
- const edgeSymbol = this.getEdgeSymbol()
- const words = this.getWords().slice(0, index)
- this.setLine(words.concat(value.split(edgeSymbol)).join(edgeSymbol))
- await this._runAfterTileUpdate(this)
- }
- async updateChildrenCommand(val) {
- this.setChildren(val)
- // reload the whole doc for now.
- await this._runAfterTileUpdate(this)
- }
- async _runAfterTileUpdate(tile) {
- tile.makeDirty() // ugly!
- tile.getChildTiles().forEach(tile => {
- tile.makeDirty() // todo: ugly!
- })
- // todo: what if you have a tile that has a contextare that allows editing of its children/
- // if you edit a child, then that parent tile needs to update to...should we allow that or ban that?
- await tile.getTab().autosaveTab()
- await tile.runAndrenderAndGetRenderReport()
- tile
- .getTab()
- .getRootNode()
- .renderApp() // Need to render full app because of code editor
- }
- // todo: downstream data changes?
- async changeTileContentAndRenderCommand(value) {
- this.setContent(value)
- await this._runAfterTileUpdate(this)
- }
- async copyTileCommand() {
- // todo: remove cousin tiles?
- this.getRootNode()
- .getWillowBrowser()
- .copyTextToClipboard(this.getFirstAncestor().toString())
- }
- async createProgramFromTileExampleCommand(index) {
- const template = this.getExampleTemplate(index)
- if (!template) return undefined
- const fileExtension = "ohayo" // todo: generalize
- const tab = await this.getTab()
- .getRootNode()
- ._createAndOpen(template, \`help-for-\${this.getFirstWord()}.\${fileExtension}\`)
- tab.addStumpCodeMessageToLog(\`div Created '\${tab.getFullTabFilePath()}'\`)
- }
- async inspectTileCommand() {
- if (!this.isNodeJs()) {
- console.log("Tile available at window.tile")
- window.tile = this
- console.log(this)
- }
- this.getTab().addStumpCodeMessageToLog(this.toInspectionStumpCode())
- this.getTab()
- .getRootNode()
- .renderApp()
- }
- async toggleTileMenuCommand() {
- const app = this.getTab().getRootNode()
- app.setTargetTile(this)
- app.toggleAndRender(\`\${StudioConstants.tileMenu}\`)
- }
- async createProgramFromTemplateCommand(id) {
- const programTemplate = this.getProgramTemplate(id)
- if (!programTemplate) return undefined
- const tab = await this.getTab()
- .getRootNode()
- ._createAndOpen(programTemplate.template, programTemplate.name)
- tab.addStumpCodeMessageToLog(\`div Created '\${tab.getFullTabFilePath()}'\`)
- }
- async appendSnippetTemplateCommand(id) {
- const snippet = this.getSnippetTemplate(id)
- if (!snippet) return undefined
- const tab = this.getTab()
- const tabProgram = tab.getTabProgram()
- const newNodes = tabProgram.concat(snippet)
- const newTiles = newNodes.filter(tile => tile.doesExtend && tile.doesExtend("abstractTileTreeComponentNode"))
- tab.autosaveTab()
- tabProgram.clearSelection()
- tab.getTabWall().unmount()
- await tabProgram.loadAndIncrementalRender()
- newTiles.forEach(tile => tile.selectTile())
- newTiles[0].scrollIntoView()
- }
- async copyDataCommand(delimiter) {
- this.getRootNode()
- .getWillowBrowser()
- .copyTextToClipboard(this.getOutputOrInputTable().toDelimited(delimiter))
- }
- async copyDataAsJavascriptCommand() {
- const table = this.getOutputOrInputTable()
- this.getRootNode()
- .getWillowBrowser()
- .copyTextToClipboard(JSON.stringify(table.toTree().toDataTable(table.getColumnNames()), null, 2))
- }
- async copyDataAsTreeCommand() {
- const text = this.getOutputOrInputTable()
- .toTree()
- .toString()
- this.getRootNode()
- .getWillowBrowser()
- .copyTextToClipboard(text)
- }
- async exportTileDataCommand(format = "csv") {
- // todo: figure this out. use the browsers filename? tile title? id?
- let extension = "csv"
- let type = "text/csv"
- let str = this.getOutputOrInputTable().toDelimited(",")
- if (format === "tree") {
- extension = "tree"
- type = "text"
- str = this.getOutputOrInputTable()
- .toTree()
- .toString()
- }
- this.getRootNode()
- .getWillowBrowser()
- .downloadFile(str, this.getTab().getFileName() + "." + extension, type)
- }
- abstractChartNode
- inScope rowDisplayLimitNode
- int rowDisplayLimit 10000
- extends abstractTileTreeComponentNode
- abstract
- string tileFooterTemplate
- span Rows: {rowCount} Columns Out: {columnCount}
- {tileMenuButton}
- javascript
- getTileFooterStumpCode() {
- const table = this.getParentOrDummyTable()
- return this.qFormat(this.tileFooterTemplate, { rowCount: table.getRowCount(), columnCount: table.getColumnCount(), tileMenuButton: this.getTileMenuButtonStumpCode() })
- }
- getTileRunTimeWidth() {
- return this.isNodeJs() ? 456 : jQuery(".WallTreeComponent").width() - 100
- }
- getTileRunTimeHeight() {
- return 300
- }
- toDisplayString(value, columnName) {
- // todo: remove.
- if (value === undefined) return ""
- return this.getParentOrDummyTable()
- .getTableColumnByName(columnName)
- .toDisplayString(value)
- }
- _getRowDisplayLimit() {
- const limitStr = this.getSettingsStruct()[this.rowDisplayLimitKey] || this.rowDisplayLimit
- const limit = parseInt(limitStr)
- if (!limitStr || isNaN(limit)) return undefined
- return limit
- }
- getRowsWithRowDisplayLimit() {
- return this.getParentOrDummyTable()
- .getRows()
- .slice(0, this._getRowDisplayLimit())
- }
- abstractTextNode
- catchAllCellType stringCell
- frequency 0
- description Prints a message
- inScope contentNode
- string bodyStumpTemplate
- div
- class TileSelectable
- bern
- {content}
- javascript
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { content: this.content ? jtree.Utils.linkify(this.content) : "" })
- }
- extends abstractChartNode
- abstract
- abstractInstructionsNode
- string tileSize 600 240
- string content Instructions go here.
- extends abstractTextNode
- abstract
- amazonHistoryNode
- description Instructions on how to get your Amazon order history.
- string content Step 1. Go to https://www.amazon.com/gp/b2b/reports to download your Amazon order history. Step 2. Add the data here.
- string dummyDataSetName amazonPurchases
- extends abstractInstructionsNode
- crux amazon.history
- fitbitAllNode
- description Instructions on how to get your Fitbit data.
- extends abstractInstructionsNode
- crux fitbit.all
- abstractComingSoonNode
- frequency 0
- description Coming soon
- string tileSize 600 240
- string content Instructions go here.
- extends abstractTextNode
- abstract
- datawrapperComingSoonNode
- string content We don't have support yet for https://www.datawrapper.de/
- extends abstractComingSoonNode
- crux datawrapper.comingSoon
- dcjsComingSoonNode
- string content We don't have support yet for https://github.com/dc-js/dc.js
- extends abstractComingSoonNode
- crux dcjs.comingSoon
- finosPerspectiveComingSoonNode
- string content We don't have support yet for https://perspective.finos.org/
- extends abstractComingSoonNode
- crux finos.perspective.comingSoon
- fivethirtyeightComingSoonNode
- string content We don't have support yet for https://github.com/fivethirtyeight/data/
- extends abstractComingSoonNode
- crux fivethirtyeight.comingSoon
- GovNode
- string content We don't have support yet for https://www.data.gov/
- extends abstractComingSoonNode
- crux gov.comingSoon
- highchartsComingSoonNode
- string content We don't have support yet for https://www.highcharts.com/blog/snippets/3d-solar-system/
- extends abstractComingSoonNode
- crux highcharts.comingSoon
- re3dataComingSoonNode
- string content We don't have support yet for https://www.re3data.org/
- extends abstractComingSoonNode
- crux re3data.comingSoon
- zingComingSoonNode
- string content We don't have support yet for https://www.zingchart.com/
- extends abstractComingSoonNode
- crux zing.comingSoon
- editorHelloWorldNode
- description Prints hello world
- example Say hello.
- editor.helloWorld
- string content Ohayo world!
- extends abstractTextNode
- crux editor.helloWorld
- abstractSnippetGalleryNode
- string tileSize 600 240
- extends abstractChartNode
- abstract
- string bodyStumpTemplate
- h4 {title}
- ol
- class TileSelectable
- {options}
- string optionStumpTemplate
- li
- a {title}
- value {value}
- class appendSnippetButton
- clickCommand appendSnippetTemplateCommand
- javascript
- getGalleryNodes() {}
- async _execute() {
- this._outputTable = new Table(
- this.getGalleryNodes()
- .toDataTable()
- .slice(1)
- )
- this.setIsDataLoaded(true)
- await this._executeChildNodes()
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, {
- title: this.title,
- options: new jtree.TreeNode(
- this.getGalleryNodes()
- .map(node => this.qFormat(this.optionStumpTemplate, { title: node.evalTemplateString(this.itemFormat), value: node.get("id") }))
- .join("\\n")
- ).toString()
- })
- }
- abstractTemplateGalleryNode
- extends abstractSnippetGalleryNode
- abstract
- string optionStumpTemplate
- li
- a {title}
- value {value}
- class createProgramButton
- clickCommand createProgramFromTemplateCommand
- challengeListNode
- description View all challenges
- string title Try a challenge:
- string itemFormat {question}
- extends abstractSnippetGalleryNode
- crux challenge.list
- javascript
- getGalleryNodes() {
- return typeof challengesTree === "undefined" ? jtree.TreeNode.fromDisk("ohayo/packages/challenge/challenges.tree") : new jtree.TreeNode(challengesTree)
- }
- getSnippetTemplate(id) {
- return \`challenge.play \${id}\`
- }
- samplesListNode
- description View all available sample tiles
- string title All samples:
- string itemFormat {id} - {description}
- boolean isDataPublicDomain true
- extends abstractSnippetGalleryNode
- crux samples.list
- javascript
- getGalleryNodes() {
- // todo: cleanup.
- const ohayo = this.getWebApp().getOhayoGrammarAsTree()
- const hits = ohayo.getNodesByRegex(/^samples/).map(node => {
- return {
- id: node.get("crux"),
- description: node.get("description")
- }
- })
- return new jtree.TreeNode(hits)
- }
- getSnippetTemplate(id) {
- return id
- }
- vegaDataListNode
- description View all available Vega datasets
- frequency .001
- string title All Vega datasets:
- string itemFormat {id}
- extends abstractSnippetGalleryNode
- crux vega.data.list
- javascript
- getGalleryNodes() {
- // todo: cleanup this line.
- const node = this.getWebApp()
- .getOhayoGrammarAsTree()
- .getNodesByRegex(/^vegaDataSetCell/)[0]
- return new jtree.TreeNode(
- node
- .get("enum")
- .split(" ")
- .map(item => {
- return {
- id: item
- }
- })
- )
- }
- getSnippetTemplate(id) {
- return \`vega.data \${id}\`
- }
- vegaExampleListNode
- description View all available Vega examples
- frequency .001
- string title All Vega examples:
- string itemFormat {id}
- extends abstractSnippetGalleryNode
- crux vega.example.list
- javascript
- getGalleryNodes() {
- // todo: cleanup this line.
- const node = this.getWebApp()
- .getOhayoGrammarAsTree()
- .getNodesByRegex(/^vegaExampleNameCell/)[0]
- return new jtree.TreeNode(
- node
- .get("enum")
- .split(" ")
- .map(item => {
- return {
- id: item
- }
- })
- )
- }
- getSnippetTemplate(id) {
- return \`vega.example \${id}\`
- }
- abstractPickerTileNode
- extends abstractChartNode
- string tileSize 480 420
- boolean needsData false
- abstract
- string hakonTemplate
- .abstractPickerTileNode
- .PickerCategory
- width 100%
- margin-top 20px
- text-align center
- .TileBody
- display flex
- flex-flow row wrap
- a
- &:hover
- background-color {borderColor}
- padding 10px
- margin 5px
- height 30px
- background-color {backgroundColor}
- border 1px solid {borderColor}
- overflow hidden
- text-align center
- text-overflow ellipsis
- font-size 14px
- width 120px
- span
- font-size 70%
- string itemStumpTemplate
- {categoryBreak}
- a {name}
- br
- span {description}
- title {description}
- tabindex -1
- value {value}
- class pickerItemButton
- clickCommand {command}
- string categoryBreakStumpTemplate
- div {category}
- class PickerCategory
- javascript
- async fetchTableInputs() {
- return { rows: this.getChoices() }
- }
- getTileBodyStumpCode() {
- let lastCat = ""
- return this.getChoices()
- .map(choice => {
- choice.categoryBreak = lastCat !== choice.category ? this.qFormat(this.categoryBreakStumpTemplate, { category: choice.category }) : ""
- lastCat = choice.category
- return this.qFormat(this.itemStumpTemplate, choice)
- })
- .join("\\n")
- }
- PickerTileNode
- extends abstractPickerTileNode
- description Displays list of available tiles.
- crux doc.picker
- javascript
- getChoices() {
- const allChoices = this.getRootNode()
- .getHandGrammarProgram()
- .getTopNodeTypeDefinitions()
- const filteredChoices = allChoices.filter(nodeDef => !(nodeDef.get(jtree.GrammarConstants.tags) || "").includes(OhayoConstants.noPicker))
- const theChoices = filteredChoices.length ? filteredChoices : allChoices
- return theChoices.map(nodeDefinition => {
- const nodeId = nodeDefinition.get("crux") || nodeDefinition.getNodeTypeIdFromDefinition()
- const name = nodeId.split(".")[1] || ""
- const category = lodash.upperFirst(nodeId.split(".")[0])
- const description = nodeDefinition.getDescription()
- return { name, category, description, value: nodeId, command: "changeTileTypeCommand" }
- })
- }
- templatesListNode
- extends abstractPickerTileNode
- description Displays templates.
- frequency .22
- crux templates.list
- javascript
- getChoices() {
- // todo: cleanup.
- const choices = this.getWebApp()
- .getStandardTemplates()
- .map(node => {
- const id = node
- .getWord(1)
- .replace("templates/", "")
- .replace(this.ohayoFileExtensionKey, "")
- return {
- command: "createProgramFromTemplateCommand",
- name: node.get("data doc.title"),
- value: id,
- category: lodash.upperFirst(node.get("data doc.categories")),
- description: ""
- }
- })
- return lodash.sortBy(choices, "category")
- }
- getProgramTemplate(id) {
- const node = this.getWebApp()
- .getStandardTemplates()
- .filter(node => node.getContent() === \`templates/\${id}\${this.ohayoFileExtensionKey}\`)[0]
- return {
- template: node.getNode("data").childrenToString(),
- name: id + this.ohayoFileExtensionKey
- }
- }
- asciiChartNode
- description Lightweight ASCII line chart from the library https://github.com/kroitor/asciichart
- string tileScript ohayo/packages/asciichart/asciichart.js
- extends abstractChartNode
- crux asciichart.line
- catchAllCellType titleCell
- inScope yColumnNode
- example
- samples.waterBill
- asciichart.line Water Bill
- string columnPredictionHints
- yColumn isString=false
- string bodyStumpTemplate
- pre
- class TileSelectable
- style overflow: scroll; width: 100%; height: 100%; white-space: pre;
- bern
- {title}
- {chart}
- javascript
- getTileBodyStumpCode() {
- // todo: autodetect column
- const columName = this.mapSettingNamesToColumnNames(["yColumn"])[0]
- const column = this.getParentOrDummyTable().getColumnByName(columName)
- const values = column.getValues()
- const chart = asciichart.plot(values, { height: 15 })
- const title = this.getContent() || ""
- const leftPad = Math.max(0, Math.floor((chart.split("\\n")[0].length - title.length) / 2))
- return this.qFormat(this.bodyStumpTemplate, { title: " ".repeat(leftPad) + title, chart })
- }
- calendarHeatNode
- description Shows which days have higher counts.
- inScope countNode dayColumnNode
- string tileSize 750 200
- string columnPredictionHints
- count getPrimitiveTypeName=number
- dayColumn getPrimitiveTypeName=day
- string dummyDataSetName waterBill
- extends abstractChartNode
- crux calendar.heat
- string bodyStumpTemplate
- div
- class heatCal
- bern
- {svg}
- string hakonTemplate
- .heatCal
- rect
- fill {darkerBackground}
- shape-rendering crispedges
- text
- font-size 10px
- fill #ddd
- javascript
- _getLegend(quins, squareSideWithPadding, position) {
- const theme = this.getTheme()
- const quinSvgs = quins
- .map((quin, index) => {
- const left = position.left + squareSideWithPadding * index
- const style = "fill: " + theme.getHeatColor(1 - quin.percent)
- return \` \`
- })
- .join("")
- return \`
- Less
- \${quinSvgs}
- More
- \`
- }
- getTileBodyStumpCode() {
- const svg = this._getSvg()
- return this.qFormat(this.bodyStumpTemplate, { svg })
- }
- _getDayMap(quins, rows, dayColumnName, countColumnName) {
- const getQuin = val => {
- for (let index = 0; index < quins.length; index++) {
- if (val <= quins[index].value) return quins[index].percent
- }
- }
- const dayMap = {}
- rows.forEach(row => {
- dayMapoment(row[dayColumnName]).format("MM/DD/YYYY")] = {
- Quin: getQuin(row[countColumnName]),
- count: row[countColumnName],
- row: row
- }
- })
- return dayMap
- }
- _getDaysArray(startDay, daysToShow) {
- const days = []
- const firstDay = parseInt(startDay.format("e"))
- for (let dayIndex = 0; dayIndex <= daysToShow; dayIndex++) {
- const day = startDay.clone().add(dayIndex, "days")
- days.push({
- day: day,
- row: parseInt(day.format("e")),
- col: Math.floor((firstDay + dayIndex) / 7)
- })
- }
- return days
- }
- _getDayNamesG(squareSideWithPadding) {
- const dayNames = [{ day: "Mon", row: 2 }, { day: "Wed", row: 4 }, { day: "Fri", row: 6 }]
- .map(day => {
- const _top = 20 + day.row * squareSideWithPadding - 3
- return \`\${day.day} \`
- })
- .join("")
- return \`\${dayNames} \`
- }
- _getMonthNamesG(daysArray, squareSideWithPadding) {
- const _usedMonths = {}
- const monthNames = daysArray
- .map(day => {
- const monthName = day.day.format("MMM")
- const monthYear = day.day.format("MM/YYYY")
- if (_usedMonthsonthYear]) return ""
- _usedMonthsonthYear] = true
- const left = 40 + day.col * squareSideWithPadding
- return \`\${monthName} \`
- })
- .join("")
- return \`\${monthNames} \`
- }
- _getDataSquaresG(daysArray, squareSideWithPadding, dayMap) {
- const dayFormat = "MM/DD/YYYY"
- const today = moment(Date.now()).format(dayFormat)
- const theme = this.getTheme()
- const dataSquares = daysArray
- .map(day => {
- const dayKey = day.day.format(dayFormat)
- const _top = 20 + day.row * squareSideWithPadding
- const left = 40 + day.col * squareSideWithPadding
- const value = dayMap[dayKey]
- const todayStyle = dayKey === today ? "stroke-width:2;stroke:rgb(0,0,0);" : ""
- const style = (value ? "fill: " + theme.getHeatColor(1 - value.Quin) : "") + ";" + todayStyle
- const title = \`\${dayKey}: \${value ? value.count : 0}\`
- return \`\${title} \`
- })
- .join("")
- return \`\${dataSquares} \`
- }
- _getSvg() {
- const inputTable = this.getParentOrDummyTable()
- const rows = inputTable.getJavascriptNativeTypedValues()
- if (!rows.length) return ""
- const tileStruct = this.getSettingsStruct()
- const dayColumnName = tileStruct.dayColumn
- const countColumnName = tileStruct.count
- if (!dayColumnName || !countColumnName) return ""
- const dayCol = inputTable.getTableColumnByName(dayColumnName)
- const countCol = inputTable.getTableColumnByName(countColumnName)
- let daysToShow = 365 * 1 // todo: make configurable
- let endDay = moment(Date.now())
- let startDay = endDay.clone().subtract(daysToShow, "days")
- // todo: make configurable
- // reductions = dayCol.getReductions()
- // startDay = moment(reductions.min)
- // endDay = moment(reductions.max)
- // daysToShow = endDay.diff(startDay, "days")
- const squareSide = 10
- const squarePadding = 2
- const squareSideWithPadding = squareSide + squarePadding
- const width = squareSideWithPadding * (daysToShow / 6)
- const height = 7 * squareSideWithPadding + 100
- const quins = countCol.getQuins()
- const dayMap = this._getDayMap(quins, rows, dayColumnName, countColumnName)
- const daysArray = this._getDaysArray(startDay, daysToShow)
- const dayNamesG = this._getDayNamesG(squareSideWithPadding)
- const monthNamesG = this._getMonthNamesG(daysArray, squareSideWithPadding)
- const squaresG = this._getDataSquaresG(daysArray, squareSideWithPadding, dayMap)
- const keyG = this._getLegend(quins, squareSideWithPadding, { top: 110, left: 60 })
- return \`\${dayNamesG + squaresG + monthNamesG + keyG} \`
- }
- challengePlayNode
- cells tileKeywordCell challengeIdCell
- description Learn ohayo by trying a challenge.
- catchAllCellType challengeAnswerCell
- tags aTileThatCreatesPrograms
- example
- challenge.list
- challenge.play 1
- challenge.play 2
- string tileSize 640 240
- extends abstractChartNode
- crux challenge.play
- javascript
- getProgramTemplate(id) {
- const challengeNode = this._getChallengeNode(parseInt(id))
- return {
- template: challengeNode.getNode("solution").childrenToString(),
- name: "challenge-" + id + "-solution.ohayo"
- }
- }
- _getChallengeNode(challengeId) {
- const challenges = typeof challengesTree === "undefined" ? jtree.TreeNode.fromDisk("ohayo/packages/challenge/challenges.tree") : new jtree.TreeNode(challengesTree)
- return challenges.nodeAt(challengeId - 1) || challenges.nodeAt(0)
- }
- getTileBodyStumpCode() {
- const challengeId = parseInt(this.getWord(1))
- const answer = this.getWord(2)
- const challengeNode = this._getChallengeNode(challengeId)
- const isCorrect = answer === challengeNode.get("answer")
- const theme = this.getTheme()
- const color = answer ? (isCorrect ? theme.successColor : theme.errorColor) : theme.warningColor
- const answerMessage = answer !== undefined ? (isCorrect ? "CORRECT!" : "Wrong.") : ""
- return \`h3 Challenge #\${challengeId}
- style color:\${color}
- br
- div \${challengeNode.evalTemplateString(\`Question: {question}\`)}
- class TileSelectable
- br
- input
- placeholder Enter your answer here. All answers are a number.
- value \${answer !== undefined ? answer : ""}
- style width: 300px;
- name 2
- changeCommand changeWordAndRenderCommand
- span \${answerMessage}
- style color: \${color};
- br
- div
- a See a solution
- clickCommand createProgramFromTemplateCommand
- value \${challengeId}\`
- }
- debugDumpNode
- description Dumps data from content or dump's first column input as 1 concatenated string.
- example Print a poem.
- samples.poem
- debug.dump
- extends abstractChartNode
- crux debug.dump
- string bodyStumpTemplate
- div
- style overflow: scroll; width: 100%; height: 100%; white-space: pre;
- bern
- {text}
- javascript
- _getCharacterLimit() {
- // Todo: great example of a scale test. I found it to be slow with:
- /*
- vega.sample movies.json
- web.dump
- So some tiles will have characterLimit, rowDisplayLimit, et cetera. And have "speedTestExamples" .
- */
- return 20000
- }
- getTileBodyStumpCode() {
- const text = this._getTextToDump()
- const characterLimit = this._getCharacterLimit()
- let sub = text.substr(0, characterLimit)
- if (text.length > characterLimit)
- // todo: Show standardized truncation warning
- sub = \`(Notice: Results truncated to \${characterLimit} characters) \` + sub
- return this.qFormat(this.bodyStumpTemplate, { text: sub || "No data to dump" })
- }
- _getTextToDump() {
- return this.getPipishInput()
- }
- webDumpNode
- frequency .02
- description Dump the raw text from the web request.
- extends debugDumpNode
- javascript
- _getTextToDump() {
- return this.getParent().getWillowHttpResponse ? this.getParent().getWillowHttpResponse().text : \`\${this.constructor.name} requires a parent web tile.\`
- }
- crux web.dump
- debugCommandsNode
- description Tools for ohayo developers.
- tags noPicker
- example Show debug commands
- debug.commands
- extends abstractChartNode
- crux debug.commands
- string bodyStumpTemplate
- a Run Speed Test on all Templates
- clickCommand _runTemplateSpeedTestCommand
- br
- a Open all Templates Command
- clickCommand _openAllTemplatesCommand
- br
- a Run Tile Quality Check
- clickCommand _doTileQualityCheckCommand
- javascript
- getTileBodyStumpCode() {
- return this.bodyStumpTemplate
- }
- _runTemplateSpeedTestCommand() {
- return this.getWebApp()._runTemplateSpeedTestCommand()
- }
- _openAllTemplatesCommand() {
- return this.getWebApp()._openAllTemplatesCommand()
- }
- _doTileQualityCheckCommand() {
- return this.getWebApp()._doTileQualityCheckCommand()
- }
- debugSleepNode
- cells tileKeywordCell millisecondsCell dummyDataSetIdCell
- description Sleeps for a few seconds and then loads data. Useful for testing and development.
- example Sleep for 1 second then load data
- debug.sleep 100 waterBill
- tables.basic
- string dummyDataSetName waterBill
- extends abstractChartNode
- crux debug.sleep
- javascript
- async fetchTableInputs() {
- const ms = parseInt(this.getWord(1) || 1)
- await this.getWebApp().sleepCommand(ms)
- return { rows: jtree.Utils.javascriptTableWithHeaderRowToObjects(DummyDataSets[this.getWord(2) || "stockPrice"]) }
- }
- debugNoOpNode
- cells tileKeywordCell
- catchAllCellType anyCell
- description A noop.
- boolean visible false
- crux debug.noop
- extends abstractChartNode
- debugThrowNode
- description Throws an error during load. Used for testing.
- cells tileKeywordCell tileEventNameCell
- example Throw an error
- samples.poem
- debug.throw fetchTableInputs
- extends abstractChartNode
- crux debug.throw
- javascript
- async fetchTableInputs() {
- this._throwIfMethodNameIs("fetchTableInputs")
- return {
- rows: []
- }
- }
- _throwIfMethodNameIs(name) {
- // Never throw if no word provided. That ensures it wont throw during testing.
- const lookingFor = this.getContent()
- if (lookingFor === name) throw new Error(\`DebugTile threw an error on purpose on event: "\${lookingFor}"\`)
- }
- getTileBodyStumpCode() {
- this._throwIfMethodNameIs("getTileBodyStumpCode")
- }
- treeComponentDidMount() {
- this._throwIfMethodNameIs("treeComponentDidMount")
- }
- treeComponentDidUpdate() {
- this._throwIfMethodNameIs("treeComponentDidUpdate")
- }
- dtjsBasicNode
- description A spreadsheet-like table.
- string tileSize 1200 500
- string tileCssScript ohayo/packages/dtjs/datatables.min.css
- string tileScript ohayo/packages/dtjs/datatables.min.js
- extends abstractChartNode
- crux dtjs.basic
- string bodyStumpTemplate
- div
- table
- class DataTable
- thead
- tr
- {headerRows}
- tbody
- {rows}
- string cellStumpTemplate
- td
- bern
- {box}
- string rowStumpTemplate
- tr
- {cols}
- javascript
- getTileBodyStumpCode() {
- const columnDefs = this.getParentOrDummyTable()
- .getColumnsArray()
- .slice(0, 10)
- const headerRows = this._getHeaderRowsStumpCode(columnDefs.map(col => col.getColumnName()))
- const rows = this._getTableRowsStumpCode(columnDefs)
- return this.qFormat(this.bodyStumpTemplate, { headerRows, rows })
- }
- _getHeaderRowsStumpCode(columns) {
- return columns.map(colName => \`th \${colName}\`).join("\\n")
- }
- _getTableRowsStumpCode(columns) {
- return this.getRowsWithRowDisplayLimit()
- .slice(0, 10)
- .map((row, index) => {
- const cols = columns
- .map(column => {
- const box = row.getRowHtmlSafeValue(column.getColumnName()) // todo: cache?
- return this.qFormat(this.cellStumpTemplate, { box })
- })
- .join("\\n")
- return this.qFormat(this.cellStumpTemplate, { cols })
- })
- .join("\\n")
- }
- treeComponentWillUnmount() {
- // cleanup
- }
- treeComponentDidUpdate() {
- super.treeComponentDidUpdate()
- const table = this.getParentOrDummyTable()
- const columnDefs = this.getParentOrDummyTable()
- .getColumnsArray()
- .slice(0, 10)
- const container = this.getStumpNode().findStumpNodeByChild("class DataTable")
- if (this.isNodeJs()) return undefined
- const width = this.getTileRunTimeWidth()
- const height = this.getTileRunTimeHeight()
- const shadow = container.getShadow()
- const el = shadow.getShadowElement()
- shadow.setShadowCss({ width, height })
- const rows = this.getRowsWithRowDisplayLimit()
- // todo: note, this is only works with jQuery
- jQuery.fn.dataTable.ext.errMode = "throw"
- this._dataTables = jQuery(el).DataTable({
- data: this.getRowsAsDataTableArrayWithHeader(rows, columnDefs.map(col => col.getColumnName())).slice(1),
- pageLength: 10,
- scrollY: height
- //"scrollCollapse": true,
- //"paging": false
- })
- }
- treeComponentDidMount() {
- this.treeComponentDidUpdate()
- }
- editorGalleryNode
- description Show a thumbnail of all the Ohayo documents in the input table.
- example
- editor.files
- editor.gallery
- string tileSize 1080 600
- string dummyDataSetName ohayoPrograms
- extends abstractChartNode
- crux editor.gallery
- string hakonTemplate
- .MiniMapTile
- .miniMap
- background {backgroundColor}
- width 120px
- height 90px
- margin 6px
- position relative
- overflow hidden
- box-sizing border-box
- display inline-block
- &:hover
- border 1px solid {boxShadow}
- &:active
- border 2px solid {boxShadow}
- .miniFooter
- font-size 12px
- position absolute
- bottom 0
- width 100%
- height 15px
- line-height 15px
- white-space nowrap
- text-align center
- .miniPreview
- position absolute
- width 100%
- height calc(100% - 15px)
- top 0
- overflow hidden
- div
- background {linkColor}
- height 5px
- margin 1px
- string miniStumpTemplate
- a
- class miniMap
- {onClick}
- {value}
- {href}
- div
- class miniPreview
- {theTiles}
- div {filename}
- class miniFooter
- string bodyStumpTemplate
- div
- class MiniMapTile
- {minis}
- string miniStyleTemplate
- div
- javascript
- async openFullPathInNewTabAndFocusCommand(url) {
- return this.getTab()
- .getRootNode()
- .openFullPathInNewTabAndFocusCommand(url)
- }
- _getMiniStumpCode(sourceCode, filename, permalink, width = 120, height = 75) {
- const ohayoProgram = new ohayoNode(sourceCode)
- const theTiles = ohayoProgram
- .getTiles()
- .filter(tile => tile.isVisible())
- .map(tile => this.qFormat(this.miniStyleTemplate, {}))
- .join("\\n")
- const onClick = permalink ? "clickCommand openFullPathInNewTabAndFocusCommand" : ""
- const value = permalink ? \`value \${permalink}\` : ""
- const href = permalink ? \`href \${permalink}\` : ""
- return this.qFormat(this.miniStumpTemplate, { filename, theTiles, onClick, value, href })
- }
- getTileBodyStumpCode() {
- // todo: cache.
- const minis = this.getRowsWithRowDisplayLimit()
- .map(row => this._getMiniStumpCode(row.getRowOriginalValue("bytes"), row.getRowOriginalValue("filename"), row.getRowOriginalValue("link")))
- .join("\\n")
- return this.qFormat(this.bodyStumpTemplate, { minis })
- }
- handsontableBasicNode
- description A spreadsheet-like table.
- string tileSize 1200 500
- string tileCssScript ohayo/packages/handsontable/handsontable.min.css
- string tileScript ohayo/packages/handsontable/handsontable.full.min.js
- string hakonTemplate
- .hot
- color black
- string bodyStumpTemplate
- div
- class hot
- javascript
- getTileBodyStumpCode() {
- return this.bodyStumpTemplate
- }
- // todo: allow editing
- treeComponentWillUnmount() {
- if (this._hot) this._hot.destroy()
- delete this._hot
- }
- treeComponentDidUpdate() {
- super.treeComponentDidUpdate()
- const table = this.getParentOrDummyTable()
- const columnDefs = table.getColumnsByImportance()
- const colNames = columnDefs.map(col => col.getColumnName())
- const rows = this.getRowsWithRowDisplayLimit()
- const data = this.getRowsAsDataTableArrayWithHeader(rows, colNames)
- const container = this.getStumpNode().findStumpNodeByChild("class hot")
- const app = this.getWebApp()
- if (this.isNodeJs()) return undefined
- const width = this.getTileRunTimeWidth()
- const height = this.getTileRunTimeHeight()
- this._hot = new Handsontable(container.getShadow().getShadowElement(), {
- data: data,
- rowHeaders: true,
- colHeaders: true,
- stretchH: "all",
- width,
- minSpareCols: 10,
- minSpareRows: 30,
- afterSelection: () => app.pauseShortcutListener(),
- afterDeselect: () => app.startShortcutListener(),
- height
- })
- return this._hot
- }
- treeComponentDidMount() {
- this.treeComponentDidUpdate()
- }
- extends abstractChartNode
- crux handsontable.basic
- abstractHtmlNode
- catchAllCellType htmlCell
- frequency 0
- description An HTML element
- inScope styleNode contentNode
- extends abstractChartNode
- string hakonTemplate
- .abstractHtmlNode
- code
- user-select text
- abstract
- string bodyStumpTemplate
- {tag}
- {style}
- {src}
- bern
- {content}
- javascript
- getTileFooterStumpCode() {
- return this.getTileMenuButtonStumpCode()
- }
- async fetchTableInputs() {
- return { rows: [{ text: this.getHtmlContent() }] }
- }
- getHtmlContent() {
- return this.getWordsFrom(2).join(" ") || "No html content to show."
- }
- getTag() {
- return this.getWord(1) || "div" // todo: verify this is legal tag.
- }
- getSrc() {
- return this.getSettingsStruct().src
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { tag: this.getTag(), style: this.style ? \`style \${this.style}\` : "", src: this.getSrc() ? \`src \${this.getSrc()}\` : "", content: this.getHtmlContent() || "" })
- }
- htmlTextNode
- description Displays fixed text in the given HTML element.
- cells tileKeywordCell htmlTextTagCell
- catchAllCellType htmlCell
- frequency .002
- extends abstractHtmlNode
- crux html.text
- htmlPrintAsNode
- description Displays input table in the given HTML element.
- cells tileKeywordCell htmlTextTagCell
- frequency .002
- javascript
- getHtmlContent() {
- return this.getPipishInput()
- }
- extends abstractHtmlNode
- crux html.printAs
- abstractHTMLFixedTagTileNode
- abstract
- extends abstractHtmlNode
- javascript
- getHtmlContent() {
- return this.getContent()
- }
- getTag() {
- return this.htmlTagName
- }
- htmlH1Node
- catchAllCellType htmlCell
- description Displays an H1 Header with fixed text
- example A title
- html.h1 Hello world
- frequency .002
- string tileSize 600 75
- string htmlTagName h1
- string style text-align:center;
- extends abstractHTMLFixedTagTileNode
- crux html.h1
- abstractHTMLContentIsSrcTileNode
- abstract
- extends abstractHTMLFixedTagTileNode
- javascript
- getHtmlContent() {
- return ""
- }
- getSrc() {
- return this.getContent() || super.getSrc()
- }
- htmlImgNode
- description Displays an image from given url.
- cells tileKeywordCell urlCell
- frequency .002
- string htmlTagName img
- string style width:100%;
- extends abstractHTMLContentIsSrcTileNode
- crux html.img
- htmlIframeNode
- description Displays an iframe from given url.
- cells tileKeywordCell urlCell
- string htmlTagName iframe
- extends abstractHTMLContentIsSrcTileNode
- crux html.iframe
- htmlCustomNode
- description Display custom HTML.
- example Hello world
- html.custom
- content
-
Hello world - inScope contentNode
- extends abstractChartNode
- crux html.custom
- string bodyStumpTemplate
- div
- bern
- {content}
- javascript
- getTileBodyStumpCode() {
- // https://meta.stackexchange.com/questions/1777/what-html-tags-are-allowed-on-stack-exchange-sites
- // todo: sanitize tags
- const contentNode = this.getNode("content")
- const content = contentNode ? contentNode.childrenToString() : "No HTML content to show"
- return this.qFormat(this.bodyStumpTemplate, { content })
- }
- iconsIconNode
- extends abstractChartNode
- abstract
- iconsHumanNode
- description Assuming each row in your data represents a human, creates a human icon.
- example
- samples.patients
- icons.human
- inScope genderColumnNode headSizeNode
- string columnPredictionHints
- headSize isString=false
- genderColumn isString=true
- string bodyStumpTemplate
- div
- bern
- {bern}
- javascript
- getTileBodyStumpCode() {
- // Now, what if there is no input table?
- const table = this.getParentOrDummyTable()
- const rows = table.getRows()
- // Now, what if we are using dummy input table?
- const headSizeColumn = this.getSettingsStruct().headSize
- const genderColumn = this.getSettingsStruct().genderColumn
- const reducts = table.getColumnByName(headSizeColumn).getReductions()
- const headColMax = reducts.max
- const bern = rows
- .map(row => {
- const typedRow = row.rowToObjectWithOnlyNativeJavascriptTypes()
- const value = typedRow[headSizeColumn]
- // TODO: ADD TYPINGS
- const genderVal = typedRow[genderColumn].toLowerCase()
- const gender = genderVal === "male" ? "blue" : "pink"
- let character = "O"
- let percent = value / headColMax
- if (isNaN(value)) {
- character = "x"
- percent = reducts.median / headColMax
- }
- const title = row.getHoverTitle()
- percent = Math.round(18 * percent)
- return \`\${character} \`
- })
- .join(" ")
- return this.qFormat(this.bodyStumpTemplate, { bern: bern })
- }
- string dummyDataSetName patients
- extends iconsIconNode
- crux icons.human
- iconsCircleNode
- description Displays a simple icon for each row of your data.
- example
- samples.iris
- icons.circle
- radius Petal.Length
- inScope radiusNode
- string columnPredictionHints
- radius isString=false
- string dummyDataSetName playerGoals
- extends iconsIconNode
- crux icons.circle
- string bodyStumpTemplate
- div
- bern
- {bern}
- javascript
- getTileBodyStumpCode() {
- const column = this.getSettingsStruct().radius
- const bern = this.getParentOrDummyTable()
- .getRows()
- .map(row => \`O \`)
- .join(" ")
- return this.qFormat(this.bodyStumpTemplate, { bern: bern })
- }
- listBasicNode
- catchAllCellType columnNameCell
- description Show 1 column as a text list.
- example List of world's telescopes
- samples.telescopes
- list.basic
- inScope labelNode
- string bodyStumpTemplate
- ol
- {items}
- string listItemStumpTemplate
- li
- span {label}
- javascript
- _getListItem(label) {
- return this.qFormat(this.listItemStumpTemplate, { label })
- }
- _getLabelColumnName() {
- // todo: more automatic! Need to fix our columns/keywords issues
- return this.getWord(1) || this.getSettingsStruct().label
- }
- getTileBodyStumpCode() {
- const labelColumnName = this._getLabelColumnName()
- const items = this.getRowsWithRowDisplayLimit()
- .map(row => this._getListItem(jtree.Utils.stripHtml(row.getRowOriginalValue(labelColumnName)), row))
- .join("\\n")
- return this.qFormat(this.bodyStumpTemplate, { items })
- }
- string tileSize 400 400
- string dummyDataSetName telescopes
- string columnPredictionHints
- label getTitlePotential
- extends abstractChartNode
- crux list.basic
- listLinksNode
- description Show 1 column as a list of links, using 1 column for the url.
- catchAllCellType columnNameCell
- example List of world's telescopes with links
- samples.telescopes
- list.links
- inScope labelNode linkNode
- string dummyDataSetName telescopes
- string listItemHakonTemplate
- li
- a {label}
- href {link}
- javascript
- _getUrlColumnName() {
- // todo: more automatic! Need to fix our columns/keywords issues
- return this.getWord(2) || this.getSettingsStruct().link
- }
- _getListItem(label, row) {
- const urlColumnName = this._getUrlColumnName()
- if (!urlColumnName) return super._getListItem(label, row)
- return this.qFormat(this.listItemHakonTemplate, { label, link: jtree.Utils.stripHtml(row.getRowOriginalValue(urlColumnName)) })
- }
- string columnPredictionHints
- label getTitlePotential
- link isLink
- extends listBasicNode
- crux list.links
- markdownToHtmlNode
- description Displays Markdown rendered as HTML.
- example Show a text editor and some rendered Markdown.
- data.inline
- parser text
- content
- # My header
- ## My subheader
-
- Hello world
- markdown.toHtml
- inScope contentNode
- string bodyStumpTemplate
- div
- class TileSelectable
- bern
- {md}
- javascript
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { md: marked(this.getPipishInput()) })
- }
- string tileSize 400 400
- string dummyDataSetName markdown
- extends abstractChartNode
- crux markdown.toHtml
- abstractRoughJsChartNode
- description Create sketchy/hand-drawn styled charts https://github.com/jwilber/roughViz
- string tileScript ohayo/packages/roughjs/roughviz.min.js
- extends abstractChartNode
- abstract
- catchAllCellType titleCell
- inScope roughnessNode
- javascript
- _getRoughId() {
- return \`rough\${this._getUid()}\`
- }
- treeComponentDidUpdate() {
- super.treeComponentDidUpdate()
- if (this.isNodeJs()) return undefined
- this._drawRough()
- }
- treeComponentDidMount() {
- this.treeComponentDidUpdate()
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { id: this._getRoughId() })
- }
- get _roughness() {
- const value = this.get("roughness")
- return value ? parseInt(value) : 1
- }
- _getOptions() {
- return {}
- }
- _drawRough() {
- const colors = this.get("colors") ? this.get("colors").split(" ") : undefined
- const options = Object.assign(this._getOptions(), {
- title: this.getContent() || "",
- element: "#" + this._getRoughId(),
- roughness: this._roughness,
- width: this.getTileRunTimeWidth(),
- height: this.getTileRunTimeHeight(),
- colors,
- data: this._getRoughData()
- })
- const roughEl = new roughViz[this.roughChartType](options)
- }
- _getValues(settingName) {
- const columName = this.mapSettingNamesToColumnNames([settingName])[0]
- return this.getParentOrDummyTable()
- .getColumnByName(columName)
- .getValues()
- }
- string bodyStumpTemplate
- div
- id {id}
- abstractRoughJsLabelValueNode
- extends abstractRoughJsChartNode
- string dummyDataSetName stockPrice
- abstract
- string columnPredictionHints
- value getPrimitiveTypeName=number
- javascript
- _getRoughData() {
- const data = { labels: this._getValues("label"), values: this._getValues("value") }
- console.log(data)
- return data
- }
- roughJsBarNode
- crux roughjs.bar
- inScope labelNode valueNode
- extends abstractRoughJsLabelValueNode
- string roughChartType Bar
- example
- samples.waterBill
- roughjs.bar Past Year's Water Bill
- label PaidOn
- value Amount
- roughJsPieNode
- inScope labelNode valueNode
- crux roughjs.pie
- extends abstractRoughJsLabelValueNode
- string roughChartType Pie
- roughJsLineNode
- inScope colorsNode
- crux roughjs.line
- extends abstractRoughJsChartNode
- string roughChartType Line
- example
- samples.waterBill
- roughjs.line Water Bill
- javascript
- _getNumericColumns() {
- return Object.values(this.getParentOrDummyTable().getColumnsMap()).filter(col => col.isNumeric())
- }
- _getRoughData() {
- const data = {}
- const numerics = this._getNumericColumns()
- numerics.forEach(col => {
- data[col.getColumnName()] = col.getValues()
- })
- return data
- }
- _getOptions() {
- const options = {}
- const numerics = this._getNumericColumns()
- numerics.forEach((col, index) => {
- options["y" + index] = col.getColumnName()
- })
- return options
- }
- abstractShowTileNode
- cells tileKeywordCell columnNameCell
- catchAllCellType titleCell
- frequency .02
- example A dashboard for a Seattle family's water bill.
- samples.waterBill
- hidden
- vega.scatter
- tables.basic
- show.mean Amount
- show.median Amount
- show.sum Amount
- show.min Amount
- show.max Amount
- string tileSize 140 120
- string dummyDataSetName stockPrice
- extends abstractChartNode
- abstract
- string hakonTemplate
- .abstractShowTileNode
- h3
- text-align center
- h6
- text-align center
- height 40px
- overflow hidden
- string bodyStumpTemplate
- h6 {title}
- h3 {number}
- javascript
- getTileBodyStumpCode() {
- const columnName = this.getWord(1)
- if (!columnName) return \`No data for \${this.getFirstWord()}\`
- const table = this.getParentOrDummyTable()
- const col = table.getTableColumnByName(columnName)
- if (!col) {
- console.log(\`No column named \${columnName}\`)
- return ""
- }
- const reductionName = this.reductionName || this.getWord(0).split(".")[1]
- const title = this.getWordsFrom(2).join(" ") || [columnName, reductionName].join(" ")
- const number = this.toDisplayString(col.getReductions()[reductionName], columnName)
- return this.qFormat(this.bodyStumpTemplate, { title, number })
- }
- showRowCountNode
- catchAllCellType titleCell
- description Show the total number of rows
- frequency .02
- string tileSize 140 120
- string dummyDataSetName stockPrice
- cells tileKeywordCell
- extends abstractShowTileNode
- string defaultTitle Total rows
- crux show.rowCount
- javascript
- getTileBodyStumpCode() {
- const title = this.getWordsFrom(1).join(" ") || this.defaultTitle
- return this.qFormat(this.bodyStumpTemplate, { title, number: this._getNumber() })
- }
- _getNumber() {
- return this.getParentOrDummyTable().getRowCount()
- }
- showColumnCountNode
- extends showRowCountNode
- string defaultTitle Total columns
- description Show the total number of columns
- crux show.columnCount
- javascript
- _getNumber() {
- return this.getParentOrDummyTable().getColumnNames().length
- }
- showStaticNode
- description Show a hard coded number
- extends abstractShowTileNode
- example
- show.static 20 Sales
- cells tileKeywordCell numberCell
- catchAllCellType titleCell
- crux show.static
- javascript
- getTileBodyStumpCode() {
- const title = this.getWordsFrom(2).join(" ")
- return this.qFormat(this.bodyStumpTemplate, { title, number: this.getWord(1) || "" })
- }
- showValueNode
- description Show the value of a column with 1 row
- extends abstractShowTileNode
- crux show.value
- string reductionName median
- showMedianNode
- description Show the median value of a column
- extends abstractShowTileNode
- crux show.median
- showSumNode
- description Show the sum of a column
- extends abstractShowTileNode
- crux show.sum
- showMeanNode
- description Show the mean of a column
- extends abstractShowTileNode
- crux show.mean
- showMinNode
- description Show the min value of a column
- extends abstractShowTileNode
- crux show.min
- showMaxNode
- description Show the max value of a column
- extends abstractShowTileNode
- crux show.max
- tablesBasicNode
- frequency .1
- description Basic table with sorting.
- example Basic table with Iris data
- samples.iris
- tables.basic
- inScope columnLimitNode
- int rowDisplayLimit 100
- int columnLimit 20
- string tileSize 750 300
- todo added the below to allow custom body styling in tables
- string customBodyStyle padding:0px;
- string hakonTemplate
- .tablesBasicNode
- font-size 14px
- box-sizing border-box
- {enableTextSelect1}
- table
- width 100%
- tr
- white-space nowrap
- padding 0
- td
- border 1px solid {lineColor}
- tr:nth-child(even)
- background-color {veryLightGrey}
- td,th
- padding 2px 3px
- text-align left
- overflow hidden
- text-overflow ellipsis
- max-width 250px
- td:hover,th:hover
- overflow visible
- td:first-child,th:first-child
- padding-left 5px
- color {greyish}
- width 60px
- th
- cursor pointer
- background-color {lightGrey}
- border 1px solid {lineColor}
- border-bottom-color {greyish}
- string cellStumpTemplate
- td
- bern
- {content}
- string cellLinkStumpTemplate
- td
- a
- href {content}
- bern
- {content}
- string rowStumpTemplate
- tr
- class tableRow
- value {value}
- td {number}
- {cols}
- javascript
- _getTableRowsStumpCode(columns) {
- return this.getRowsWithRowDisplayLimit()
- .map((row, index) => {
- const cols = columns
- .map(column => {
- return this.qFormat(column.isLink() ? this.cellLinkStumpTemplate : this.cellStumpTemplate, { content: row.getRowHtmlSafeValue(column.getColumnName()) })
- })
- .join("\\n")
- return this.qFormat(this.rowStumpTemplate, { number: index + 1, value: row.getPuid(), cols })
- })
- .join("\\n")
- }
- _getHeaderRowsStumpCode(columns) {
- // todo: can we get a copy column command?
- return ["Row"]
- .concat(columns)
- .map(colName => this.qFormat(this.headerRowStumpTemplate, { colName }))
- .join("\\n")
- }
- getTileBodyStumpCode() {
- const tileStruct = this.getSettingsStruct()
- const table = this.getParentOrDummyTable()
- if (table.isBlankTable()) return \`div No data to show\`
- let columnDefs = tileStruct.columnOrder === "importance" ? table.getColumnsByImportance() : table.getColumnsArray()
- columnDefs = columnDefs.slice(0, tileStruct.columnLimit || this.columnLimit)
- const columnNames = columnDefs.map(col => col.getColumnName())
- // todo: if the types for a column are all equal, add a total row to the bottom.
- // todo: if the types for a row are all equal, add a total column to the right.
- const headerRows = this._getHeaderRowsStumpCode(columnNames)
- const bodyRows = this._getTableRowsStumpCode(columnDefs)
- return this.qFormat(this.bodyStumpTemplate, { headerRows, bodyRows })
- }
- string headerRowStumpTemplate
- th
- value {colName}
- span {colName}
- value {colName}
- string bodyStumpTemplate
- div
- class tablesBasicNode
- table
- thead
- {headerRows}
- tbody
- {bodyRows}
- extends abstractChartNode
- crux tables.basic
- tablesInterestingNode
- frequency .01
- description Prints most interesting columns.
- string columnOrder importance
- extends tablesBasicNode
- crux tables.interesting
- tablesDumpNode
- description Prints data with no formatting or column reordering.
- frequency .01
- string columnOrder default
- extends tablesBasicNode
- crux tables.dump
- textWordcloudNode
- description Turn text into a word cloud.
- inScope columnNode countNode
- example A poem analyzed
- samples.poem
- text.wordCount
- text.wordcloud
- string bodyStumpTemplate
- div
- class divWhereWordCloudWillGo
- style height: 300px;
- javascript
- getTileBodyStumpCode() {
- return this.bodyStumpTemplate
- }
- _getAllWords() {
- return this.getRequiredTableWithHeader(["name", "count"])
- }
- treeComponentDidUpdate() {
- this._draw()
- }
- treeComponentDidMount() {
- this._draw()
- }
- _draw() {
- if (this.isNodeJs()) return undefined
- const tileStruct = this.getSettingsStruct()
- const words = this._getAllWords()
- if (!words.length) return
- words.shift() // drop header
- const shadow = this.getStumpNode().getShadow()
- const width = shadow.getShadowOuterWidth()
- const powConstant = 10 / Math.log(words.length) // breaks if too hgih.
- const options = {
- list: words.map(word => [word[0], word[1]]),
- shuffle: false,
- gridSize: Math.round((16 * width) / 1024),
- weightFactor: size => (Math.pow(size, powConstant) * width) / 1024,
- backgroundColor: "transparent",
- random: jtree.Utils.makeSemiRandomFn(),
- wait: 0
- }
- Object.assign(options, tileStruct)
- const element = this.getStumpNode()
- .findStumpNodeByChild("class divWhereWordCloudWillGo")
- .getShadow()
- .getShadowElement()
- WordCloud(element, options)
- }
- string tileScript ohayo/packages/text/wordcloud2.min.js
- string dummyDataSetName wordCounts
- string columnPredictionHints
- name isString=true
- count isString=false
- extends abstractChartNode
- crux text.wordcloud
- treenotation3dNode
- description A 3D visualization of Ohayo source code.
- example 3D vis of a Ohayo Program.
- samples.treeProgram
- treenotation.3d
- inScope contentNode sizeNode cameraPositionNode
- string tileSize 800 500
- string tileScript ohayo/packages/treenotation/vis.min.js
- string dummyDataSetName treeProgram
- extends abstractChartNode
- crux treenotation.3d
- javascript
- getTileBodyStumpCode() {
- return \`div
- class visjs\`
- }
- treeComponentDidMount() {
- super.treeComponentDidMount()
- this.treeComponentDidUpdate()
- }
- // Called when the Visualization API is loaded.
- treeComponentDidUpdate() {
- super.treeComponentDidUpdate()
- if (this.isNodeJs()) return undefined
- try {
- this._tryVis()
- } catch (err) {
- // log error
- console.error(err)
- }
- }
- _tryVis() {
- const tileStruct = this.getSettingsStruct()
- const source = this.getPipishInput()
- const app = this.getWebApp()
- const program = new ohayoNode(source)
- const rows = this._treeTo3D(program)
- // Create and populate a data table.
- const data = new vis.DataSet()
- rows.forEach(row => data.add(row))
- const dotSize = tileStruct.size
- const showGrid = tileStruct.showGrid
- // specify options
- // docs: http://visjs.org/docs/graph3d/
- const cameraPositionNode = this.getNode("cameraPosition") || new jtree.TreeNode("cameraPosition 4 .1 1.5").getNode("cameraPosition")
- const distance = parseFloat(cameraPositionNode.getWord(1))
- const horizontal = parseFloat(cameraPositionNode.getWord(2))
- const vertical = parseFloat(cameraPositionNode.getWord(3))
- const options = {
- width: this.getTileRunTimeWidth() + "px",
- height: this.getTileRunTimeHeight() - 80 + "px",
- style: "dot-color", // dot?
- showPerspective: false,
- showLegend: false,
- showShadow: false,
- keepAspectRatio: true,
- xStep: 1,
- yStep: 1,
- zStep: 1,
- zMax: 5,
- showGrid: true,
- showZAxis: false,
- showXAxis: false,
- showYAxis: false,
- dotSizeRatio: dotSize,
- dotSizeMinFraction: 1,
- cameraPosition: {
- distance: distance,
- horizontal: horizontal,
- vertical: vertical
- },
- verticalRatio: 1.0,
- // parameter point contains properties x, y, z, and data
- // data is the original object passed to the point constructor
- tooltip: point => point.data.line
- }
- // create a graph3d
- const element = this.getStumpNode()
- .findStumpNodeByChild("class visjs")
- .getShadow()
- .getShadowElement()
- this._graph3d = new vis.Graph3d(element, data, options)
- const throttled = lodash.throttle(evt => this._onCameraPositionChange(evt), 100)
- this._graph3d.on("cameraPositionChange", throttled)
- }
- async _onCameraPositionChange(evt) {
- // todo: throttle
- const pos = this._graph3d.getCameraPosition()
- const str = \`\${pos.distance} \${pos.horizontal} \${pos.vertical}\`
- this.touchNode("cameraPosition").setContent(str)
- await this.getTab().autosaveTab()
- }
- _treeTo3D(program) {
- // getCameraPosition
- // setCameraPosition
- // onCameraPositionChange
- const theme = this.getWebApp().getTheme()
- // todo: use node type for color.
- const tagMap = {}
- const tagTree = new jtree.TreeNode(program.toCellTypeTree())
- // const outlineFn = node => node.getIndex()
- // use language to get dict, use dict to get type overlay to get tag types.
- const randomFn = jtree.Utils.makeSemiRandomFn()
- const makeColor = word => {
- if (!tagMap[word]) tagMap[word] = randomFn() // todo: give word types certain colors. green for keword, red for error, etc
- const color = tagMap[word]
- //console.log(color)
- return color
- }
- const points = []
- const nodeToPoint = (node, index) => {
- const nodePath = node.getPathVector(program)
- const tagNode = tagTree.nodeAt(nodePath)
- node.getWords().forEach((word, wordIndex) => {
- const wordType = tagNode.getWord(wordIndex)
- const colorNumber = makeColor(wordType)
- const xcc = node.getIndentLevel(program) + wordIndex
- const ycc = -node.getLineNumber()
- const zcc = 0
- points.push({
- x: xcc,
- y: ycc,
- z: zcc,
- line: \`cellType: \${wordType} | word: \${word}\`,
- style: colorNumber
- })
- })
- }
- program.getTopDownArray().forEach(nodeToPoint)
- return points
- }
- treenotationOutlineNode
- description A simple pretty text-only view of a Tree Notation document.
- example Outer space
- samples.outerSpace
- treenotation.outline
- string dummyDataSetName outerSpace
- string tileSize 800 500
- extends abstractChartNode
- crux treenotation.outline
- string bodyStumpTemplate
- pre
- style overflow: scroll; width: 100%; height: 100%; margin: 0; box-sizing: border-box; font-family: monospace; line-height: 13px;
- bern
- {bern}
- javascript
- _getTheBern() {
- return new jtree.TreeNode(this.getPipishInput()).toOutline()
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { bern: this._getTheBern() })
- }
- treenotationDotlineNode
- description A simple pretty icon-only visualization of the structure of a Tree Notation doc.
- example Outer space
- samples.outerSpace
- treenotation.dotline
- boolean dots true
- string dummyDataSetName outerSpace
- javascript
- _getTheBern() {
- return new jtree.TreeNode(this.getPipishInput()).toMappedOutline(
- node =>
- "o" +
- node
- .getLine()
- .split(" ")
- .map(word => "º")
- .join("")
- )
- }
- extends treenotationOutlineNode
- crux treenotation.dotline
- abstractVegaNode
- frequency .1
- catchAllCellType titleCell
- string tileSize 800 300
- string tileScript ohayo/packages/vega/vega.combined.min.js
- string dummyDataSetName stockPrice
- string markName bar
- extends abstractChartNode
- abstract
- string bodyStumpTemplate
- div
- class divForExternalLibrary
- javascript
- // todo: I don't think vega handles . in column names.
- getTileBodyStumpCode() {
- return this.bodyStumpTemplate
- }
- _getColumnToField(columnName) {
- if (!columnName) return undefined
- const columnsMap = this.getParentOrDummyTable().getColumnsMap()
- const col = columnsMap[columnName]
- const obj = { field: columnName, type: col.getVegaType() }
- if (col.isTemporal()) {
- const timeUnit = col.getVegaTimeUnit()
- if (timeUnit) obj.timeUnit = timeUnit
- }
- return obj
- }
- _getElementForVega() {
- return this.getStumpNode()
- .findStumpNodeByChild("class divForExternalLibrary")
- .getShadow()
- .getShadowElement()
- }
- async _drawVega() {
- // todo: don't rerun this if we dont need to.
- await vegaEmbed(this._getElementForVega(), this._getVegaSpec())
- }
- treeComponentDidUpdate() {
- super.treeComponentDidUpdate()
- if (this.isNodeJs()) return undefined
- this._drawVega()
- }
- treeComponentDidMount() {
- this.treeComponentDidUpdate()
- }
- _getVegaData() {
- return {
- values: this.getParentOrDummyTable()
- .cloneNativeJavascriptTypedRows()
- .slice(0, this._getRowDisplayLimit())
- }
- }
- _getVegaTitle() {
- return this.getContent()
- }
- _getVegaSpec() {
- return {
- description: "A simple bar chart with embedded data.",
- data: this._getVegaData(),
- width: this.getTileRunTimeWidth(),
- height: this.getTileRunTimeHeight(),
- mark: this._getVegaMarkObj(),
- encoding: this._getEncodingMap(),
- transform: this._getVegaTransform(),
- title: this._getVegaTitle(),
- config: this._getVegaConfig()
- }
- }
- _getVegaTransform() {
- return undefined
- }
- _getVegaConfig() {
- return undefined
- }
- _getEncodingMap() {
- return {}
- }
- // todo: add type
- _getVegaMarkObj() {
- return { type: this._getVegaMark(), tooltip: { content: "data" } }
- }
- _getVegaMark() {
- return this.markName
- }
- vegaBarNode
- description A bar chart
- inScope xColumnNode yColumnNode colorColumnNode shapeColumnNode
- javascript
- _getEncodingMap() {
- const columnNames = this.mapSettingNamesToColumnNames([this.xColumnKey, this.yColumnKey, this.colorColumnKey])
- return {
- x: this._getColumnToField(columnNames[0]),
- y: this._getColumnToField(columnNames[1]),
- color: this._getColumnToField(columnNames[2])
- }
- }
- string columnPredictionHints
- xColumn
- yColumn isString=false,!xColumn
- extends abstractVegaNode
- crux vega.bar
- vegaLineNode
- description A line chart
- string markName line
- extends vegaBarNode
- crux vega.line
- vegaAreaNode
- description An area chart
- string markName area
- extends vegaLineNode
- crux vega.area
- vegaScatterNode
- description A scatterplot
- string markName point
- extends vegaBarNode
- crux vega.scatter
- javascript
- _getEncodingMap() {
- const columnNames = this.mapSettingNamesToColumnNames([this.xColumnKey, this.yColumnKey, this.colorColumnKey, this.shapeColumnKey])
- return {
- x: this._getColumnToField(columnNames[0]),
- y: this._getColumnToField(columnNames[1]),
- color: this._getColumnToField(columnNames[2]),
- shape: this._getColumnToField(columnNames[3])
- }
- }
- vegaBubbleNode
- description A bubble plot
- inScope sizeColumnNode colorColumnNode
- string markName circle
- string dummyDataSetName gapMinder
- string columnPredictionHints
- sizeColumn isString=false
- xColumn isString=false
- extends vegaScatterNode
- crux vega.bubble
- javascript
- _getEncodingMap() {
- const columnNames = this.mapSettingNamesToColumnNames([this.xColumnKey, this.yColumnKey, this.sizeColumnKey, this.colorColumnKey])
- return {
- y: {
- field: columnNames[1],
- type: "quantitative",
- scale: { zero: false },
- axis: { minExtent: 30 }
- },
- x: this._getColumnToField(columnNames[0]),
- size: { field: columnNames[2], type: "quantitative" },
- color: { value: "#000" }
- }
- }
- vegaEmojiNode
- description A bar chart with emojis
- frequency .001
- inScope yColumnNode emojiColumnNode
- string columnPredictionHints
- emojiColumn isString=true
- yColumn isString=false
- string dummyDataSetName emojis
- javascript
- _getVegaConfig() {
- return { view: { stroke: "" } }
- }
- _getVegaMark() {
- return { type: "text", baseline: "middle" }
- }
- _getEncodingMap() {
- const columnNames = this.mapSettingNamesToColumnNames([this.yColumnKey, "emoji"])
- return {
- x: { field: columnNames[1], type: "nominal", axis: null },
- y: { field: columnNames[0], type: "quantitative", axis: null, sort: null },
- text: { field: columnNames[1], type: "nominal" },
- size: { value: 65 }
- }
- }
- extends vegaBarNode
- crux vega.emoji
- vegaHistogramNode
- description A histogram
- inScope xColumnNode
- javascript
- _getEncodingMap() {
- const columnName = this.getContent() || this.mapSettingNamesToColumnNames([this.xColumnKey])[0]
- return {
- x: {
- bin: true,
- field: columnName,
- type: "quantitative"
- },
- y: {
- aggregate: "count",
- type: "quantitative"
- }
- }
- }
- string columnPredictionHints
- xColumn isString=false
- string dummyDataSetName wordCounts
- extends abstractVegaNode
- crux vega.histogram
- vegaExampleNode
- description Shows a chart from the Vega Example Gallery
- frequency .001
- cells tileKeywordCell vegaExampleNameCell
- example
- vega.example trellis_anscombe
- extends abstractVegaNode
- crux vega.example
- javascript
- _getVegaSpec() {
- return this._spec
- }
- async _fetchSpec() {
- // todo: localtesting.
- if (this.isNodeJs()) return undefined
- const exampleName = this.getContent() || "area" // todo: pull this default from the gram?
- const url = \`ohayo/packages/vega/ignore/vega-lite/examples/compiled/\${exampleName}.vg.json\`
- const res = await this.getWebApp()
- .getWillowBrowser()
- .httpGetUrl(url)
- const spec = JSON.parse(res.text)
- // rewrite data urls
- spec.data.forEach(row => {
- if (row.url) row.url = row.url.replace("data/", "packages/vega/datasets/")
- })
- this._spec = spec
- return spec
- }
- // todo: clean this up.
- async fetchTableInputs() {
- const spec = await this._fetchSpec()
- if (this.isNodeJs()) return { rows: [] }
- const el = jQuery("
")[0]
- const embedded = await vegaEmbed(el, spec)
- const rows = await this._getVegaPostTransformOutputRows(spec, embedded)
- return { rows: rows }
- }
- async _getVegaPostTransformOutputRows(spec, embedded) {
- const tableName = spec.data[0] && spec.data[0].name
- if (tableName) return embedded.view.data(tableName)
- // const values = spec.data.values
- // if (values && values.entries) return Array.from(values.entries())
- // if (typeof values === "function") return []
- // else if (values) return values
- return []
- }
- DidYouMeanTileNode
- tags noPicker
- description Provides suggestions for misspelled tiles.
- extends abstractTileTreeComponentNode
- crux tiles.didyoumean
- string bodyStumpTemplate
- div
- span No tile '{input}' found. Line {lineNo}. Did you mean
- a {closestTile}
- collapse
- tabindex -1
- value {closestTile}
- clickCommand changeTileTypeCommand
- span ?
- javascript
- getTileBodyStumpCode() {
- const input = this.getFirstWord()
- const lineNo = this.getLineNumber()
- const closestTile = jtree.Utils.didYouMean(
- input,
- this.getRootNode()
- .getHandGrammarProgram()
- .getTopNodeTypeDefinitions()
- .map(def => def.get("crux"))
- )
- if (!closestTile) {
- if (!input) return \`div Your program has a blank line on line \${lineNo}.\`
- return \`div No tile '\${input}' found.\`
- }
- return this.qFormat(this.bodyStumpTemplate, { input, lineNo, closestTile })
- }
- getErrors() {
- return [new jtree.UnknownNodeTypeError(this)]
- }
- abstractDocTileNode
- tags noPicker
- cells tileKeywordCell
- extends abstractTileTreeComponentNode
- abstract
- string bodyStumpTemplate
- {tagName}
- bern
- {content}
- string tileStumpTemplate
- div
- class {classes}
- id {id}
- div
- class TileBody
- {body}
- div
- class TileFooter
- {footer}
- javascript
- _getBody() {
- return this.qFormat(this.bodyStumpTemplate, { content: this.getContent() || "", tagName: this.tagName })
- }
- toStumpCode() {
- return this.qFormat(this.tileStumpTemplate, { classes: this.getCssClassNames().join(" "), footer: this.getTileMenuButtonStumpCode(), id: this.getTreeComponentId(), body: this._getBody() })
- }
- docTitleNode
- catchAllCellType stringCell
- description A title
- example A doc
- doc.title A Tale of Two Cities
- string tileSize 600 75
- extends abstractDocTileNode
- cells tileKeywordCell
- crux doc.title
- string tagName h1
- docSubtitleNode
- extends docTitleNode
- description A subheader
- string tagName h2
- crux doc.subtitle
- docSectionNode
- description A section containing subtitles, paragraphs, code blocks, etc.
- crux doc.section
- extends abstractDocTileNode
- inScope abstractDocSectionComponentNode
- javascript
- _getBody() {
- return this.compile()
- }
- _getCompiledLine() {
- return ""
- }
- example
- doc.section
- subtitle Subtitle
- paragraph Paragraph
- code python
- # some code
- docReferenceNode
- extends abstractDocTileNode
- crux doc.ref
- cells tileKeywordCell referenceIdCell
- inScope docReferenceUrlNode
- string tagName p
- example
- doc.ref someRefId
- url https://en.wikipedia.org/wiki/Note_(typography)
- description A reference to an external source
- docCommentNode
- description A comment node
- cells commentKeywordCell
- extends abstractTileTreeComponentNode
- boolean visible false
- frequency 0
- example An example program with comments
- doc.comment get iris data
- samples.iris
- doc.comment filter is
- filter.where Species = virginica
- doc.comment display results
- tables.basic
- catchAllCellType commentCell
- catchAllNodeType commentLineNode
- crux doc.comment
- docToolingNode
- extends docCommentNode
- crux doc.tooling
- abstractProviderNode
- string tileSize 140 60
- extends abstractTileTreeComponentNode
- abstract
- string tileStumpTemplate
- div
- class {classes}
- id {id}
- div
- class TileBody
- {body}
- div
- class TileFooter
- {footer}
- string tileFooterTemplate
- span Rows Out: {outputCount} Columns Out: {columnCount} Time: {time}s Parser: {parserId} {errorMessageHtml}
- {tileMenuButton}
- javascript
- getTileFooterStumpCode() {
- const table = this.getOutputOrInputTable()
- const time = (this.getTimeToLoad() / 1000).toFixed(1)
- const parserId = this.getParserId() || "?"
- return this.qFormat(this.tileFooterTemplate, {
- parserId,
- errorMessageHtml: this.getErrorMessageHtml() || "",
- time,
- outputCount: table.getRowCount(),
- columnCount: table.getColumnCount(),
- tileMenuButton: this.getTileMenuButtonStumpCode()
- })
- }
- getRowClass() {
- return Row
- }
- getTileBodyStumpCode() {
- const description = this._getDescription()
- return "div " + (description ? jtree.Utils.linkify(description) : "")
- }
- _getDescription() {
- return this.getDefinition().get("description")
- }
- toStumpCode() {
- return this.qFormat(this.tileStumpTemplate, { classes: this.getCssClassNames().join(" "), id: this.getTreeComponentId(), body: this._getBodyStumpCodeCache(), footer: this.getTileFooterStumpCode() })
- }
- getParserId() {
- return this.getSettingsStruct().parser
- }
- async fetchTableInputs() {
- return {
- rows: []
- }
- }
- async _execute() {
- const timeLoadStarted = this._getProcessTimeInMilliseconds()
- this._timeLastLoadStarted = timeLoadStarted
- const fetchedTableInputs = await this.fetchTableInputs()
- // If a new request happened after this one, abort this one.
- // todo: what happens to children?
- // todo: add testing for this.
- if (this._timeLastLoadStarted !== timeLoadStarted) {
- console.log("superceded")
- return null
- }
- this._outputTable = new Table(fetchedTableInputs.rows, fetchedTableInputs.columnDefinitions, this.getRowClass())
- this._timeToLoad = this._getProcessTimeInMilliseconds() - timeLoadStarted
- this.setIsDataLoaded(true)
- await this._executeChildNodes()
- }
- abstractUrlNoCellsNode
- boolean useCache true
- inScope parserNode useCacheNode
- string tileSize 300 150
- extends abstractProviderNode
- abstract
- javascript
- getUrl() {
- const struct = Object.assign(this.getSettingsStruct(), this.getDefinition().getConstantsObject())
- if (struct.urlTemplate && this.getContent()) return new jtree.TreeNode({ content: this.getContent() }).evalTemplateString(struct.urlTemplate)
- if (struct.urlPrefix && this.getContent()) return struct.urlPrefix + this.getContent()
- return struct.urlCell || this.getContent() || this.url || ""
- }
- getParserId() {
- if (this.parser) return this.parser
- const url = this.getUrl()
- if (super.getParserId()) return super.getParserId()
- const extension = jtree.Utils.getFileExtension(url)
- if (new TableParser().getAllTableParserIds().includes(extension)) return extension
- }
- getWillowHttpResponse() {
- return this._willowHttpResponse
- }
- _setWillowHttpResponse(willowHttpResponse) {
- this._willowHttpResponse = willowHttpResponse
- return this
- }
- // todo: add support for Arrow.
- // todo: remove this cache. use higher level.
- async _getData(url) {
- const useCache = this.getSettingsStruct().useCache !== "false" || this.useCache
- const willowBrowser = this.getWebApp().getWillowBrowser()
- let response
- if (useCache) response = await willowBrowser.httpGetUrlFromCache(url)
- else response = await willowBrowser.httpGetUrl(url)
- if (response.fromCache)
- this.emitLogMessage(\`div
- bern
- Loading from cache: \${url}\`)
- this._setWillowHttpResponse(response)
- return response.getParsedDataOrText()
- }
- async fetchTableInputs() {
- let url = this.getUrl()
- if (!url) return { rows: [] }
- url = encodeURI(url)
- const parserId = this.getParserId()
- this.setRunTimePhaseError("fetchUrl")
- try {
- const data = await this._getData(url)
- const parser = new TableParser()
- if (typeof data === "string") return parser.parseTableInputsFromString(data, parserId)
- if (this.jsonPath) return parser.parseTableInputsFromObject(data[this.jsonPath], parserId)
- return parser.parseTableInputsFromObject(data, parserId)
- } catch (err) {
- // todo: solve the superagent not throwing response message thing.
- const txt = (err.text || err.toString()).substr(0, 280)
- this.emitLogMessage(\`Error getting url: \${url}
- \${txt}\`)
- this.setRunTimePhaseError("fetchUrl", txt)
- return { rows: [] }
- }
- }
- abstractUrlNode
- cells tileKeywordCell urlCell
- string tileSize 300 100
- extends abstractUrlNoCellsNode
- abstract
- abstractUrlsNode
- extends abstractUrlNode
- javascript
- getUrls() {
- return this.getWordsFrom(1).map(url => (this.urlPrefix || "") + url)
- }
- async fetchTableInputs() {
- // todo: allow cache breaking.
- const app = this.getWebApp()
- const willowBrowser = app.getWillowBrowser()
- let allResults = []
- const urls = this.getUrls()
- const fetchMethod = async url => (app.isUrlGetProxyAvailable() ? willowBrowser.httpGetUrlFromProxyCache(url) : willowBrowser.httpGetUrlFromCache(url))
- for (let url of urls) {
- const response = await fetchMethod(url)
- allResults.push(response)
- }
- return { rows: allResults.map(res => res.asJson) }
- }
- githubInfoNode
- frequency .01
- tags internetConnectionRequired
- extends abstractUrlsNode
- string dataDomain github.com
- catchAllCellType githubRepoCell
- cells tileKeywordCell
- description Get basic information on a GitHub repo(s)
- string urlPrefix https://api.github.com/repos/
- crux github.info
- diskBrowseNode
- frequency .01
- tags localVersion
- catchAllCellType pathCell
- description An interactive list of files and folders.
- string hakonTemplate
- .DiskTile
- table
- width 100%
- td,th
- overflow hidden
- text-overflow ellipsis
- tr
- white-space nowrap
- javascript
- getUrl() {
- return this.getContent() ? "/disk?path=" + this.getContent() : "/disk"
- }
- getTileBodyStumpCode() {
- const labelCol = "name"
- const path = this.getContent() || ""
- const parentPath = path.replace(/\\/[^\\/]*$/, "")
- const rowDisplayLimit = 1000 // todo: adjustable?
- let rows = this.getOutputTable()
- .getRows()
- .slice(0, rowDisplayLimit)
- rows = lodash.sortBy(rows, row => row.getRowOriginalValue("isDirectory") === "false")
- return \`input
- placeholder Filepath
- value \${path}
- changeCommand changeTileContentAndRenderCommand
- class LargeTileInput
- table
- tr
- td
- a ..
- clickCommand changeTileContentAndRenderCommand
- value \${parentPath}
- \${rows
- .map(row => {
- const label = jtree.Utils.stripHtml(row.getRowOriginalValue(labelCol))
- const isDir = row.getRowOriginalValue("isDirectory") === "true"
- const size = row.getRowOriginalValue("bytes")
- const mtime = row.getRowOriginalValue("mtime")
- if (!isDir)
- return \` tr
- td \${label}
- td \${numeral(size).format("0.0 b")}
- td \${moment(parseFloat(mtime)).fromNow()}\`
- return \` tr
- td
- a \${label}
- clickCommand changeTileContentAndRenderCommand
- value \${path.replace(/\\/$/, "") + "/" + label}\`
- })
- .join("\\n")}\`
- }
- string tileSize 500 500
- extends abstractUrlNode
- crux disk.browse
- diskReadNode
- frequency .01
- tags localVersion
- description Reads a file from disk.
- string urlPrefix /disk.read?path=
- extends abstractUrlNode
- crux disk.read
- abstractHackernewsNode
- frequency .01
- tags internetConnectionRequired
- extends abstractUrlNode
- string dataDomain news.ycombinator.com
- abstract
- hackernewsTopNode
- cells tileKeywordCell quantityCell
- description Get the top stories on hackernews
- example A dashboard of the Hacker News homepage.
- hackernews.top 10
- rows.sortBy by
- tables.basic
- vega.scatter
- xColumn time
- yColumn score
- javascript
- async fetchTableInputs() {
- // todo: allow cache breaking.
- const willowBrowser = this.getWebApp().getWillowBrowser()
- const firstUrls = this._getFirstUrls()
- if (!firstUrls.length || this.isNodeJs()) return []
- let allResults = []
- const fetchMethod = async url => (this.getWebApp().isUrlGetProxyAvailable() ? willowBrowser.httpGetUrlFromProxyCache(url) : willowBrowser.httpGetUrlFromCache(url))
- for (let mainUrl of firstUrls) {
- const response = await fetchMethod(mainUrl)
- const nextUrls = this._parseNextUrls(response)
- const batchResults = await Promise.all(nextUrls.slice(0, this._getLimit()).map(url => fetchMethod(url)))
- allResults = allResults.concat(batchResults)
- }
- return { rows: allResults.map(res => res.asJson) }
- }
- _getLimit() {
- return parseInt(this.getContent() || 10)
- }
- _parseNextUrls(response) {
- return response.asJson.map(id => \`https://hacker-news.firebaseio.com/v0/item/\${id}.json?print=pretty\`)
- }
- _getFirstUrls() {
- return ["https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty"]
- }
- extends abstractHackernewsNode
- crux hackernews.top
- hackernewsSubmissionsNode
- description Get a user's comments and submissions
- cells tileKeywordCell quantityCell
- catchAllCellType hackerNewsUserNameCell
- example View a users comment and story submissions
- hackernews.submissions 100 breck
- tables.basic
- filter.where type = story
- vega.scatter Stories
- xColumn time
- yColumn score
- filter.where type = comment
- vega.scatter Comments
- xColumn time
- yColumn score
- javascript
- _getFirstUrls() {
- return this.getWordsFrom(2).map(username => \`https://hacker-news.firebaseio.com/v0/user/\${username}.json?print=pretty\`)
- }
- _getLimit() {
- return parseInt(this.getWord(1) || 10)
- }
- _parseNextUrls(response) {
- return response.asJson.submitted.map(id => \`https://hacker-news.firebaseio.com/v0/item/\${id}.json?print=pretty\`)
- }
- extends hackernewsTopNode
- crux hackernews.submissions
- publicApisNode
- frequency .01
- tags internetConnectionRequired
- extends abstractUrlNode
- string dataDomain publicapis.org
- cells tileKeywordCell
- description Get all public APIs
- string url https://api.publicapis.org/entries
- crux publicapis.entries
- string parser json
- string jsonPath entries
- webGetNode
- description Get a URL and parse the fetched data.
- example Fetch a TSV from the web and show results
- web.get https://raw.githubusercontent.com/treenotation/ohayo/master/ohayo/packages/samples/iris.tsv
- tables.basic
- frequency .1
- string placeholderMessage Enter a url.
- string bodyStumpTemplate
- span {kind}
- class LargeLabel
- input
- value {content}
- placeholder {placeholderMessage}
- changeCommand changeTileContentAndRenderCommand
- class LargeTileInput
- javascript
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { kind: this.getFirstWord(), content: this.getContent() || "", placeholderMessage: this.placeholderMessage })
- }
- string tileSize 400 100
- extends abstractUrlNode
- crux web.get
- webPostNode
- frequency .02
- description Post data to a URL and parse the returned data.
- inScope postNode
- string webPostBodyStumpTemplate
- textarea
- bern
- {post}
- placeholder This data will be sent as the value of the 'q' param
- name post
- changeCommand changeTileSettingMultilineCommand
- class TileTextArea
- javascript
- getTileBodyStumpCode() {
- return super.getTileBodyStumpCode() + this.qFormat(this.webPostBodyStumpTemplate, { post: jtree.Utils.stripHtml(this.getSettingsStruct().post || "") })
- }
- async _getData(url) {
- const settings = this.getSettingsStruct()
- // todo, but make a separate tile
- // if (settings.pushButton) {
- // if (!settings.pushed) return ""
- // settings.pushed = false
- // }
- const postData = settings.post || ""
- const res = await this.getWebApp()
- .getWillowBrowser()
- .httpPostUrl(url, { q: postData.trim() })
- this._setWillowHttpResponse(res)
- return res.getParsedDataOrText()
- }
- string tileSize 400 130
- extends abstractUrlNode
- crux web.post
- wikipediaContentNode
- frequency .01
- tags internetConnectionRequired
- extends abstractUrlNode
- string dataDomain wikipedia.org
- catchAllCellType wikipediaPermalinkCell
- cells tileKeywordCell
- description Get content of a wikipedia page(s)
- javascript
- getUrl() {
- return this.urlPrefix + this.wikipediaPermalinkCell.join("|")
- }
- async fetchTableInputs() {
- const inputs = await super.fetchTableInputs()
- // todo: cleanup
- return { rows: Object.values(inputs.rows[0].query.pages) }
- }
- string urlPrefix https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&explaintext&redirects=1&origin=*&titles=
- crux wikipedia.page
- abstractFixedDatasetFromUrlNode
- description A dataset that generally is fixed and will never change.
- extends abstractUrlNoCellsNode
- abstract
- javascript
- _getDescription() {
- const desc = super._getDescription()
- if (this.dataUrl) return (desc ? desc + " from " : "") + this.dataUrl
- return desc
- }
- abstractFixedDatasetFromOhayoCollectionNode
- description A dataset that ships with Ohayo.
- string tileSize 300 150
- javascript
- async _getData(url) {
- if (!this.isNodeJs()) return super._getData(url)
- const fs = require("fs")
- const filepath = __dirname + "/../" + url
- return fs.readFileSync(filepath, "utf8")
- }
- extends abstractFixedDatasetFromUrlNode
- abstract
- cancerCasesNode
- description Estimated new cancer cases in the U.S. in 2019 from https://cancerstatisticscenter.cancer.org/
- string url ohayo/packages/cancer/cases.csv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux cancer.cases
- abstractCdcInfantPercentileNode
- boolean isDataPublicDomain true
- string dataUrl https://www.cdc.gov/growthcharts/percentile_data_files.htm
- abstract
- extends abstractFixedDatasetFromOhayoCollectionNode
- weightPercentilesNode
- description Weight percentiles for birth to 36 months in kilograms, by sex and age
- string url ohayo/packages/cdc/wtageinf.csv
- extends abstractCdcInfantPercentileNode
- crux cdc.infants.weight
- lengthPercentilesNode
- description Length percentiles for birth to 36 months in centimeters, by sex and age
- string url ohayo/packages/cdc/lenageinf.csv
- extends abstractCdcInfantPercentileNode
- crux cdc.infants.length
- headPercentilesNode
- description Head circumference percentiles for birth to 36 months in centimeters, by sex and age
- string url ohayo/packages/cdc/hcageinf.csv
- extends abstractCdcInfantPercentileNode
- crux cdc.infants.headCircumference
- kaggleDatasetsHeartNode
- tags internetConnectionRequired
- description Heart Disease dataset from https://www.kaggle.com/ronitf/heart-disease-uci
- string url ohayo/packages/kaggle/heart.csv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux kaggle.datasets.heart
- mozTop500Node
- description Moz's list of the most popular 500 websites on the internet.
- string dataUrl https://moz.com/top500
- boolean isDataPublicDomain true
- string url ohayo/packages/moz/top500Domains.csv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux moz.top500
- lifeExpectancyNode
- description Life expectancy data.
- string url ohayo/packages/owid/life-expectancy.csv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux owid.lifeExpectancy
- owidListNode
- string parser treeRows
- description Gets all datasets on Our World in Data.
- string url ohayo/packages/owid/owid.tree
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux owid.list
- samplesTelescopesNode
- description A partial list of humankind's largest telescopes.
- string dataDescription
- ## Provenance
- This list was put together by a group of remote workers in a Google spreadsheet in 2017 and hasn't been updated in a while.
- boolean isDataPublicDomain true
- string dataUrl https://github.com/treenotation/ohayo/blob/master/ohayo/packages/samples/telescopes.tsv
- tags astronomy
- frequency .03
- example Display list of links to telescope websites.
- samples.telescopes
- list.links Name Url
- string url ohayo/packages/samples/telescopes.tsv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.telescopes
- samplesMtcarsNode
- description Dataset from 1974 Motor Trend US magazine.
- boolean isDataPublicDomain true
- string dataUrl https://stat.ethz.ch/R-manual/R-devel/library/datasets/html/mtcars.html
- frequency .03
- string url ohayo/packages/samples/mtcars.tsv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.mtcars
- samplesIrisNode
- description The famous Iris flower data set.
- string dataUrl https://archive.ics.uci.edu/ml/datasets/iris
- boolean isDataPublicDomain true
- frequency .15
- string url ohayo/packages/samples/iris.tsv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.iris
- samplesFlights14Node
- description On-Time flights data from the Bureau of Transportation Statistics for all the flights that departed from New York City airports in 2014. The data is available only for Jan-Oct'14.
- string dataUrl https://github.com/Rdatatable/data.table/blob/master/vignettes/flights14.csv
- boolean isDataPublicDomain true
- string url ohayo/packages/samples/flights14-sample.csv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.flights14
- samplesSiNode
- description A description of The International System of Units (SI) aka the metric system.
- boolean isDataPublicDomain true
- string dataUrl https://github.com/treenotation/ohayo/blob/master/ohayo/packages/samples/si.tree
- example View outline of SI system.
- samples.si
- treenotation.outline
- frequency .03
- string url ohayo/packages/samples/si.tree
- string parser text
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.si
- samplesPortalNode
- description A list of online data portals.
- boolean isDataPublicDomain true
- string dataUrl https://github.com/treenotation/ohayo/blob/master/ohayo/packages/samples/portals.ssv
- frequency .03
- string url ohayo/packages/samples/portals.ssv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.portals
- samplesStarWarsNode
- description All Star Wars characters. Data comes from https://swapi.co/
- frequency .03
- boolean isDataPublicDomain false
- string dataLicense BSD
- string url ohayo/packages/samples/starwars.json
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.starWars
- samplesPopulationsNode
- description Countries of the world and their populations.
- frequency .15
- string url ohayo/packages/samples/populations.tsv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.populations
- samplesBabyNamesNode
- description 30 rows of a much larger dataset of baby name popularity over time in the U.S.
- frequency .03
- string url ohayo/packages/samples/baby-names-sample.csv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.babyNames
- samplesDeclarationNode
- description The 1776 Declaration of Independence
- boolean isDataPublicDomain true
- frequency .02
- string url ohayo/packages/samples/declaration-of-independence.text
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.declaration
- samplesPeriodicTableNode
- description Periodic table from https://gist.github.com/GoodmanSciences
- string dataUrl https://gist.github.com/GoodmanSciences/c2dd862cd38f21b0ad36b8f96b4bf1ee
- frequency .15
- extends abstractFixedDatasetFromOhayoCollectionNode
- string url ohayo/packages/samples/periodic-table.csv
- crux samples.periodicTable
- samplesLettersNode
- description Letter usage frequency in English from mobostock.
- frequency .03
- extends abstractFixedDatasetFromOhayoCollectionNode
- string url ohayo/packages/samples/letters.tsv
- crux samples.letters
- samplesPresidentsNode
- boolean isDataPublicDomain true
- description CSV of president's of United States.
- frequency .03
- string url ohayo/packages/samples/presidents.csv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux samples.presidents
- ucimlrDatasetsNode
- description A list of datasets from the UC Irvine Machine Learning Repository at http://archive.ics.uci.edu/ml/index.php.
- frequency .001
- example Display list of datasets from UCIMLR
- ucimlr.datasets
- tables.basic
- string url ohayo/packages/ucimlr/datasets.tsv
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux ucimlr.datasets
- vegaDataNode
- cells tileKeywordCell vegaDataSetCell
- frequency .001
- description Fetchs a Vega sample dataset
- string urlPrefix ohayo/packages/vega/datasets/
- extends abstractFixedDatasetFromOhayoCollectionNode
- crux vega.data
- redditAllNode
- tags internetConnectionRequired
- string dataDomain reddit.com
- description Fetches top stories from r/all.
- example A simple reddit dashboard
- reddit.all
- hidden
- columns.keep title created_utc score subreddit url
- hidden
- rows.sortBy score
- rows.reverse
- tables.basic
- vega.scatter
- yColumn score
- xColumn created_utc
- frequency .05
- javascript
- async fetchTableInputs() {
- const inputs = await super.fetchTableInputs()
- // Todo: add tests/external dependency, as reddit API changes.
- // Here it looks like we have the equivalent of a custom parser just for a Reddit Data source.
- // todo: explore/define/typescriptAPI this custom parser pattern more. probably will be common.
- return inputs.rows.length ? { rows: inputs.rows[0].data.children.map(obj => obj.data) } : inputs
- }
- getParserId() {
- return "json"
- }
- string url https://www.reddit.com/r/all/top/.json?sort=top
- string offlineDataSet ohayo/packages/reddit/all.json
- extends abstractUrlNoCellsNode
- crux reddit.all
- redditSubsNode
- description Fetches top subreddits.
- frequency .005
- string url https://www.reddit.com/reddits.json
- extends redditAllNode
- crux reddit.subs
- redditSubNode
- cells tileKeywordCell subredditNameCell
- frequency .006
- description Fetches top stories in a subreddit.
- javascript
- getUrl() {
- const subreddit = this.getContent() || "all"
- return \`https://www.reddit.com/r/\${subreddit}/top/.json?sort=top\`
- }
- extends redditAllNode
- crux reddit.sub
- abstractDummyNode
- javascript
- async _execute() {
- this.setIsDataLoaded(true)
- await this._executeChildNodes()
- }
- string tileSize 300 150
- extends abstractProviderNode
- abstract
- samplesPatientsNode
- description A row for each patient in a sample clinical dataset.
- string dummyDataSetName patients
- boolean isDataPublicDomain true
- extends abstractDummyNode
- crux samples.patients
- samplesPoemNode
- description The Road Not Taken by Robert Frost
- string dummyDataSetName poem
- boolean isDataPublicDomain true
- extends abstractDummyNode
- crux samples.poem
- samplesOuterSpaceNode
- description A simple text document of major structures in the universe.
- string dummyDataSetName outerSpace
- extends abstractDummyNode
- crux samples.outerSpace
- samplesTreeProgramNode
- description A simple program in a Tree Language.
- string dummyDataSetName treeProgram
- boolean isDataPublicDomain true
- extends abstractDummyNode
- crux samples.treeProgram
- samplesWaterBillNode
- description A family's water bill.
- frequency .15
- string dummyDataSetName waterBill
- boolean isDataPublicDomain true
- extends abstractDummyNode
- crux samples.waterBill
- samplesGapMinderNode
- description Health and income data from gapMinder
- frequency .15
- string dummyDataSetName gapMinder
- extends abstractDummyNode
- crux samples.gapMinder
- abstractTransformerNode
- string tileSize 160 100
- extends abstractProviderNode
- string placeholderMessage
- abstract
- string bodyStumpTemplate
- span {kind}
- class LargeLabel
- input
- value {content}
- placeholder {placeholderMessage}
- changeCommand changeTileContentAndRenderCommand
- class LargeTileInput
- string tileFooterTemplate
- span Rows In: {inputCount} Rows Out: {outputCount} Columns Out: {columnCount}
- {tileMenuButton}
- javascript
- getTileFooterStumpCode() {
- const table = this.getParentOrDummyTable()
- const inputCount = table.getRowCount()
- const outputTable = this.getOutputOrInputTable()
- return this.qFormat(this.tileFooterTemplate, { inputCount, outputCount: table.getRowCount(), columnCount: outputTable.getColumnCount(), tileMenuButton: this.getTileMenuButtonStumpCode() })
- }
- async _execute() {
- this._outputTable = this._createOutputTable()
- this.setIsDataLoaded(true)
- await this._executeChildNodes()
- }
- getTileBodyStumpCode() {
- return this.qFormat(this.bodyStumpTemplate, { kind: this.getFirstWord(), content: this.getContent() || "", placeholderMessage: this.placeholderMessage })
- }
- abstractColumnAdderTileNode
- abstract
- extends abstractTransformerNode
- javascript
- _createOutputTable() {
- return this.getParentOrDummyTable().addColumns(this.getNewColumns())
- }
- dateAddColumnsNode
- description Takes an input table with a time column and adds a day, month and year column.
- catchAllCellType dateColumnTypeCell
- inScope sourceColumnNode
- string columnPredictionHints
- sourceColumn isTemporal=true
- extends abstractColumnAdderTileNode
- crux date.addColumns
- string placeholderMessage Enter the source column and new date columns you want, or leave blank to get 'day month year'.
- javascript
- getNewColumns() {
- const inputColumnName = this.getSettingsStruct().sourceColumn // todo: this is probably broken. need to fix settings timing issues.
- if (!inputColumnName) return []
- const addColumns = this.getContent() ? this.getWordsFrom(1) : ["day", "week", "month"]
- // what happened to dayName? timeOfDay?
- return addColumns.map(outputCol => {
- return {
- source: inputColumnName,
- name: outputCol,
- type: outputCol
- }
- })
- }
- genConstantNode
- crux gen.constant
- example
- gen.range year -1000 2020 1
- gen.constant birthRate number 0.035
- cells tileKeywordCell columnNameCell primitiveTypeCell anyCell
- description Add a column that contains a constant for each row.
- extends abstractColumnAdderTileNode
- javascript
- getNewColumns() {
- return [
- {
- name: this.columnNameCell,
- type: this.primitiveTypeCell,
- accessorFn: row => this.anyCell
- }
- ]
- }
- genGrowthNode
- crux gen.growth
- example
- gen.range year -1000 2020 1
- gen.constant birthRate number 0.035
- gen.growth population 4 0.01
- cells tileKeywordCell columnNameCell minCell growthRateCell
- description Add a column that contains a constant for each row.
- extends abstractColumnAdderTileNode
- javascript
- getNewColumns() {
- let total = this.minCell
- return [
- {
- name: this.columnNameCell,
- accessorFn: (row, rowIndex) => {
- total = total * (1 + this.growthRateCell)
- return total
- }
- }
- ]
- }
- mathLogNode
- description Add a column that is the natural log (base e) of another column.
- cells tileKeywordCell columnNameCell
- extends abstractColumnAdderTileNode
- crux math.log
- javascript
- getNewColumns() {
- const inputColumnName = this.getWord(1)
- if (!inputColumnName) return []
- const inputCol = this.getParentOrDummyTable().getColumnByName(inputColumnName)
- return [
- {
- source: inputColumnName,
- name: inputColumnName + "Log",
- type: inputCol.getPrimitiveTypeName(),
- mathFn: Math.log
- }
- ]
- }
- rowsAddIndexColumnNode
- description Add an index column to the data.
- extends abstractColumnAdderTileNode
- crux rows.addIndexColumn
- javascript
- getNewColumns() {
- let index = 0
- return [
- {
- name: "index",
- accessorFn: row => index++
- }
- ]
- }
- rowsRunningTotalNode
- cells tileKeywordCell columnNameCell
- description Add a column that accumulates the running total of a column.
- extends abstractColumnAdderTileNode
- crux rows.runningTotal
- javascript
- getNewColumns() {
- const sourceColumnName = this.getWord(1)
- let total = 0
- return [
- {
- source: sourceColumnName,
- name: "total",
- accessorFn: row => {
- total += row[sourceColumnName]
- return total
- }
- }
- ]
- }
- textLengthNode
- cells tileKeywordCell columnNameCell
- description Add a column which contains the string length of the given column.
- example Show the largest words in declaration of independence
- samples.declaration
- text.wordCount
- text.length word
- filter.where wordLength > 5
- rows.sortByReverse wordLength
- tables.basic
- javascript
- getNewColumns() {
- const sourceColumnName = this.getWord(1)
- const destinationColumnName = sourceColumnName + "Length"
- return [
- {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => row[sourceColumnName].length
- }
- ]
- }
- extends abstractColumnAdderTileNode
- crux text.length
- textSplitNode
- description Split one column into multiple by a string
- cells tileKeywordCell columnNameCell delimiterCell
- catchAllCellType newColumnNamesCell
- example Split a filename into name and extension
- vega.data descriptions.json
- text.split filename . name extension
- tables.basic
- string dummyDataSetName poem
- javascript
- // note: delimiter can probably be ""
- // todo: how would we split on a space???
- // perhaps its better to use getContent() as delimiter, and if you want to name the columns, you can do that later?
- getNewColumns() {
- const sourceColumnName = this.getWord(1)
- const delimiter = this.getWord(2)
- const destinationColumns = this.getWordsFrom(3)
- return destinationColumns.map((destinationColumnName, index) => {
- return {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => {
- const words = row[sourceColumnName].split(delimiter)
- return this.reverseSplit ? words.reverse()[index] : words[index]
- }
- }
- })
- return [
- {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => row[sourceColumnName].length
- }
- ]
- }
- extends abstractColumnAdderTileNode
- crux text.split
- reverseTextSplitNode
- extends textSplitNode
- crux text.reverseSplit
- description Split one column into multiple by a string reversing the order.
- boolean reverseSplit true
- textToLowerCaseNode
- description Convert all cells in a column to LowerCase text
- cells tileKeywordCell columnNameCell
- example Select the first character of someone's name
- samples.declaration
- text.wordCount
- tables.basic
- text.toLowerCase text
- tables.basic
- string dummyDataSetName poem
- javascript
- getNewColumns() {
- const sourceColumnName = this.getWord(1) || "text"
- return [
- {
- source: sourceColumnName,
- name: sourceColumnName,
- accessorFn: row => row[sourceColumnName].toLowerCase()
- }
- ]
- }
- extends abstractColumnAdderTileNode
- crux text.toLowerCase
- textTemplateNode
- inScope contentNode
- description Evaluates a common programming template string and generates a new cell for each row.
- cells tileKeywordCell
- catchAllCellType newColumnNameCell
- string bodyStumpTemplate
- textarea
- name content
- changeCommand changeTileSettingMultilineCommand
- placeholder Enter template here.
- class TileTextArea savable
- bern
- {text}
- example
- samples.presidents
- text.template
- content
- Hello {name}!
- How did you like being born in {HomeState}?
- tables.basic
- javascript
- getNewColumns() {
- const contentNode = this.getNode("content")
- const templateString = contentNode ? contentNode.childrenToString() : ""
- const destColumnName = this.getWord(1) || "Output"
- return [
- {
- name: destColumnName,
- accessorFn: row => new jtree.TreeNode(templateString).templateToString(row)
- }
- ]
- }
- getDataContent() {
- const node = this.getNode("content")
- return node ? node.childrenToString() : ""
- }
- getTileBodyStumpCode() {
- const text = lodash.escape(this.getDataContent())
- return this.qFormat(this.bodyStumpTemplate, { text })
- }
- extends abstractColumnAdderTileNode
- crux text.template
- textPermalinkNode
- description Convert all cells in a column to a url friendly permalink.
- cells tileKeywordCell columnNameCell newColumnNameCell
- example
- samples.presidents
- text.permalink name Permalink
- tables.basic
- javascript
- getNewColumns() {
- const sourceColumnName = this.getWord(1) || "text"
- const destinationColumnName = this.getWord(2) || "Permalink"
- return [
- {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => jtree.Utils.stringToPermalink(row[sourceColumnName])
- }
- ]
- }
- extends abstractColumnAdderTileNode
- crux text.permalink
- textReplaceNode
- description Does a global search/replace across all cells in a column.
- cells tileKeywordCell columnNameCell
- catchAllCellType anyCell
- example
- samples.presidents
- text.replace name George Georgette
- tables.basic
- javascript
- getNewColumns() {
- const sourceColumnName = this.getWord(1) || "text"
- const simpleSearch = this.getWord(2)
- const simpleReplace = this.getWord(3)
- const destinationColumnName = sourceColumnName
- return [
- {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => row[sourceColumnName].replace(new RegExp(simpleSearch, "g"), simpleReplace)
- }
- ]
- }
- extends abstractColumnAdderTileNode
- crux text.replace
- textTrimNode
- description Trims whitespace or a provided sequence from both sides of a string in all cells in a column.
- cells tileKeywordCell columnNameCell
- catchAllCellType anyCell
- example
- samples.presidents
- text.trim HomeState New
- tables.basic
- javascript
- getNewColumns() {
- const sourceColumnName = this.getWord(1) || "text"
- const trimChar = this.getWord(2)
- const destinationColumnName = sourceColumnName
- return [
- {
- source: sourceColumnName,
- name: destinationColumnName,
- accessorFn: row => (trimChar ? row[sourceColumnName].replace(new RegExp(\`(^\${trimChar}|\${trimChar}$)\`, "g"), "") : row[sourceColumnName].trim())
- }
- ]
- }
- extends abstractColumnAdderTileNode
- crux text.trim
- textSubstringNode
- description Extract parts of one column into another column called "substring".
- cells tileKeywordCell columnNameCell startIndexCell lengthCell
- example Select the first character of someone's name
- samples.presidents
- text.substring name 0 1
- tables.basic
- string dummyDataSetName poem
- extends abstractColumnAdderTileNode
- crux text.substring
- string destinationColumnName substring
- javascript
- getNewColumns() {
- const sourceColumnName = this.getWord(1)
- const startPosition = typeof this.startIndex !== undefined ? this.startIndex : parseInt(this.getWord(2))
- const endPosition = typeof this.endIndex !== undefined ? this.endIndex : this.getWord(3) === undefined ? undefined : parseInt(this.getWord(3))
- return [
- {
- source: sourceColumnName,
- name: this.destinationColumnName,
- accessorFn: row => (row[sourceColumnName] ? row[sourceColumnName].toString().substr(startPosition, endPosition) : "")
- }
- ]
- }
- testFirstLetterNode
- extends textSubstringNode
- crux text.firstLetter
- description Extracts the first letter of a column into a new column called "firstLetter"
- cells tileKeywordCell columnNameCell
- string destinationColumnName firstLetter
- int startIndex 0
- int endIndex 1
- abstractNewRowsTransformerTileNode
- abstract
- extends abstractTransformerNode
- javascript
- _createOutputTable() {
- // todo: remove this
- return new Table(this.makeNewRows())
- }
- columnsDescribeNode
- description Computes statistics for each input column.
- example List column information
- samples.iris
- columns.describe
- extends abstractNewRowsTransformerTileNode
- crux columns.describe
- javascript
- makeNewRows() {
- return this.getParentOrDummyTable().getColumnNamesAndTypesAndReductions()
- }
- columnsListNode
- description Provides a list of table columns and their types.
- example List columns
- samples.iris
- columns.list
- extends columnsDescribeNode
- crux columns.list
- javascript
- makeNewRows() {
- return this.getParentOrDummyTable().getColumnNamesAndTypes()
- }
- dataEvalNode
- description Passes input rows, if any, to a Javascript function and returns transformed or new rows.
- example Generate some data.
- data.eval
- content
- inputRows => [{name: "Swift", year: 2015}, {name: "Kotlin", year: 2011}]
- tables.basic
- inScope contentNode
- extends abstractNewRowsTransformerTileNode
- crux data.eval
- javascript
- makeNewRows() {
- const node = this.getNode(this.contentKey)
- const code = node && node.childrenToString() // "rows => { return []}"
- let fn
- try {
- fn = code && eval(code)
- } catch (err) {
- // todo: warn user
- console.error(err)
- }
- const inputRows = this.getParentOrDummyTable().cloneNativeJavascriptTypedRows()
- return fn ? fn(inputRows) : inputRows
- }
- joinByNode
- catchAllCellType columnNameCell
- description Combines multiple tables into one, linking the rows by the provided key column.
- extends abstractNewRowsTransformerTileNode
- crux join.by
- javascript
- makeNewRows() {
- // Todo: move to table project
- const parentTile = this.getParent()
- if (parentTile.isRoot()) return []
- const grandParentTile = parentTile.getParent()
- if (grandParentTile.isRoot()) return parentTile.getOutputOrInputTable().cloneNativeJavascriptTypedRows()
- const tiles = [parentTile, grandParentTile]
- const arrays = tiles.map(tile => tile.getOutputOrInputTable().cloneNativeJavascriptTypedRows())
- const joinOn = this.getContent()
- if (!joinOn) return jtree.Utils.flatten(arrays)
- const cols = tiles.map(tile => tile.getOutputOrInputTable().getColumnNames())
- return jtree.Utils.joinArraysOn(joinOn, arrays, cols)
- }
- matchColumnsFuzzyNode
- description Attempts to fuzzy match words in one column of parent table against words in a column in grandparent table.
- cells tileKeywordCell needleColumnNameCell haystackColumnNameCell
- example Try to match different spellings of country names.
- samples.gapMinder
- samples.populations
- match.columnsFuzzy Country country
- rows.sortBy confidence
- rows.reverse
- tables.basic
- string tileScript ohayo/packages/match/fuse.min.js
- extends abstractNewRowsTransformerTileNode
- crux match.columnsFuzzy
- javascript
- makeNewRows() {
- // Todo: move some of this logic to table project?
- const parentTile = this.getParent()
- if (parentTile.isRoot()) return []
- const grandParentTile = parentTile.getParent()
- if (grandParentTile.isRoot()) return parentTile.getOutputOrInputTable().cloneNativeJavascriptTypedRows()
- const tiles = [parentTile, grandParentTile]
- const arrays = tiles.map(tile => tile.getOutputOrInputTable().cloneNativeJavascriptTypedRows())
- return this._addFuzz(arrays[0], arrays[1])
- }
- get fuse() {
- return this.isNodeJs() ? require("fuse.js") : Fuse
- }
- _addFuzz(needles, haystacks) {
- const needleColumnName = this.getWord(1) || "name"
- const haystackColumnName = this.getWord(2) || "name"
- const options = {
- shouldSort: true,
- includeScore: true,
- threshold: 0.6,
- location: 0,
- distance: 100,
- maxPatternLength: 32,
- minMatchCharLength: 1,
- keys: [haystackColumnName]
- }
- const fuse = new this.fuse(haystacks, options) // "list" is the item array
- return needles.map(needle => {
- const searchValue = needle[needleColumnName]
- const result = fuse.search(searchValue)
- if (!result.length)
- return {
- search: searchValue,
- match: ""
- }
- const match = result[0]
- return {
- search: searchValue,
- match: match.item[haystackColumnName],
- confidence: parseFloat((1 - match.score).toFixed(3))
- }
- })
- }
- schemaTypeScriptNode
- description Generates a TypeScript interface for the parent table.
- extends abstractNewRowsTransformerTileNode
- crux schema.toTypescript
- example
- samples.presidents
- schema.toTypescript
- html.printAs code
- javascript
- makeNewRows() {
- return [{ text: this.getParentOrDummyTable().toTypeScriptInterface() }]
- }
- schemaSimpleNode
- description Generate a simple schema of the parent table.
- extends abstractNewRowsTransformerTileNode
- crux schema.toSimple
- example
- samples.presidents
- schema.toSimple
- html.printAs code
- javascript
- makeNewRows() {
- const schema = this.getParentOrDummyTable().toSimpleSchema()
- const oneLiner = schema.replace(/ /g, ":").replace(/\\n/g, " ")
- return [{ text: oneLiner + "\\n\\n" + schema }]
- }
- textWordCountNode
- description Splits a string into words and counts the number of uses of each word.
- string dummyDataSetName poem
- extends abstractNewRowsTransformerTileNode
- crux text.wordCount
- javascript
- makeNewRows() {
- return this._getAllWords(this.getPipishInput())
- }
- _getAllWords(text) {
- const rows = []
- if (!text) return rows
- const words = text
- .split(/\\s/g)
- .map(word => word.replace(/[^a-z0-9\\-]/gi, ""))
- .filter(word => word)
- const index = {}
- words.forEach(word => {
- if (!index[word]) index[word] = 1
- else index[word]++
- })
- Object.keys(index).forEach(word => {
- const trimmedWord = word.trim()
- if (trimmedWord)
- rows.push({
- word: trimmedWord,
- count: index[trimmedWord]
- })
- })
- return rows
- }
- textLineCountNode
- description Counts the number of lines in the input data.
- string dummyDataSetName poem
- javascript
- makeNewRows() {
- return [{ lines: this.getPipishInput().split(/\\n/g).length }]
- }
- extends abstractNewRowsTransformerTileNode
- crux text.lineCount
- example
- text.lineCount
- tables.basic
- treenotationWordTypesNode
- description Generates the word types for a Ohayo Language program.
- example See counts of word types in a Ohayo Language program.
- treenotation.wordTypes
- treenotation.outline
- text.wordCount
- tables.basic
- string dummyDataSetName treeProgram
- extends abstractNewRowsTransformerTileNode
- crux treenotation.wordTypes
- javascript
- makeNewRows() {
- return [{ text: new ohayoNode(this.getPipishInput()).toCellTypeTree() }]
- }
- abstractColumnFilterTileNode
- abstract
- extends abstractTransformerNode
- javascript
- _createOutputTable() {
- return this.getParentOrDummyTable().dropAllColumnsExcept(this.getColumnNamesToKeep())
- }
- columnsFirstNode
- cells tileKeywordCell intCell
- description Keeps only the first N columns.
- example Drop all but first column
- samples.iris
- columns.first 1
- tables.basic
- extends abstractColumnFilterTileNode
- crux columns.first
- string placeholderMessage Enter the number of columns you want to keep
- javascript
- getColumnNamesToKeep() {
- return this.getParentOrDummyTable()
- .getColumnsArrayOfObjects()
- .slice(0, parseInt(this.getContent()))
- .map(col => col.name)
- }
- columnsLastNode