diff --git a/.babelrc b/.babelrc
index d8d6229..e087903 100644
--- a/.babelrc
+++ b/.babelrc
@@ -6,13 +6,9 @@
"last 2 versions",
"> 4%",
"ie 11",
- "safari 8",
- "android 4.4"
+ "safari 8"
]
}
- }],
- ["minify", {
- "mergeVars": false
}]
]
}
diff --git a/.eslintignore b/.eslintignore
index 76fd6af..94e5049 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -2,7 +2,8 @@
out/**/*
# libraries
-js/lib/*
+js/lib/chibi.js
+js/lib/polyfills.js
# php generated file
js/lang.js
diff --git a/js/index.js b/js/index.js
index eff520a..dfb22b0 100644
--- a/js/index.js
+++ b/js/index.js
@@ -14,3 +14,5 @@ window.$ = $
window.qs = qs
window.themes = require('./term/themes')
+
+window.TermConf = require('./term_conf')
diff --git a/js/lib/color_utils.js b/js/lib/color_utils.js
new file mode 100644
index 0000000..74cff36
--- /dev/null
+++ b/js/lib/color_utils.js
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2010 Tim Baumann
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// NOTE:
+// Extracted from ColorTriangle and
+// Converted to ES6 by MightyPork (2017)
+
+/*******************
+ * Color conversion *
+ *******************/
+
+const M = Math
+const PI = M.PI
+
+exports.hue_to_rgb = function (v1, v2, h) {
+ if (h < 0) h += 1
+ if (h > 1) h -= 1
+
+ if ((6 * h) < 1) return v1 + (v2 - v1) * 6 * h
+ if ((2 * h) < 1) return v2
+ if ((3 * h) < 2) return v1 + (v2 - v1) * ((2 / 3) - h) * 6
+ return v1
+}
+
+exports.hsl_to_rgb = function (h, s, l) {
+ h /= 2 * PI
+ let r, g, b
+
+ if (s === 0) {
+ r = g = b = l
+ } else {
+ let var_1, var_2
+
+ if (l < 0.5) var_2 = l * (1 + s)
+ else var_2 = (l + s) - (s * l)
+
+ var_1 = 2 * l - var_2
+
+ r = exports.hue_to_rgb(var_1, var_2, h + (1 / 3))
+ g = exports.hue_to_rgb(var_1, var_2, h)
+ b = exports.hue_to_rgb(var_1, var_2, h - (1 / 3))
+ }
+ return [r, g, b]
+}
+
+exports.rgb_to_hsl = function (r, g, b) {
+ const min = M.min(r, g, b)
+ const max = M.max(r, g, b)
+ const d = max - min // delta
+
+ let h, s, l
+
+ l = (max + min) / 2
+
+ if (d === 0) {
+ // gray
+ h = s = 0 // HSL results from 0 to 1
+ } else {
+ // chroma
+ if (l < 0.5) s = d / (max + min)
+ else s = d / (2 - max - min)
+
+ const d_r = (((max - r) / 6) + (d / 2)) / d
+ const d_g = (((max - g) / 6) + (d / 2)) / d
+ const d_b = (((max - b) / 6) + (d / 2)) / d // deltas
+
+ if (r === max) h = d_b - d_g
+ else if (g === max) h = (1 / 3) + d_r - d_b
+ else if (b === max) h = (2 / 3) + d_g - d_r
+
+ if (h < 0) h += 1
+ else if (h > 1) h -= 1
+ }
+ h *= 2 * PI
+ return [h, s, l]
+}
+
+exports.hex_to_rgb = function (hex) {
+ const groups = hex.match(/^#([A-Fa-f0-9]+)$/)
+ if (groups && groups[1].length % 3 === 0) {
+ hex = groups[1]
+ const bytes = hex.length / 3
+ const max = Math.pow(16, bytes) - 1
+ const r = parseInt(hex.slice(0 * bytes, 1 * bytes), 16) / max
+ const g = parseInt(hex.slice(1 * bytes, 2 * bytes), 16) / max
+ const b = parseInt(hex.slice(2 * bytes, 3 * bytes), 16) / max
+ return [r, g, b]
+ }
+ return [0, 0, 0]
+}
+
+function pad (n) {
+ if (n.length === 1) n = '0' + n
+ return n
+}
+
+exports.rgb_to_hex = function (r, g, b) {
+ r = Math.round(r * 255).toString(16)
+ g = Math.round(g * 255).toString(16)
+ b = Math.round(b * 255).toString(16)
+ return `#${pad(r)}${pad(g)}${pad(b)}`
+}
diff --git a/js/lib/colortriangle.js b/js/lib/colortriangle.js
new file mode 100644
index 0000000..2c4e60d
--- /dev/null
+++ b/js/lib/colortriangle.js
@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 2010 Tim Baumann
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// NOTE: Converted to ES6 by MightyPork (2017)
+
+const {
+ rgb_to_hex,
+ hex_to_rgb,
+ hsl_to_rgb,
+ rgb_to_hsl
+} = require('./color_utils')
+
+const win = window
+const doc = document
+const M = Math
+const PI = M.PI
+
+function times (i, fn) {
+ for (let j = 0; j < i; j++) {
+ fn(j)
+ }
+}
+
+function each (obj, fn) {
+ if (obj.length) {
+ times(obj.length, function (i) {
+ fn(obj[i], i)
+ })
+ } else {
+ for (let key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ fn(obj[key], key)
+ }
+ }
+ }
+}
+
+function getOffsets (el) {
+ let left = 0
+ let top = 0
+
+ while (el !== null) {
+ console.log(el)
+ console.log(el.offsetLeft, el.offsetTop)
+ left += el.offsetLeft
+ top += el.offsetTop
+ el = el.offsetParent
+ }
+
+ return [left, top]
+}
+
+module.exports = class ColorTriangle {
+ /****************
+ * ColorTriangle *
+ ****************/
+
+ // Constructor function:
+ constructor (color, options) {
+ this.options = {
+ size: 150,
+ padding: 8,
+ triangleSize: 0.8,
+ wheelPointerColor1: '#444',
+ wheelPointerColor2: '#eee',
+ trianglePointerSize: 16,
+ // wheelPointerSize: 16,
+ trianglePointerColor1: '#eee',
+ trianglePointerColor2: '#444',
+ background: 'transparent'
+ }
+
+ this._setOptions(options)
+ this._calculateProperties()
+
+ this._createContainer()
+ this._createTriangle()
+ this._createWheel()
+ this._createWheelPointer()
+ this._createTrianglePointer()
+ this._attachEvents()
+
+ color = color || '#f00'
+ if (typeof color == 'string') {
+ this.setHEX(color)
+ }
+ }
+
+ _calculateProperties () {
+ let opts = this.options
+
+ this.padding = opts.padding
+ this.innerSize = opts.size - opts.padding * 2
+ this.triangleSize = opts.triangleSize * this.innerSize
+ this.wheelThickness = (this.innerSize - this.triangleSize) / 2
+ this.wheelPointerSize = opts.wheelPointerSize || this.wheelThickness
+
+ this.wheelRadius = (this.innerSize) / 2
+ this.triangleRadius = (this.triangleSize) / 2
+ this.triangleSideLength = M.sqrt(3) * this.triangleRadius
+ }
+
+ _calculatePositions () {
+ const r = this.triangleRadius
+ const hue = this.hue
+ const third = (2 / 3) * PI
+ const s = this.saturation
+ const l = this.lightness
+
+ // Colored point
+ const hx = this.hx = M.cos(hue) * r
+ const hy = this.hy = -M.sin(hue) * r
+ // Black point
+ const sx = this.sx = M.cos(hue - third) * r
+ const sy = this.sy = -M.sin(hue - third) * r
+ // White point
+ const vx = this.vx = M.cos(hue + third) * r
+ const vy = this.vy = -M.sin(hue + third) * r
+ // Current point
+ const mx = (sx + vx) / 2
+ const my = (sy + vy) / 2
+ const a = (1 - 2 * M.abs(l - 0.5)) * s
+ this.x = sx + (vx - sx) * l + (hx - mx) * a
+ this.y = sy + (vy - sy) * l + (hy - my) * a
+ }
+
+ _createContainer () {
+ let c = this.container = doc.createElement('div')
+ c.className = 'color-triangle'
+
+ c.style.display = 'block'
+ c.style.padding = `${this.padding}px`
+ c.style.position = 'relative'
+ c.style.boxShadow = '0 1px 10px black'
+ c.style.borderRadius = '5px'
+ c.style.width = c.style.height = `${this.innerSize + 2 * this.padding}px`
+ c.style.background = this.options.background
+ }
+
+ _createWheel () {
+ let c = this.wheel = doc.createElement('canvas')
+ c.width = c.height = this.innerSize
+ c.style.position = 'absolute'
+ c.style.margin = c.style.padding = '0'
+ c.style.left = c.style.top = `${this.padding}px`
+
+ this._drawWheel(c.getContext('2d'))
+ this.container.appendChild(c)
+ }
+
+ _drawWheel (ctx) {
+ let s, i
+
+ ctx.save()
+ ctx.translate(this.wheelRadius, this.wheelRadius)
+ s = this.wheelRadius - this.triangleRadius
+ // Draw a circle for every color
+ for (i = 0; i < 360; i++) {
+ ctx.rotate(PI / -180) // rotate one degree
+ ctx.beginPath()
+ ctx.fillStyle = 'hsl(' + i + ', 100%, 50%)'
+ ctx.arc(this.wheelRadius - (s / 2), 0, s / 2, 0, PI * 2, true)
+ ctx.fill()
+ }
+ ctx.restore()
+ }
+
+ _createTriangle () {
+ let c = this.triangle = doc.createElement('canvas')
+
+ c.width = c.height = this.innerSize
+ c.style.position = 'absolute'
+ c.style.margin = c.style.padding = '0'
+ c.style.left = c.style.top = this.padding + 'px'
+
+ this.triangleCtx = c.getContext('2d')
+
+ this.container.appendChild(c)
+ }
+
+ _drawTriangle () {
+ const hx = this.hx
+ const hy = this.hy
+ const sx = this.sx
+ const sy = this.sy
+ const vx = this.vx
+ const vy = this.vy
+ const size = this.innerSize
+
+ let ctx = this.triangleCtx
+
+ // clear
+ ctx.clearRect(0, 0, size, size)
+
+ ctx.save()
+ ctx.translate(this.wheelRadius, this.wheelRadius)
+
+ // make a triangle
+ ctx.beginPath()
+ ctx.moveTo(hx, hy)
+ ctx.lineTo(sx, sy)
+ ctx.lineTo(vx, vy)
+ ctx.closePath()
+ ctx.clip()
+
+ ctx.fillStyle = '#000'
+ ctx.fillRect(-this.wheelRadius, -this.wheelRadius, size, size)
+ // => black triangle
+
+ // create gradient from hsl(hue, 1, 1) to transparent
+ let grad0 = ctx.createLinearGradient(hx, hy, (sx + vx) / 2, (sy + vy) / 2)
+ const hsla = 'hsla(' + M.round(this.hue * (180 / PI)) + ', 100%, 50%, '
+ grad0.addColorStop(0, hsla + '1)')
+ grad0.addColorStop(1, hsla + '0)')
+ ctx.fillStyle = grad0
+ ctx.fillRect(-this.wheelRadius, -this.wheelRadius, size, size)
+ // => gradient: one side of the triangle is black, the opponent angle is $color
+
+ // create color gradient from white to transparent
+ let grad1 = ctx.createLinearGradient(vx, vy, (hx + sx) / 2, (hy + sy) / 2)
+ grad1.addColorStop(0, '#fff')
+ grad1.addColorStop(1, 'rgba(255, 255, 255, 0)')
+ ctx.globalCompositeOperation = 'lighter'
+ ctx.fillStyle = grad1
+ ctx.fillRect(-this.wheelRadius, -this.wheelRadius, size, size)
+ // => white angle
+
+ ctx.restore()
+ }
+
+ // The two pointers
+ _createWheelPointer () {
+ let c = this.wheelPointer = doc.createElement('canvas')
+ const size = this.wheelPointerSize
+ c.width = c.height = size
+ c.style.position = 'absolute'
+ c.style.margin = c.style.padding = '0'
+ this._drawPointer(c.getContext('2d'), size / 2, this.options.wheelPointerColor1, this.options.wheelPointerColor2)
+ this.container.appendChild(c)
+ }
+
+ _moveWheelPointer () {
+ const r = this.wheelPointerSize / 2
+ const s = this.wheelPointer.style
+ s.top = this.padding + this.wheelRadius - M.sin(this.hue) * (this.triangleRadius + this.wheelThickness / 2) - r + 'px'
+ s.left = this.padding + this.wheelRadius + M.cos(this.hue) * (this.triangleRadius + this.wheelThickness / 2) - r + 'px'
+ }
+
+ _createTrianglePointer () { // create pointer in the triangle
+ let c = this.trianglePointer = doc.createElement('canvas')
+ const size = this.options.trianglePointerSize
+
+ c.width = c.height = size
+ c.style.position = 'absolute'
+ c.style.margin = c.style.padding = '0'
+ this._drawPointer(c.getContext('2d'), size / 2, this.options.trianglePointerColor1, this.options.trianglePointerColor2)
+ this.container.appendChild(c)
+ }
+
+ _moveTrianglePointer (x, y) {
+ const s = this.trianglePointer.style
+ const r = this.options.trianglePointerSize / 2
+ s.top = (this.y + this.wheelRadius + this.padding - r) + 'px'
+ s.left = (this.x + this.wheelRadius + this.padding - r) + 'px'
+ }
+
+ _drawPointer (ctx, r, color1, color2) {
+ ctx.fillStyle = color2
+ ctx.beginPath()
+ ctx.arc(r, r, r, 0, PI * 2, true)
+ ctx.fill() // => black circle
+ ctx.fillStyle = color1
+ ctx.beginPath()
+ ctx.arc(r, r, r - 2, 0, PI * 2, true)
+ ctx.fill() // => white circle with 1px black border
+ ctx.fillStyle = color2
+ ctx.beginPath()
+ ctx.arc(r, r, r / 4 + 2, 0, PI * 2, true)
+ ctx.fill() // => black circle with big white border and a small black border
+ ctx.globalCompositeOperation = 'destination-out'
+ ctx.beginPath()
+ ctx.arc(r, r, r / 4, 0, PI * 2, true)
+ ctx.fill() // => transparent center
+ }
+
+ // The Element and the DOM
+ inject (parent) {
+ parent.appendChild(this.container)
+
+ // calculate canvas position on page
+ const offsets = getOffsets(this.triangle)
+ this.offset = {
+ x: offsets[0],
+ y: offsets[1]
+ }
+ }
+
+ dispose () {
+ let parent = this.container.parentNode
+ if (parent) {
+ parent.removeChild(this.container)
+ }
+ }
+
+ getElement () {
+ return this.container
+ }
+
+ // Color accessors
+ getCSS () {
+ const h = Math.round(this.hue * (180 / PI))
+ const s = Math.round(this.saturation * 100)
+ const l = Math.round(this.lightness * 100)
+
+ return `hsl(${h}, ${s}%, ${l}%)`
+ }
+
+ getHEX () {
+ return rgb_to_hex(...this.getRGB())
+ }
+
+ setHEX (hex) {
+ this.setRGB(...hex_to_rgb(hex))
+ }
+
+ getRGB () {
+ return hsl_to_rgb(...this.getHSL())
+ }
+
+ setRGB (r, g, b) {
+ this.setHSL(...rgb_to_hsl(r, g, b))
+ }
+
+ getHSL () {
+ return [this.hue, this.saturation, this.lightness]
+ }
+
+ setHSL (h, s, l) {
+ this.hue = h
+ this.saturation = s
+ this.lightness = l
+
+ this._initColor()
+ }
+
+ _initColor () {
+ this._calculatePositions()
+ this._moveWheelPointer()
+ this._drawTriangle()
+ this._moveTrianglePointer()
+ }
+
+ // Mouse event handling
+ _attachEvents () {
+ this.down = null
+
+ let mousedown = (evt) => {
+ evt.stopPropagation()
+ evt.preventDefault()
+
+ doc.body.addEventListener('mousemove', mousemove, false)
+ doc.body.addEventListener('mouseup', mouseup, false)
+ this._map(evt.pageX, evt.pageY)
+ }
+
+ let mousemove = (evt) => {
+ this._move(evt.pageX, evt.pageY)
+ }
+
+ let mouseup = (evt) => {
+ if (this.down) {
+ this.down = null
+ this._fireEvent('dragend')
+ }
+ doc.body.removeEventListener('mousemove', mousemove, false)
+ doc.body.removeEventListener('mouseup', mouseup, false)
+ }
+
+ this.container.addEventListener('mousedown', mousedown, false)
+ this.container.addEventListener('mousemove', mousemove, false)
+ }
+
+ _map (x, y) {
+ const ox = x
+ const oy = y
+
+ x -= this.offset.x + this.wheelRadius
+ y -= this.offset.y + this.wheelRadius
+
+ const r = M.sqrt(x * x + y * y) // Pythagoras
+ if (r > this.triangleRadius && r < this.wheelRadius) {
+ // Wheel
+ this.down = 'wheel'
+ this._fireEvent('dragstart')
+ this._move(ox, oy)
+ } else if (r < this.triangleRadius) {
+ // Inner circle
+ this.down = 'triangle'
+ this._fireEvent('dragstart')
+ this._move(ox, oy)
+ }
+ }
+
+ _move (x, y) {
+ if (!this.down) {
+ return
+ }
+
+ x -= this.offset.x + this.wheelRadius
+ y -= this.offset.y + this.wheelRadius
+
+ let rad = M.atan2(-y, x)
+ if (rad < 0) {
+ rad += 2 * PI
+ }
+
+ if (this.down === 'wheel') {
+ this.hue = rad
+ this._initColor()
+ this._fireEvent('drag')
+ } else if (this.down === 'triangle') {
+ // get radius and max radius
+ let rad0 = (rad + 2 * PI - this.hue) % (2 * PI)
+ let rad1 = rad0 % ((2 / 3) * PI) - (PI / 3)
+ let a = 0.5 * this.triangleRadius
+ let b = M.tan(rad1) * a
+ let r = M.sqrt(x * x + y * y) // Pythagoras
+ let maxR = M.sqrt(a * a + b * b) // Pythagoras
+
+ if (r > maxR) {
+ const dx = M.tan(rad1) * r
+ let rad2 = M.atan(dx / maxR)
+ if (rad2 > PI / 3) {
+ rad2 = PI / 3
+ } else if (rad2 < -PI / 3) {
+ rad2 = -PI / 3
+ }
+ rad += rad2 - rad1
+
+ rad0 = (rad + 2 * PI - this.hue) % (2 * PI)
+ rad1 = rad0 % ((2 / 3) * PI) - (PI / 3)
+ b = M.tan(rad1) * a
+ r = maxR = M.sqrt(a * a + b * b) // Pythagoras
+ }
+
+ x = M.round(M.cos(rad) * r)
+ y = M.round(-M.sin(rad) * r)
+
+ const l = this.lightness = ((M.sin(rad0) * r) / this.triangleSideLength) + 0.5
+
+ const widthShare = 1 - (M.abs(l - 0.5) * 2)
+ let s = this.saturation = (((M.cos(rad0) * r) + (this.triangleRadius / 2)) / (1.5 * this.triangleRadius)) / widthShare
+ s = M.max(0, s) // cannot be lower than 0
+ s = M.min(1, s) // cannot be greater than 1
+
+ this.lightness = l
+ this.saturation = s
+
+ this.x = x
+ this.y = y
+ this._moveTrianglePointer()
+
+ this._fireEvent('drag')
+ }
+ }
+
+ /***************
+ * Init helpers *
+ ***************/
+
+ static initInput (input, options) {
+ options = options || {}
+
+ let ct
+ let openColorTriangle = function () {
+ let hex = input.value
+ if (options.parseColor) hex = options.parseColor(hex)
+ if (!ct) {
+ options.size = options.size || input.offsetWidth
+ options.background = win.getComputedStyle(input, null).backgroundColor
+ options.margin = options.margin || 10
+ options.event = options.event || 'dragend'
+
+ ct = new ColorTriangle(hex, options)
+ ct.addEventListener(options.event, () => {
+ const hex = ct.getHEX()
+ input.value = options.uppercase ? hex.toUpperCase() : hex
+ fireChangeEvent()
+ })
+ } else {
+ ct.setHEX(hex)
+ }
+
+ let top = input.offsetTop
+ if (win.innerHeight - input.getBoundingClientRect().top > input.offsetHeight + options.margin + options.size) {
+ top += input.offsetHeight + options.margin // below
+ } else {
+ top -= options.margin + options.size // above
+ }
+
+ let el = ct.getElement()
+ el.style.position = 'absolute'
+ el.style.left = input.offsetLeft + 'px'
+ el.style.top = top + 'px'
+ el.style.zIndex = '1338' // above everything
+
+ ct.inject(input.parentNode)
+ }
+
+ let closeColorTriangle = () => {
+ if (ct) {
+ ct.dispose()
+ }
+ }
+
+ let fireChangeEvent = () => {
+ let evt = doc.createEvent('HTMLEvents')
+ evt.initEvent('input', true, false) // bubbles = true, cancable = false
+ input.dispatchEvent(evt) // fire event
+ }
+
+ input.addEventListener('focus', openColorTriangle, false)
+ input.addEventListener('blur', closeColorTriangle, false)
+ input.addEventListener('keyup', () => {
+ const val = input.value
+ if (val.match(/^#((?:[0-9A-Fa-f]{3})|(?:[0-9A-Fa-f]{6}))$/)) {
+ openColorTriangle()
+ fireChangeEvent()
+ } else {
+ closeColorTriangle()
+ }
+ }, false)
+ }
+
+ /*******************
+ * Helper functions *
+ *******************/
+
+ _setOptions (opts) {
+ opts = opts || {}
+ let dflt = this.options
+ let options = this.options = {}
+
+ each(dflt, function (val, key) {
+ options[key] = (opts.hasOwnProperty(key))
+ ? opts[key]
+ : val
+ })
+ }
+
+ addEventListener (type, fn) {
+ if (!this.events) {
+ this.events = {}
+ }
+ if (!this.events[type]) {
+ this.events[type] = []
+ }
+ this.events[type].push(fn)
+ }
+
+ removeEventListener (type, fn) {
+ if (this.events) {
+ let fns = this.events[type]
+ const l = fns.length
+ for (let i = 0; i < l; i += 1) {
+ if (fns[i] === fn) {
+ fns.splice(i, 1)
+ break
+ }
+ }
+ }
+ }
+
+ _fireEvent (type) {
+ if (this.events) {
+ let evts = this.events[type]
+ if (evts) {
+ const args = Array.prototype.slice.call(arguments, 1)
+ each(evts, function (evt) {
+ evt(...args)
+ })
+ }
+ }
+ }
+}
diff --git a/js/term/themes.js b/js/term/themes.js
index 699c017..aa93bc1 100644
--- a/js/term/themes.js
+++ b/js/term/themes.js
@@ -1,4 +1,5 @@
const $ = require('../lib/chibi')
+const { rgb_to_hex } = require('../lib/color_utils')
const themes = exports.themes = [
[ // 0 - Tango - terminator
@@ -73,7 +74,7 @@ exports.buildColorTable = function () {
// 256color lookup table
// should not be used to look up 0-15 (will return transparent)
- colorTable256 = new Array(16).fill('rgba(0, 0, 0, 0)')
+ colorTable256 = new Array(16).fill('#000000')
// fill color table
// colors 16-231 are a 6x6x6 color cube
@@ -83,14 +84,14 @@ exports.buildColorTable = function () {
let redValue = red * 40 + (red ? 55 : 0)
let greenValue = green * 40 + (green ? 55 : 0)
let blueValue = blue * 40 + (blue ? 55 : 0)
- colorTable256.push(`rgb(${redValue}, ${greenValue}, ${blueValue})`)
+ colorTable256.push(rgb_to_hex(redValue, greenValue, blueValue))
}
}
}
// colors 232-255 are a grayscale ramp, sans black and white
for (let gray = 0; gray < 24; gray++) {
let value = gray * 10 + 8
- colorTable256.push(`rgb(${value}, ${value}, ${value})`)
+ colorTable256.push(rgb_to_hex(value, value, value))
}
return colorTable256
@@ -102,17 +103,17 @@ exports.SELECTION_BG = '#b2d7fe'
exports.themePreview = function (themeN) {
$('[data-fg]').forEach((elem) => {
let shade = elem.dataset.fg
- if (/^\d+$/.test(shade)) shade = exports.toCss(shade, themeN)
+ if (/^\d+$/.test(shade)) shade = exports.toHex(shade, themeN)
elem.style.color = shade
})
$('[data-bg]').forEach((elem) => {
let shade = elem.dataset.bg
- if (/^\d+$/.test(shade)) shade = exports.toCss(shade, themeN)
+ if (/^\d+$/.test(shade)) shade = exports.toHex(shade, themeN)
elem.style.backgroundColor = shade
})
}
-exports.toCss = function (shade, themeN) {
+exports.toHex = function (shade, themeN) {
if (/^\d+$/.test(shade)) {
shade = +shade
if (shade < 16) shade = themes[themeN][shade]
diff --git a/js/term_conf.js b/js/term_conf.js
new file mode 100644
index 0000000..87d9d70
--- /dev/null
+++ b/js/term_conf.js
@@ -0,0 +1,105 @@
+const ColorTriangle = require('./lib/colortriangle')
+const $ = require('./lib/chibi')
+const themes = require('./term/themes')
+const { qs } = require('./utils')
+
+function selectedTheme () {
+ return +$('#theme').val()
+}
+
+exports.init = function () {
+ $('#theme').on('change', showColor)
+
+ $('#default_fg').on('input', showColor)
+ $('#default_bg').on('input', showColor)
+
+ let opts = {
+ padding: 10,
+ event: 'drag',
+ uppercase: true,
+ trianglePointerSize: 20,
+ // wheelPointerSize: 12,
+ size: 200,
+ parseColor: (color) => {
+ return themes.toHex(color, selectedTheme())
+ }
+ }
+
+ ColorTriangle.initInput(qs('#default_fg'), opts)
+ ColorTriangle.initInput(qs('#default_bg'), opts)
+
+ $('.colorprev.bg span').on('click', function () {
+ const bg = this.dataset.bg
+ if (typeof bg != 'undefined') $('#default_bg').val(bg)
+ showColor()
+ })
+
+ $('.colorprev.fg span').on('click', function () {
+ const fg = this.dataset.fg
+ if (typeof fg != 'undefined') $('#default_fg').val(fg)
+ showColor()
+ })
+
+ let $presets = $('#fgbg_presets')
+ for (let i = 0; i < themes.fgbgThemes.length; i++) {
+ const thm = themes.fgbgThemes[i]
+ const fg = thm[0]
+ const bg = thm[1]
+ const lbl = thm[2]
+ const tit = thm[3]
+ $presets.htmlAppend(
+ ' ' + lbl + ' ')
+
+ if ((i + 1) % 5 === 0) $presets.htmlAppend('
')
+ }
+
+ $('.preset').on('click', function () {
+ $('#default_fg').val(this.dataset.xfg)
+ $('#default_bg').val(this.dataset.xbg)
+ showColor()
+ })
+
+ showColor()
+}
+
+function showColor () {
+ let ex = qs('.color-example')
+ let fg = $('#default_fg').val()
+ let bg = $('#default_bg').val()
+
+ if (/^\d+$/.test(fg)) {
+ fg = +fg
+ } else if (!/^#[\da-f]{6}$/i.test(fg)) {
+ fg = 'black'
+ }
+
+ if (/^\d+$/.test(bg)) {
+ bg = +bg
+ } else if (!/^#[\da-f]{6}$/i.test(bg)) {
+ bg = 'black'
+ }
+
+ const themeN = selectedTheme()
+ ex.dataset.fg = fg
+ ex.dataset.bg = bg
+
+ themes.themePreview(themeN)
+
+ $('.colorprev.fg span').css('background', themes.toHex(bg, themeN))
+}
+
+exports.nextTheme = () => {
+ let sel = qs('#theme')
+ let i = sel.selectedIndex
+ sel.options[++i % sel.options.length].selected = true
+ showColor()
+}
+
+exports.prevTheme = () => {
+ let sel = qs('#theme')
+ let i = sel.selectedIndex
+ sel.options[(sel.options.length + (--i)) % sel.options.length].selected = true
+ showColor()
+}
diff --git a/pages/cfg_term.php b/pages/cfg_term.php
index 51fe377..9db48f2 100644
--- a/pages/cfg_term.php
+++ b/pages/cfg_term.php
@@ -26,8 +26,8 @@
- ◀
- ▶
+ ◀
+ ▶