mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-27 03:13:37 +08:00
1426 lines
33 KiB
HTML
1426 lines
33 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
|
<title>fish shell configuration</title>
|
|
<style type="text/css">
|
|
|
|
body {
|
|
background-color: #292929;
|
|
font-family: Courier, "Courier New", monospace;
|
|
color: white;
|
|
}
|
|
|
|
#ancestor {
|
|
width: 800px;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
margin-top: 25px;
|
|
}
|
|
|
|
#parent {
|
|
width: 100%;
|
|
min-height: 480px;
|
|
margin-top: 12px;
|
|
}
|
|
|
|
#tab_parent {
|
|
display: table;
|
|
width: 100%;
|
|
height: 50px;;
|
|
}
|
|
|
|
.tab {
|
|
display: table-cell;
|
|
border: 1px solid #111;
|
|
border-right: none;
|
|
padding-bottom: 15px;
|
|
padding-top: 15px;
|
|
font-size: 17pt;
|
|
text-align: center;
|
|
width: 20%;
|
|
background-color: #292929;
|
|
cursor: pointer;
|
|
}
|
|
|
|
#tab_parent :first-child {
|
|
border-top-left-radius: 8px;
|
|
}
|
|
|
|
#tab_parent :last-child {
|
|
border-right: 1px solid #111;
|
|
border-top-right-radius: 8px;
|
|
}
|
|
|
|
.tab_first {
|
|
border-top-left-radius: 5px;
|
|
border-bottom-left-radius: 5px;
|
|
}
|
|
|
|
.tab_last {
|
|
border-top-right-radius: 5px;
|
|
border-bottom-right-radius: 5px;
|
|
}
|
|
|
|
.selected_tab {
|
|
background-color: black;
|
|
border-color: black;
|
|
}
|
|
|
|
#tab_contents {
|
|
padding-top: 35px;
|
|
width: 100%;
|
|
background-color: black;
|
|
border-bottom-left-radius: 8px;
|
|
border-bottom-right-radius: 8px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.footer {
|
|
clear: both;
|
|
height: 30px;
|
|
}
|
|
|
|
#master_detail_table {
|
|
display: table;
|
|
margin-top: 10px;
|
|
margin-left: 12px;
|
|
margin-right: 12px;
|
|
}
|
|
|
|
#master {
|
|
display: table-cell;
|
|
text-align: right;
|
|
min-width: 200px;
|
|
font-size: 16pt;
|
|
padding-bottom: 20px;
|
|
padding-top: 35px;
|
|
vertical-align: top;
|
|
}
|
|
|
|
#detail {
|
|
display: table-cell;
|
|
border: 1px solid #555;
|
|
background-color: #181818;
|
|
padding-top: 30px;
|
|
padding-bottom: 20px;
|
|
padding-left: 30px;
|
|
padding-right: 30px;
|
|
border-radius: 5;
|
|
width: 100%;
|
|
}
|
|
|
|
#detail_function {
|
|
white-space: pre-wrap;
|
|
width: 100%;
|
|
font-size: 11pt;
|
|
color: #BBB;
|
|
}
|
|
|
|
.master_element {
|
|
padding-top: 6px;
|
|
padding-bottom: 11px;
|
|
padding-left: 5px;
|
|
padding-right: 22px;
|
|
font-size: 12pt;
|
|
/* Make our border overlap the detail, even if we're unselected (so it doesn't jump when selected) */
|
|
position: relative;
|
|
left: 1px;
|
|
border-bottom-style: solid;
|
|
border-bottom-width: 0px;
|
|
}
|
|
|
|
.selected_master_elem {
|
|
border: 1px solid #555;
|
|
border-right: none;
|
|
background-color: #181818;
|
|
|
|
border-top-left-radius: 5;
|
|
border-bottom-left-radius: 5;
|
|
|
|
/* Pad one less than .master_element, to accomodate our border. */
|
|
padding-top: 5px;
|
|
padding-bottom: 10px;
|
|
padding-left: 4px;
|
|
}
|
|
|
|
.master_element_text {
|
|
text-decoration: none;
|
|
padding-bottom: 1px;
|
|
border-bottom-style: inherit;
|
|
border-bottom-color: inherit;
|
|
border-bottom-width: 1px;
|
|
}
|
|
|
|
.master_element_description {
|
|
text-decoration: none;
|
|
padding-top: 15px;
|
|
font-size: 10pt;
|
|
border-bottom-style: inherit;
|
|
border-bottom-color: inherit;
|
|
border-bottom-width: 1px;
|
|
display: none;
|
|
}
|
|
|
|
.selected_master_elem > .master_element_description {
|
|
display: inline;
|
|
}
|
|
|
|
/* We have a newline between the label and description; hide it initially, but show it when it's selected */
|
|
.master_element > br { display: none; }
|
|
.selected_master_elem > br { display: inherit; }
|
|
|
|
/* Set this class to suppress the border bottom on master_element_texts with visible descriptions */
|
|
.master_element_no_border { border-bottom-width: 0 }
|
|
|
|
#colorpicker_term256 {
|
|
border: solid #444 1px;
|
|
}
|
|
|
|
.colorpicker_modifiers {
|
|
margin-top: 10px;
|
|
display:inline-block;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
color: #AAA;
|
|
font-size: smaller;
|
|
}
|
|
|
|
.colorpicker_modifier_cell {
|
|
cursor: pointer;
|
|
display:inline-block;
|
|
text-align: center;
|
|
border: groove #333 2px;
|
|
padding: 5px;
|
|
margin-top: 5px;
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
}
|
|
|
|
.modifier_cell_selected {
|
|
color: #CCC;
|
|
border-color: #AAA;
|
|
background-color: #444;
|
|
}
|
|
|
|
#data_table {
|
|
table-layout:fixed;
|
|
color: #CCC;
|
|
width: 100%;
|
|
padding-left: 10px;
|
|
padding-right: 10px;
|
|
}
|
|
|
|
.data_table_row {
|
|
}
|
|
|
|
.data_table_cell {
|
|
padding-top: 5px;
|
|
padding-bottom: 5px;
|
|
vertical-align: top;
|
|
overflow:hidden;
|
|
border-bottom: #444 dotted 1px;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
/* The CSS we apply when a table row is filtered */
|
|
.data_table_row_filtered {
|
|
display: none;
|
|
}
|
|
|
|
.no_overflow {
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.colorpicker_target {
|
|
margin: 0 0 -50px 0;
|
|
position: relative;
|
|
bottom: 47px;
|
|
float: left; /* for some reason this makes the cells that it overlaps (before adjusting its bottom) clickable in Safari */
|
|
}
|
|
|
|
.colorpicker_target_tab {
|
|
cursor: pointer;
|
|
color: #555;
|
|
border: solid 1px #555;
|
|
padding-top: 5px;
|
|
padding-bottom: 5px;
|
|
padding-left: 7px;
|
|
padding-right: 7px;
|
|
display: inline-block;
|
|
background-color: black;
|
|
margin-right: -2px;
|
|
min-width: 110px;
|
|
text-align: center;
|
|
}
|
|
|
|
.colorpicker_target_selected {
|
|
background-color: #181818; /* same as #detail */
|
|
color: white;
|
|
}
|
|
|
|
.colorpicker_term256_row {
|
|
}
|
|
|
|
.colorpicker_term256_cell {
|
|
width: 29;
|
|
height: 29;
|
|
}
|
|
|
|
.colorpicker_cell_selected {
|
|
border: dashed white 3px;
|
|
width: 23;
|
|
height: 23;
|
|
}
|
|
|
|
.error_msg {
|
|
color: red;
|
|
font-size: 12pt;
|
|
margin-left: 24pt;
|
|
margin-top: 5pt;
|
|
margin-bottom: 5pt;
|
|
}
|
|
|
|
img.delete_icon {
|
|
width: 20px;
|
|
height: 20px;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
border: none;
|
|
}
|
|
|
|
#table_filter_container {
|
|
/* top right bottom left*/
|
|
padding: 0 10 10 30;
|
|
text-align: right;
|
|
position: relative;
|
|
bottom: 10px;
|
|
}
|
|
|
|
#table_filter_text_box {
|
|
|
|
}
|
|
|
|
.filter_text_box {
|
|
width: 250px;
|
|
padding: 5 10 5 10;
|
|
background-color: #888;
|
|
border: #222 solid 3px;
|
|
border-radius: 15px;
|
|
font-size: 12pt;
|
|
color: white;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.text_box_transient {
|
|
color: #C8C8C8;
|
|
}
|
|
|
|
.prompt_demo {
|
|
/* This is the div that holds what the prompt looks like */
|
|
width: 100%;
|
|
background-color: black;
|
|
border-radius: 5px;
|
|
display: table;
|
|
}
|
|
|
|
.prompt_save_button {
|
|
border-radius: 5px;
|
|
border: solid #474747 1px;
|
|
text-shadow: 0px 1px #000;
|
|
padding: 5px 8px;
|
|
font-size: 12pt;
|
|
display: inline-block;
|
|
margin-top: 12px;
|
|
background-color: #282828;
|
|
color: #DDD;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.prompt_save_button:hover {
|
|
background-color: #333;
|
|
border: solid #525252 1px;
|
|
color: #EEE;
|
|
}
|
|
|
|
.prompt_demo_text {
|
|
white-space: pre;
|
|
line-height: 170%;
|
|
padding: 4px 12px;
|
|
font-size: 14pt;
|
|
top: 0px;
|
|
bottom: 0px;
|
|
vertical-align: middle;
|
|
display: table-cell;
|
|
height: 72px; /* this is really the min height */
|
|
}
|
|
|
|
.prompt_function {
|
|
/* This is the div that holds the prompt function's definition */
|
|
width: 100%;
|
|
color: #BBB;
|
|
font-size: 10pt;
|
|
}
|
|
|
|
.prompt_function_text {
|
|
white-space: pre-wrap;
|
|
padding: 25px 3px;
|
|
}
|
|
|
|
</style>
|
|
|
|
<script type="text/javascript" src="jquery.js"></script>
|
|
<script type="text/javascript">
|
|
|
|
function show_error(msg) {
|
|
$('#global_error').text(msg)
|
|
}
|
|
|
|
function request_failed(jqXHR, textStatus, errorThrown) {
|
|
msg = ''
|
|
if (textStatus == "timeout") {
|
|
msg = 'The request timed out. Perhaps the server has shut down or is hung.'
|
|
} else if (textStatus == "error") {
|
|
msg = 'The request received an error.'
|
|
if (jqXHR.status == 0)
|
|
msg = msg + ' Perhaps the server has shut down.'
|
|
} else if (msg == 'abort') {
|
|
msg = 'The request aborted.'
|
|
} else if (msg == 'parsererror') {
|
|
msg = 'The request experienced a parser error.'
|
|
} else {
|
|
msg = 'The request had an unknown error "' + textStatus + '."'
|
|
}
|
|
|
|
if (errorThrown.length > 0) {
|
|
msg = msg + ' The HTTP reply returned ' + errorThrown
|
|
}
|
|
show_error(msg)
|
|
}
|
|
|
|
/* Runs a GET request, parses the JSON, and invokes the handler for each element in it. The JSON result is assumed to be an array. */
|
|
function run_get_request_with_bulk_handler(url, handler) {
|
|
$.ajax({
|
|
type: "GET",
|
|
url: url,
|
|
success: function(data){
|
|
$('#global_error').text('')
|
|
handler($.parseJSON(data))
|
|
},
|
|
error: request_failed
|
|
})
|
|
}
|
|
|
|
function run_get_request(url, handler) {
|
|
run_get_request_with_bulk_handler(url, function(json_contents){
|
|
$.each(json_contents, function(idx, contents){
|
|
handler(contents)
|
|
})
|
|
})
|
|
}
|
|
|
|
|
|
/* As above but with POST request. */
|
|
function run_post_request(url, data_map, handler) {
|
|
$.ajax({
|
|
type: "POST",
|
|
url: url,
|
|
data: data_map,
|
|
success: function(data){
|
|
$('#global_error').text('')
|
|
$.each($.parseJSON(data), function(idx, contents) {
|
|
handler(contents)
|
|
})
|
|
},
|
|
error: request_failed
|
|
})
|
|
}
|
|
|
|
function rgb_to_hsl(r, g, b){
|
|
r /= 255, g /= 255, b /= 255;
|
|
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
var h, s, l = (max + min) / 2;
|
|
|
|
if(max == min){
|
|
h = s = 0; // achromatic
|
|
}else{
|
|
var d = max - min;
|
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
switch(max){
|
|
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
|
case g: h = (b - r) / d + 2; break;
|
|
case b: h = (r - g) / d + 4; break;
|
|
}
|
|
h /= 6;
|
|
}
|
|
|
|
return [h, s, l];
|
|
}
|
|
|
|
function hsl_to_rgb(h, s, l){
|
|
var r, g, b;
|
|
|
|
if(s == 0){
|
|
r = g = b = l; // achromatic
|
|
}else{
|
|
function hue2rgb(p, q, t){
|
|
if(t < 0) t += 1;
|
|
if(t > 1) t -= 1;
|
|
if(t < 1/6) return p + (q - p) * 6 * t;
|
|
if(t < 1/2) return q;
|
|
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
|
return p;
|
|
}
|
|
|
|
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
var p = 2 * l - q;
|
|
r = hue2rgb(p, q, h + 1.0/3);
|
|
g = hue2rgb(p, q, h);
|
|
b = hue2rgb(p, q, h - 1.0/3);
|
|
}
|
|
|
|
return [r * 255, g * 255, b * 255]
|
|
}
|
|
|
|
/* Given an RGB color as a hex string, like FF0033, convert to HSL, apply the function to adjust its lightness, then return the new color as an RGB string */
|
|
function adjust_lightness(color_str, func) {
|
|
/* Hack to handle for example F00 */
|
|
if (color_str.length == 3) {
|
|
color_str = color_str[0] + color_str[0] + color_str[1] + color_str[1] + color_str[2] + color_str[2]
|
|
}
|
|
|
|
rgb = parseInt(color_str, 16)
|
|
r = (rgb >> 16) & 0xFF
|
|
g = (rgb >> 8) & 0xFF
|
|
b = (rgb >> 0) & 0xFF
|
|
|
|
hsl = rgb_to_hsl(r, g, b)
|
|
new_lightness = func(hsl[2])
|
|
function to_int_str(val) {
|
|
str = Math.round(val).toString(16)
|
|
while (str.length < 2)
|
|
str = '0' + str
|
|
return str
|
|
}
|
|
|
|
new_rgb = hsl_to_rgb(hsl[0], hsl[1], new_lightness)
|
|
return to_int_str(new_rgb[0]) + to_int_str(new_rgb[1]) + to_int_str(new_rgb[2])
|
|
}
|
|
|
|
/* Given a color, compute the master text color for it, by giving it a minimum brightness */
|
|
function master_color_for_color(color_str) {
|
|
return adjust_lightness(color_str, function(lightness){
|
|
if (lightness < .33) {
|
|
lightness = .33
|
|
}
|
|
return lightness
|
|
})
|
|
}
|
|
|
|
/* Update prompt_demo_text with the given text, adjusting the font */
|
|
function set_prompt_demo_text(txt, font_size) {
|
|
/* Set the font size and the text */
|
|
var prompt_demo_text = $('.prompt_demo_text')
|
|
prompt_demo_text.css('font-size', font_size)
|
|
prompt_demo_text.html(txt)
|
|
}
|
|
|
|
|
|
function switch_tab(new_tab) {
|
|
/* Switch selected tab */
|
|
$(".selected_tab").removeClass("selected_tab")
|
|
$('#' + new_tab).addClass("selected_tab")
|
|
|
|
/* Empty master element */
|
|
$('#master').empty()
|
|
|
|
/* Unselect some things */
|
|
$(".colorpicker_cell_selected").removeClass('colorpicker_cell_selected')
|
|
|
|
/* Hide some things */
|
|
$('#master_detail_table').hide()
|
|
$('#detail_colorpicker').hide()
|
|
$('#detail_prompt').hide()
|
|
$('#detail_function').hide()
|
|
$('#data_table').hide()
|
|
$('#table_filter_container').hide()
|
|
$('#data_table').empty()
|
|
|
|
/* Determine if we will want to show the data table (and associated filter box) */
|
|
var wants_data_table = false
|
|
|
|
/* Load something new */
|
|
if (new_tab == 'tab_colors') {
|
|
/* Keep track of whether this is the first element */
|
|
var first = true
|
|
run_get_request('/colors/', function(key_and_values){
|
|
/* Result is name, description, value */
|
|
var key = key_and_values[0]
|
|
var description = key_and_values[1]
|
|
var style = new Style(key_and_values[2])
|
|
style_map[key] = style
|
|
elem = create_master_element(key, description, style.color, '', select_color_master_element)
|
|
if (first) {
|
|
/* It's the first element, so select it, so something gets selected */
|
|
select_color_master_element(elem)
|
|
first = false
|
|
}
|
|
})
|
|
$('#detail_colorpicker').show()
|
|
$('#master_detail_table').show()
|
|
wants_data_table = false
|
|
} else if (new_tab == 'tab_prompt') {
|
|
/* Get rid of all sample prompts */
|
|
sample_prompts.length = 0
|
|
/* Color the first one blue */
|
|
var first = true;
|
|
run_get_request('/sample_prompts/', function(sample_prompt){
|
|
var name = sample_prompt['name']
|
|
sample_prompts[name] = sample_prompt
|
|
var color = first ? '66F' : 'AAA'
|
|
var func = first ? select_current_prompt_master_element : select_sample_prompt_master_element;
|
|
var elem = create_master_element(name, false/* description */, color, '13pt', func)
|
|
if (first) {
|
|
elem.children('.master_element_text').css('border-bottom-color', color)
|
|
select_current_prompt_master_element(elem)
|
|
}
|
|
first = false;
|
|
})
|
|
$('#detail_prompt').show()
|
|
$('#master_detail_table').show()
|
|
} else if (new_tab == 'tab_functions') {
|
|
/* Keep track of whether this is the first element */
|
|
var first = true
|
|
run_get_request('/functions/', function(contents){
|
|
var elem = create_master_element(contents, false/* description */, 'AAAAAA', '11pt', select_function_master_element)
|
|
if (first) {
|
|
/* It's the first element, so select it, so something gets selected */
|
|
select_function_master_element(elem)
|
|
first = false
|
|
}
|
|
})
|
|
$('#detail_function').show()
|
|
$('#master_detail_table').show()
|
|
wants_data_table = false
|
|
} else if (new_tab == 'tab_variables') {
|
|
run_get_request_with_bulk_handler('/variables/', function(json_contents){
|
|
var rows = new Array()
|
|
for (var i = 0; i < json_contents.length; i++) {
|
|
var contents = json_contents[i]
|
|
var name = contents[0]
|
|
var value = contents[1]
|
|
var flags = contents[2]
|
|
var row = create_data_table_element_text([name, value], false)
|
|
rows[i] = row
|
|
}
|
|
$('#data_table').append(rows.join(''))
|
|
})
|
|
wants_data_table = true
|
|
} else if (new_tab == 'tab_history') {
|
|
// Clear the history map
|
|
history_element_map.length = 0
|
|
run_get_request_with_bulk_handler('/history/', function(json_contents){
|
|
start = new Date().getTime()
|
|
var rows = new Array()
|
|
for (var i = 0; i < json_contents.length; i++) {
|
|
var history_text = json_contents[i]
|
|
rows[i] = create_data_table_element_text([history_text], true)
|
|
history_element_map[last_global_element_identifier] = history_text
|
|
}
|
|
$('#data_table').append(rows.join(''))
|
|
end = new Date().getTime()
|
|
//alert(rows.length + " rows in " + (end - start) + " msec")
|
|
})
|
|
wants_data_table = true
|
|
} else {
|
|
alert("Unknown tab");
|
|
}
|
|
|
|
/* Show or hide the data table and its search field */
|
|
if (wants_data_table) {
|
|
$('#data_table').show()
|
|
$('#table_filter_container').show()
|
|
} else {
|
|
$('#data_table').hide()
|
|
$('#table_filter_container').hide()
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
function current_master_element_name() {
|
|
/* Get the name of the current color variable, like 'autosuggestion' */
|
|
var elems = $('.selected_master_elem')
|
|
if (elems.length == 0) {
|
|
return ''
|
|
}
|
|
elem = elems[0]
|
|
if (elem.id.indexOf('master_') != 0) {
|
|
show_error('Unknown master variable')
|
|
return ''
|
|
}
|
|
return elem.id.substring(7)
|
|
}
|
|
|
|
function is_foreground() {
|
|
/* Returns true if the selected tab is foreground, false if it's background */
|
|
who = $('.colorpicker_target_selected')
|
|
if (who.length == 0) {
|
|
show_error('Not sure if we are in foreground or background')
|
|
return false
|
|
}
|
|
return who[0].id == 'foreground'
|
|
}
|
|
|
|
function current_style() {
|
|
/* Returns the style object corresponding to the current color variable */
|
|
return style_map[current_master_element_name()]
|
|
}
|
|
|
|
function reflect_style() {
|
|
/* Unselect everything */
|
|
$('.colorpicker_cell_selected').removeClass('colorpicker_cell_selected')
|
|
$('.modifier_cell_selected').removeClass('modifier_cell_selected')
|
|
$('.master_element_no_border').removeClass('master_element_no_border')
|
|
|
|
/* Now update the color picker with the current style (if we have one) */
|
|
style = current_style()
|
|
if (style) {
|
|
|
|
/* Use this function to make a color that contrasts well with the given color */
|
|
var adjust = .5
|
|
function compute_constrast(lightness){
|
|
var new_lightness = lightness + adjust
|
|
if (new_lightness > 1.0 || new_lightness < 0.0) {
|
|
new_lightness -= 2 * adjust
|
|
}
|
|
return new_lightness
|
|
}
|
|
|
|
color = is_foreground() ? style.color : style.background_color
|
|
var color_cell = $('#color_' + color)
|
|
color_cell.addClass('colorpicker_cell_selected')
|
|
color_cell.css('border-color', adjust_lightness(is_foreground() ? style.color : style.background_color, compute_constrast))
|
|
|
|
if (style.underline) {
|
|
$('#modifier_underline').addClass('modifier_cell_selected')
|
|
}
|
|
|
|
/* In the master list, ensure the color is visible against the dark background. If we're deselecting, use COLOR_NORMAL */
|
|
master_color = style.color ? master_color_for_color(style.color) : COLOR_NORMAL
|
|
//$('.selected_master_elem').children('.master_element_text').css({'color': master_color, 'border-bottom-color': master_color})
|
|
|
|
var selected_elem = $('.selected_master_elem');
|
|
var desc_elems = selected_elem.children('.master_element_description')
|
|
selected_elem.css({'color': master_color})
|
|
selected_elem.children().css({'border-bottom-color': master_color})
|
|
if (desc_elems.length) {
|
|
/* We have a description element, so hide the bottom border of the master element */
|
|
selected_elem.children('.master_element_text').addClass('master_element_no_border')
|
|
}
|
|
}
|
|
}
|
|
|
|
function cleanup_fish_function(contents) {
|
|
/* Replace leading tabs and groups of four spaces at the beginning of a line with two spaces. */
|
|
lines = contents.split('\n')
|
|
rx = /^[\t ]+/
|
|
for (var i=0; i < lines.length; i++) {
|
|
line = lines[i]
|
|
/* Get leading tabs and spaces */
|
|
whitespace_arr = rx.exec(line)
|
|
if (whitespace_arr) {
|
|
/* Replace four spaces with two spaces, and tabs with two spaces */
|
|
var whitespace = whitespace_arr[0]
|
|
new_whitespace = whitespace.replace(/( )|(\t)/g, ' ')
|
|
lines[i] = new_whitespace + line.slice(whitespace.length)
|
|
}
|
|
}
|
|
return lines.join('\n')
|
|
}
|
|
|
|
function select_master_element(elem) {
|
|
$('.selected_master_elem').removeClass('selected_master_elem')
|
|
$(elem).addClass('selected_master_elem')
|
|
}
|
|
|
|
function select_color_master_element(elem) {
|
|
select_master_element(elem)
|
|
|
|
/* This changed the current style; reflect that */
|
|
reflect_style()
|
|
}
|
|
|
|
function select_function_master_element(elem) {
|
|
select_master_element(elem)
|
|
|
|
run_post_request('/get_function/', {
|
|
what: current_master_element_name()
|
|
}, function(contents){
|
|
/* Replace leading tabs and groups of four spaces at the beginning of a line with two spaces. */
|
|
munged_contents = cleanup_fish_function(contents)
|
|
$('#detail_function').text(munged_contents)
|
|
});
|
|
}
|
|
|
|
var sample_prompts = new Array();
|
|
|
|
function select_sample_prompt_master_element(elem) {
|
|
$('.prompt_save_button').show()
|
|
select_master_element(elem)
|
|
var name = current_master_element_name()
|
|
sample_prompt = sample_prompts[name]
|
|
run_post_request('/get_sample_prompt/', {
|
|
what: sample_prompt['function']
|
|
}, function(keys_and_values){
|
|
var prompt_func = keys_and_values['function']
|
|
var prompt_demo = keys_and_values['demo']
|
|
var prompt_font_size = keys_and_values['font_size']
|
|
set_prompt_demo_text(prompt_demo, prompt_font_size)
|
|
//$('.prompt_demo_text').html(prompt_demo)
|
|
$('.prompt_function_text').text(cleanup_fish_function(prompt_func))
|
|
})
|
|
}
|
|
|
|
function select_current_prompt_master_element(elem) {
|
|
$('.prompt_save_button').hide()
|
|
select_master_element(elem)
|
|
run_get_request_with_bulk_handler('/current_prompt/', function(keys_and_values){
|
|
var prompt_func = keys_and_values['function']
|
|
var prompt_demo = keys_and_values['demo']
|
|
var prompt_font_size = keys_and_values['font_size']
|
|
set_prompt_demo_text(prompt_demo, prompt_font_size)
|
|
$('.prompt_function_text').text(cleanup_fish_function(prompt_func))
|
|
})
|
|
}
|
|
|
|
/* Applies the current prompt */
|
|
function save_current_prompt() {
|
|
var name = current_master_element_name()
|
|
var sample_prompt = sample_prompts[name]
|
|
run_post_request('/set_prompt/', {
|
|
what: sample_prompt['function']
|
|
}, function(contents){
|
|
if (contents == "OK") {
|
|
select_current_prompt_master_element($('#master_Current'))
|
|
} else {
|
|
show_error(contents)
|
|
}
|
|
})
|
|
}
|
|
|
|
function post_style_to_server() {
|
|
style = current_style()
|
|
if (! style)
|
|
return
|
|
|
|
run_post_request('/set_color/', {
|
|
what: current_master_element_name(),
|
|
color: style.color,
|
|
background_color: style.background_color,
|
|
bold: style.bold,
|
|
underline: style.underline
|
|
}, function(contents){
|
|
|
|
})
|
|
}
|
|
|
|
function picked_color_cell(cell) {
|
|
|
|
/* Get the color to set */
|
|
if (cell.id.indexOf('color_') != 0) {
|
|
show_error('Unknown cell')
|
|
return
|
|
}
|
|
color = cell.id.substring(6)
|
|
|
|
/* Determine whether we are going to select or unselect this cell */
|
|
var deselect = $(cell).hasClass('colorpicker_cell_selected')
|
|
|
|
/* Get the current style */
|
|
style = current_style()
|
|
if (! style)
|
|
return
|
|
|
|
/* Change the color */
|
|
if (is_foreground()) {
|
|
style.color = deselect ? '' : color
|
|
} else {
|
|
style.background_color = deselect ? '' : color
|
|
}
|
|
|
|
/* Show our changes */
|
|
reflect_style()
|
|
|
|
/* Tell the server */
|
|
post_style_to_server()
|
|
}
|
|
|
|
function picked_modifier(cell) {
|
|
style = current_style()
|
|
if (! style)
|
|
return
|
|
if (cell.id == 'modifier_underline') {
|
|
style.underline = ! style.underline
|
|
} else if (cell.id == 'modifier_bold') {
|
|
style.bold = ! style.bold
|
|
} else {
|
|
show_error('Unknown cell')
|
|
}
|
|
|
|
reflect_style()
|
|
post_style_to_server()
|
|
}
|
|
|
|
function picked_colorpicker_target(tab) {
|
|
/* The function that gets called when a tab is selected */
|
|
$('.colorpicker_target_selected').removeClass('colorpicker_target_selected')
|
|
$(tab).addClass('colorpicker_target_selected')
|
|
reflect_style()
|
|
}
|
|
|
|
/* Given a color name, like 'normal' or 'red' or 'FF00F0', return an RGB color string (or empty string) */
|
|
function interpret_color(str) {
|
|
str = str.toLowerCase()
|
|
if (str == 'black') return '000000'
|
|
if (str == 'red') return 'FF0000'
|
|
if (str == 'green') return '00FF00'
|
|
if (str == 'brown') return '725000'
|
|
if (str == 'yellow') return 'FFFF00'
|
|
if (str == 'blue') return '0000FF'
|
|
if (str == 'magenta') return 'FF00FF'
|
|
if (str == 'purple') return 'FF00FF'
|
|
if (str == 'cyan') return '00FFFF'
|
|
if (str == 'white') return 'FFFFFF'
|
|
if (str == 'normal') return ''
|
|
return str
|
|
}
|
|
|
|
/* Class representing a color style */
|
|
function Style(stuff) {
|
|
this.color = interpret_color(stuff[0])
|
|
this.background_color = interpret_color(stuff[1])
|
|
this.bold = stuff[2]
|
|
this.underline = stuff[3]
|
|
}
|
|
|
|
var style_map = new Array();
|
|
|
|
/* The first index here corresponds to value 16 */
|
|
term256_colors = [ //247
|
|
"ffd7d7",
|
|
"d7afaf",
|
|
"af8787",
|
|
"875f5f",
|
|
"5f0000",
|
|
"870000",
|
|
"af0000",
|
|
"d70000",
|
|
"ff0000",
|
|
"ff5f5f",
|
|
"d75f5f",
|
|
"d78787",
|
|
"ff8787",
|
|
"ffafaf",
|
|
"ffaf87",
|
|
"ffaf5f",
|
|
"ffaf00",
|
|
"ff875f",
|
|
"ff8700",
|
|
"ff5f00",
|
|
"d75f00",
|
|
"af5f5f",
|
|
"af5f00",
|
|
"d78700",
|
|
"d7875f",
|
|
"af875f",
|
|
"af8700",
|
|
"875f00",
|
|
"d7af87",
|
|
"ffd7af",
|
|
"ffd787",
|
|
"ffd75f",
|
|
"d7af00",
|
|
"d7af5f",
|
|
"ffd700",
|
|
"ffff5f",
|
|
"ffff00",
|
|
"ffff87",
|
|
"ffffaf",
|
|
"ffffd7",
|
|
"d7ff00",
|
|
"afd75f",
|
|
"d7d700",
|
|
"d7d787",
|
|
"d7d7af",
|
|
"afaf87",
|
|
"87875f",
|
|
"5f5f00",
|
|
"878700",
|
|
"afaf00",
|
|
"afaf5f",
|
|
"d7d75f",
|
|
"d7ff5f",
|
|
"d7ff87",
|
|
"87ff00",
|
|
"afff00",
|
|
"afff5f",
|
|
"afd700",
|
|
"87d700",
|
|
"87af00",
|
|
"5f8700",
|
|
"87af5f",
|
|
"5faf00",
|
|
"afd787",
|
|
"d7ffd7",
|
|
"d7ffaf",
|
|
"afffaf",
|
|
"afff87",
|
|
"5fff00",
|
|
"5fd700",
|
|
"87d75f",
|
|
"5fd75f",
|
|
"87ff5f",
|
|
"5fff5f",
|
|
"87ff87",
|
|
"afd7af",
|
|
"87d787",
|
|
"87d7af",
|
|
"87af87",
|
|
"5f875f",
|
|
"5faf5f",
|
|
"005f00",
|
|
"008700",
|
|
"00af00",
|
|
"00d700",
|
|
"00ff00",
|
|
"00ff5f",
|
|
"5fff87",
|
|
"00ff87",
|
|
"87ffaf",
|
|
"afffd7",
|
|
"5fd787",
|
|
"00d75f",
|
|
"5faf87",
|
|
"00af5f",
|
|
"5fffaf",
|
|
"00ffaf",
|
|
"5fd7af",
|
|
"00d787",
|
|
"00875f",
|
|
"00af87",
|
|
"00d7af",
|
|
"5fffd7",
|
|
"87ffd7",
|
|
"00ffd7",
|
|
"d7ffff",
|
|
"afd7d7",
|
|
"87afaf",
|
|
"5f8787",
|
|
"5fafaf",
|
|
"87d7d7",
|
|
"5fd7d7",
|
|
"5fffff",
|
|
"00ffff",
|
|
"87ffff",
|
|
"afffff",
|
|
"00d7d7",
|
|
"00d7ff",
|
|
"5fd7ff",
|
|
"5fafd7",
|
|
"00afd7",
|
|
"00afff",
|
|
"0087af",
|
|
"00afaf",
|
|
"008787",
|
|
"005f5f",
|
|
"005f87",
|
|
"0087d7",
|
|
"0087ff",
|
|
"5fafff",
|
|
"87afff",
|
|
"5f87d7",
|
|
"5f87ff",
|
|
"005fd7",
|
|
"005fff",
|
|
"005faf",
|
|
"5f87af",
|
|
"87afd7",
|
|
"afd7ff",
|
|
"87d7ff",
|
|
"d7d7ff",
|
|
"afafd7",
|
|
"8787af",
|
|
"afafff",
|
|
"8787d7",
|
|
"8787ff",
|
|
"5f5fff",
|
|
"5f5fd7",
|
|
"5f5faf",
|
|
"5f5f87",
|
|
"00005f",
|
|
"000087",
|
|
"0000af",
|
|
"0000d7",
|
|
"0000ff",
|
|
"5f00ff",
|
|
"5f00d7",
|
|
"5f00af",
|
|
"5f0087",
|
|
"8700af",
|
|
"8700d7",
|
|
"8700ff",
|
|
"af00ff",
|
|
"af00d7",
|
|
"d700ff",
|
|
"d75fff",
|
|
"d787ff",
|
|
"ffafd7",
|
|
"ffafff",
|
|
"ffd7ff",
|
|
"d7afff",
|
|
"d7afd7",
|
|
"af87af",
|
|
"af87d7",
|
|
"af87ff",
|
|
"875fd7",
|
|
"875faf",
|
|
"875fff",
|
|
"af5fff",
|
|
"af5fd7",
|
|
"af5faf",
|
|
"d75fd7",
|
|
"d787d7",
|
|
"ff87ff",
|
|
"ff5fff",
|
|
"ff5fd7",
|
|
"ff00ff",
|
|
"ff00af",
|
|
"ff00d7",
|
|
"d700af",
|
|
"d700d7",
|
|
"af00af",
|
|
"870087",
|
|
"5f005f",
|
|
"87005f",
|
|
"af005f",
|
|
"af0087",
|
|
"d70087",
|
|
"d7005f",
|
|
"ff0087",
|
|
"ff005f",
|
|
"ff5f87",
|
|
"d75f87",
|
|
"d75faf",
|
|
"ff5faf",
|
|
"ff87af",
|
|
"ff87d7",
|
|
"d787af",
|
|
"af5f87",
|
|
"875f87",
|
|
"000000",
|
|
"080808",
|
|
"121212",
|
|
"1c1c1c",
|
|
"262626",
|
|
"303030",
|
|
"3a3a3a",
|
|
"444444",
|
|
"4e4e4e",
|
|
"585858",
|
|
"5f5f5f",
|
|
"626262",
|
|
"6c6c6c",
|
|
"767676",
|
|
"808080",
|
|
"878787",
|
|
"8a8a8a",
|
|
"949494",
|
|
"9e9e9e",
|
|
"a8a8a8",
|
|
"afafaf",
|
|
"b2b2b2",
|
|
"bcbcbc",
|
|
"c6c6c6",
|
|
"d0d0d0",
|
|
"d7d7d7",
|
|
"dadada",
|
|
"e4e4e4",
|
|
"eeeeee",
|
|
"ffffff",
|
|
]
|
|
|
|
var items_per_row = 15
|
|
var show_labels = 0
|
|
|
|
var COLOR_NORMAL = 'CCC'
|
|
|
|
/* Adds a new element to master */
|
|
function create_master_element(contents, description_or_false, color, font_size, click_handler) {
|
|
/* In the master list, ensure the color is visible against the dark background */
|
|
var master_color = color ? master_color_for_color(color) : COLOR_NORMAL
|
|
var master_style = 'color: #' + master_color
|
|
var master_children_style = 'border-bottom-color: #' + master_color
|
|
var text_style = ''
|
|
|
|
if (font_size.length > 0) {
|
|
text_style += 'font-size: ' + font_size + ';'
|
|
}
|
|
|
|
if (contents.length >= 20) {
|
|
text_style += 'letter-spacing:-2px;'
|
|
}
|
|
|
|
elem = $('<div/>', {
|
|
class: 'master_element',
|
|
id: 'master_' + contents,
|
|
style: master_style,
|
|
click: function(){
|
|
click_handler(this)
|
|
}
|
|
}).append(
|
|
$("<span/>", {
|
|
class: 'master_element_text',
|
|
style: text_style,
|
|
text: contents,
|
|
})
|
|
)
|
|
|
|
/* Append description if we have one */
|
|
if (description_or_false) {
|
|
/* Newline between label and description */
|
|
elem.append($('<br/>'))
|
|
elem.append(
|
|
$('<span/>', {
|
|
class: 'master_element_description',
|
|
text: description_or_false
|
|
})
|
|
)
|
|
}
|
|
|
|
/* Update border color of the master element's children */
|
|
elem.children().css(master_children_style)
|
|
|
|
elem.appendTo('#master')
|
|
return elem
|
|
}
|
|
|
|
/* Toggle the no_overflow class */
|
|
function toggle_overflow(who) {
|
|
$(who).toggleClass('no_overflow')
|
|
}
|
|
|
|
function escape_HTML(foo) {
|
|
return foo.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
|
}
|
|
|
|
/* Given the image, walk up to the table */
|
|
function tell_fish_to_delete_element(idx) {
|
|
var row_elem = $('#data_table_row_' + idx)
|
|
var txt = history_element_map[idx]
|
|
run_post_request('/delete_history_item/', {
|
|
what: txt
|
|
}, function(contents){
|
|
if (contents == "OK") {
|
|
row_elem.remove();
|
|
} else {
|
|
show_error(contents)
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
/* Creates a new row in the data table */
|
|
var last_global_element_identifier = 0
|
|
var history_element_map = new Array();
|
|
|
|
function create_data_table_element_text(contents_list, show_delete_button) {
|
|
var element_identifier = (++last_global_element_identifier).toString()
|
|
lines = new Array()
|
|
var result_str = '<tr class="data_table_row" id="data_table_row_' + element_identifier + '">'
|
|
for (idx = 0; idx < contents_list.length; idx++) {
|
|
/* If we have more than one, then align the first one right, subsequent ones left */
|
|
if (idx == 0 && contents_list.length > 1) {
|
|
result_str += '<td class="data_table_cell no_overflow" style="text-align: right; padding-right: 30px;">'
|
|
} else {
|
|
result_str += '<td class="data_table_cell no_overflow" style="text-align: left; padding-right: 30px;" onClick:"toggle_overflow(this)">'
|
|
}
|
|
text_list = contents_list[idx].split("\n")
|
|
for (j=0; j < text_list.length; j++) {
|
|
if (j > 0) result_str += '<br>'
|
|
result_str += escape_HTML(text_list[j]);
|
|
}
|
|
result_str += '</td>'
|
|
}
|
|
if (show_delete_button) {
|
|
result_str += '<td class="data_table_cell" style="text-align: right; width: 25px"><a onClick="tell_fish_to_delete_element(' + element_identifier + ')"><img class="delete_icon" src="delete.png"></a></td>'
|
|
}
|
|
result_str += '</tr>'
|
|
return result_str
|
|
}
|
|
|
|
/* Put stuff in colorpicker_term256 */
|
|
function populate_colorpicker_term256() {
|
|
$('#colorpicker_term256').empty()
|
|
var idx
|
|
for (idx = 0; idx < term256_colors.length; idx += items_per_row) {
|
|
var row = $('<tr>', {
|
|
class: 'colorpicker_term256_row'
|
|
})
|
|
|
|
for (var subidx = 0; subidx < items_per_row && idx + subidx < term256_colors.length; subidx++) {
|
|
cell_style = 'background-color: #' + term256_colors[idx + subidx]
|
|
var cell = $('<td>', {
|
|
class: 'colorpicker_term256_cell',
|
|
style: cell_style,
|
|
id: 'color_' + term256_colors[idx + subidx],
|
|
text: show_labels ? String(subidx + idx + 223) : '',
|
|
onClick: 'picked_color_cell(this)'
|
|
})
|
|
|
|
/* For reasons I don't understand, this makes the selected cell border appear in Firefox */
|
|
cell.append($('<div>', {
|
|
style: 'width: 100%; height: 100%'
|
|
}))
|
|
row.append(cell)
|
|
}
|
|
|
|
$('#colorpicker_term256').append(row)
|
|
}
|
|
}
|
|
|
|
/* Update the filter text box */
|
|
function update_table_filter_text_box(allow_transient_message) {
|
|
var box = $('#table_filter_text_box')
|
|
var has_transient = box.hasClass('text_box_transient')
|
|
if (! allow_transient_message && has_transient) {
|
|
box.val('')
|
|
box.removeClass('text_box_transient')
|
|
has_transient = false
|
|
} else if (allow_transient_message && ! has_transient && ! box.val().length) {
|
|
box.val('Filter')
|
|
box.addClass('text_box_transient')
|
|
has_transient = true
|
|
}
|
|
|
|
var search_text = box.val()
|
|
if (has_transient || search_text.length == 0) {
|
|
/* Unfilter all */
|
|
$('.data_table_row_filtered').removeClass('data_table_row_filtered')
|
|
} else {
|
|
/* Helper function to return whether a node (or its descendants) matches the given text */
|
|
function match_text(node) {
|
|
if (node.nodeType == 3) {
|
|
return node.nodeValue.indexOf(search_text) != -1
|
|
} else {
|
|
for (var i = 0, len = node.childNodes.length; i < len; ++i) {
|
|
if (match_text(node.childNodes[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
$('.data_table_row').each(function(idx) {
|
|
var row = $(this)
|
|
var is_hidden = row.hasClass('data_table_row_filtered')
|
|
var should_be_hidden = ! match_text(this)
|
|
if (is_hidden && ! should_be_hidden) {
|
|
row.removeClass('data_table_row_filtered')
|
|
} else if (! is_hidden && should_be_hidden) {
|
|
row.addClass('data_table_row_filtered')
|
|
}
|
|
})
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
populate_colorpicker_term256()
|
|
var tab_name
|
|
switch (window.location.hash) {
|
|
case '#functions':
|
|
tab_name = 'tab_functions'
|
|
break
|
|
case '#variables':
|
|
tab_name = 'tab_variables'
|
|
break
|
|
case '#history':
|
|
tab_name = 'tab_history'
|
|
break
|
|
case '#prompt':
|
|
tab_name = 'tab_prompt'
|
|
break
|
|
case '#colors':
|
|
default:
|
|
tab_name = 'tab_colors'
|
|
break;
|
|
}
|
|
switch_tab(tab_name)
|
|
})
|
|
</script>
|
|
|
|
</head>
|
|
<body>
|
|
|
|
<div id="ancestor">
|
|
<span style="font-size: 30pt">fish</span><p id="global_error" class="error_msg"></p>
|
|
<div id="parent">
|
|
<div id="tab_parent">
|
|
<div class="tab selected_tab" id="tab_colors" onClick="switch_tab('tab_colors')">colors</div>
|
|
<div class="tab" id="tab_prompt" onClick="switch_tab('tab_prompt')">prompt</div>
|
|
<div class="tab" id="tab_functions" onClick="switch_tab('tab_functions')">functions</div>
|
|
<div class="tab" id="tab_variables" onClick="switch_tab('tab_variables')">variables</div>
|
|
<div class="tab" id="tab_history" onClick="switch_tab('tab_history')">history</div>
|
|
</div>
|
|
<div id="tab_contents">
|
|
<div id="master_detail_table">
|
|
<div id="master"></div>
|
|
<div id="detail">
|
|
<div id="detail_colorpicker">
|
|
<div class="colorpicker_target">
|
|
<div class="colorpicker_target_tab tab_first colorpicker_target_selected" id="foreground" onClick="picked_colorpicker_target(this)">Text</div><div class="colorpicker_target_tab tab_last" id="background" onClick="picked_colorpicker_target(this)">Background</div>
|
|
</div>
|
|
<table id="colorpicker_term256"><tr><td></td></tr>
|
|
</table>
|
|
<div class="colorpicker_modifiers">
|
|
<div class="colorpicker_modifier_cell" id="modifier_underline" style="text-decoration: underline" onClick='picked_modifier(this)'>Underline</div>
|
|
</div>
|
|
</div>
|
|
<div id="detail_function"></div>
|
|
<div id="detail_prompt">
|
|
<div class="prompt_demo">
|
|
<div class="prompt_demo_text">
|
|
</div>
|
|
</div>
|
|
<div style="text-align: right">
|
|
<span class="prompt_save_button" onClick="save_current_prompt()">
|
|
use prompt
|
|
</span>
|
|
</div>
|
|
<div class="prompt_function">
|
|
<div class="prompt_function_text">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<table id="data_table">
|
|
<div id="table_filter_container">
|
|
<input type="text" id="table_filter_text_box" class="filter_text_box text_box_transient" value="Filter" onInput="update_table_filter_text_box(false)" onFocus="update_table_filter_text_box(false)" onBlur="update_table_filter_text_box(true)">
|
|
</div>
|
|
<tr><td>
|
|
</td></tr>
|
|
<tr><td></td></tr>
|
|
</table>
|
|
<div class="footer">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</body></html>
|