mirror of https://github.com/Aidaho12/haproxy-wi
parent
74862811b2
commit
f3de2ec64d
|
@ -10,8 +10,8 @@ print('Content-type: text/html\n')
|
|||
funct.check_login()
|
||||
|
||||
form = funct.form
|
||||
serv = form.getvalue('serv')
|
||||
service = form.getvalue('service')
|
||||
serv = funct.is_ip_or_dns(form.getvalue('serv'))
|
||||
service = funct.checkAjaxInput(form.getvalue('service'))
|
||||
is_serv_protected = False
|
||||
try:
|
||||
config_file_name = form.getvalue('config_file_name').replace('92', '/')
|
||||
|
@ -22,40 +22,31 @@ cfg = ""
|
|||
stderr = ""
|
||||
error = ""
|
||||
aftersave = ""
|
||||
is_restart = ''
|
||||
|
||||
try:
|
||||
user, user_id, role, token, servers, user_services = funct.get_users_params()
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
|
||||
if service == 'keepalived':
|
||||
if funct.check_login(service=3):
|
||||
title = "Working with Keepalived configuration files"
|
||||
action = "config.py?service=keepalived"
|
||||
if service in ('haproxy', 'nginx', 'keepalived', 'apache'):
|
||||
service_desc = sql.select_service(service)
|
||||
if funct.check_login(service=service_desc.service_id):
|
||||
title = f"Working with {service_desc.service} configuration files"
|
||||
action = f"config.py?service={service_desc.slug}"
|
||||
configs_dir = funct.get_config_var('configs', 'kp_save_configs_dir')
|
||||
file_format = 'conf'
|
||||
servers = sql.get_dick_permit(keepalived=1)
|
||||
elif service == 'nginx':
|
||||
if funct.check_login(service=2):
|
||||
title = "Working with NGINX configuration files"
|
||||
action = "config.py?service=nginx"
|
||||
configs_dir = funct.get_config_var('configs', 'nginx_save_configs_dir')
|
||||
file_format = 'conf'
|
||||
servers = sql.get_dick_permit(nginx=1)
|
||||
elif service == 'apache':
|
||||
if funct.check_login(service=4):
|
||||
title = "Working with Apache configuration files"
|
||||
action = "config.py?service=apache"
|
||||
configs_dir = funct.get_config_var('configs', 'apache_save_configs_dir')
|
||||
file_format = 'conf'
|
||||
servers = sql.get_dick_permit(apache=1)
|
||||
servers = sql.get_dick_permit(serivce=service_desc.service)
|
||||
|
||||
if service in ('haproxy', 'nginx', 'apache'):
|
||||
configs_dir = funct.get_config_var('configs', f'{service_desc.service}_save_configs_dir')
|
||||
else:
|
||||
configs_dir = funct.get_config_var('configs', 'kp_save_configs_dir')
|
||||
|
||||
if service == 'haproxy':
|
||||
file_format = 'cfg'
|
||||
else:
|
||||
if funct.check_login(service=1):
|
||||
title = "Working with HAProxy configuration files"
|
||||
action = "config.py"
|
||||
configs_dir = funct.get_config_var('configs', 'haproxy_save_configs_dir')
|
||||
file_format = 'cfg'
|
||||
servers = sql.get_dick_permit(haproxy=1)
|
||||
print('<meta http-equiv="refresh" content="0; url=/app/overview.py">')
|
||||
|
||||
if serv is not None:
|
||||
if service == 'nginx' or service == 'apache':
|
||||
|
@ -67,6 +58,8 @@ if serv is not None:
|
|||
if serv is not None and form.getvalue('open') is not None and form.getvalue('new_config') is None:
|
||||
funct.check_is_server_in_group(serv)
|
||||
is_serv_protected = sql.is_serv_protected(serv)
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
is_restart = sql.select_service_setting(server_id, service, 'restart')
|
||||
|
||||
if service == 'keepalived':
|
||||
error = funct.get_config(serv, cfg, keepalived=1)
|
||||
|
@ -142,7 +135,7 @@ if serv is not None and form.getvalue('config') is not None:
|
|||
|
||||
template = template.render(
|
||||
h2=1, title=title, role=role, action=action, user=user, select_id="serv", serv=serv, aftersave=aftersave,
|
||||
config=config_read, cfg=cfg, selects=servers, stderr=stderr, error=error, service=service,
|
||||
config=config_read, cfg=cfg, selects=servers, stderr=stderr, error=error, service=service, is_restart=is_restart,
|
||||
user_services=user_services, config_file_name=config_file_name, is_serv_protected=is_serv_protected, token=token
|
||||
)
|
||||
print(template)
|
||||
|
|
|
@ -181,16 +181,21 @@ def default_values():
|
|||
print(str(e))
|
||||
|
||||
data_source = [
|
||||
{'service_id': 1, 'service': 'HAProxy'},
|
||||
{'service_id': 2, 'service': 'NGINX'},
|
||||
{'service_id': 3, 'service': 'Keepalived'},
|
||||
{'service_id': 4, 'service': 'Apache'},
|
||||
{'service_id': 1, 'service': 'HAProxy', 'slug': 'haproxy'},
|
||||
{'service_id': 2, 'service': 'NGINX', 'slug': 'nginx'},
|
||||
{'service_id': 3, 'service': 'Keepalived', 'slug': 'keepalived'},
|
||||
{'service_id': 4, 'service': 'Apache', 'slug': 'apache'},
|
||||
]
|
||||
|
||||
try:
|
||||
Services.insert_many(data_source).on_conflict_ignore().execute()
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
except Exception:
|
||||
Services.drop_table()
|
||||
Services.create_table()
|
||||
try:
|
||||
Services.insert_many(data_source).on_conflict_ignore().execute()
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
|
||||
data_source = [
|
||||
{'param': 'aws', 'name': 'AWS', 'optgroup': 'aws', 'section': 'provider', 'provider': 'aws', 'image': '/inc/images/provisioning/providers/aws.svg'},
|
||||
|
@ -962,8 +967,25 @@ def update_db_v_6_1_3(**kwargs):
|
|||
pass
|
||||
|
||||
|
||||
def update_db_v_6_1_4():
|
||||
servers = Server.select()
|
||||
services = Services.select()
|
||||
for server in servers:
|
||||
for service in services:
|
||||
service_name = service.service.lower()
|
||||
setting = 'restart'
|
||||
if service_name == 'keepalived':
|
||||
continue
|
||||
try:
|
||||
ServiceSetting.insert(
|
||||
server_id=server.server_id, service=service_name, setting=setting, value=1
|
||||
).on_conflict_ignore().execute()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def update_ver():
|
||||
query = Version.update(version='6.1.3.0')
|
||||
query = Version.update(version='6.1.4.0')
|
||||
try:
|
||||
query.execute()
|
||||
except Exception:
|
||||
|
@ -991,6 +1013,7 @@ def update_all():
|
|||
update_db_v_6_0_1()
|
||||
update_db_v_6_1_0()
|
||||
update_db_v_6_1_3()
|
||||
update_db_v_6_1_4()
|
||||
update_ver()
|
||||
|
||||
|
||||
|
@ -1014,6 +1037,7 @@ def update_all_silent():
|
|||
update_db_v_6_0(silent=1)
|
||||
update_db_v_6_0_1(silent=1)
|
||||
update_db_v_6_1_3(silent=1)
|
||||
update_db_v_6_1_4()
|
||||
update_ver()
|
||||
|
||||
|
||||
|
|
|
@ -502,6 +502,7 @@ class SystemInfo(BaseModel):
|
|||
class Services(BaseModel):
|
||||
service_id = IntegerField(null=True)
|
||||
service = CharField(null=True)
|
||||
slug = CharField(null=True)
|
||||
|
||||
class Meta:
|
||||
table_name = 'services'
|
||||
|
|
13
app/funct.py
13
app/funct.py
|
@ -753,6 +753,7 @@ def install_haproxy(server_ip, **kwargs):
|
|||
if docker == '1':
|
||||
server_id = sql.select_server_id_by_ip(server_ip)
|
||||
sql.insert_or_update_service_setting(server_id, 'haproxy', 'dockerized', '1')
|
||||
sql.insert_or_update_service_setting(server_id, 'haproxy', 'restart', '1')
|
||||
|
||||
os.system("rm -f %s" % script)
|
||||
|
||||
|
@ -876,6 +877,7 @@ def install_nginx(server_ip, **kwargs):
|
|||
if docker == '1':
|
||||
server_id = sql.select_server_id_by_ip(server_ip)
|
||||
sql.insert_or_update_service_setting(server_id, 'nginx', 'dockerized', '1')
|
||||
sql.insert_or_update_service_setting(server_id, 'nginx', 'restart', '1')
|
||||
|
||||
os.system("rm -f %s" % script)
|
||||
|
||||
|
@ -986,6 +988,7 @@ def upload_and_restart(server_ip, cfg, **kwargs):
|
|||
tmp_file = sql.get_setting('tmp_config_path') + "/" + get_data('config') + ".cfg"
|
||||
|
||||
is_docker = sql.select_service_setting(server_id, service, 'dockerized')
|
||||
|
||||
if is_docker == '1':
|
||||
service_cont_name = service + '_container_name'
|
||||
container_name = sql.get_setting(service_cont_name)
|
||||
|
@ -1011,6 +1014,7 @@ def upload_and_restart(server_ip, cfg, **kwargs):
|
|||
action = 'reload'
|
||||
reload_or_restart_command = reload_command
|
||||
else:
|
||||
is_not_allowed_to_restart(server_id, service)
|
||||
action = 'restart'
|
||||
reload_or_restart_command = restart_command
|
||||
|
||||
|
@ -2090,6 +2094,15 @@ def is_restarted(server_ip, action):
|
|||
sys.exit()
|
||||
|
||||
|
||||
def is_not_allowed_to_restart(server_id: int, service: str) -> bool:
|
||||
import sql
|
||||
is_restart = sql.select_service_setting(server_id, service, 'restart')
|
||||
|
||||
if int(is_restart) == 1:
|
||||
print('warning: this service is not allowed to be restarted')
|
||||
sys.exit()
|
||||
|
||||
|
||||
def return_user_status():
|
||||
import sql
|
||||
|
||||
|
|
|
@ -17,62 +17,32 @@ user, user_id, role, token, servers, user_services = funct.get_users_params()
|
|||
|
||||
form = funct.form
|
||||
serv = funct.is_ip_or_dns(form.getvalue('serv'))
|
||||
service = form.getvalue('service')
|
||||
service = funct.checkAjaxInput(form.getvalue('service'))
|
||||
autorefresh = 0
|
||||
servers_waf = ()
|
||||
title = "HAProxy servers overview"
|
||||
cmd = "ps ax |grep -e 'keep_alive.py' |grep -v grep |wc -l"
|
||||
keep_alive, stderr = funct.subprocess_execute(cmd)
|
||||
is_restart = ''
|
||||
restart_settings = ''
|
||||
|
||||
if service == 'nginx':
|
||||
if funct.check_login(service=2):
|
||||
title = 'NGINX servers overview'
|
||||
if service in ('haproxy', 'nginx', 'keepalived', 'apache'):
|
||||
service_desc = sql.select_service(service)
|
||||
if funct.check_login(service=service_desc.service_id):
|
||||
title = f'{service_desc.service} servers overview'
|
||||
if serv:
|
||||
if funct.check_is_server_in_group(serv):
|
||||
servers = sql.select_servers(server=serv)
|
||||
autorefresh = 1
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
service_settings = sql.select_docker_service_settings(server_id, service)
|
||||
docker_settings = sql.select_docker_service_settings(server_id, service_desc.service)
|
||||
restart_settings = sql.select_restart_service_settings(server_id, service_desc.service)
|
||||
else:
|
||||
servers = sql.get_dick_permit(virt=1, nginx=1)
|
||||
service_settings = sql.select_docker_services_settings(service)
|
||||
elif service == 'keepalived':
|
||||
if funct.check_login(service=3):
|
||||
title = 'Keepalived servers overview'
|
||||
if serv:
|
||||
if funct.check_is_server_in_group(serv):
|
||||
servers = sql.select_servers(server=serv)
|
||||
autorefresh = 1
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
service_settings = sql.select_docker_service_settings(server_id, service)
|
||||
else:
|
||||
servers = sql.get_dick_permit(virt=1, keepalived=1)
|
||||
service_settings = sql.select_docker_services_settings(service)
|
||||
elif service == 'apache':
|
||||
if funct.check_login(service=4):
|
||||
title = 'Apache servers overview'
|
||||
if serv:
|
||||
if funct.check_is_server_in_group(serv):
|
||||
servers = sql.select_servers(server=serv)
|
||||
autorefresh = 1
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
service_settings = sql.select_docker_service_settings(server_id, service)
|
||||
else:
|
||||
servers = sql.get_dick_permit(virt=1, apache=1)
|
||||
service_settings = sql.select_docker_services_settings(service)
|
||||
servers = sql.get_dick_permit(virt=1, service=service_desc.slug)
|
||||
docker_settings = sql.select_docker_services_settings(service_desc.service)
|
||||
restart_settings = sql.select_restart_services_settings(service_desc.service)
|
||||
else:
|
||||
if funct.check_login(service=1):
|
||||
service = 'haproxy'
|
||||
if serv:
|
||||
if funct.check_is_server_in_group(serv):
|
||||
servers = sql.select_servers(server=serv)
|
||||
autorefresh = 1
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
servers_waf = sql.select_waf_servers_metrics(user_id.value)
|
||||
service_settings = sql.select_docker_service_settings(server_id, service)
|
||||
else:
|
||||
servers = sql.get_dick_permit(virt=1, haproxy=1)
|
||||
service_settings = sql.select_docker_services_settings(service)
|
||||
print('<meta http-equiv="refresh" content="0; url=/app/overview.py">')
|
||||
|
||||
services_name = {'roxy-wi-checker': 'Master backends checker service',
|
||||
'roxy-wi-keep_alive': 'Auto start service',
|
||||
|
@ -106,7 +76,7 @@ for s in servers:
|
|||
cmd = [
|
||||
"/usr/sbin/nginx -v 2>&1|awk '{print $3}' && systemctl status nginx |grep -e 'Active' |awk "
|
||||
"'{print $2, $9$10$11$12$13}' && ps ax |grep nginx:|grep -v grep |wc -l"]
|
||||
for service_set in service_settings:
|
||||
for service_set in docker_settings:
|
||||
if service_set.server_id == s[0] and service_set.setting == 'dockerized' and service_set.value == '1':
|
||||
container_name = sql.get_setting('nginx_container_name')
|
||||
cmd = [
|
||||
|
@ -206,7 +176,7 @@ except Exception as e:
|
|||
template = template.render(
|
||||
h2=1, autorefresh=autorefresh, title=title, role=role, user=user, servers=servers_with_status1,
|
||||
keep_alive=''.join(keep_alive), serv=serv, service=service, services=services, user_services=user_services,
|
||||
service_settings=service_settings, user_status=user_status, user_plan=user_plan, servers_waf=servers_waf,
|
||||
token=token
|
||||
docker_settings=docker_settings, user_status=user_status, user_plan=user_plan, servers_waf=servers_waf,
|
||||
restart_settings=restart_settings, token=token
|
||||
)
|
||||
print(template)
|
||||
|
|
|
@ -21,47 +21,15 @@ serv = funct.is_ip_or_dns(form.getvalue('serv'))
|
|||
service = form.getvalue('service')
|
||||
user_id = form.getvalue('user_id')
|
||||
|
||||
|
||||
if service == 'nginx':
|
||||
if funct.check_login(service=2):
|
||||
title = 'Nginx service history'
|
||||
if serv:
|
||||
if funct.check_is_server_in_group(serv):
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
history = sql.select_action_history_by_server_id_and_service(
|
||||
server_id,
|
||||
service
|
||||
)
|
||||
elif service == 'keepalived':
|
||||
if funct.check_login(service=3):
|
||||
title = 'Keepalived service history'
|
||||
if serv:
|
||||
if funct.check_is_server_in_group(serv):
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
history = sql.select_action_history_by_server_id_and_service(
|
||||
server_id,
|
||||
service
|
||||
)
|
||||
elif service == 'apache':
|
||||
if funct.check_login(service=4):
|
||||
title = 'Apache service history'
|
||||
if serv:
|
||||
if funct.check_is_server_in_group(serv):
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
history = sql.select_action_history_by_server_id_and_service(
|
||||
server_id,
|
||||
service
|
||||
)
|
||||
elif service == 'haproxy':
|
||||
if funct.check_login(service=1):
|
||||
title = "HAProxy service history"
|
||||
if serv:
|
||||
if funct.check_is_server_in_group(serv):
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
history = sql.select_action_history_by_server_id_and_service(
|
||||
server_id,
|
||||
service
|
||||
)
|
||||
if service in ('haproxy', 'nginx', 'keepalived', 'apache'):
|
||||
service_desc = sql.select_service(service)
|
||||
if funct.check_login(service=service_desc.service_id):
|
||||
title = f'{service_desc.service} service history'
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
history = sql.select_action_history_by_server_id_and_service(
|
||||
server_id,
|
||||
service_desc.service
|
||||
)
|
||||
elif service == 'server':
|
||||
if serv:
|
||||
title = serv + ' history'
|
||||
|
|
19
app/logs.py
19
app/logs.py
|
@ -26,7 +26,7 @@ hour1 = form.getvalue('hour1')
|
|||
minut = form.getvalue('minut')
|
||||
minut1 = form.getvalue('minut1')
|
||||
waf = form.getvalue('waf')
|
||||
service = form.getvalue('service')
|
||||
service = funct.checkAjaxInput(form.getvalue('service'))
|
||||
remote_file = form.getvalue('file')
|
||||
|
||||
print('Content-type: text/html\n')
|
||||
|
@ -37,22 +37,17 @@ try:
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
if service == 'nginx':
|
||||
if funct.check_login(service=2):
|
||||
title = "NGINX`s logs"
|
||||
servers = sql.get_dick_permit(nginx=1)
|
||||
elif service == 'apache':
|
||||
if funct.check_login(service=4):
|
||||
title = "Apache's logs"
|
||||
servers = sql.get_dick_permit(apache=1)
|
||||
if service in ('haproxy', 'nginx', 'keepalived', 'apache'):
|
||||
service_desc = sql.select_service(service)
|
||||
if funct.check_login(service=service_desc.service_id):
|
||||
title = f"{service_desc.service}`s logs"
|
||||
servers = sql.get_dick_permit(service=service_desc.slug)
|
||||
elif waf == '1':
|
||||
if funct.check_login(service=1):
|
||||
title = "WAF logs"
|
||||
servers = sql.get_dick_permit(haproxy=1)
|
||||
else:
|
||||
if funct.check_login(service=1):
|
||||
title = "HAProxy`s logs"
|
||||
servers = sql.get_dick_permit(haproxy=1)
|
||||
print('<meta http-equiv="refresh" content="0; url=/app/overview.py">')
|
||||
|
||||
template = template.render(
|
||||
h2=1, autorefresh=1, title=title, role=role, user=user, select_id="serv", selects=servers,
|
||||
|
|
120
app/options.py
120
app/options.py
|
@ -443,6 +443,10 @@ if form.getvalue('action_hap') is not None and serv is not None:
|
|||
|
||||
if funct.check_haproxy_config(serv):
|
||||
server_id = sql.select_server_id_by_ip(server_ip=serv)
|
||||
|
||||
if action == 'restart':
|
||||
funct.is_not_allowed_to_restart(server_id, 'haproxy')
|
||||
|
||||
is_docker = sql.select_service_setting(server_id, 'haproxy', 'dockerized')
|
||||
|
||||
if is_docker == '1':
|
||||
|
@ -471,7 +475,11 @@ if form.getvalue('action_nginx') is not None and serv is not None:
|
|||
|
||||
if funct.check_nginx_config(serv):
|
||||
server_id = sql.select_server_id_by_ip(server_ip=serv)
|
||||
|
||||
if action == 'restart':
|
||||
funct.is_not_allowed_to_restart(server_id, 'nginx')
|
||||
is_docker = sql.select_service_setting(server_id, 'nginx', 'dockerized')
|
||||
|
||||
if is_docker == '1':
|
||||
container_name = sql.get_setting('nginx_container_name')
|
||||
commands = ["sudo docker %s %s" % (action, container_name)]
|
||||
|
@ -540,6 +548,10 @@ if form.getvalue('action_apache') is not None and serv is not None:
|
|||
funct.is_restarted(serv, action)
|
||||
|
||||
server_id = sql.select_server_id_by_ip(serv)
|
||||
|
||||
if action == 'restart':
|
||||
funct.is_not_allowed_to_restart(server_id, 'apache')
|
||||
|
||||
is_docker = sql.select_service_setting(server_id, 'apache', 'dockerized')
|
||||
if is_docker == '1':
|
||||
container_name = sql.get_setting('apache_container_name')
|
||||
|
@ -1207,7 +1219,7 @@ if form.getvalue('servaction') is not None:
|
|||
if enable != "show":
|
||||
funct.logging(serv, 'Has been ' + enable + 'ed ' + backend, login=1, keep_history=1, service='haproxy')
|
||||
print(
|
||||
'<center><h3>You %s %s on HAProxy %s. <a href="viewsttats.py?serv=%s" title="View stat" target="_blank">Look it</a> or <a href="runtimeapi.py" title="Runtime API">Edit something else</a></h3><br />' % (enable, backend, serv, serv))
|
||||
'<center><h3>You %s %s on HAProxy %s. <a href="statsview.py?serv=%s" title="View stat" target="_blank">Look it</a> or <a href="runtimeapi.py" title="Runtime API">Edit something else</a></h3><br />' % (enable, backend, serv, serv))
|
||||
|
||||
print(funct.ssh_command(serv, command, show_log="1"))
|
||||
action = 'runtimeapi.py ' + enable + ' ' + backend
|
||||
|
@ -1894,33 +1906,45 @@ if form.getvalue('metrics_waf'):
|
|||
sql.update_waf_metrics_enable(form.getvalue('metrics_waf'), form.getvalue('enable'))
|
||||
|
||||
if form.getvalue('table_metrics'):
|
||||
service = form.getvalue('service')
|
||||
if service in ('nginx', 'apache'):
|
||||
metrics = sql.select_service_table_metrics(service)
|
||||
else:
|
||||
metrics = sql.select_table_metrics()
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
env = Environment(loader=FileSystemLoader('templates/ajax'), autoescape=True)
|
||||
template = env.get_template('table_metrics.html')
|
||||
|
||||
template = template.render(table_stat=sql.select_table_metrics())
|
||||
template = template.render(table_stat=metrics, service=service)
|
||||
print(template)
|
||||
|
||||
if form.getvalue('metrics_hapwi_ram'):
|
||||
import json
|
||||
|
||||
ip = form.getvalue('ip')
|
||||
metrics = {'chartData': {}}
|
||||
rams = ''
|
||||
|
||||
if ip == '1':
|
||||
cmd = "free -m |grep Mem |awk '{print $2,$3,$4,$5,$6,$7}'"
|
||||
metric, error = funct.subprocess_execute(cmd)
|
||||
import psutil
|
||||
|
||||
rams_list = psutil.virtual_memory()
|
||||
rams += str(round(rams_list.total/1048576, 2)) + ' '
|
||||
rams += str(round(rams_list.used/1048576, 2)) + ' '
|
||||
rams += str(round(rams_list.free/1048576, 2)) + ' '
|
||||
rams += str(round(rams_list.shared/1048576, 2)) + ' '
|
||||
rams += str(round(rams_list.cached/1048576, 2)) + ' '
|
||||
rams += str(round(rams_list.available/1048576, 2)) + ' '
|
||||
else:
|
||||
commands = ["free -m |grep Mem |awk '{print $2,$3,$4,$5,$6,$7}'"]
|
||||
metric, error = funct.subprocess_execute(commands[0])
|
||||
|
||||
for i in metric:
|
||||
rams = i
|
||||
for i in metric:
|
||||
rams = i
|
||||
|
||||
metrics['chartData']['rams'] = rams
|
||||
|
||||
import json
|
||||
|
||||
print(json.dumps(metrics))
|
||||
|
||||
if form.getvalue('metrics_hapwi_cpu'):
|
||||
|
@ -1929,15 +1953,26 @@ if form.getvalue('metrics_hapwi_cpu'):
|
|||
cpus = ''
|
||||
|
||||
if ip == '1':
|
||||
cmd = "top -b -n 1 |grep Cpu |awk -F':' '{print $2}'|awk -F' ' 'BEGIN{ORS=\" \";} { for (i=1;i<=NF;i+=2) print $i}'"
|
||||
metric, error = funct.subprocess_execute(cmd)
|
||||
# cmd = "top -b -n 1 |grep Cpu |awk -F':' '{print $2}'|awk -F' ' 'BEGIN{ORS=\" \";} { for (i=1;i<=NF;i+=2) print $i}'"
|
||||
# metric, error = funct.subprocess_execute(cmd)
|
||||
import psutil
|
||||
|
||||
cpus_list = psutil.cpu_times_percent(interval=1, percpu=False)
|
||||
cpus += str(cpus_list.user) + ' '
|
||||
cpus += str(cpus_list.system) + ' '
|
||||
cpus += str(cpus_list.nice) + ' '
|
||||
cpus += str(cpus_list.idle) + ' '
|
||||
cpus += str(cpus_list.iowait) + ' '
|
||||
cpus += str(cpus_list.irq) + ' '
|
||||
cpus += str(cpus_list.softirq) + ' '
|
||||
cpus += str(cpus_list.steal) + ' '
|
||||
else:
|
||||
commands = [
|
||||
"top -b -n 1 |grep Cpu |awk -F':' '{print $2}'|awk -F' ' 'BEGIN{ORS=\" \";} { for (i=1;i<=NF;i+=2) print $i}'"]
|
||||
metric, error = funct.subprocess_execute(commands[0])
|
||||
|
||||
for i in metric:
|
||||
cpus = i
|
||||
for i in metric:
|
||||
cpus = i
|
||||
|
||||
metrics['chartData']['cpus'] = cpus
|
||||
|
||||
|
@ -4334,6 +4369,9 @@ if form.getvalue('serverSettingsSave') is not None:
|
|||
haproxy_dockerized = form.getvalue('serverSettingshaproxy_dockerized')
|
||||
nginx_dockerized = form.getvalue('serverSettingsnginx_dockerized')
|
||||
apache_dockerized = form.getvalue('serverSettingsapache_dockerized')
|
||||
haproxy_restart = form.getvalue('serverSettingsHaproxyrestart')
|
||||
nginx_restart = form.getvalue('serverSettingsNginxrestart')
|
||||
apache_restart = form.getvalue('serverSettingsApache_restart')
|
||||
server_ip = sql.select_server_ip_by_id(server_id)
|
||||
|
||||
if service == 'haproxy':
|
||||
|
@ -4353,6 +4391,14 @@ if form.getvalue('serverSettingsSave') is not None:
|
|||
else:
|
||||
funct.logging(server_ip, 'Service has been flagged as a system service', haproxywi=1, login=1,
|
||||
keep_history=1, service=service)
|
||||
if sql.insert_or_update_service_setting(server_id, service, 'restart', haproxy_restart):
|
||||
print('Ok')
|
||||
if haproxy_restart == '1':
|
||||
funct.logging(server_ip, 'Restart option is disabled for this service', haproxywi=1, login=1,
|
||||
keep_history=1, service=service)
|
||||
else:
|
||||
funct.logging(server_ip, 'Restart option is disabled for this service', haproxywi=1, login=1,
|
||||
keep_history=1, service=service)
|
||||
|
||||
if service == 'nginx':
|
||||
if sql.insert_or_update_service_setting(server_id, service, 'dockerized', nginx_dockerized):
|
||||
|
@ -4363,6 +4409,14 @@ if form.getvalue('serverSettingsSave') is not None:
|
|||
else:
|
||||
funct.logging(server_ip, 'Service has been flagged as a system service', haproxywi=1, login=1,
|
||||
keep_history=1, service=service)
|
||||
if sql.insert_or_update_service_setting(server_id, service, 'restart', nginx_dockerized):
|
||||
print('Ok')
|
||||
if nginx_restart == '1':
|
||||
funct.logging(server_ip, 'Restart option is disabled for this service', haproxywi=1, login=1,
|
||||
keep_history=1, service=service)
|
||||
else:
|
||||
funct.logging(server_ip, 'Restart option is disabled for this service', haproxywi=1, login=1,
|
||||
keep_history=1, service=service)
|
||||
|
||||
if service == 'apache':
|
||||
if sql.insert_or_update_service_setting(server_id, service, 'dockerized', apache_dockerized):
|
||||
|
@ -4373,6 +4427,14 @@ if form.getvalue('serverSettingsSave') is not None:
|
|||
else:
|
||||
funct.logging(server_ip, 'Service has been flagged as a system service', haproxywi=1, login=1,
|
||||
keep_history=1, service=service)
|
||||
if sql.insert_or_update_service_setting(server_id, service, 'restart', nginx_dockerized):
|
||||
print('Ok')
|
||||
if apache_restart == '1':
|
||||
funct.logging(server_ip, 'Restart option is disabled for this service', haproxywi=1, login=1,
|
||||
keep_history=1, service=service)
|
||||
else:
|
||||
funct.logging(server_ip, 'Restart option is disabled for this service', haproxywi=1, login=1,
|
||||
keep_history=1, service=service)
|
||||
|
||||
if act == 'showListOfVersion':
|
||||
service = form.getvalue('service')
|
||||
|
@ -4380,27 +4442,21 @@ if act == 'showListOfVersion':
|
|||
for_delver = form.getvalue('for_delver')
|
||||
style = form.getvalue('style')
|
||||
users = sql.select_users()
|
||||
service_desc = sql.select_service(service)
|
||||
|
||||
if service == 'keepalived':
|
||||
configs_dir = funct.get_config_var('configs', 'kp_save_configs_dir')
|
||||
files = funct.get_files(dir=configs_dir, format='conf')
|
||||
configs = sql.select_config_version(serv, service)
|
||||
action = 'versions.py?service=keepalived'
|
||||
elif service == 'nginx':
|
||||
configs_dir = funct.get_config_var('configs', 'nginx_save_configs_dir')
|
||||
files = funct.get_files(dir=configs_dir, format='conf')
|
||||
configs = sql.select_config_version(serv, service)
|
||||
action = 'versions.py?service=nginx'
|
||||
elif service == 'apache':
|
||||
configs_dir = funct.get_config_var('configs', 'apache_save_configs_dir')
|
||||
files = funct.get_files(dir=configs_dir, format='conf')
|
||||
configs = sql.select_config_version(serv, service)
|
||||
action = 'versions.py?service=apache'
|
||||
else:
|
||||
service = 'haproxy'
|
||||
files = funct.get_files()
|
||||
configs = sql.select_config_version(serv, service)
|
||||
action = "versions.py"
|
||||
if service in ('haproxy', 'nginx', 'keepalived', 'apache'):
|
||||
configs = sql.select_config_version(serv, service_desc.service)
|
||||
action = f'versions.py?service={service_desc.slug}'
|
||||
|
||||
if service in ('haproxy', 'nginx', 'apache'):
|
||||
configs_dir = funct.get_config_var('configs', f'{service_desc.service}_save_configs_dir')
|
||||
else:
|
||||
configs_dir = funct.get_config_var('configs', 'kp_save_configs_dir')
|
||||
|
||||
if service == 'haproxy':
|
||||
files = funct.get_files()
|
||||
else:
|
||||
files = funct.get_files(dir=configs_dir, format='conf')
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
|
|
162
app/sql.py
162
app/sql.py
|
@ -1947,6 +1947,131 @@ def select_table_metrics():
|
|||
return cursor.fetchall()
|
||||
|
||||
|
||||
def select_service_table_metrics(service):
|
||||
cursor = conn.cursor()
|
||||
group_id = funct.get_user_group(id=1)
|
||||
|
||||
if service in ('nginx', 'apache'):
|
||||
metrics_table = '{}_metrics'.format(service)
|
||||
|
||||
if funct.check_user_group():
|
||||
if group_id == 1:
|
||||
groups = ""
|
||||
else:
|
||||
groups = "and servers.groups = '{group}' ".format(group=group_id)
|
||||
if mysql_enable == '1':
|
||||
sql = """
|
||||
select ip.ip, hostname, avg_cur_1h, avg_cur_24h, avg_cur_3d, max_con_1h, max_con_24h, max_con_3d from
|
||||
(select servers.ip from servers where {metrics} = 1 ) as ip,
|
||||
|
||||
(select servers.ip, servers.hostname as hostname from servers left join {metrics} as metr on servers.ip = metr.serv where servers.{metrics} = 1 {groups}) as hostname,
|
||||
|
||||
(select servers.ip,round(avg(metr.conn), 1) as avg_cur_1h from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= now() and metr.date >= DATE_ADD(NOW(),INTERVAL -1 HOUR)
|
||||
group by servers.ip) as avg_cur_1h,
|
||||
|
||||
(select servers.ip,round(avg(metr.conn), 1) as avg_cur_24h from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= now() and metr.date >= DATE_ADD(NOW(),INTERVAL -24 HOUR)
|
||||
group by servers.ip) as avg_cur_24h,
|
||||
|
||||
(select servers.ip,round(avg(metr.conn), 1) as avg_cur_3d from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= now() and metr.date >= DATE_ADD(NOW(),INTERVAL -3 DAY)
|
||||
group by servers.ip ) as avg_cur_3d,
|
||||
|
||||
(select servers.ip,max(metr.conn) as max_con_1h from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= now() and metr.date >= DATE_ADD(NOW(),INTERVAL -1 HOUR)
|
||||
group by servers.ip) as max_con_1h,
|
||||
|
||||
(select servers.ip,max(metr.conn) as max_con_24h from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= now() and metr.date >= DATE_ADD(NOW(),INTERVAL -24 HOUR)
|
||||
group by servers.ip) as max_con_24h,
|
||||
|
||||
(select servers.ip,max(metr.conn) as max_con_3d from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= now() and metr.date >= DATE_ADD(NOW(),INTERVAL -3 DAY)
|
||||
group by servers.ip ) as max_con_3d
|
||||
|
||||
where ip.ip=hostname.ip
|
||||
and ip.ip=avg_cur_1h.ip
|
||||
and ip.ip=avg_cur_24h.ip
|
||||
and ip.ip=avg_cur_3d.ip
|
||||
and ip.ip=max_con_1h.ip
|
||||
and ip.ip=max_con_24h.ip
|
||||
and ip.ip=max_con_3d.ip
|
||||
|
||||
group by hostname.ip """.format(metrics=metrics_table, groups=groups)
|
||||
else:
|
||||
sql = """
|
||||
select ip.ip, hostname, avg_cur_1h, avg_cur_24h, avg_cur_3d, max_con_1h, max_con_24h, max_con_3d from
|
||||
(select servers.ip from servers where {metrics} = 1 ) as ip,
|
||||
|
||||
(select servers.ip, servers.hostname as hostname from servers left join {metrics} as metr on servers.ip = metr.serv where servers.{metrics} = 1 {groups}) as hostname,
|
||||
|
||||
(select servers.ip,round(avg(metr.conn), 1) as avg_cur_1h from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= datetime('now', 'localtime') and metr.date >= datetime('now', '-1 hours', 'localtime')
|
||||
group by servers.ip) as avg_cur_1h,
|
||||
|
||||
(select servers.ip,round(avg(metr.conn), 1) as avg_cur_24h from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= datetime('now', 'localtime') and metr.date >= datetime('now', '-24 hours', 'localtime')
|
||||
group by servers.ip) as avg_cur_24h,
|
||||
|
||||
(select servers.ip,round(avg(metr.conn), 1) as avg_cur_3d from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= datetime('now', 'localtime') and metr.date >= datetime('now', '-3 days', 'localtime')
|
||||
group by servers.ip ) as avg_cur_3d,
|
||||
|
||||
(select servers.ip,max(metr.conn) as max_con_1h from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= datetime('now', 'localtime') and metr.date >= datetime('now', '-1 hours', 'localtime')
|
||||
group by servers.ip) as max_con_1h,
|
||||
|
||||
(select servers.ip,max(metr.conn) as max_con_24h from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= datetime('now', 'localtime') and metr.date >= datetime('now', '-24 hours', 'localtime')
|
||||
group by servers.ip) as max_con_24h,
|
||||
|
||||
(select servers.ip,max(metr.conn) as max_con_3d from servers
|
||||
left join {metrics} as metr on metr.serv = servers.ip
|
||||
where servers.{metrics} = 1 and
|
||||
metr.date <= datetime('now', 'localtime') and metr.date >= datetime('now', '-3 days', 'localtime')
|
||||
group by servers.ip ) as max_con_3d
|
||||
|
||||
where ip.ip=hostname.ip
|
||||
and ip.ip=avg_cur_1h.ip
|
||||
and ip.ip=avg_cur_24h.ip
|
||||
and ip.ip=avg_cur_3d.ip
|
||||
and ip.ip=max_con_1h.ip
|
||||
and ip.ip=max_con_24h.ip
|
||||
and ip.ip=max_con_3d.ip
|
||||
|
||||
group by hostname.ip """.format(metrics=metrics_table, groups=groups)
|
||||
|
||||
try:
|
||||
cursor.execute(sql)
|
||||
except Exception as e:
|
||||
out_error(e)
|
||||
else:
|
||||
return cursor.fetchall()
|
||||
|
||||
|
||||
def get_setting(param, **kwargs):
|
||||
try:
|
||||
user_group = funct.get_user_group(id=1)
|
||||
|
@ -3159,6 +3284,33 @@ def select_docker_services_settings(service: str) -> str:
|
|||
return query_res
|
||||
|
||||
|
||||
def select_restart_service_settings(server_id: int, service: str) -> str:
|
||||
query = ServiceSetting.select().where(
|
||||
(ServiceSetting.server_id == server_id)
|
||||
& (ServiceSetting.service == service)
|
||||
& (ServiceSetting.setting == 'restart')
|
||||
)
|
||||
try:
|
||||
query_res = query.execute()
|
||||
except Exception as e:
|
||||
out_error(e)
|
||||
else:
|
||||
return query_res
|
||||
|
||||
|
||||
def select_restart_services_settings(service: str) -> str:
|
||||
query = ServiceSetting.select().where(
|
||||
(ServiceSetting.service == service)
|
||||
& (ServiceSetting.setting == 'restart')
|
||||
)
|
||||
try:
|
||||
query_res = query.execute()
|
||||
except Exception as e:
|
||||
out_error(e)
|
||||
else:
|
||||
return query_res
|
||||
|
||||
|
||||
def select_service_setting(server_id: int, service: str, setting: str) -> str:
|
||||
try:
|
||||
result = ServiceSetting.get(
|
||||
|
@ -3603,3 +3755,13 @@ def select_provisioning_params():
|
|||
return
|
||||
else:
|
||||
return query_res
|
||||
|
||||
|
||||
def select_service(slug: str) -> str:
|
||||
try:
|
||||
query_res = Services.get(Services.slug == slug)
|
||||
except Exception as e:
|
||||
out_error(e)
|
||||
return 'there is no service'
|
||||
else:
|
||||
return query_res
|
||||
|
|
|
@ -3,7 +3,7 @@ import funct
|
|||
import sql
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
env = Environment(loader=FileSystemLoader('templates/'), autoescape=True)
|
||||
template = env.get_template('viewstats.html')
|
||||
template = env.get_template('statsview.html')
|
||||
form = funct.form
|
||||
serv = form.getvalue('serv')
|
||||
service = form.getvalue('service')
|
||||
|
@ -22,17 +22,13 @@ try:
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
if service == 'nginx':
|
||||
if funct.check_login(service=2):
|
||||
title = 'NGINX stats page'
|
||||
servers = sql.get_dick_permit(nginx=1)
|
||||
elif service == 'apache':
|
||||
if funct.check_login(service=4):
|
||||
title = 'Apache stats page'
|
||||
servers = sql.get_dick_permit(apache=1)
|
||||
if service in ('haproxy', 'nginx', 'apache'):
|
||||
service_desc = sql.select_service(service)
|
||||
if funct.check_login(service=service_desc.service_id):
|
||||
title = f'{service_desc.service} stats page'
|
||||
sql.get_dick_permit(service=service_desc.slug)
|
||||
else:
|
||||
if funct.check_login(service=1):
|
||||
title = 'HAProxy stats page'
|
||||
print('<meta http-equiv="refresh" content="0; url=/app/overview.py">')
|
||||
|
||||
rendered_template = template.render(
|
||||
h2=1, autorefresh=1, title=title, role=role, user=user, onclick="showStats()", select_id="serv",
|
|
@ -29,16 +29,16 @@
|
|||
<script defer src="/inc/fa-solid.min.js"></script>
|
||||
<script defer src="/inc/fontawesome.min.js"></script>
|
||||
<script defer src="/inc/ion.sound.min.js"></script>
|
||||
<link href="/inc/awesome.css" rel="stylesheet">
|
||||
<link href="/inc/style.css" rel="stylesheet">
|
||||
<link href="/inc/nprogress.css" rel="stylesheet">
|
||||
<link href="/inc/jquery-ui.css" rel="stylesheet">
|
||||
<link href="/inc/css/awesome.css" rel="stylesheet">
|
||||
<link href="/inc/css/style.css" rel="stylesheet">
|
||||
<link href="/inc/css/nprogress.css" rel="stylesheet">
|
||||
<link href="/inc/css/jquery-ui.css" rel="stylesheet">
|
||||
<script src="/inc/jquery-1.12.4.js"></script>
|
||||
<script src="/inc/jquery-ui.js"></script>
|
||||
<script src="/inc/js.cookie.min.js"></script>
|
||||
<script src="/inc/script.js"></script>
|
||||
<script src="/inc/nprogress.js"></script>
|
||||
<link href="/inc/toastr.css" rel="stylesheet"/>
|
||||
<link href="/inc/css/toastr.css" rel="stylesheet"/>
|
||||
<script src="/inc/toastr.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -29,16 +29,16 @@
|
|||
<script defer src="/inc/fa-solid.min.js"></script>
|
||||
<script defer src="/inc/fontawesome.min.js"></script>
|
||||
<script defer src="/inc/ion.sound.min.js"></script>
|
||||
<link href="/inc/awesome.css" rel="stylesheet">
|
||||
<link href="/inc/style.css" rel="stylesheet">
|
||||
<link href="/inc/nprogress.css" rel="stylesheet">
|
||||
<link href="/inc/jquery-ui.css" rel="stylesheet">
|
||||
<link href="/inc/css/awesome.css" rel="stylesheet">
|
||||
<link href="/inc/css/style.css" rel="stylesheet">
|
||||
<link href="/inc/css/nprogress.css" rel="stylesheet">
|
||||
<link href="/inc/css/jquery-ui.css" rel="stylesheet">
|
||||
<script src="/inc/jquery-1.12.4.js"></script>
|
||||
<script src="/inc/jquery-ui.js"></script>
|
||||
<script src="/inc/js.cookie.min.js"></script>
|
||||
<script src="/inc/script.js"></script>
|
||||
<script src="/inc/nprogress.js"></script>
|
||||
<link href="/inc/toastr.css" rel="stylesheet"/>
|
||||
<link href="/inc/css/toastr.css" rel="stylesheet"/>
|
||||
<script src="/inc/toastr.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -183,5 +183,5 @@
|
|||
{% endfor %}
|
||||
});
|
||||
</script>
|
||||
<link href="/inc/servers.css" rel="stylesheet"/>
|
||||
<link href="/inc/css/servers.css" rel="stylesheet"/>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<link href="/inc/table.css" rel="stylesheet" type="text/css">
|
||||
<link href="/inc/css/table.css" rel="stylesheet" type="text/css">
|
||||
<script type="text/javascript" charset="utf8" src="/inc/dataTables.min.js"></script>
|
||||
{% if action == "history" %}
|
||||
{% set column_for_sort = 4 %}
|
||||
|
|
|
@ -234,7 +234,7 @@
|
|||
{% endif %}
|
||||
{%- set backend = line.split(' ') -%}
|
||||
<span class="accordion-link">
|
||||
<a href="/app/viewsttats.py?service=haproxy&serv={{-serv-}}#{{- backend[1]-}}" target="_blank">Stats</a>
|
||||
<a href="/app/statsview.py?service=haproxy&serv={{-serv-}}#{{- backend[1]-}}" target="_blank">Stats</a>
|
||||
</span>
|
||||
{%- set backend = backend|join('_') -%}
|
||||
{%- do section_name.update({i: backend}) -%}
|
||||
|
@ -251,7 +251,7 @@
|
|||
{% endif %}
|
||||
{% set backend = line.split(' ') %}
|
||||
<span class="accordion-link">
|
||||
<a href="/app/viewsttats.py?serv={{serv}}#{{ backend[1]}}" target="_blank">Stats</a>
|
||||
<a href="/app/statsview.py?serv={{serv}}#{{ backend[1]}}" target="_blank">Stats</a>
|
||||
</span>
|
||||
{% set backend = backend|join('_') %}
|
||||
{% do section_name.update({i: backend}) %}
|
||||
|
@ -268,7 +268,7 @@
|
|||
{% endif %}
|
||||
{% set backend = line.split(' ') %}
|
||||
<span class="accordion-link">
|
||||
<a href="/app/viewsttats.py?serv={{serv}}#{{ backend[1]}}" target="_blank">Stats</a>
|
||||
<a href="/app/statsview.py?serv={{serv}}#{{ backend[1]}}" target="_blank">Stats</a>
|
||||
</span>
|
||||
</span><div>
|
||||
{% continue %}
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if waf_service == 'haproxy' %}
|
||||
<a href="/app/logs.py?serv={{ service.1 }}&rows=10&grep=&hour=00&minut=00&hour1=24&minut1=00&waf=1" class="ui-button ui-widget ui-corner-all" title="View log">View</a>
|
||||
<a href="/app/logs.py?service={{waf_service}}&serv={{ service.1 }}&rows=10&grep=&hour=00&minut=00&hour1=24&minut1=00&waf=1" class="ui-button ui-widget ui-corner-all" title="View log">View</a>
|
||||
{% elif waf_service == 'nginx' %}
|
||||
<a href="/app/logs.py?service={{waf_service}}&serv={{ service.1 }}&rows=10&grep=ModSecurity&hour=00&minut=00&hour1=24&minut1=00&file=error.log&waf=0" class="ui-button ui-widget ui-corner-all" title="View log">View</a>
|
||||
{% endif %}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
$('#table_version').on('page.dt')
|
||||
.DataTable( {
|
||||
"pageLength": 25,
|
||||
"order": [ 6, "desc" ],
|
||||
"order": [ 5, "desc" ],
|
||||
stateSave: true,
|
||||
"columnDefs": [
|
||||
{
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
{% if service == 'haproxy' %}
|
||||
{% if settings %}
|
||||
{% for s in settings %}
|
||||
{{s.dockerized}}
|
||||
{% if s.haproxy_enterprise != '' and s.setting == 'haproxy_enterprise' %}
|
||||
<tr>
|
||||
<td class="padding20 help_cursor" style="width: 70%"
|
||||
|
@ -30,6 +29,18 @@
|
|||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if s.restart != '' and s.setting == 'restart' %}
|
||||
<tr>
|
||||
<td class="padding20 help_cursor" style="width: 70%" title="If enabled, the restart button disabled">Disable restart</td>
|
||||
<td>
|
||||
{% if s.value == '1' and s.setting == 'restart' %}
|
||||
{{ checkbox('haproxy_restart', checked='checked', title='Restart option is disabled for this server') }}
|
||||
{% elif s.setting == 'restart' %}
|
||||
{{ checkbox('haproxy_restart', title='Only the reload button will be active') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
|
@ -46,6 +57,13 @@
|
|||
{{ checkbox('haproxy_dockerized', title='This server will be used as Docker container') }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding20 help_cursor" style="width: 70%"
|
||||
title="If enabled, the restart button disabled">Disable restart</td>
|
||||
<td>
|
||||
{{ checkbox('haproxy_restart', title='Only the reload button will be active') }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if service == 'nginx' %}
|
||||
|
@ -64,6 +82,18 @@
|
|||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if s.restart != '' and s.setting == 'restart' %}
|
||||
<tr>
|
||||
<td class="padding20 help_cursor" style="width: 70%" title="If enabled, the restart button disabled">Disable restart</td>
|
||||
<td>
|
||||
{% if s.value == '1' and s.setting == 'restart' %}
|
||||
{{ checkbox('nginx_restart', checked='checked', title='Restart option is disabled for this server') }}
|
||||
{% elif s.setting == 'restart' %}
|
||||
{{ checkbox('nginx_restart', title='Only the reload button will be active') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
|
@ -73,6 +103,12 @@
|
|||
{{ checkbox('nginx_dockerized', title='This server will be used as Docker container') }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding20 help_cursor" style="width: 70%" title="If enabled, the restart button disabled">Disable restart</td>
|
||||
<td>
|
||||
{{ checkbox('nginx_restart', title='Only the reload button will be active') }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if service == 'apache' %}
|
||||
|
@ -91,6 +127,18 @@
|
|||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if s.restart != '' and s.setting == 'restart' %}
|
||||
<tr>
|
||||
<td class="padding20 help_cursor" style="width: 70%" title="If enabled, the restart button disabled">Disable restart</td>
|
||||
<td>
|
||||
{% if s.value == '1' and s.setting == 'restart' %}
|
||||
{{ checkbox('apache_restart', checked='checked', title='Restart option is disabled for this server') }}
|
||||
{% elif s.setting == 'restart' %}
|
||||
{{ checkbox('apache_restart', title='Only the reload button will be active') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<tr>
|
||||
|
@ -100,6 +148,12 @@
|
|||
{{ checkbox('apache_dockerized', title='This server will be used as Docker container') }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding20 help_cursor" style="width: 70%" title="If enabled, the restart button disabled">Disable restart</td>
|
||||
<td>
|
||||
{{ checkbox('apache_restart', title='Only the reload button will be active') }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</table>
|
||||
</table>
|
||||
|
|
|
@ -30,20 +30,21 @@
|
|||
<script defer src="/inc/fontawesome.min.js"></script>
|
||||
<script>FontAwesomeConfig = { searchPseudoElements: true, observeMutations: false };</script>
|
||||
<script defer src="/inc/ion.sound.min.js"></script>
|
||||
<link href="/inc/awesome.css" rel="stylesheet">
|
||||
<link href="/inc/style.css" rel="stylesheet">
|
||||
<link href="/inc/nprogress.css" rel="stylesheet">
|
||||
<link href="/inc/jquery-ui.min.css" rel="stylesheet">
|
||||
<link href="/inc/jquery-ui.structure.min.css" rel="stylesheet">
|
||||
<link href="/inc/css/awesome.css" rel="stylesheet">
|
||||
<link href="/inc/css/style.css" rel="stylesheet">
|
||||
<link href="/inc/css/nprogress.css" rel="stylesheet">
|
||||
<link href="/inc/css/jquery-ui.min.css" rel="stylesheet">
|
||||
<link href="/inc/css/jquery-ui.structure.min.css" rel="stylesheet">
|
||||
<script src="/inc/jquery-3.6.0.min.js"></script>
|
||||
<script src="/inc/jquery-ui.min.js"></script>
|
||||
<script src="/inc/js.cookie.min.js"></script>
|
||||
<script src="/inc/reconnecting-websocket.js"></script>
|
||||
<link href="/inc/select2.css" rel="stylesheet" />
|
||||
<script src="/inc/hotkeys.js"></script>
|
||||
<link href="/inc/css/select2.css" rel="stylesheet" />
|
||||
<script src="/inc/select2.js"></script>
|
||||
<script src="/inc/script.js"></script>
|
||||
<script src="/inc/nprogress.js"></script>
|
||||
<link href="/inc/toastr.css" rel="stylesheet"/>
|
||||
<link href="/inc/css/toastr.css" rel="stylesheet"/>
|
||||
<script src="/inc/toastr.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -65,15 +66,15 @@
|
|||
<li class="p_menu">
|
||||
<a href="/app/hapservers.py" title="HAProxy servers overview" class="config-show">Haproxy</a>
|
||||
<ul class="v_menu">
|
||||
<li><a href="/app/hapservers.py" title="HAProxy servers overview" class="overview-link head-submenu">Overview</a> </li>
|
||||
<li><a href="/app/config.py" title="Working with HAProxy configs" class="edit head-submenu">Configs</a></li>
|
||||
<li><a href="/app/viewsttats.py" title="HAProxy statistics " class="stats head-submenu">Stats</a></li>
|
||||
<li><a href="/app/logs.py" title="HAProxy logs " class="logs head-submenu">Logs</a></li>
|
||||
<li><a href="/app/hapservers.py?service=haproxy" title="HAProxy servers overview" class="overview-link head-submenu">Overview</a> </li>
|
||||
<li><a href="/app/config.py?service=haproxy" title="Working with HAProxy configs" class="edit head-submenu">Configs</a></li>
|
||||
<li><a href="/app/statsview.py?service=haproxy" title="HAProxy statistics " class="stats head-submenu">Stats</a></li>
|
||||
<li><a href="/app/logs.py?service=haproxy" title="HAProxy logs " class="logs head-submenu">Logs</a></li>
|
||||
<li><a href="/app/runtimeapi.py" title="Runtime API - Roxy-WI" class="runtime head-submenu">Runtime API</a></li>
|
||||
<li><a href="/app/metrics.py" title="HAProxy's metrics" class="metrics head-submenu">Metrics</a></li>
|
||||
<li><a href="/app/metrics.py?service=haproxy" title="HAProxy's metrics" class="metrics head-submenu">Metrics</a></li>
|
||||
{% if role <= 3 %}
|
||||
<li><a href="/app/add.py#proxy" title="Add proxy: Create proxy - Roxy-WI" class="add-proxy head-submenu" id="add1">Add proxy</a></li>
|
||||
<li><a href="/app/versions.py" title="Working with versions HAProxy configs" class="version head-submenu">Versions</a></li>
|
||||
<li><a href="/app/versions.py?service=haproxy" title="Working with versions HAProxy configs" class="version head-submenu">Versions</a></li>
|
||||
<li><a href="/app/add.py#ssl" title="Add proxy: Upload SSL certificates - Roxy-WI" class="cert head-submenu" id="add3">SSL</a></li>
|
||||
<li><a href="/app/add.py#lists" title="Add proxy: Create and upload whitelists or blacklists - Roxy-WI" class="lists head-submenu" id="add7">Lists</a></li>
|
||||
<li><a href="/app/waf.py?service=haproxy" title="Web application firewall" class="waf-menu head-submenu">WAF</a> </li>
|
||||
|
@ -87,7 +88,7 @@
|
|||
<ul class="v_menu">
|
||||
<li><a href="/app/hapservers.py?service=nginx" title="Overview NGINX servers" class="overview-link head-submenu">Overview</a></li>
|
||||
<li><a href="/app/config.py?service=nginx" title="Working with NGINX configs" class="edit head-submenu">Configs</a></li>
|
||||
<li><a href="/app/viewsttats.py?service=nginx" title="NGINX statistics" class="stats head-submenu">Stats</a></li>
|
||||
<li><a href="/app/statsview.py?service=nginx" title="NGINX statistics" class="stats head-submenu">Stats</a></li>
|
||||
<li><a href="/app/logs.py?service=nginx" title="NGINX logs " class="logs head-submenu">Logs</a></li>
|
||||
<li><a href="/app/metrics.py?service=nginx" title="NGINX's metrics" class="metrics head-submenu">Metrics</a></li>
|
||||
{% if role <= 3 %}
|
||||
|
@ -104,7 +105,7 @@
|
|||
<ul class="v_menu">
|
||||
<li><a href="/app/hapservers.py?service=apache" title="Overview Apache servers" class="overview-link head-submenu">Overview</a></li>
|
||||
<li><a href="/app/config.py?service=apache" title="Working with Apache configs" class="edit head-submenu">Configs</a></li>
|
||||
<li><a href="/app/viewsttats.py?service=apache" title="Apache statistics" class="stats head-submenu">Stats</a></li>
|
||||
<li><a href="/app/statsview.py?service=apache" title="Apache statistics" class="stats head-submenu">Stats</a></li>
|
||||
<li><a href="/app/logs.py?service=apache" title="Apache logs " class="logs head-submenu">Logs</a></li>
|
||||
<li><a href="/app/metrics.py?service=apache" title="Apache's metrics" class="metrics head-submenu">Metrics</a></li>
|
||||
{% if role <= 3 %}
|
||||
|
|
|
@ -1,14 +1,27 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<link rel="stylesheet" href="/inc/codemirror/codemirror.css">
|
||||
<link rel="stylesheet" href="/inc/codemirror/dialog.css">
|
||||
<script src="/inc/codemirror/codemirror.js"></script>
|
||||
<script src="/inc/codemirror/nginx.js"></script>
|
||||
<script src="/inc/codemirror/haproxy.js"></script>
|
||||
<script src="/inc/codemirror/dialog.js"></script>
|
||||
<script src="/inc/codemirror/search.js"></script>
|
||||
<script src="/inc/codemirror/searchcursor.js"></script>
|
||||
<script src="/inc/codemirror/jump-to-line.js"></script>
|
||||
<link rel="stylesheet" href="/inc/codemirror/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="/inc/codemirror/addon/dialog/dialog.css">
|
||||
<link rel="stylesheet" href="/inc/codemirror/addon/fold/foldgutter.css">
|
||||
<script src="/inc/codemirror/lib/codemirror.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/search.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/searchcursor.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/jump-to-line.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/matchesonscrollbar.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/match-highlighter.js"></script>
|
||||
<script src="/inc/codemirror/addon/dialog/dialog.js"></script>
|
||||
<script src="/inc/codemirror/addon/edit/matchbrackets.js"></script>
|
||||
<script src="/inc/codemirror/addon/edit/closebrackets.js"></script>
|
||||
<script src="/inc/codemirror/addon/comment/comment.js"></script>
|
||||
<script src="/inc/codemirror/addon/wrap/hardwrap.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/foldcode.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/foldgutter.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/brace-fold.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/comment-fold.js"></script>
|
||||
<script src="/inc/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
||||
<script src="/inc/codemirror/mode/nginx.js"></script>
|
||||
<script src="/inc/codemirror/mode/haproxy.js"></script>
|
||||
<script src="/inc/codemirror/keymap/sublime.js"></script>
|
||||
<script src="/inc/configshow.js"></script>
|
||||
{% if is_serv_protected and role > 2 %}
|
||||
<meta http-equiv="refresh" content="0; url=/app/hapservers.py?service={{service}}">
|
||||
|
@ -65,7 +78,9 @@
|
|||
<button type="submit" value="test" name="save" class="btn btn-default" title="Check config without saving the config">Check config</button>
|
||||
{% endif %}
|
||||
<button type="submit" value="save" name="save" class="btn btn-default" title="Save config without reloading the service">Save</button>
|
||||
{% if is_restart|int == 0 %}
|
||||
<button type="submit" value="" name="" class="btn btn-default">Save and restart</button>
|
||||
{% endif %}
|
||||
<button type="submit" value="reload" name="save" class="btn btn-default">Save and reload</button>
|
||||
{% if service != 'keepalived' %}
|
||||
<div class="alert alert-info alert-two-rows"><b>Note:</b> When reconfiguring the master server, the slave will be reconfigured automatically</div>
|
||||
|
@ -102,10 +117,49 @@
|
|||
}
|
||||
if (cur_url[1].split('&')[0] == 'service=haproxy' || cur_url[1].split('&')[0] == 'service=None') {
|
||||
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("config_text_area"),
|
||||
{mode: "haproxy", lineNumbers: true, autocapitalize: true, autocorrect: true, spellcheck: true, autoCloseBrackets: true});
|
||||
{
|
||||
mode: "haproxy",
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
autocapitalize: true,
|
||||
autocorrect: true,
|
||||
spellcheck: true,
|
||||
autoCloseBrackets: true,
|
||||
keyMap: "sublime",
|
||||
matchBrackets: true,
|
||||
foldGutter: true,
|
||||
showCursorWhenSelecting: true,
|
||||
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "breakpoints"],
|
||||
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: true}
|
||||
});
|
||||
} else {
|
||||
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("config_text_area"),
|
||||
{mode: "nginx", lineNumbers: true, autocapitalize: true, autocorrect: true, spellcheck: true, autoCloseBrackets: true});
|
||||
{
|
||||
mode: "nginx",
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
autocapitalize: true,
|
||||
autocorrect: true,
|
||||
spellcheck: true,
|
||||
autoCloseBrackets: true,
|
||||
keyMap: "sublime",
|
||||
matchBrackets: true,
|
||||
foldGutter: true,
|
||||
showCursorWhenSelecting: true,
|
||||
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "breakpoints"],
|
||||
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: true}
|
||||
});
|
||||
}
|
||||
myCodeMirror.on("gutterClick", function(cm, n) {
|
||||
var info = cm.lineInfo(n);
|
||||
cm.setGutterMarker(n, "breakpoints", info.gutterMarkers ? null : makeMarker());
|
||||
});
|
||||
|
||||
function makeMarker() {
|
||||
var marker = document.createElement("div");
|
||||
marker.style.color = "#822";
|
||||
marker.innerHTML = "●";
|
||||
return marker;
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
{% include 'include/errors.html' %}
|
||||
{% else %}
|
||||
<div class="alert alert-success">Config is ok</div>
|
||||
<a href="viewsttats.py?serv={{ serv }}" target="_blank" title="View stats">Go to view stats</a>
|
||||
<a href="statsview.py?serv={{ serv }}" target="_blank" title="View stats">Go to view stats</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</center>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
{% if selects|length == 0 %}
|
||||
{% include 'include/getstarted.html' %}
|
||||
{% else %}
|
||||
<link href="/inc/table.css" rel="stylesheet" type="text/css">
|
||||
<link href="/inc/css/table.css" rel="stylesheet" type="text/css">
|
||||
<script type="text/javascript" charset="utf8" src="/inc/dataTables.min.js"></script>
|
||||
<center>
|
||||
<p>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{% from 'include/input_macros.html' import input, checkbox %}
|
||||
<script src="/inc/users.js"></script>
|
||||
<script src="/inc/ha.js"></script>
|
||||
<link href="/inc/provisioning.css" rel="stylesheet">
|
||||
<link href="/inc/css/provisioning.css" rel="stylesheet">
|
||||
<style>
|
||||
p {margin: 0;}
|
||||
</style>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% block content %}
|
||||
{% from 'include/input_macros.html' import input, checkbox, select, copy_to_clipboard %}
|
||||
<script src="/inc/overview.js"></script>
|
||||
<link href="/inc/chart.min.css" rel="stylesheet">
|
||||
<link href="/inc/css/chart.min.css" rel="stylesheet">
|
||||
<script src="/inc/metrics.js"></script>
|
||||
<script src="/inc/chart.min.js"></script>
|
||||
{% if servers|length == 0 %} {% include 'include/getstarted.html' %} {% endif %}
|
||||
|
@ -171,7 +171,7 @@
|
|||
{% if s.8.0.20 == 1 %}
|
||||
<span class="lock" title="This server is inaccessible for editing by everyone except the admin role"></span>
|
||||
{% endif %}
|
||||
{% for set in service_settings %}
|
||||
{% for set in docker_settings %}
|
||||
{% if set.server_id == s.0 and set.setting == 'dockerized' and set.value == '1' %}
|
||||
<span class="box" title="This server is dockerized"></span>
|
||||
{% endif %}
|
||||
|
@ -181,12 +181,23 @@
|
|||
<a id="start-{{ s.2 }}" class="start" title="Start {{service}} service">
|
||||
<span class="service-start" onclick="confirmAjaxAction('start', '{{action_service}}', '{{s.2}}')"></span>
|
||||
</a>
|
||||
{% if service != 'keepalived' %}
|
||||
<a id="reload-{{ s.2 }}" class="reload" title="Reload {{service}} service">
|
||||
<span class="service-reload" onclick="confirmAjaxAction('reload', '{{action_service}}', '{{s.2}}', '{{s.1}}')"></span>
|
||||
</a>
|
||||
<a id="restart-{{ s.2 }}" class="restart" title="Restart {{service}} service">
|
||||
<span class="service-reload service-restart" onclick="confirmAjaxAction('restart', '{{action_service}}', '{{s.2}}')"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% for set in restart_settings %}
|
||||
{% if set.server_id == s.0 and set.setting == 'restart' and set.value|int == 0 %}
|
||||
<a id="restart-{{ s.2 }}" class="restart" title="Restart {{service}} service">
|
||||
<span class="service-reload service-restart" onclick="confirmAjaxAction('restart', '{{action_service}}', '{{s.2}}')"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if restart_settings|length == 0 %}
|
||||
<a id="restart-{{ s.2 }}" class="restart" title="Restart {{service}} service">
|
||||
<span class="service-reload service-restart" onclick="confirmAjaxAction('restart', '{{action_service}}', '{{s.2}}')"></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
<a id="stop-{{ s.2 }}" class="stop" title="Stop {{service}} service">
|
||||
<span class="service-stop" onclick="confirmAjaxAction('stop', '{{action_service}}', '{{s.2}}')"></span>
|
||||
</a>
|
||||
|
@ -285,8 +296,8 @@
|
|||
{% if service != 'nginx' and service != 'keepalived' and service != 'apache' %}
|
||||
<a href="/app/config.py?serv={{s.2}}&showMap" class="ui-button ui-widget ui-corner-all" title="Show map">Map</a>
|
||||
{% endif %}
|
||||
{% if service != 'keepalived' and service != 'apache' %}
|
||||
<a href="/app/viewsttats.py?service={{service}}&serv={{s.2}}" class="ui-button ui-widget ui-corner-all" title="View {{service}} statistics">Stat</a>
|
||||
{% if service != 'keepalived' %}
|
||||
<a href="/app/statsview.py?service={{service}}&serv={{s.2}}" class="ui-button ui-widget ui-corner-all" title="View {{service}} statistics">Stats</a>
|
||||
{% endif %}
|
||||
<a href="/app/logs.py?service={{service}}&serv={{s.2}}&rows=10&grep=&hour=00&minut=00&hour1=24&minut1=00" class="ui-button ui-widget ui-corner-all" title="View {{service}} logs">Logs</a>
|
||||
{% if role <= 2 %}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
{% if user_status == 0 or user_plan == 'user' %}
|
||||
{% include 'include/no_sub.html' %}
|
||||
{% else %}
|
||||
<link href="/inc/table.css" rel="stylesheet" type="text/css">
|
||||
<link href="/inc/css/table.css" rel="stylesheet" type="text/css">
|
||||
<script type="text/javascript" charset="utf8" src="/inc/dataTables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<link href="/inc/table.css" rel="stylesheet" type="text/css">
|
||||
<link href="/inc/css/table.css" rel="stylesheet" type="text/css">
|
||||
<script type="text/javascript" charset="utf8" src="/inc/dataTables.min.js"></script>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
|
|
@ -20,7 +20,7 @@ body, .container {
|
|||
font-weight: bold !important;
|
||||
}
|
||||
#main_div {
|
||||
margin-top: 15%;
|
||||
margin-top: 10%;
|
||||
background-color: #239dee;
|
||||
}
|
||||
@supports (-moz-osx-font-smoothing: auto) {
|
||||
|
@ -34,7 +34,7 @@ body, .container {
|
|||
{{error}}
|
||||
<div id="login-form" style="padding-top: 40px; padding-bottom: 50px; height: 250px; color: #000;">
|
||||
<span id="logo_span">
|
||||
<img src="/inc/images/logo_login.png" width="330">
|
||||
<img src="/inc/images/logo_login.png">
|
||||
</span>
|
||||
<form name="auth" id="auth" action="login.py" class="form-horizontal" method="post" style="margin-top: 40px;left: 0;float: left;margin-left: 93px;">
|
||||
<br>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
<link href="/inc/chart.min.css" rel="stylesheet">
|
||||
<link href="/inc/css/chart.min.css" rel="stylesheet">
|
||||
<script src="/inc/metrics.js"></script>
|
||||
<script src="/inc/chart.min.js"></script>
|
||||
{% if user_status == 0 %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
{% from 'include/input_macros.html' import input, select, checkbox %}
|
||||
<link href="/inc/provisioning.css" rel="stylesheet">
|
||||
<link href="/inc/css/provisioning.css" rel="stylesheet">
|
||||
<script src="/inc/users.js"></script>
|
||||
<script src="/inc/fontawesome.min.js"></script>
|
||||
{% include 'include/del_confirm.html' %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
{% from 'include/input_macros.html' import input, checkbox %}
|
||||
<link href="/inc/table.css" rel="stylesheet" type="text/css">
|
||||
<link href="/inc/css/table.css" rel="stylesheet" type="text/css">
|
||||
<script type="text/javascript" charset="utf8" src="/inc/dataTables.min.js"></script>
|
||||
<script type="text/javascript" charset="utf8" src="/inc/runtimeapi.js"></script>
|
||||
<div id="tabs">
|
||||
|
@ -74,7 +74,7 @@
|
|||
</script>
|
||||
<div id="ajaxruntime"></div>
|
||||
<div class="add-note addName alert-info" style="width: inherit; margin-right: 15px;">
|
||||
You can read the description of all Run Time API <a href="https://roxy-wi.org/description.py?description=runtimeapi#commands" title="Run Time API description" target="_blank">here</a>
|
||||
You can read the description of all Run Time API <a href="https://roxy-wi.org/description/runtimeapi#commands" title="Run Time API description" target="_blank">here</a>
|
||||
</div>
|
||||
</div>
|
||||
{% if role <= 3 %}
|
||||
|
@ -111,7 +111,7 @@
|
|||
</table>
|
||||
<div id="ajaxmaxconn"></div>
|
||||
<div class="add-note addName alert-info" style="width: inherit; margin-right: 15px;">
|
||||
You can read how it works <a href="https://roxy-wi.org/description.py?description=runtimeapi#maxconn" title="Change Maxconn" target="_blank">here</a>
|
||||
You can read how it works <a href="https://roxy-wi.org/description/runtimeapi#maxconn" title="Change Maxconn" target="_blank">here</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -157,7 +157,7 @@
|
|||
</table>
|
||||
<div id="ajaxip"></div>
|
||||
<div class="add-note addName alert-info" style="width: inherit; margin-right: 15px;">
|
||||
You can read how it works <a href="https://roxy-wi.org/description.py?description=runtimeapi#ip" title="Change IP and Port" target="_blank">here</a>
|
||||
You can read how it works <a href="https://roxy-wi.org/description/runtimeapi#ip" title="Change IP and Port" target="_blank">here</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="table">
|
||||
|
@ -189,7 +189,7 @@
|
|||
</table>
|
||||
<div id="ajaxtable"></div>
|
||||
<div class="add-note addName alert-info" style="width: inherit; margin-right: 15px;">
|
||||
You can read how it works <a href="https://roxy-wi.org/description.py?description=runtimeapi#ip" title="Change IP and Port" target="_blank">here</a>
|
||||
You can read how it works <a href="https://roxy-wi.org/description/runtimeapi#ip" title="Change IP and Port" target="_blank">here</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="lists">
|
||||
|
@ -221,7 +221,7 @@
|
|||
</table>
|
||||
<div id="ajaxlist"></div>
|
||||
<div class="add-note addName alert-info" style="width: inherit; margin-right: 15px;">
|
||||
You can read how it works <a href="https://roxy-wi.org/description.py?description=runtimeapi#lists" title="Manage lists" target="_blank">here</a>
|
||||
You can read how it works <a href="https://roxy-wi.org/description.py/runtimeapi#lists" title="Manage lists" target="_blank">here</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="sessions">
|
||||
|
@ -248,7 +248,7 @@
|
|||
</table>
|
||||
<div id="ajaxsessions"></div>
|
||||
<div class="add-note addName alert-info" style="width: inherit; margin-right: 15px;">
|
||||
You can read how it works <a href="https://roxy-wi.org/description.py?description=runtimeapi#lists" title="Manage lists" target="_blank">here</a>
|
||||
You can read how it works <a href="https://roxy-wi.org/description/runtimeapi#lists" title="Manage lists" target="_blank">here</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -271,4 +271,4 @@ $( function() {
|
|||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -3,10 +3,28 @@
|
|||
{% if is_serv_protected and role > 2 %}
|
||||
<meta http-equiv="refresh" content="0; url=/app/hapservers.py?service={{service}}">
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="/inc/codemirror/codemirror.css">
|
||||
<script src="/inc/codemirror/codemirror.js"></script>
|
||||
<script src="/inc/codemirror/nginx.js"></script>
|
||||
<script src="/inc/codemirror/haproxy.js"></script>
|
||||
<link rel="stylesheet" href="/inc/codemirror/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="/inc/codemirror/addon/dialog/dialog.css">
|
||||
<link rel="stylesheet" href="/inc/codemirror/addon/fold/foldgutter.css">
|
||||
<script src="/inc/codemirror/lib/codemirror.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/search.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/searchcursor.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/jump-to-line.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/matchesonscrollbar.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/match-highlighter.js"></script>
|
||||
<script src="/inc/codemirror/addon/dialog/dialog.js"></script>
|
||||
<script src="/inc/codemirror/addon/edit/matchbrackets.js"></script>
|
||||
<script src="/inc/codemirror/addon/edit/closebrackets.js"></script>
|
||||
<script src="/inc/codemirror/addon/comment/comment.js"></script>
|
||||
<script src="/inc/codemirror/addon/wrap/hardwrap.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/foldcode.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/foldgutter.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/brace-fold.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/comment-fold.js"></script>
|
||||
<script src="/inc/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
||||
<script src="/inc/codemirror/mode/nginx.js"></script>
|
||||
<script src="/inc/codemirror/mode/haproxy.js"></script>
|
||||
<script src="/inc/codemirror/keymap/sublime.js"></script>
|
||||
<center>
|
||||
<p>
|
||||
<form action="{{ action }}" method="post">
|
||||
|
@ -20,7 +38,7 @@
|
|||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="hidden" value="{{ serv }}" name="serv">
|
||||
<input type="hidden" value="{{ serv }}" name="serv">
|
||||
{% if role <= 3 %}
|
||||
<button type="submit" value="open" name="open" class="btn btn-default" title="Edit running config">Edit</button>
|
||||
{% endif %}
|
||||
|
@ -31,8 +49,8 @@
|
|||
<div id="config">
|
||||
<h4>You are editing "{{section}}" section from server {{ serv }}</h4>
|
||||
<form action="{{ action }}" name="saveconfig" method="post">
|
||||
<input type="hidden" value="{{ serv }}" name="serv">
|
||||
<input type="hidden" value="{{ start_line }}" name="start_line">
|
||||
<input type="hidden" value="{{ serv }}" name="serv">
|
||||
<input type="hidden" value="{{ start_line }}" name="start_line">
|
||||
<input type="hidden" value="{{ end_line }}" name="end_line">
|
||||
<input type="hidden" value="{{ cfg }}.old" name="oldconfig">
|
||||
<div style="margin-left: 23%;width: 60%; text-align: left">
|
||||
|
@ -74,7 +92,7 @@
|
|||
<div class="alert alert-warning">{{warning}}</div>
|
||||
{% endif %}
|
||||
<a href="config.py?serv={{ serv }}" title="Working with HAProxy config">Config</a> |
|
||||
<a href="viewsttats.py?serv={{ serv }}" target="_blank" title="View stats">Go to stats</a>
|
||||
<a href="statsview.py?serv={{ serv }}" target="_blank" title="View stats">Go to stats</a>
|
||||
{% endif %}
|
||||
<script>window.history.pushState("Config", "Config", cur_url[0])</script>
|
||||
{% endif %}
|
||||
|
|
|
@ -210,5 +210,5 @@
|
|||
{% endfor %}
|
||||
});
|
||||
</script>
|
||||
<link href="/inc/servers.css" rel="stylesheet"/>
|
||||
<link href="/inc/css/servers.css" rel="stylesheet"/>
|
||||
{% endblock %}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<style>
|
||||
|
||||
</style>
|
||||
<form style="padding-left: 5px;" action="viewsttats.py" method="post">
|
||||
<form style="padding-left: 5px;" action="statsview.py" method="post">
|
||||
<input type="hidden" id="service" value="{{service}}" />
|
||||
<select autofocus required name="serv" id="serv">
|
||||
<option disabled>------</option>
|
||||
|
@ -152,6 +152,6 @@
|
|||
showStats();
|
||||
});
|
||||
</script>
|
||||
<link href="/inc/style.css" rel="stylesheet">
|
||||
<link href="/inc/css/style.css" rel="stylesheet">
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -46,10 +46,29 @@
|
|||
</div>
|
||||
{% elif waf_rule_file %}
|
||||
<center>
|
||||
<link rel="stylesheet" href="/inc/codemirror/codemirror.css">
|
||||
<link rel="stylesheet" href="/inc/codemirror/lib/codemirror.css">
|
||||
<link rel="stylesheet" href="/inc/codemirror/addon/dialog/dialog.css">
|
||||
<link rel="stylesheet" href="/inc/codemirror/addon/fold/foldgutter.css">
|
||||
<script src="/inc/codemirror/lib/codemirror.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/search.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/searchcursor.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/jump-to-line.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/matchesonscrollbar.js"></script>
|
||||
<script src="/inc/codemirror/addon/search/match-highlighter.js"></script>
|
||||
<script src="/inc/codemirror/addon/dialog/dialog.js"></script>
|
||||
<script src="/inc/codemirror/addon/edit/matchbrackets.js"></script>
|
||||
<script src="/inc/codemirror/addon/edit/closebrackets.js"></script>
|
||||
<script src="/inc/codemirror/addon/comment/comment.js"></script>
|
||||
<script src="/inc/codemirror/addon/wrap/hardwrap.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/foldcode.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/foldgutter.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/brace-fold.js"></script>
|
||||
<script src="/inc/codemirror/addon/fold/comment-fold.js"></script>
|
||||
<script src="/inc/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
||||
<script src="/inc/codemirror/mode/nginx.js"></script>
|
||||
<script src="/inc/codemirror/mode/haproxy.js"></script>
|
||||
<script src="/inc/codemirror/keymap/sublime.js"></script>
|
||||
<script src="/inc/configshow.js"></script>
|
||||
<script src="/inc/codemirror/codemirror.js"></script>
|
||||
<script src="/inc/codemirror/modsec.js"></script>
|
||||
<h4>Config {{waf_rule_file}} from {{ serv }}</h4>
|
||||
</center>
|
||||
<form action="waf.py" name="saveconfig" id="saveconfig" method="post">
|
||||
|
@ -83,7 +102,21 @@
|
|||
</style>
|
||||
<script>
|
||||
var myCodeMirror = CodeMirror.fromTextArea(document.getElementById("config_text_area"),
|
||||
{mode: "modsec", lineNumbers: true, autocapitalize: true, autocorrect: true, spellcheck: true});
|
||||
{
|
||||
mode: "modsec",
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
autocapitalize: true,
|
||||
autocorrect: true,
|
||||
spellcheck: true,
|
||||
autoCloseBrackets: true,
|
||||
keyMap: "sublime",
|
||||
matchBrackets: true,
|
||||
foldGutter: true,
|
||||
showCursorWhenSelecting: true,
|
||||
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "breakpoints"],
|
||||
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: true}
|
||||
});
|
||||
myCodeMirror.refresh();
|
||||
</script>
|
||||
{% else %}
|
||||
|
@ -187,8 +220,8 @@
|
|||
});
|
||||
</script>
|
||||
<div class="add-note addName alert-info" style="width: inherit; margin-right: 15px; clear: both;">
|
||||
Read the description and watch a video about WAF <a href="https://roxy-wi.org/description.py?description=waf" class="link" title="WAF" target="_blank">here</a>
|
||||
Read the description and watch a video about WAF <a href="https://roxy-wi.org/description/waf" class="link" title="WAF" target="_blank">here</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -12,10 +12,10 @@ funct.check_login()
|
|||
funct.page_for_admin(level=3)
|
||||
|
||||
form = funct.form
|
||||
serv = form.getvalue('serv')
|
||||
serv = funct.is_ip_or_dns(form.getvalue('serv'))
|
||||
service = funct.checkAjaxInput(form.getvalue('service'))
|
||||
Select = form.getvalue('del')
|
||||
configver = form.getvalue('configver')
|
||||
service = form.getvalue('service')
|
||||
conf_format = 'cfg'
|
||||
configs_dir = ''
|
||||
stderr = ""
|
||||
|
@ -30,34 +30,23 @@ try:
|
|||
except Exception:
|
||||
pass
|
||||
|
||||
if service == 'keepalived':
|
||||
if funct.check_login(service=3):
|
||||
configs_dir = funct.get_config_var('configs', 'kp_save_configs_dir')
|
||||
title = "Working with versions Keepalived configs"
|
||||
if service in ('haproxy', 'nginx', 'keepalived', 'apache'):
|
||||
service_desc = sql.select_service(service)
|
||||
if funct.check_login(service=service_desc.service_id):
|
||||
title = f"Working with versions {service_desc.service} configs"
|
||||
servers = sql.get_dick_permit(service=service_desc.slug)
|
||||
action = f'versions.py?service={service_desc.slug}'
|
||||
conf_format = 'conf'
|
||||
servers = sql.get_dick_permit(keepalived=1)
|
||||
action = 'versions.py?service=keepalived'
|
||||
elif service == 'nginx':
|
||||
if funct.check_login(service=2):
|
||||
configs_dir = funct.get_config_var('configs', 'nginx_save_configs_dir')
|
||||
title = "Working with versions NGINX configs"
|
||||
conf_format = 'conf'
|
||||
servers = sql.get_dick_permit(nginx=1)
|
||||
action = 'versions.py?service=nginx'
|
||||
elif service == 'apache':
|
||||
if funct.check_login(service=4):
|
||||
configs_dir = funct.get_config_var('configs', 'apache_save_configs_dir')
|
||||
title = "Working with versions Apache configs"
|
||||
conf_format = 'conf'
|
||||
servers = sql.get_dick_permit(apache=1)
|
||||
action = 'versions.py?service=apache'
|
||||
|
||||
if service in ('haproxy', 'nginx', 'apache'):
|
||||
configs_dir = funct.get_config_var('configs', f'{service_desc.service}_save_configs_dir')
|
||||
else:
|
||||
configs_dir = funct.get_config_var('configs', 'kp_save_configs_dir')
|
||||
|
||||
if service == 'haproxy':
|
||||
conf_format = 'cfg'
|
||||
else:
|
||||
service = 'haproxy'
|
||||
if funct.check_login(service=1):
|
||||
title = "Working with versions HAProxy configs"
|
||||
configs_dir = funct.get_config_var('configs', 'haproxy_save_configs_dir')
|
||||
servers = sql.get_dick_permit(haproxy=1)
|
||||
action = "versions.py"
|
||||
print('<meta http-equiv="refresh" content="0; url=/app/overview.py">')
|
||||
|
||||
if serv is not None and form.getvalue('del') is not None:
|
||||
if Select is not None:
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var noOptions = {};
|
||||
var nonWS = /[^\s\u00a0]/;
|
||||
var Pos = CodeMirror.Pos, cmp = CodeMirror.cmpPos;
|
||||
|
||||
function firstNonWS(str) {
|
||||
var found = str.search(nonWS);
|
||||
return found == -1 ? 0 : found;
|
||||
}
|
||||
|
||||
CodeMirror.commands.toggleComment = function(cm) {
|
||||
cm.toggleComment();
|
||||
};
|
||||
|
||||
CodeMirror.defineExtension("toggleComment", function(options) {
|
||||
if (!options) options = noOptions;
|
||||
var cm = this;
|
||||
var minLine = Infinity, ranges = this.listSelections(), mode = null;
|
||||
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||
var from = ranges[i].from(), to = ranges[i].to();
|
||||
if (from.line >= minLine) continue;
|
||||
if (to.line >= minLine) to = Pos(minLine, 0);
|
||||
minLine = from.line;
|
||||
if (mode == null) {
|
||||
if (cm.uncomment(from, to, options)) mode = "un";
|
||||
else { cm.lineComment(from, to, options); mode = "line"; }
|
||||
} else if (mode == "un") {
|
||||
cm.uncomment(from, to, options);
|
||||
} else {
|
||||
cm.lineComment(from, to, options);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Rough heuristic to try and detect lines that are part of multi-line string
|
||||
function probablyInsideString(cm, pos, line) {
|
||||
return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line)
|
||||
}
|
||||
|
||||
function getMode(cm, pos) {
|
||||
var mode = cm.getMode()
|
||||
return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)
|
||||
}
|
||||
|
||||
CodeMirror.defineExtension("lineComment", function(from, to, options) {
|
||||
if (!options) options = noOptions;
|
||||
var self = this, mode = getMode(self, from);
|
||||
var firstLine = self.getLine(from.line);
|
||||
if (firstLine == null || probablyInsideString(self, from, firstLine)) return;
|
||||
|
||||
var commentString = options.lineComment || mode.lineComment;
|
||||
if (!commentString) {
|
||||
if (options.blockCommentStart || mode.blockCommentStart) {
|
||||
options.fullLines = true;
|
||||
self.blockComment(from, to, options);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
|
||||
var pad = options.padding == null ? " " : options.padding;
|
||||
var blankLines = options.commentBlankLines || from.line == to.line;
|
||||
|
||||
self.operation(function() {
|
||||
if (options.indent) {
|
||||
var baseString = null;
|
||||
for (var i = from.line; i < end; ++i) {
|
||||
var line = self.getLine(i);
|
||||
var whitespace = line.search(nonWS) === -1 ? line : line.slice(0, firstNonWS(line));
|
||||
if (baseString == null || baseString.length > whitespace.length) {
|
||||
baseString = whitespace;
|
||||
}
|
||||
}
|
||||
for (var i = from.line; i < end; ++i) {
|
||||
var line = self.getLine(i), cut = baseString.length;
|
||||
if (!blankLines && !nonWS.test(line)) continue;
|
||||
if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
|
||||
self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
|
||||
}
|
||||
} else {
|
||||
for (var i = from.line; i < end; ++i) {
|
||||
if (blankLines || nonWS.test(self.getLine(i)))
|
||||
self.replaceRange(commentString + pad, Pos(i, 0));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("blockComment", function(from, to, options) {
|
||||
if (!options) options = noOptions;
|
||||
var self = this, mode = getMode(self, from);
|
||||
var startString = options.blockCommentStart || mode.blockCommentStart;
|
||||
var endString = options.blockCommentEnd || mode.blockCommentEnd;
|
||||
if (!startString || !endString) {
|
||||
if ((options.lineComment || mode.lineComment) && options.fullLines != false)
|
||||
self.lineComment(from, to, options);
|
||||
return;
|
||||
}
|
||||
if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return
|
||||
|
||||
var end = Math.min(to.line, self.lastLine());
|
||||
if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
|
||||
|
||||
var pad = options.padding == null ? " " : options.padding;
|
||||
if (from.line > end) return;
|
||||
|
||||
self.operation(function() {
|
||||
if (options.fullLines != false) {
|
||||
var lastLineHasText = nonWS.test(self.getLine(end));
|
||||
self.replaceRange(pad + endString, Pos(end));
|
||||
self.replaceRange(startString + pad, Pos(from.line, 0));
|
||||
var lead = options.blockCommentLead || mode.blockCommentLead;
|
||||
if (lead != null) for (var i = from.line + 1; i <= end; ++i)
|
||||
if (i != end || lastLineHasText)
|
||||
self.replaceRange(lead + pad, Pos(i, 0));
|
||||
} else {
|
||||
var atCursor = cmp(self.getCursor("to"), to) == 0, empty = !self.somethingSelected()
|
||||
self.replaceRange(endString, to);
|
||||
if (atCursor) self.setSelection(empty ? to : self.getCursor("from"), to)
|
||||
self.replaceRange(startString, from);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("uncomment", function(from, to, options) {
|
||||
if (!options) options = noOptions;
|
||||
var self = this, mode = getMode(self, from);
|
||||
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);
|
||||
|
||||
// Try finding line comments
|
||||
var lineString = options.lineComment || mode.lineComment, lines = [];
|
||||
var pad = options.padding == null ? " " : options.padding, didSomething;
|
||||
lineComment: {
|
||||
if (!lineString) break lineComment;
|
||||
for (var i = start; i <= end; ++i) {
|
||||
var line = self.getLine(i);
|
||||
var found = line.indexOf(lineString);
|
||||
if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
|
||||
if (found == -1 && nonWS.test(line)) break lineComment;
|
||||
if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
|
||||
lines.push(line);
|
||||
}
|
||||
self.operation(function() {
|
||||
for (var i = start; i <= end; ++i) {
|
||||
var line = lines[i - start];
|
||||
var pos = line.indexOf(lineString), endPos = pos + lineString.length;
|
||||
if (pos < 0) continue;
|
||||
if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
|
||||
didSomething = true;
|
||||
self.replaceRange("", Pos(i, pos), Pos(i, endPos));
|
||||
}
|
||||
});
|
||||
if (didSomething) return true;
|
||||
}
|
||||
|
||||
// Try block comments
|
||||
var startString = options.blockCommentStart || mode.blockCommentStart;
|
||||
var endString = options.blockCommentEnd || mode.blockCommentEnd;
|
||||
if (!startString || !endString) return false;
|
||||
var lead = options.blockCommentLead || mode.blockCommentLead;
|
||||
var startLine = self.getLine(start), open = startLine.indexOf(startString)
|
||||
if (open == -1) return false
|
||||
var endLine = end == start ? startLine : self.getLine(end)
|
||||
var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
|
||||
var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)
|
||||
if (close == -1 ||
|
||||
!/comment/.test(self.getTokenTypeAt(insideStart)) ||
|
||||
!/comment/.test(self.getTokenTypeAt(insideEnd)) ||
|
||||
self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
|
||||
return false;
|
||||
|
||||
// Avoid killing block comments completely outside the selection.
|
||||
// Positions of the last startString before the start of the selection, and the first endString after it.
|
||||
var lastStart = startLine.lastIndexOf(startString, from.ch);
|
||||
var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
|
||||
if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
|
||||
// Positions of the first endString after the end of the selection, and the last startString before it.
|
||||
firstEnd = endLine.indexOf(endString, to.ch);
|
||||
var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
|
||||
lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
|
||||
if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
|
||||
|
||||
self.operation(function() {
|
||||
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
|
||||
Pos(end, close + endString.length));
|
||||
var openEnd = open + startString.length;
|
||||
if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
|
||||
self.replaceRange("", Pos(start, open), Pos(start, openEnd));
|
||||
if (lead) for (var i = start + 1; i <= end; ++i) {
|
||||
var line = self.getLine(i), found = line.indexOf(lead);
|
||||
if (found == -1 || nonWS.test(line.slice(0, found))) continue;
|
||||
var foundEnd = found + lead.length;
|
||||
if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
|
||||
self.replaceRange("", Pos(i, found), Pos(i, foundEnd));
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
});
|
|
@ -0,0 +1,201 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
var defaults = {
|
||||
pairs: "()[]{}''\"\"",
|
||||
closeBefore: ")]}'\":;>",
|
||||
triples: "",
|
||||
explode: "[]{}"
|
||||
};
|
||||
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
|
||||
if (old && old != CodeMirror.Init) {
|
||||
cm.removeKeyMap(keyMap);
|
||||
cm.state.closeBrackets = null;
|
||||
}
|
||||
if (val) {
|
||||
ensureBound(getOption(val, "pairs"))
|
||||
cm.state.closeBrackets = val;
|
||||
cm.addKeyMap(keyMap);
|
||||
}
|
||||
});
|
||||
|
||||
function getOption(conf, name) {
|
||||
if (name == "pairs" && typeof conf == "string") return conf;
|
||||
if (typeof conf == "object" && conf[name] != null) return conf[name];
|
||||
return defaults[name];
|
||||
}
|
||||
|
||||
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
|
||||
function ensureBound(chars) {
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
var ch = chars.charAt(i), key = "'" + ch + "'"
|
||||
if (!keyMap[key]) keyMap[key] = handler(ch)
|
||||
}
|
||||
}
|
||||
ensureBound(defaults.pairs + "`")
|
||||
|
||||
function handler(ch) {
|
||||
return function(cm) { return handleChar(cm, ch); };
|
||||
}
|
||||
|
||||
function getConfig(cm) {
|
||||
var deflt = cm.state.closeBrackets;
|
||||
if (!deflt || deflt.override) return deflt;
|
||||
var mode = cm.getModeAt(cm.getCursor());
|
||||
return mode.closeBrackets || deflt;
|
||||
}
|
||||
|
||||
function handleBackspace(cm) {
|
||||
var conf = getConfig(cm);
|
||||
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
|
||||
var pairs = getOption(conf, "pairs");
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
if (!ranges[i].empty()) return CodeMirror.Pass;
|
||||
var around = charsAround(cm, ranges[i].head);
|
||||
if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||
}
|
||||
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||
var cur = ranges[i].head;
|
||||
cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
|
||||
}
|
||||
}
|
||||
|
||||
function handleEnter(cm) {
|
||||
var conf = getConfig(cm);
|
||||
var explode = conf && getOption(conf, "explode");
|
||||
if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
if (!ranges[i].empty()) return CodeMirror.Pass;
|
||||
var around = charsAround(cm, ranges[i].head);
|
||||
if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||
}
|
||||
cm.operation(function() {
|
||||
var linesep = cm.lineSeparator() || "\n";
|
||||
cm.replaceSelection(linesep + linesep, null);
|
||||
moveSel(cm, -1)
|
||||
ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var line = ranges[i].head.line;
|
||||
cm.indentLine(line, null, true);
|
||||
cm.indentLine(line + 1, null, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function moveSel(cm, dir) {
|
||||
var newRanges = [], ranges = cm.listSelections(), primary = 0
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i]
|
||||
if (range.head == cm.getCursor()) primary = i
|
||||
var pos = range.head.ch || dir > 0 ? {line: range.head.line, ch: range.head.ch + dir} : {line: range.head.line - 1}
|
||||
newRanges.push({anchor: pos, head: pos})
|
||||
}
|
||||
cm.setSelections(newRanges, primary)
|
||||
}
|
||||
|
||||
function contractSelection(sel) {
|
||||
var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
|
||||
return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
|
||||
head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
|
||||
}
|
||||
|
||||
function handleChar(cm, ch) {
|
||||
var conf = getConfig(cm);
|
||||
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
|
||||
var pairs = getOption(conf, "pairs");
|
||||
var pos = pairs.indexOf(ch);
|
||||
if (pos == -1) return CodeMirror.Pass;
|
||||
|
||||
var closeBefore = getOption(conf,"closeBefore");
|
||||
|
||||
var triples = getOption(conf, "triples");
|
||||
|
||||
var identical = pairs.charAt(pos + 1) == ch;
|
||||
var ranges = cm.listSelections();
|
||||
var opening = pos % 2 == 0;
|
||||
|
||||
var type;
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i], cur = range.head, curType;
|
||||
var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
|
||||
if (opening && !range.empty()) {
|
||||
curType = "surround";
|
||||
} else if ((identical || !opening) && next == ch) {
|
||||
if (identical && stringStartsAfter(cm, cur))
|
||||
curType = "both";
|
||||
else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
|
||||
curType = "skipThree";
|
||||
else
|
||||
curType = "skip";
|
||||
} else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
|
||||
cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
|
||||
if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
|
||||
curType = "addFour";
|
||||
} else if (identical) {
|
||||
var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
|
||||
if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
|
||||
else return CodeMirror.Pass;
|
||||
} else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
|
||||
curType = "both";
|
||||
} else {
|
||||
return CodeMirror.Pass;
|
||||
}
|
||||
if (!type) type = curType;
|
||||
else if (type != curType) return CodeMirror.Pass;
|
||||
}
|
||||
|
||||
var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
|
||||
var right = pos % 2 ? ch : pairs.charAt(pos + 1);
|
||||
cm.operation(function() {
|
||||
if (type == "skip") {
|
||||
moveSel(cm, 1)
|
||||
} else if (type == "skipThree") {
|
||||
moveSel(cm, 3)
|
||||
} else if (type == "surround") {
|
||||
var sels = cm.getSelections();
|
||||
for (var i = 0; i < sels.length; i++)
|
||||
sels[i] = left + sels[i] + right;
|
||||
cm.replaceSelections(sels, "around");
|
||||
sels = cm.listSelections().slice();
|
||||
for (var i = 0; i < sels.length; i++)
|
||||
sels[i] = contractSelection(sels[i]);
|
||||
cm.setSelections(sels);
|
||||
} else if (type == "both") {
|
||||
cm.replaceSelection(left + right, null);
|
||||
cm.triggerElectric(left + right);
|
||||
moveSel(cm, -1)
|
||||
} else if (type == "addFour") {
|
||||
cm.replaceSelection(left + left + left + left, "before");
|
||||
moveSel(cm, 1)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function charsAround(cm, pos) {
|
||||
var str = cm.getRange(Pos(pos.line, pos.ch - 1),
|
||||
Pos(pos.line, pos.ch + 1));
|
||||
return str.length == 2 ? str : null;
|
||||
}
|
||||
|
||||
function stringStartsAfter(cm, pos) {
|
||||
var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
|
||||
return /\bstring/.test(token.type) && token.start == pos.ch &&
|
||||
(pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
|
||||
}
|
||||
});
|
|
@ -0,0 +1,160 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
|
||||
(document.documentMode == null || document.documentMode < 8);
|
||||
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"};
|
||||
|
||||
function bracketRegex(config) {
|
||||
return config && config.bracketRegex || /[(){}[\]]/
|
||||
}
|
||||
|
||||
function findMatchingBracket(cm, where, config) {
|
||||
var line = cm.getLineHandle(where.line), pos = where.ch - 1;
|
||||
var afterCursor = config && config.afterCursor
|
||||
if (afterCursor == null)
|
||||
afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
|
||||
var re = bracketRegex(config)
|
||||
|
||||
// A cursor is defined as between two characters, but in in vim command mode
|
||||
// (i.e. not insert mode), the cursor is visually represented as a
|
||||
// highlighted box on top of the 2nd character. Otherwise, we allow matches
|
||||
// from before or after the cursor.
|
||||
var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||
|
||||
re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];
|
||||
if (!match) return null;
|
||||
var dir = match.charAt(1) == ">" ? 1 : -1;
|
||||
if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
|
||||
var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
|
||||
|
||||
var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config);
|
||||
if (found == null) return null;
|
||||
return {from: Pos(where.line, pos), to: found && found.pos,
|
||||
match: found && found.ch == match.charAt(0), forward: dir > 0};
|
||||
}
|
||||
|
||||
// bracketRegex is used to specify which type of bracket to scan
|
||||
// should be a regexp, e.g. /[[\]]/
|
||||
//
|
||||
// Note: If "where" is on an open bracket, then this bracket is ignored.
|
||||
//
|
||||
// Returns false when no bracket was found, null when it reached
|
||||
// maxScanLines and gave up
|
||||
function scanForBracket(cm, where, dir, style, config) {
|
||||
var maxScanLen = (config && config.maxScanLineLength) || 10000;
|
||||
var maxScanLines = (config && config.maxScanLines) || 1000;
|
||||
|
||||
var stack = [];
|
||||
var re = bracketRegex(config)
|
||||
var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
|
||||
: Math.max(cm.firstLine() - 1, where.line - maxScanLines);
|
||||
for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
|
||||
var line = cm.getLine(lineNo);
|
||||
if (!line) continue;
|
||||
var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
|
||||
if (line.length > maxScanLen) continue;
|
||||
if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
|
||||
for (; pos != end; pos += dir) {
|
||||
var ch = line.charAt(pos);
|
||||
if (re.test(ch) && (style === undefined ||
|
||||
(cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") == (style || ""))) {
|
||||
var match = matching[ch];
|
||||
if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
|
||||
else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
|
||||
else stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
|
||||
}
|
||||
|
||||
function matchBrackets(cm, autoclear, config) {
|
||||
// Disable brace matching in long lines, since it'll cause hugely slow updates
|
||||
var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000,
|
||||
highlightNonMatching = config && config.highlightNonMatching;
|
||||
var marks = [], ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
|
||||
if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) {
|
||||
var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
||||
marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
|
||||
if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
|
||||
marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
|
||||
}
|
||||
}
|
||||
|
||||
if (marks.length) {
|
||||
// Kludge to work around the IE bug from issue #1193, where text
|
||||
// input stops going to the textarea whenever this fires.
|
||||
if (ie_lt8 && cm.state.focused) cm.focus();
|
||||
|
||||
var clear = function() {
|
||||
cm.operation(function() {
|
||||
for (var i = 0; i < marks.length; i++) marks[i].clear();
|
||||
});
|
||||
};
|
||||
if (autoclear) setTimeout(clear, 800);
|
||||
else return clear;
|
||||
}
|
||||
}
|
||||
|
||||
function doMatchBrackets(cm) {
|
||||
cm.operation(function() {
|
||||
if (cm.state.matchBrackets.currentlyHighlighted) {
|
||||
cm.state.matchBrackets.currentlyHighlighted();
|
||||
cm.state.matchBrackets.currentlyHighlighted = null;
|
||||
}
|
||||
cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
|
||||
});
|
||||
}
|
||||
|
||||
function clearHighlighted(cm) {
|
||||
if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
|
||||
cm.state.matchBrackets.currentlyHighlighted();
|
||||
cm.state.matchBrackets.currentlyHighlighted = null;
|
||||
}
|
||||
}
|
||||
|
||||
CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
|
||||
if (old && old != CodeMirror.Init) {
|
||||
cm.off("cursorActivity", doMatchBrackets);
|
||||
cm.off("focus", doMatchBrackets)
|
||||
cm.off("blur", clearHighlighted)
|
||||
clearHighlighted(cm);
|
||||
}
|
||||
if (val) {
|
||||
cm.state.matchBrackets = typeof val == "object" ? val : {};
|
||||
cm.on("cursorActivity", doMatchBrackets);
|
||||
cm.on("focus", doMatchBrackets)
|
||||
cm.on("blur", clearHighlighted)
|
||||
}
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
|
||||
CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
|
||||
// Backwards-compatibility kludge
|
||||
if (oldConfig || typeof config == "boolean") {
|
||||
if (!oldConfig) {
|
||||
config = config ? {strict: true} : null
|
||||
} else {
|
||||
oldConfig.strict = config
|
||||
config = oldConfig
|
||||
}
|
||||
}
|
||||
return findMatchingBracket(this, pos, config)
|
||||
});
|
||||
CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
|
||||
return scanForBracket(this, pos, dir, style, config);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,119 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
function bracketFolding(pairs) {
|
||||
return function(cm, start) {
|
||||
var line = start.line, lineText = cm.getLine(line);
|
||||
|
||||
function findOpening(pair) {
|
||||
var tokenType;
|
||||
for (var at = start.ch, pass = 0;;) {
|
||||
var found = at <= 0 ? -1 : lineText.lastIndexOf(pair[0], at - 1);
|
||||
if (found == -1) {
|
||||
if (pass == 1) break;
|
||||
pass = 1;
|
||||
at = lineText.length;
|
||||
continue;
|
||||
}
|
||||
if (pass == 1 && found < start.ch) break;
|
||||
tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1));
|
||||
if (!/^(comment|string)/.test(tokenType)) return {ch: found + 1, tokenType: tokenType, pair: pair};
|
||||
at = found - 1;
|
||||
}
|
||||
}
|
||||
|
||||
function findRange(found) {
|
||||
var count = 1, lastLine = cm.lastLine(), end, startCh = found.ch, endCh
|
||||
outer: for (var i = line; i <= lastLine; ++i) {
|
||||
var text = cm.getLine(i), pos = i == line ? startCh : 0;
|
||||
for (;;) {
|
||||
var nextOpen = text.indexOf(found.pair[0], pos), nextClose = text.indexOf(found.pair[1], pos);
|
||||
if (nextOpen < 0) nextOpen = text.length;
|
||||
if (nextClose < 0) nextClose = text.length;
|
||||
pos = Math.min(nextOpen, nextClose);
|
||||
if (pos == text.length) break;
|
||||
if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == found.tokenType) {
|
||||
if (pos == nextOpen) ++count;
|
||||
else if (!--count) { end = i; endCh = pos; break outer; }
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (end == null || line == end) return null
|
||||
return {from: CodeMirror.Pos(line, startCh),
|
||||
to: CodeMirror.Pos(end, endCh)};
|
||||
}
|
||||
|
||||
var found = []
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
var open = findOpening(pairs[i])
|
||||
if (open) found.push(open)
|
||||
}
|
||||
found.sort(function(a, b) { return a.ch - b.ch })
|
||||
for (var i = 0; i < found.length; i++) {
|
||||
var range = findRange(found[i])
|
||||
if (range) return range
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
CodeMirror.registerHelper("fold", "brace", bracketFolding([["{", "}"], ["[", "]"]]));
|
||||
|
||||
CodeMirror.registerHelper("fold", "brace-paren", bracketFolding([["{", "}"], ["[", "]"], ["(", ")"]]));
|
||||
|
||||
CodeMirror.registerHelper("fold", "import", function(cm, start) {
|
||||
function hasImport(line) {
|
||||
if (line < cm.firstLine() || line > cm.lastLine()) return null;
|
||||
var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
|
||||
if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
|
||||
if (start.type != "keyword" || start.string != "import") return null;
|
||||
// Now find closing semicolon, return its position
|
||||
for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
|
||||
var text = cm.getLine(i), semi = text.indexOf(";");
|
||||
if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
|
||||
}
|
||||
}
|
||||
|
||||
var startLine = start.line, has = hasImport(startLine), prev;
|
||||
if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1))
|
||||
return null;
|
||||
for (var end = has.end;;) {
|
||||
var next = hasImport(end.line + 1);
|
||||
if (next == null) break;
|
||||
end = next.end;
|
||||
}
|
||||
return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end};
|
||||
});
|
||||
|
||||
CodeMirror.registerHelper("fold", "include", function(cm, start) {
|
||||
function hasInclude(line) {
|
||||
if (line < cm.firstLine() || line > cm.lastLine()) return null;
|
||||
var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
|
||||
if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
|
||||
if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
|
||||
}
|
||||
|
||||
var startLine = start.line, has = hasInclude(startLine);
|
||||
if (has == null || hasInclude(startLine - 1) != null) return null;
|
||||
for (var end = startLine;;) {
|
||||
var next = hasInclude(end + 1);
|
||||
if (next == null) break;
|
||||
++end;
|
||||
}
|
||||
return {from: CodeMirror.Pos(startLine, has + 1),
|
||||
to: cm.clipPos(CodeMirror.Pos(end))};
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.registerGlobalHelper("fold", "comment", function(mode) {
|
||||
return mode.blockCommentStart && mode.blockCommentEnd;
|
||||
}, function(cm, start) {
|
||||
var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;
|
||||
if (!startToken || !endToken) return;
|
||||
var line = start.line, lineText = cm.getLine(line);
|
||||
|
||||
var startCh;
|
||||
for (var at = start.ch, pass = 0;;) {
|
||||
var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1);
|
||||
if (found == -1) {
|
||||
if (pass == 1) return;
|
||||
pass = 1;
|
||||
at = lineText.length;
|
||||
continue;
|
||||
}
|
||||
if (pass == 1 && found < start.ch) return;
|
||||
if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&
|
||||
(found == 0 || lineText.slice(found - endToken.length, found) == endToken ||
|
||||
!/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) {
|
||||
startCh = found + startToken.length;
|
||||
break;
|
||||
}
|
||||
at = found - 1;
|
||||
}
|
||||
|
||||
var depth = 1, lastLine = cm.lastLine(), end, endCh;
|
||||
outer: for (var i = line; i <= lastLine; ++i) {
|
||||
var text = cm.getLine(i), pos = i == line ? startCh : 0;
|
||||
for (;;) {
|
||||
var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
|
||||
if (nextOpen < 0) nextOpen = text.length;
|
||||
if (nextClose < 0) nextClose = text.length;
|
||||
pos = Math.min(nextOpen, nextClose);
|
||||
if (pos == text.length) break;
|
||||
if (pos == nextOpen) ++depth;
|
||||
else if (!--depth) { end = i; endCh = pos; break outer; }
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
if (end == null || line == end && endCh == startCh) return;
|
||||
return {from: CodeMirror.Pos(line, startCh),
|
||||
to: CodeMirror.Pos(end, endCh)};
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,159 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
function doFold(cm, pos, options, force) {
|
||||
if (options && options.call) {
|
||||
var finder = options;
|
||||
options = null;
|
||||
} else {
|
||||
var finder = getOption(cm, options, "rangeFinder");
|
||||
}
|
||||
if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
|
||||
var minSize = getOption(cm, options, "minFoldSize");
|
||||
|
||||
function getRange(allowFolded) {
|
||||
var range = finder(cm, pos);
|
||||
if (!range || range.to.line - range.from.line < minSize) return null;
|
||||
if (force === "fold") return range;
|
||||
|
||||
var marks = cm.findMarksAt(range.from);
|
||||
for (var i = 0; i < marks.length; ++i) {
|
||||
if (marks[i].__isFold) {
|
||||
if (!allowFolded) return null;
|
||||
range.cleared = true;
|
||||
marks[i].clear();
|
||||
}
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
var range = getRange(true);
|
||||
if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) {
|
||||
pos = CodeMirror.Pos(pos.line - 1, 0);
|
||||
range = getRange(false);
|
||||
}
|
||||
if (!range || range.cleared || force === "unfold") return;
|
||||
|
||||
var myWidget = makeWidget(cm, options, range);
|
||||
CodeMirror.on(myWidget, "mousedown", function(e) {
|
||||
myRange.clear();
|
||||
CodeMirror.e_preventDefault(e);
|
||||
});
|
||||
var myRange = cm.markText(range.from, range.to, {
|
||||
replacedWith: myWidget,
|
||||
clearOnEnter: getOption(cm, options, "clearOnEnter"),
|
||||
__isFold: true
|
||||
});
|
||||
myRange.on("clear", function(from, to) {
|
||||
CodeMirror.signal(cm, "unfold", cm, from, to);
|
||||
});
|
||||
CodeMirror.signal(cm, "fold", cm, range.from, range.to);
|
||||
}
|
||||
|
||||
function makeWidget(cm, options, range) {
|
||||
var widget = getOption(cm, options, "widget");
|
||||
|
||||
if (typeof widget == "function") {
|
||||
widget = widget(range.from, range.to);
|
||||
}
|
||||
|
||||
if (typeof widget == "string") {
|
||||
var text = document.createTextNode(widget);
|
||||
widget = document.createElement("span");
|
||||
widget.appendChild(text);
|
||||
widget.className = "CodeMirror-foldmarker";
|
||||
} else if (widget) {
|
||||
widget = widget.cloneNode(true)
|
||||
}
|
||||
return widget;
|
||||
}
|
||||
|
||||
// Clumsy backwards-compatible interface
|
||||
CodeMirror.newFoldFunction = function(rangeFinder, widget) {
|
||||
return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
|
||||
};
|
||||
|
||||
// New-style interface
|
||||
CodeMirror.defineExtension("foldCode", function(pos, options, force) {
|
||||
doFold(this, pos, options, force);
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("isFolded", function(pos) {
|
||||
var marks = this.findMarksAt(pos);
|
||||
for (var i = 0; i < marks.length; ++i)
|
||||
if (marks[i].__isFold) return true;
|
||||
});
|
||||
|
||||
CodeMirror.commands.toggleFold = function(cm) {
|
||||
cm.foldCode(cm.getCursor());
|
||||
};
|
||||
CodeMirror.commands.fold = function(cm) {
|
||||
cm.foldCode(cm.getCursor(), null, "fold");
|
||||
};
|
||||
CodeMirror.commands.unfold = function(cm) {
|
||||
cm.foldCode(cm.getCursor(), { scanUp: false }, "unfold");
|
||||
};
|
||||
CodeMirror.commands.foldAll = function(cm) {
|
||||
cm.operation(function() {
|
||||
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
|
||||
cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, "fold");
|
||||
});
|
||||
};
|
||||
CodeMirror.commands.unfoldAll = function(cm) {
|
||||
cm.operation(function() {
|
||||
for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
|
||||
cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, "unfold");
|
||||
});
|
||||
};
|
||||
|
||||
CodeMirror.registerHelper("fold", "combine", function() {
|
||||
var funcs = Array.prototype.slice.call(arguments, 0);
|
||||
return function(cm, start) {
|
||||
for (var i = 0; i < funcs.length; ++i) {
|
||||
var found = funcs[i](cm, start);
|
||||
if (found) return found;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.registerHelper("fold", "auto", function(cm, start) {
|
||||
var helpers = cm.getHelpers(start, "fold");
|
||||
for (var i = 0; i < helpers.length; i++) {
|
||||
var cur = helpers[i](cm, start);
|
||||
if (cur) return cur;
|
||||
}
|
||||
});
|
||||
|
||||
var defaultOptions = {
|
||||
rangeFinder: CodeMirror.fold.auto,
|
||||
widget: "\u2194",
|
||||
minFoldSize: 0,
|
||||
scanUp: false,
|
||||
clearOnEnter: true
|
||||
};
|
||||
|
||||
CodeMirror.defineOption("foldOptions", null);
|
||||
|
||||
function getOption(cm, options, name) {
|
||||
if (options && options[name] !== undefined)
|
||||
return options[name];
|
||||
var editorOptions = cm.options.foldOptions;
|
||||
if (editorOptions && editorOptions[name] !== undefined)
|
||||
return editorOptions[name];
|
||||
return defaultOptions[name];
|
||||
}
|
||||
|
||||
CodeMirror.defineExtension("foldOption", function(options, name) {
|
||||
return getOption(this, options, name);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
.CodeMirror-foldmarker {
|
||||
color: blue;
|
||||
text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
|
||||
font-family: arial;
|
||||
line-height: .3;
|
||||
cursor: pointer;
|
||||
}
|
||||
.CodeMirror-foldgutter {
|
||||
width: .7em;
|
||||
}
|
||||
.CodeMirror-foldgutter-open,
|
||||
.CodeMirror-foldgutter-folded {
|
||||
cursor: pointer;
|
||||
}
|
||||
.CodeMirror-foldgutter-open:after {
|
||||
content: "\25BE";
|
||||
}
|
||||
.CodeMirror-foldgutter-folded:after {
|
||||
content: "\25B8";
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"), require("./foldcode"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "./foldcode"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
|
||||
if (old && old != CodeMirror.Init) {
|
||||
cm.clearGutter(cm.state.foldGutter.options.gutter);
|
||||
cm.state.foldGutter = null;
|
||||
cm.off("gutterClick", onGutterClick);
|
||||
cm.off("changes", onChange);
|
||||
cm.off("viewportChange", onViewportChange);
|
||||
cm.off("fold", onFold);
|
||||
cm.off("unfold", onFold);
|
||||
cm.off("swapDoc", onChange);
|
||||
cm.off("optionChange", optionChange);
|
||||
}
|
||||
if (val) {
|
||||
cm.state.foldGutter = new State(parseOptions(val));
|
||||
updateInViewport(cm);
|
||||
cm.on("gutterClick", onGutterClick);
|
||||
cm.on("changes", onChange);
|
||||
cm.on("viewportChange", onViewportChange);
|
||||
cm.on("fold", onFold);
|
||||
cm.on("unfold", onFold);
|
||||
cm.on("swapDoc", onChange);
|
||||
cm.on("optionChange", optionChange);
|
||||
}
|
||||
});
|
||||
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
function State(options) {
|
||||
this.options = options;
|
||||
this.from = this.to = 0;
|
||||
}
|
||||
|
||||
function parseOptions(opts) {
|
||||
if (opts === true) opts = {};
|
||||
if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
|
||||
if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
|
||||
if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
|
||||
return opts;
|
||||
}
|
||||
|
||||
function isFolded(cm, line) {
|
||||
var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));
|
||||
for (var i = 0; i < marks.length; ++i) {
|
||||
if (marks[i].__isFold) {
|
||||
var fromPos = marks[i].find(-1);
|
||||
if (fromPos && fromPos.line === line)
|
||||
return marks[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function marker(spec) {
|
||||
if (typeof spec == "string") {
|
||||
var elt = document.createElement("div");
|
||||
elt.className = spec + " CodeMirror-guttermarker-subtle";
|
||||
return elt;
|
||||
} else {
|
||||
return spec.cloneNode(true);
|
||||
}
|
||||
}
|
||||
|
||||
function updateFoldInfo(cm, from, to) {
|
||||
var opts = cm.state.foldGutter.options, cur = from - 1;
|
||||
var minSize = cm.foldOption(opts, "minFoldSize");
|
||||
var func = cm.foldOption(opts, "rangeFinder");
|
||||
// we can reuse the built-in indicator element if its className matches the new state
|
||||
var clsFolded = typeof opts.indicatorFolded == "string" && classTest(opts.indicatorFolded);
|
||||
var clsOpen = typeof opts.indicatorOpen == "string" && classTest(opts.indicatorOpen);
|
||||
cm.eachLine(from, to, function(line) {
|
||||
++cur;
|
||||
var mark = null;
|
||||
var old = line.gutterMarkers;
|
||||
if (old) old = old[opts.gutter];
|
||||
if (isFolded(cm, cur)) {
|
||||
if (clsFolded && old && clsFolded.test(old.className)) return;
|
||||
mark = marker(opts.indicatorFolded);
|
||||
} else {
|
||||
var pos = Pos(cur, 0);
|
||||
var range = func && func(cm, pos);
|
||||
if (range && range.to.line - range.from.line >= minSize) {
|
||||
if (clsOpen && old && clsOpen.test(old.className)) return;
|
||||
mark = marker(opts.indicatorOpen);
|
||||
}
|
||||
}
|
||||
if (!mark && !old) return;
|
||||
cm.setGutterMarker(line, opts.gutter, mark);
|
||||
});
|
||||
}
|
||||
|
||||
// copied from CodeMirror/src/util/dom.js
|
||||
function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
|
||||
|
||||
function updateInViewport(cm) {
|
||||
var vp = cm.getViewport(), state = cm.state.foldGutter;
|
||||
if (!state) return;
|
||||
cm.operation(function() {
|
||||
updateFoldInfo(cm, vp.from, vp.to);
|
||||
});
|
||||
state.from = vp.from; state.to = vp.to;
|
||||
}
|
||||
|
||||
function onGutterClick(cm, line, gutter) {
|
||||
var state = cm.state.foldGutter;
|
||||
if (!state) return;
|
||||
var opts = state.options;
|
||||
if (gutter != opts.gutter) return;
|
||||
var folded = isFolded(cm, line);
|
||||
if (folded) folded.clear();
|
||||
else cm.foldCode(Pos(line, 0), opts);
|
||||
}
|
||||
|
||||
function optionChange(cm, option) {
|
||||
if (option == "mode") onChange(cm)
|
||||
}
|
||||
|
||||
function onChange(cm) {
|
||||
var state = cm.state.foldGutter;
|
||||
if (!state) return;
|
||||
var opts = state.options;
|
||||
state.from = state.to = 0;
|
||||
clearTimeout(state.changeUpdate);
|
||||
state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
|
||||
}
|
||||
|
||||
function onViewportChange(cm) {
|
||||
var state = cm.state.foldGutter;
|
||||
if (!state) return;
|
||||
var opts = state.options;
|
||||
clearTimeout(state.changeUpdate);
|
||||
state.changeUpdate = setTimeout(function() {
|
||||
var vp = cm.getViewport();
|
||||
if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
|
||||
updateInViewport(cm);
|
||||
} else {
|
||||
cm.operation(function() {
|
||||
if (vp.from < state.from) {
|
||||
updateFoldInfo(cm, vp.from, state.from);
|
||||
state.from = vp.from;
|
||||
}
|
||||
if (vp.to > state.to) {
|
||||
updateFoldInfo(cm, state.to, vp.to);
|
||||
state.to = vp.to;
|
||||
}
|
||||
});
|
||||
}
|
||||
}, opts.updateViewportTimeSpan || 400);
|
||||
}
|
||||
|
||||
function onFold(cm, from) {
|
||||
var state = cm.state.foldGutter;
|
||||
if (!state) return;
|
||||
var line = from.line;
|
||||
if (line >= state.from && line < state.to)
|
||||
updateFoldInfo(cm, line, line + 1);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,160 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
function findParagraph(cm, pos, options) {
|
||||
var startRE = options.paragraphStart || cm.getHelper(pos, "paragraphStart");
|
||||
for (var start = pos.line, first = cm.firstLine(); start > first; --start) {
|
||||
var line = cm.getLine(start);
|
||||
if (startRE && startRE.test(line)) break;
|
||||
if (!/\S/.test(line)) { ++start; break; }
|
||||
}
|
||||
var endRE = options.paragraphEnd || cm.getHelper(pos, "paragraphEnd");
|
||||
for (var end = pos.line + 1, last = cm.lastLine(); end <= last; ++end) {
|
||||
var line = cm.getLine(end);
|
||||
if (endRE && endRE.test(line)) { ++end; break; }
|
||||
if (!/\S/.test(line)) break;
|
||||
}
|
||||
return {from: start, to: end};
|
||||
}
|
||||
|
||||
function findBreakPoint(text, column, wrapOn, killTrailingSpace, forceBreak) {
|
||||
var at = column
|
||||
while (at < text.length && text.charAt(at) == " ") at++
|
||||
for (; at > 0; --at)
|
||||
if (wrapOn.test(text.slice(at - 1, at + 1))) break;
|
||||
|
||||
if (!forceBreak && at <= text.match(/^[ \t]*/)[0].length) {
|
||||
// didn't find a break point before column, in non-forceBreak mode try to
|
||||
// find one after 'column'.
|
||||
for (at = column + 1; at < text.length - 1; ++at) {
|
||||
if (wrapOn.test(text.slice(at - 1, at + 1))) break;
|
||||
}
|
||||
}
|
||||
|
||||
for (var first = true;; first = false) {
|
||||
var endOfText = at;
|
||||
if (killTrailingSpace)
|
||||
while (text.charAt(endOfText - 1) == " ") --endOfText;
|
||||
if (endOfText == 0 && first) at = column;
|
||||
else return {from: endOfText, to: at};
|
||||
}
|
||||
}
|
||||
|
||||
function wrapRange(cm, from, to, options) {
|
||||
from = cm.clipPos(from); to = cm.clipPos(to);
|
||||
var column = options.column || 80;
|
||||
var wrapOn = options.wrapOn || /\s\S|-[^\.\d]/;
|
||||
var forceBreak = options.forceBreak !== false;
|
||||
var killTrailing = options.killTrailingSpace !== false;
|
||||
var changes = [], curLine = "", curNo = from.line;
|
||||
var lines = cm.getRange(from, to, false);
|
||||
if (!lines.length) return null;
|
||||
var leadingSpace = lines[0].match(/^[ \t]*/)[0];
|
||||
if (leadingSpace.length >= column) column = leadingSpace.length + 1
|
||||
|
||||
for (var i = 0; i < lines.length; ++i) {
|
||||
var text = lines[i], oldLen = curLine.length, spaceInserted = 0;
|
||||
if (curLine && text && !wrapOn.test(curLine.charAt(curLine.length - 1) + text.charAt(0))) {
|
||||
curLine += " ";
|
||||
spaceInserted = 1;
|
||||
}
|
||||
var spaceTrimmed = "";
|
||||
if (i) {
|
||||
spaceTrimmed = text.match(/^\s*/)[0];
|
||||
text = text.slice(spaceTrimmed.length);
|
||||
}
|
||||
curLine += text;
|
||||
if (i) {
|
||||
var firstBreak = curLine.length > column && leadingSpace == spaceTrimmed &&
|
||||
findBreakPoint(curLine, column, wrapOn, killTrailing, forceBreak);
|
||||
// If this isn't broken, or is broken at a different point, remove old break
|
||||
if (!firstBreak || firstBreak.from != oldLen || firstBreak.to != oldLen + spaceInserted) {
|
||||
changes.push({text: [spaceInserted ? " " : ""],
|
||||
from: Pos(curNo, oldLen),
|
||||
to: Pos(curNo + 1, spaceTrimmed.length)});
|
||||
} else {
|
||||
curLine = leadingSpace + text;
|
||||
++curNo;
|
||||
}
|
||||
}
|
||||
while (curLine.length > column) {
|
||||
var bp = findBreakPoint(curLine, column, wrapOn, killTrailing, forceBreak);
|
||||
if (bp.from != bp.to ||
|
||||
forceBreak && leadingSpace !== curLine.slice(0, bp.to)) {
|
||||
changes.push({text: ["", leadingSpace],
|
||||
from: Pos(curNo, bp.from),
|
||||
to: Pos(curNo, bp.to)});
|
||||
curLine = leadingSpace + curLine.slice(bp.to);
|
||||
++curNo;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (changes.length) cm.operation(function() {
|
||||
for (var i = 0; i < changes.length; ++i) {
|
||||
var change = changes[i];
|
||||
if (change.text || CodeMirror.cmpPos(change.from, change.to))
|
||||
cm.replaceRange(change.text, change.from, change.to);
|
||||
}
|
||||
});
|
||||
return changes.length ? {from: changes[0].from, to: CodeMirror.changeEnd(changes[changes.length - 1])} : null;
|
||||
}
|
||||
|
||||
CodeMirror.defineExtension("wrapParagraph", function(pos, options) {
|
||||
options = options || {};
|
||||
if (!pos) pos = this.getCursor();
|
||||
var para = findParagraph(this, pos, options);
|
||||
return wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options);
|
||||
});
|
||||
|
||||
CodeMirror.commands.wrapLines = function(cm) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections(), at = cm.lastLine() + 1;
|
||||
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||
var range = ranges[i], span;
|
||||
if (range.empty()) {
|
||||
var para = findParagraph(cm, range.head, {});
|
||||
span = {from: Pos(para.from, 0), to: Pos(para.to - 1)};
|
||||
} else {
|
||||
span = {from: range.from(), to: range.to()};
|
||||
}
|
||||
if (span.to.line >= at) continue;
|
||||
at = span.from.line;
|
||||
wrapRange(cm, span.from, span.to, {});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
CodeMirror.defineExtension("wrapRange", function(from, to, options) {
|
||||
return wrapRange(this, from, to, options || {});
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("wrapParagraphsInRange", function(from, to, options) {
|
||||
options = options || {};
|
||||
var cm = this, paras = [];
|
||||
for (var line = from.line; line <= to.line;) {
|
||||
var para = findParagraph(cm, Pos(line, 0), options);
|
||||
paras.push(para);
|
||||
line = para.to;
|
||||
}
|
||||
var madeChange = false;
|
||||
if (paras.length) cm.operation(function() {
|
||||
for (var i = paras.length - 1; i >= 0; --i)
|
||||
madeChange = madeChange || wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options);
|
||||
});
|
||||
return madeChange;
|
||||
});
|
||||
});
|
|
@ -0,0 +1,128 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.defineExtension("annotateScrollbar", function(options) {
|
||||
if (typeof options == "string") options = {className: options};
|
||||
return new Annotation(this, options);
|
||||
});
|
||||
|
||||
CodeMirror.defineOption("scrollButtonHeight", 0);
|
||||
|
||||
function Annotation(cm, options) {
|
||||
this.cm = cm;
|
||||
this.options = options;
|
||||
this.buttonHeight = options.scrollButtonHeight || cm.getOption("scrollButtonHeight");
|
||||
this.annotations = [];
|
||||
this.doRedraw = this.doUpdate = null;
|
||||
this.div = cm.getWrapperElement().appendChild(document.createElement("div"));
|
||||
this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";
|
||||
this.computeScale();
|
||||
|
||||
function scheduleRedraw(delay) {
|
||||
clearTimeout(self.doRedraw);
|
||||
self.doRedraw = setTimeout(function() { self.redraw(); }, delay);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
cm.on("refresh", this.resizeHandler = function() {
|
||||
clearTimeout(self.doUpdate);
|
||||
self.doUpdate = setTimeout(function() {
|
||||
if (self.computeScale()) scheduleRedraw(20);
|
||||
}, 100);
|
||||
});
|
||||
cm.on("markerAdded", this.resizeHandler);
|
||||
cm.on("markerCleared", this.resizeHandler);
|
||||
if (options.listenForChanges !== false)
|
||||
cm.on("changes", this.changeHandler = function() {
|
||||
scheduleRedraw(250);
|
||||
});
|
||||
}
|
||||
|
||||
Annotation.prototype.computeScale = function() {
|
||||
var cm = this.cm;
|
||||
var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) /
|
||||
cm.getScrollerElement().scrollHeight
|
||||
if (hScale != this.hScale) {
|
||||
this.hScale = hScale;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Annotation.prototype.update = function(annotations) {
|
||||
this.annotations = annotations;
|
||||
this.redraw();
|
||||
};
|
||||
|
||||
Annotation.prototype.redraw = function(compute) {
|
||||
if (compute !== false) this.computeScale();
|
||||
var cm = this.cm, hScale = this.hScale;
|
||||
|
||||
var frag = document.createDocumentFragment(), anns = this.annotations;
|
||||
|
||||
var wrapping = cm.getOption("lineWrapping");
|
||||
var singleLineH = wrapping && cm.defaultTextHeight() * 1.5;
|
||||
var curLine = null, curLineObj = null;
|
||||
|
||||
function getY(pos, top) {
|
||||
if (curLine != pos.line) {
|
||||
curLine = pos.line
|
||||
curLineObj = cm.getLineHandle(pos.line)
|
||||
var visual = cm.getLineHandleVisualStart(curLineObj)
|
||||
if (visual != curLineObj) {
|
||||
curLine = cm.getLineNumber(visual)
|
||||
curLineObj = visual
|
||||
}
|
||||
}
|
||||
if ((curLineObj.widgets && curLineObj.widgets.length) ||
|
||||
(wrapping && curLineObj.height > singleLineH))
|
||||
return cm.charCoords(pos, "local")[top ? "top" : "bottom"];
|
||||
var topY = cm.heightAtLine(curLineObj, "local");
|
||||
return topY + (top ? 0 : curLineObj.height);
|
||||
}
|
||||
|
||||
var lastLine = cm.lastLine()
|
||||
if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) {
|
||||
var ann = anns[i];
|
||||
if (ann.to.line > lastLine) continue;
|
||||
var top = nextTop || getY(ann.from, true) * hScale;
|
||||
var bottom = getY(ann.to, false) * hScale;
|
||||
while (i < anns.length - 1) {
|
||||
if (anns[i + 1].to.line > lastLine) break;
|
||||
nextTop = getY(anns[i + 1].from, true) * hScale;
|
||||
if (nextTop > bottom + .9) break;
|
||||
ann = anns[++i];
|
||||
bottom = getY(ann.to, false) * hScale;
|
||||
}
|
||||
if (bottom == top) continue;
|
||||
var height = Math.max(bottom - top, 3);
|
||||
|
||||
var elt = frag.appendChild(document.createElement("div"));
|
||||
elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: "
|
||||
+ (top + this.buttonHeight) + "px; height: " + height + "px";
|
||||
elt.className = this.options.className;
|
||||
if (ann.id) {
|
||||
elt.setAttribute("annotation-id", ann.id);
|
||||
}
|
||||
}
|
||||
this.div.textContent = "";
|
||||
this.div.appendChild(frag);
|
||||
};
|
||||
|
||||
Annotation.prototype.clear = function() {
|
||||
this.cm.off("refresh", this.resizeHandler);
|
||||
this.cm.off("markerAdded", this.resizeHandler);
|
||||
this.cm.off("markerCleared", this.resizeHandler);
|
||||
if (this.changeHandler) this.cm.off("changes", this.changeHandler);
|
||||
this.div.parentNode.removeChild(this.div);
|
||||
};
|
||||
});
|
|
@ -0,0 +1,167 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
// Highlighting text that matches the selection
|
||||
//
|
||||
// Defines an option highlightSelectionMatches, which, when enabled,
|
||||
// will style strings that match the selection throughout the
|
||||
// document.
|
||||
//
|
||||
// The option can be set to true to simply enable it, or to a
|
||||
// {minChars, style, wordsOnly, showToken, delay} object to explicitly
|
||||
// configure it. minChars is the minimum amount of characters that should be
|
||||
// selected for the behavior to occur, and style is the token style to
|
||||
// apply to the matches. This will be prefixed by "cm-" to create an
|
||||
// actual CSS class name. If wordsOnly is enabled, the matches will be
|
||||
// highlighted only if the selected text is a word. showToken, when enabled,
|
||||
// will cause the current token to be highlighted when nothing is selected.
|
||||
// delay is used to specify how much time to wait, in milliseconds, before
|
||||
// highlighting the matches. If annotateScrollbar is enabled, the occurrences
|
||||
// will be highlighted on the scrollbar via the matchesonscrollbar addon.
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"), require("./matchesonscrollbar"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "./matchesonscrollbar"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var defaults = {
|
||||
style: "matchhighlight",
|
||||
minChars: 2,
|
||||
delay: 100,
|
||||
wordsOnly: false,
|
||||
annotateScrollbar: false,
|
||||
showToken: false,
|
||||
trim: true
|
||||
}
|
||||
|
||||
function State(options) {
|
||||
this.options = {}
|
||||
for (var name in defaults)
|
||||
this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name]
|
||||
this.overlay = this.timeout = null;
|
||||
this.matchesonscroll = null;
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
|
||||
if (old && old != CodeMirror.Init) {
|
||||
removeOverlay(cm);
|
||||
clearTimeout(cm.state.matchHighlighter.timeout);
|
||||
cm.state.matchHighlighter = null;
|
||||
cm.off("cursorActivity", cursorActivity);
|
||||
cm.off("focus", onFocus)
|
||||
}
|
||||
if (val) {
|
||||
var state = cm.state.matchHighlighter = new State(val);
|
||||
if (cm.hasFocus()) {
|
||||
state.active = true
|
||||
highlightMatches(cm)
|
||||
} else {
|
||||
cm.on("focus", onFocus)
|
||||
}
|
||||
cm.on("cursorActivity", cursorActivity);
|
||||
}
|
||||
});
|
||||
|
||||
function cursorActivity(cm) {
|
||||
var state = cm.state.matchHighlighter;
|
||||
if (state.active || cm.hasFocus()) scheduleHighlight(cm, state)
|
||||
}
|
||||
|
||||
function onFocus(cm) {
|
||||
var state = cm.state.matchHighlighter
|
||||
if (!state.active) {
|
||||
state.active = true
|
||||
scheduleHighlight(cm, state)
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleHighlight(cm, state) {
|
||||
clearTimeout(state.timeout);
|
||||
state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay);
|
||||
}
|
||||
|
||||
function addOverlay(cm, query, hasBoundary, style) {
|
||||
var state = cm.state.matchHighlighter;
|
||||
cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
|
||||
if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
|
||||
var searchFor = hasBoundary ? new RegExp((/\w/.test(query.charAt(0)) ? "\\b" : "") +
|
||||
query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") +
|
||||
(/\w/.test(query.charAt(query.length - 1)) ? "\\b" : "")) : query;
|
||||
state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
|
||||
{className: "CodeMirror-selection-highlight-scrollbar"});
|
||||
}
|
||||
}
|
||||
|
||||
function removeOverlay(cm) {
|
||||
var state = cm.state.matchHighlighter;
|
||||
if (state.overlay) {
|
||||
cm.removeOverlay(state.overlay);
|
||||
state.overlay = null;
|
||||
if (state.matchesonscroll) {
|
||||
state.matchesonscroll.clear();
|
||||
state.matchesonscroll = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function highlightMatches(cm) {
|
||||
cm.operation(function() {
|
||||
var state = cm.state.matchHighlighter;
|
||||
removeOverlay(cm);
|
||||
if (!cm.somethingSelected() && state.options.showToken) {
|
||||
var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken;
|
||||
var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
|
||||
while (start && re.test(line.charAt(start - 1))) --start;
|
||||
while (end < line.length && re.test(line.charAt(end))) ++end;
|
||||
if (start < end)
|
||||
addOverlay(cm, line.slice(start, end), re, state.options.style);
|
||||
return;
|
||||
}
|
||||
var from = cm.getCursor("from"), to = cm.getCursor("to");
|
||||
if (from.line != to.line) return;
|
||||
if (state.options.wordsOnly && !isWord(cm, from, to)) return;
|
||||
var selection = cm.getRange(from, to)
|
||||
if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "")
|
||||
if (selection.length >= state.options.minChars)
|
||||
addOverlay(cm, selection, false, state.options.style);
|
||||
});
|
||||
}
|
||||
|
||||
function isWord(cm, from, to) {
|
||||
var str = cm.getRange(from, to);
|
||||
if (str.match(/^\w+$/) !== null) {
|
||||
if (from.ch > 0) {
|
||||
var pos = {line: from.line, ch: from.ch - 1};
|
||||
var chr = cm.getRange(pos, from);
|
||||
if (chr.match(/\W/) === null) return false;
|
||||
}
|
||||
if (to.ch < cm.getLine(from.line).length) {
|
||||
var pos = {line: to.line, ch: to.ch + 1};
|
||||
var chr = cm.getRange(to, pos);
|
||||
if (chr.match(/\W/) === null) return false;
|
||||
}
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
|
||||
function boundariesAround(stream, re) {
|
||||
return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
|
||||
(stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
|
||||
}
|
||||
|
||||
function makeOverlay(query, hasBoundary, style) {
|
||||
return {token: function(stream) {
|
||||
if (stream.match(query) &&
|
||||
(!hasBoundary || boundariesAround(stream, hasBoundary)))
|
||||
return style;
|
||||
stream.next();
|
||||
stream.skipTo(query.charAt(0)) || stream.skipToEnd();
|
||||
}};
|
||||
}
|
||||
});
|
|
@ -0,0 +1,97 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, options) {
|
||||
if (typeof options == "string") options = {className: options};
|
||||
if (!options) options = {};
|
||||
return new SearchAnnotation(this, query, caseFold, options);
|
||||
});
|
||||
|
||||
function SearchAnnotation(cm, query, caseFold, options) {
|
||||
this.cm = cm;
|
||||
this.options = options;
|
||||
var annotateOptions = {listenForChanges: false};
|
||||
for (var prop in options) annotateOptions[prop] = options[prop];
|
||||
if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match";
|
||||
this.annotation = cm.annotateScrollbar(annotateOptions);
|
||||
this.query = query;
|
||||
this.caseFold = caseFold;
|
||||
this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};
|
||||
this.matches = [];
|
||||
this.update = null;
|
||||
|
||||
this.findMatches();
|
||||
this.annotation.update(this.matches);
|
||||
|
||||
var self = this;
|
||||
cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); });
|
||||
}
|
||||
|
||||
var MAX_MATCHES = 1000;
|
||||
|
||||
SearchAnnotation.prototype.findMatches = function() {
|
||||
if (!this.gap) return;
|
||||
for (var i = 0; i < this.matches.length; i++) {
|
||||
var match = this.matches[i];
|
||||
if (match.from.line >= this.gap.to) break;
|
||||
if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
|
||||
}
|
||||
var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), {caseFold: this.caseFold, multiline: this.options.multiline});
|
||||
var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES;
|
||||
while (cursor.findNext()) {
|
||||
var match = {from: cursor.from(), to: cursor.to()};
|
||||
if (match.from.line >= this.gap.to) break;
|
||||
this.matches.splice(i++, 0, match);
|
||||
if (this.matches.length > maxMatches) break;
|
||||
}
|
||||
this.gap = null;
|
||||
};
|
||||
|
||||
function offsetLine(line, changeStart, sizeChange) {
|
||||
if (line <= changeStart) return line;
|
||||
return Math.max(changeStart, line + sizeChange);
|
||||
}
|
||||
|
||||
SearchAnnotation.prototype.onChange = function(change) {
|
||||
var startLine = change.from.line;
|
||||
var endLine = CodeMirror.changeEnd(change).line;
|
||||
var sizeChange = endLine - change.to.line;
|
||||
if (this.gap) {
|
||||
this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);
|
||||
this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);
|
||||
} else {
|
||||
this.gap = {from: change.from.line, to: endLine + 1};
|
||||
}
|
||||
|
||||
if (sizeChange) for (var i = 0; i < this.matches.length; i++) {
|
||||
var match = this.matches[i];
|
||||
var newFrom = offsetLine(match.from.line, startLine, sizeChange);
|
||||
if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);
|
||||
var newTo = offsetLine(match.to.line, startLine, sizeChange);
|
||||
if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);
|
||||
}
|
||||
clearTimeout(this.update);
|
||||
var self = this;
|
||||
this.update = setTimeout(function() { self.updateAfterChange(); }, 250);
|
||||
};
|
||||
|
||||
SearchAnnotation.prototype.updateAfterChange = function() {
|
||||
this.findMatches();
|
||||
this.annotation.update(this.matches);
|
||||
};
|
||||
|
||||
SearchAnnotation.prototype.clear = function() {
|
||||
this.cm.off("change", this.changeHandler);
|
||||
this.annotation.clear();
|
||||
};
|
||||
});
|
|
@ -1,274 +0,0 @@
|
|||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(//themes.googleusercontent.com/static/fonts/sourcesanspro/v5/ODelI1aHBYDBqgeIAH2zlBM0YzuT7MdOe03otPbuUS0.woff) format('woff');
|
||||
}
|
||||
|
||||
body, html { margin: 0; padding: 0; height: 100%; }
|
||||
section, article { display: block; padding: 0; }
|
||||
|
||||
body {
|
||||
background: #f8f8f8;
|
||||
font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
p { margin-top: 0; }
|
||||
|
||||
h2, h3, h1 {
|
||||
font-weight: normal;
|
||||
margin-bottom: .7em;
|
||||
}
|
||||
h1 { font-size: 140%; }
|
||||
h2 { font-size: 120%; }
|
||||
h3 { font-size: 110%; }
|
||||
article > h2:first-child, section:first-child > h2 { margin-top: 0; }
|
||||
|
||||
#nav h1 {
|
||||
margin-right: 12px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 2px;
|
||||
color: #d30707;
|
||||
letter-spacing: .5px;
|
||||
}
|
||||
|
||||
a, a:visited, a:link, .quasilink {
|
||||
color: #A21313;
|
||||
}
|
||||
|
||||
em {
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.quasilink {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
article {
|
||||
max-width: 700px;
|
||||
margin: 0 0 0 160px;
|
||||
border-left: 2px solid #E30808;
|
||||
border-right: 1px solid #ddd;
|
||||
padding: 30px 50px 100px 50px;
|
||||
background: white;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
}
|
||||
|
||||
#nav {
|
||||
position: fixed;
|
||||
padding-top: 30px;
|
||||
max-height: 100%;
|
||||
box-sizing: -moz-border-box;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
left: 0; right: none;
|
||||
width: 160px;
|
||||
text-align: right;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1000px) {
|
||||
article {
|
||||
margin: 0 auto;
|
||||
}
|
||||
#nav {
|
||||
right: 50%;
|
||||
width: auto;
|
||||
border-right: 349px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
#nav ul {
|
||||
display: block;
|
||||
margin: 0; padding: 0;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
#nav a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#nav li {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
#nav li ul {
|
||||
font-size: 80%;
|
||||
margin-bottom: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#nav li.active ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#nav li li a {
|
||||
padding-right: 20px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#nav ul a {
|
||||
color: black;
|
||||
padding: 0 7px 1px 11px;
|
||||
}
|
||||
|
||||
#nav ul a.active, #nav ul a:hover {
|
||||
border-bottom: 1px solid #E30808;
|
||||
margin-bottom: -1px;
|
||||
color: #E30808;
|
||||
}
|
||||
|
||||
#logo {
|
||||
border: 0;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
section {
|
||||
border-top: 1px solid #E30808;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
section.first {
|
||||
border: none;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#demo {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#demolist {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
z-index: 25;
|
||||
}
|
||||
|
||||
.yinyang {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 0; right: 0;
|
||||
margin: auto;
|
||||
display: block;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin: 1em 0 0;
|
||||
min-height: 100px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.actionspicture {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
height: 100px;
|
||||
top: 0; left: 0; right: 0;
|
||||
}
|
||||
|
||||
.actionlink {
|
||||
pointer-events: auto;
|
||||
font-family: arial;
|
||||
font-size: 80%;
|
||||
font-weight: bold;
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
line-height: 1;
|
||||
height: 1em;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.actionlink.download {
|
||||
color: white;
|
||||
right: 50%;
|
||||
margin-right: 13px;
|
||||
text-shadow: -1px 1px 3px #b00, -1px -1px 3px #b00, 1px 0px 3px #b00;
|
||||
}
|
||||
|
||||
.actionlink.fund {
|
||||
color: #b00;
|
||||
left: 50%;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.actionlink:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.actionlink a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.actionsleft {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.actionsright {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.actions {
|
||||
padding-top: 120px;
|
||||
}
|
||||
.actionsleft, .actionsright {
|
||||
float: none;
|
||||
text-align: left;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
text-decoration: underline;
|
||||
font-weight: normal;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#features ul {
|
||||
list-style: none;
|
||||
margin: 0 0 1em;
|
||||
padding: 0 0 0 1.2em;
|
||||
}
|
||||
|
||||
#features li:before {
|
||||
content: "-";
|
||||
width: 1em;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-left: -1em;
|
||||
}
|
||||
|
||||
.rel {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.rel-note {
|
||||
margin-top: 0;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding-left: 15px;
|
||||
border-left: 2px solid #ddd;
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
strong {
|
||||
text-decoration: underline;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.field {
|
||||
border: 1px solid #A21313;
|
||||
}
|
|
@ -0,0 +1,720 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
|
||||
|
||||
// A rough approximation of Sublime Text's keybindings
|
||||
// Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/edit/matchbrackets"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/edit/matchbrackets"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var cmds = CodeMirror.commands;
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
// This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
|
||||
function findPosSubword(doc, start, dir) {
|
||||
if (dir < 0 && start.ch == 0) return doc.clipPos(Pos(start.line - 1));
|
||||
var line = doc.getLine(start.line);
|
||||
if (dir > 0 && start.ch >= line.length) return doc.clipPos(Pos(start.line + 1, 0));
|
||||
var state = "start", type, startPos = start.ch;
|
||||
for (var pos = startPos, e = dir < 0 ? 0 : line.length, i = 0; pos != e; pos += dir, i++) {
|
||||
var next = line.charAt(dir < 0 ? pos - 1 : pos);
|
||||
var cat = next != "_" && CodeMirror.isWordChar(next) ? "w" : "o";
|
||||
if (cat == "w" && next.toUpperCase() == next) cat = "W";
|
||||
if (state == "start") {
|
||||
if (cat != "o") { state = "in"; type = cat; }
|
||||
else startPos = pos + dir
|
||||
} else if (state == "in") {
|
||||
if (type != cat) {
|
||||
if (type == "w" && cat == "W" && dir < 0) pos--;
|
||||
if (type == "W" && cat == "w" && dir > 0) { // From uppercase to lowercase
|
||||
if (pos == startPos + 1) { type = "w"; continue; }
|
||||
else pos--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pos(start.line, pos);
|
||||
}
|
||||
|
||||
function moveSubword(cm, dir) {
|
||||
cm.extendSelectionsBy(function(range) {
|
||||
if (cm.display.shift || cm.doc.extend || range.empty())
|
||||
return findPosSubword(cm.doc, range.head, dir);
|
||||
else
|
||||
return dir < 0 ? range.from() : range.to();
|
||||
});
|
||||
}
|
||||
|
||||
cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };
|
||||
cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };
|
||||
|
||||
cmds.scrollLineUp = function(cm) {
|
||||
var info = cm.getScrollInfo();
|
||||
if (!cm.somethingSelected()) {
|
||||
var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
|
||||
if (cm.getCursor().line >= visibleBottomLine)
|
||||
cm.execCommand("goLineUp");
|
||||
}
|
||||
cm.scrollTo(null, info.top - cm.defaultTextHeight());
|
||||
};
|
||||
cmds.scrollLineDown = function(cm) {
|
||||
var info = cm.getScrollInfo();
|
||||
if (!cm.somethingSelected()) {
|
||||
var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
|
||||
if (cm.getCursor().line <= visibleTopLine)
|
||||
cm.execCommand("goLineDown");
|
||||
}
|
||||
cm.scrollTo(null, info.top + cm.defaultTextHeight());
|
||||
};
|
||||
|
||||
cmds.splitSelectionByLine = function(cm) {
|
||||
var ranges = cm.listSelections(), lineRanges = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var from = ranges[i].from(), to = ranges[i].to();
|
||||
for (var line = from.line; line <= to.line; ++line)
|
||||
if (!(to.line > from.line && line == to.line && to.ch == 0))
|
||||
lineRanges.push({anchor: line == from.line ? from : Pos(line, 0),
|
||||
head: line == to.line ? to : Pos(line)});
|
||||
}
|
||||
cm.setSelections(lineRanges, 0);
|
||||
};
|
||||
|
||||
cmds.singleSelectionTop = function(cm) {
|
||||
var range = cm.listSelections()[0];
|
||||
cm.setSelection(range.anchor, range.head, {scroll: false});
|
||||
};
|
||||
|
||||
cmds.selectLine = function(cm) {
|
||||
var ranges = cm.listSelections(), extended = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i];
|
||||
extended.push({anchor: Pos(range.from().line, 0),
|
||||
head: Pos(range.to().line + 1, 0)});
|
||||
}
|
||||
cm.setSelections(extended);
|
||||
};
|
||||
|
||||
function insertLine(cm, above) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
cm.operation(function() {
|
||||
var len = cm.listSelections().length, newSelection = [], last = -1;
|
||||
for (var i = 0; i < len; i++) {
|
||||
var head = cm.listSelections()[i].head;
|
||||
if (head.line <= last) continue;
|
||||
var at = Pos(head.line + (above ? 0 : 1), 0);
|
||||
cm.replaceRange("\n", at, null, "+insertLine");
|
||||
cm.indentLine(at.line, null, true);
|
||||
newSelection.push({head: at, anchor: at});
|
||||
last = head.line + 1;
|
||||
}
|
||||
cm.setSelections(newSelection);
|
||||
});
|
||||
cm.execCommand("indentAuto");
|
||||
}
|
||||
|
||||
cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };
|
||||
|
||||
cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };
|
||||
|
||||
function wordAt(cm, pos) {
|
||||
var start = pos.ch, end = start, line = cm.getLine(pos.line);
|
||||
while (start && CodeMirror.isWordChar(line.charAt(start - 1))) --start;
|
||||
while (end < line.length && CodeMirror.isWordChar(line.charAt(end))) ++end;
|
||||
return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
|
||||
}
|
||||
|
||||
cmds.selectNextOccurrence = function(cm) {
|
||||
var from = cm.getCursor("from"), to = cm.getCursor("to");
|
||||
var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
|
||||
if (CodeMirror.cmpPos(from, to) == 0) {
|
||||
var word = wordAt(cm, from);
|
||||
if (!word.word) return;
|
||||
cm.setSelection(word.from, word.to);
|
||||
fullWord = true;
|
||||
} else {
|
||||
var text = cm.getRange(from, to);
|
||||
var query = fullWord ? new RegExp("\\b" + text + "\\b") : text;
|
||||
var cur = cm.getSearchCursor(query, to);
|
||||
var found = cur.findNext();
|
||||
if (!found) {
|
||||
cur = cm.getSearchCursor(query, Pos(cm.firstLine(), 0));
|
||||
found = cur.findNext();
|
||||
}
|
||||
if (!found || isSelectedRange(cm.listSelections(), cur.from(), cur.to())) return
|
||||
cm.addSelection(cur.from(), cur.to());
|
||||
}
|
||||
if (fullWord)
|
||||
cm.state.sublimeFindFullWord = cm.doc.sel;
|
||||
};
|
||||
|
||||
cmds.skipAndSelectNextOccurrence = function(cm) {
|
||||
var prevAnchor = cm.getCursor("anchor"), prevHead = cm.getCursor("head");
|
||||
cmds.selectNextOccurrence(cm);
|
||||
if (CodeMirror.cmpPos(prevAnchor, prevHead) != 0) {
|
||||
cm.doc.setSelections(cm.doc.listSelections()
|
||||
.filter(function (sel) {
|
||||
return sel.anchor != prevAnchor || sel.head != prevHead;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function addCursorToSelection(cm, dir) {
|
||||
var ranges = cm.listSelections(), newRanges = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i];
|
||||
var newAnchor = cm.findPosV(
|
||||
range.anchor, dir, "line", range.anchor.goalColumn);
|
||||
var newHead = cm.findPosV(
|
||||
range.head, dir, "line", range.head.goalColumn);
|
||||
newAnchor.goalColumn = range.anchor.goalColumn != null ?
|
||||
range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left;
|
||||
newHead.goalColumn = range.head.goalColumn != null ?
|
||||
range.head.goalColumn : cm.cursorCoords(range.head, "div").left;
|
||||
var newRange = {anchor: newAnchor, head: newHead};
|
||||
newRanges.push(range);
|
||||
newRanges.push(newRange);
|
||||
}
|
||||
cm.setSelections(newRanges);
|
||||
}
|
||||
cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
|
||||
cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
|
||||
|
||||
function isSelectedRange(ranges, from, to) {
|
||||
for (var i = 0; i < ranges.length; i++)
|
||||
if (CodeMirror.cmpPos(ranges[i].from(), from) == 0 &&
|
||||
CodeMirror.cmpPos(ranges[i].to(), to) == 0) return true
|
||||
return false
|
||||
}
|
||||
|
||||
var mirror = "(){}[]";
|
||||
function selectBetweenBrackets(cm) {
|
||||
var ranges = cm.listSelections(), newRanges = []
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
|
||||
if (!opening) return false;
|
||||
for (;;) {
|
||||
var closing = cm.scanForBracket(pos, 1);
|
||||
if (!closing) return false;
|
||||
if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
|
||||
var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
|
||||
if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
|
||||
CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
|
||||
opening = cm.scanForBracket(opening.pos, -1);
|
||||
if (!opening) return false;
|
||||
} else {
|
||||
newRanges.push({anchor: startPos, head: closing.pos});
|
||||
break;
|
||||
}
|
||||
}
|
||||
pos = Pos(closing.pos.line, closing.pos.ch + 1);
|
||||
}
|
||||
}
|
||||
cm.setSelections(newRanges);
|
||||
return true;
|
||||
}
|
||||
|
||||
cmds.selectScope = function(cm) {
|
||||
selectBetweenBrackets(cm) || cm.execCommand("selectAll");
|
||||
};
|
||||
cmds.selectBetweenBrackets = function(cm) {
|
||||
if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
|
||||
};
|
||||
|
||||
function puncType(type) {
|
||||
return !type ? null : /\bpunctuation\b/.test(type) ? type : undefined
|
||||
}
|
||||
|
||||
cmds.goToBracket = function(cm) {
|
||||
cm.extendSelectionsBy(function(range) {
|
||||
var next = cm.scanForBracket(range.head, 1, puncType(cm.getTokenTypeAt(range.head)));
|
||||
if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
|
||||
var prev = cm.scanForBracket(range.head, -1, puncType(cm.getTokenTypeAt(Pos(range.head.line, range.head.ch + 1))));
|
||||
return prev && Pos(prev.pos.line, prev.pos.ch + 1) || range.head;
|
||||
});
|
||||
};
|
||||
|
||||
cmds.swapLineUp = function(cm) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i], from = range.from().line - 1, to = range.to().line;
|
||||
newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch),
|
||||
head: Pos(range.head.line - 1, range.head.ch)});
|
||||
if (range.to().ch == 0 && !range.empty()) --to;
|
||||
if (from > at) linesToMove.push(from, to);
|
||||
else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
|
||||
at = to;
|
||||
}
|
||||
cm.operation(function() {
|
||||
for (var i = 0; i < linesToMove.length; i += 2) {
|
||||
var from = linesToMove[i], to = linesToMove[i + 1];
|
||||
var line = cm.getLine(from);
|
||||
cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
|
||||
if (to > cm.lastLine())
|
||||
cm.replaceRange("\n" + line, Pos(cm.lastLine()), null, "+swapLine");
|
||||
else
|
||||
cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
|
||||
}
|
||||
cm.setSelections(newSels);
|
||||
cm.scrollIntoView();
|
||||
});
|
||||
};
|
||||
|
||||
cmds.swapLineDown = function(cm) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
|
||||
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||
var range = ranges[i], from = range.to().line + 1, to = range.from().line;
|
||||
if (range.to().ch == 0 && !range.empty()) from--;
|
||||
if (from < at) linesToMove.push(from, to);
|
||||
else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
|
||||
at = to;
|
||||
}
|
||||
cm.operation(function() {
|
||||
for (var i = linesToMove.length - 2; i >= 0; i -= 2) {
|
||||
var from = linesToMove[i], to = linesToMove[i + 1];
|
||||
var line = cm.getLine(from);
|
||||
if (from == cm.lastLine())
|
||||
cm.replaceRange("", Pos(from - 1), Pos(from), "+swapLine");
|
||||
else
|
||||
cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
|
||||
cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
|
||||
}
|
||||
cm.scrollIntoView();
|
||||
});
|
||||
};
|
||||
|
||||
cmds.toggleCommentIndented = function(cm) {
|
||||
cm.toggleComment({ indent: true });
|
||||
}
|
||||
|
||||
cmds.joinLines = function(cm) {
|
||||
var ranges = cm.listSelections(), joined = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i], from = range.from();
|
||||
var start = from.line, end = range.to().line;
|
||||
while (i < ranges.length - 1 && ranges[i + 1].from().line == end)
|
||||
end = ranges[++i].to().line;
|
||||
joined.push({start: start, end: end, anchor: !range.empty() && from});
|
||||
}
|
||||
cm.operation(function() {
|
||||
var offset = 0, ranges = [];
|
||||
for (var i = 0; i < joined.length; i++) {
|
||||
var obj = joined[i];
|
||||
var anchor = obj.anchor && Pos(obj.anchor.line - offset, obj.anchor.ch), head;
|
||||
for (var line = obj.start; line <= obj.end; line++) {
|
||||
var actual = line - offset;
|
||||
if (line == obj.end) head = Pos(actual, cm.getLine(actual).length + 1);
|
||||
if (actual < cm.lastLine()) {
|
||||
cm.replaceRange(" ", Pos(actual), Pos(actual + 1, /^\s*/.exec(cm.getLine(actual + 1))[0].length));
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
ranges.push({anchor: anchor || head, head: head});
|
||||
}
|
||||
cm.setSelections(ranges, 0);
|
||||
});
|
||||
};
|
||||
|
||||
cmds.duplicateLine = function(cm) {
|
||||
cm.operation(function() {
|
||||
var rangeCount = cm.listSelections().length;
|
||||
for (var i = 0; i < rangeCount; i++) {
|
||||
var range = cm.listSelections()[i];
|
||||
if (range.empty())
|
||||
cm.replaceRange(cm.getLine(range.head.line) + "\n", Pos(range.head.line, 0));
|
||||
else
|
||||
cm.replaceRange(cm.getRange(range.from(), range.to()), range.from());
|
||||
}
|
||||
cm.scrollIntoView();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function sortLines(cm, caseSensitive, direction) {
|
||||
if (cm.isReadOnly()) return CodeMirror.Pass
|
||||
var ranges = cm.listSelections(), toSort = [], selected;
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i];
|
||||
if (range.empty()) continue;
|
||||
var from = range.from().line, to = range.to().line;
|
||||
while (i < ranges.length - 1 && ranges[i + 1].from().line == to)
|
||||
to = ranges[++i].to().line;
|
||||
if (!ranges[i].to().ch) to--;
|
||||
toSort.push(from, to);
|
||||
}
|
||||
if (toSort.length) selected = true;
|
||||
else toSort.push(cm.firstLine(), cm.lastLine());
|
||||
|
||||
cm.operation(function() {
|
||||
var ranges = [];
|
||||
for (var i = 0; i < toSort.length; i += 2) {
|
||||
var from = toSort[i], to = toSort[i + 1];
|
||||
var start = Pos(from, 0), end = Pos(to);
|
||||
var lines = cm.getRange(start, end, false);
|
||||
if (caseSensitive)
|
||||
lines.sort(function(a, b) { return a < b ? -direction : a == b ? 0 : direction; });
|
||||
else
|
||||
lines.sort(function(a, b) {
|
||||
var au = a.toUpperCase(), bu = b.toUpperCase();
|
||||
if (au != bu) { a = au; b = bu; }
|
||||
return a < b ? -direction : a == b ? 0 : direction;
|
||||
});
|
||||
cm.replaceRange(lines, start, end);
|
||||
if (selected) ranges.push({anchor: start, head: Pos(to + 1, 0)});
|
||||
}
|
||||
if (selected) cm.setSelections(ranges, 0);
|
||||
});
|
||||
}
|
||||
|
||||
cmds.sortLines = function(cm) { sortLines(cm, true, 1); };
|
||||
cmds.reverseSortLines = function(cm) { sortLines(cm, true, -1); };
|
||||
cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false, 1); };
|
||||
cmds.reverseSortLinesInsensitive = function(cm) { sortLines(cm, false, -1); };
|
||||
|
||||
cmds.nextBookmark = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks;
|
||||
if (marks) while (marks.length) {
|
||||
var current = marks.shift();
|
||||
var found = current.find();
|
||||
if (found) {
|
||||
marks.push(current);
|
||||
return cm.setSelection(found.from, found.to);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cmds.prevBookmark = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks;
|
||||
if (marks) while (marks.length) {
|
||||
marks.unshift(marks.pop());
|
||||
var found = marks[marks.length - 1].find();
|
||||
if (!found)
|
||||
marks.pop();
|
||||
else
|
||||
return cm.setSelection(found.from, found.to);
|
||||
}
|
||||
};
|
||||
|
||||
cmds.toggleBookmark = function(cm) {
|
||||
var ranges = cm.listSelections();
|
||||
var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var from = ranges[i].from(), to = ranges[i].to();
|
||||
var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
|
||||
for (var j = 0; j < found.length; j++) {
|
||||
if (found[j].sublimeBookmark) {
|
||||
found[j].clear();
|
||||
for (var k = 0; k < marks.length; k++)
|
||||
if (marks[k] == found[j])
|
||||
marks.splice(k--, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == found.length)
|
||||
marks.push(cm.markText(from, to, {sublimeBookmark: true, clearWhenEmpty: false}));
|
||||
}
|
||||
};
|
||||
|
||||
cmds.clearBookmarks = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks;
|
||||
if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
|
||||
marks.length = 0;
|
||||
};
|
||||
|
||||
cmds.selectBookmarks = function(cm) {
|
||||
var marks = cm.state.sublimeBookmarks, ranges = [];
|
||||
if (marks) for (var i = 0; i < marks.length; i++) {
|
||||
var found = marks[i].find();
|
||||
if (!found)
|
||||
marks.splice(i--, 0);
|
||||
else
|
||||
ranges.push({anchor: found.from, head: found.to});
|
||||
}
|
||||
if (ranges.length)
|
||||
cm.setSelections(ranges, 0);
|
||||
};
|
||||
|
||||
function modifyWordOrSelection(cm, mod) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections(), indices = [], replacements = [];
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i];
|
||||
if (range.empty()) { indices.push(i); replacements.push(""); }
|
||||
else replacements.push(mod(cm.getRange(range.from(), range.to())));
|
||||
}
|
||||
cm.replaceSelections(replacements, "around", "case");
|
||||
for (var i = indices.length - 1, at; i >= 0; i--) {
|
||||
var range = ranges[indices[i]];
|
||||
if (at && CodeMirror.cmpPos(range.head, at) > 0) continue;
|
||||
var word = wordAt(cm, range.head);
|
||||
at = word.from;
|
||||
cm.replaceRange(mod(word.word), word.from, word.to);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cmds.smartBackspace = function(cm) {
|
||||
if (cm.somethingSelected()) return CodeMirror.Pass;
|
||||
|
||||
cm.operation(function() {
|
||||
var cursors = cm.listSelections();
|
||||
var indentUnit = cm.getOption("indentUnit");
|
||||
|
||||
for (var i = cursors.length - 1; i >= 0; i--) {
|
||||
var cursor = cursors[i].head;
|
||||
var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
|
||||
var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));
|
||||
|
||||
// Delete by one character by default
|
||||
var deletePos = cm.findPosH(cursor, -1, "char", false);
|
||||
|
||||
if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) {
|
||||
var prevIndent = new Pos(cursor.line,
|
||||
CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));
|
||||
|
||||
// Smart delete only if we found a valid prevIndent location
|
||||
if (prevIndent.ch != cursor.ch) deletePos = prevIndent;
|
||||
}
|
||||
|
||||
cm.replaceRange("", deletePos, cursor, "+delete");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
cmds.delLineRight = function(cm) {
|
||||
cm.operation(function() {
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = ranges.length - 1; i >= 0; i--)
|
||||
cm.replaceRange("", ranges[i].anchor, Pos(ranges[i].to().line), "+delete");
|
||||
cm.scrollIntoView();
|
||||
});
|
||||
};
|
||||
|
||||
cmds.upcaseAtCursor = function(cm) {
|
||||
modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
|
||||
};
|
||||
cmds.downcaseAtCursor = function(cm) {
|
||||
modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
|
||||
};
|
||||
|
||||
cmds.setSublimeMark = function(cm) {
|
||||
if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
|
||||
cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
|
||||
};
|
||||
cmds.selectToSublimeMark = function(cm) {
|
||||
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
||||
if (found) cm.setSelection(cm.getCursor(), found);
|
||||
};
|
||||
cmds.deleteToSublimeMark = function(cm) {
|
||||
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
||||
if (found) {
|
||||
var from = cm.getCursor(), to = found;
|
||||
if (CodeMirror.cmpPos(from, to) > 0) { var tmp = to; to = from; from = tmp; }
|
||||
cm.state.sublimeKilled = cm.getRange(from, to);
|
||||
cm.replaceRange("", from, to);
|
||||
}
|
||||
};
|
||||
cmds.swapWithSublimeMark = function(cm) {
|
||||
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
|
||||
if (found) {
|
||||
cm.state.sublimeMark.clear();
|
||||
cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
|
||||
cm.setCursor(found);
|
||||
}
|
||||
};
|
||||
cmds.sublimeYank = function(cm) {
|
||||
if (cm.state.sublimeKilled != null)
|
||||
cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
|
||||
};
|
||||
|
||||
cmds.showInCenter = function(cm) {
|
||||
var pos = cm.cursorCoords(null, "local");
|
||||
cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
|
||||
};
|
||||
|
||||
function getTarget(cm) {
|
||||
var from = cm.getCursor("from"), to = cm.getCursor("to");
|
||||
if (CodeMirror.cmpPos(from, to) == 0) {
|
||||
var word = wordAt(cm, from);
|
||||
if (!word.word) return;
|
||||
from = word.from;
|
||||
to = word.to;
|
||||
}
|
||||
return {from: from, to: to, query: cm.getRange(from, to), word: word};
|
||||
}
|
||||
|
||||
function findAndGoTo(cm, forward) {
|
||||
var target = getTarget(cm);
|
||||
if (!target) return;
|
||||
var query = target.query;
|
||||
var cur = cm.getSearchCursor(query, forward ? target.to : target.from);
|
||||
|
||||
if (forward ? cur.findNext() : cur.findPrevious()) {
|
||||
cm.setSelection(cur.from(), cur.to());
|
||||
} else {
|
||||
cur = cm.getSearchCursor(query, forward ? Pos(cm.firstLine(), 0)
|
||||
: cm.clipPos(Pos(cm.lastLine())));
|
||||
if (forward ? cur.findNext() : cur.findPrevious())
|
||||
cm.setSelection(cur.from(), cur.to());
|
||||
else if (target.word)
|
||||
cm.setSelection(target.from, target.to);
|
||||
}
|
||||
};
|
||||
cmds.findUnder = function(cm) { findAndGoTo(cm, true); };
|
||||
cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };
|
||||
cmds.findAllUnder = function(cm) {
|
||||
var target = getTarget(cm);
|
||||
if (!target) return;
|
||||
var cur = cm.getSearchCursor(target.query);
|
||||
var matches = [];
|
||||
var primaryIndex = -1;
|
||||
while (cur.findNext()) {
|
||||
matches.push({anchor: cur.from(), head: cur.to()});
|
||||
if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)
|
||||
primaryIndex++;
|
||||
}
|
||||
cm.setSelections(matches, primaryIndex);
|
||||
};
|
||||
|
||||
|
||||
var keyMap = CodeMirror.keyMap;
|
||||
keyMap.macSublime = {
|
||||
"Cmd-Left": "goLineStartSmart",
|
||||
"Shift-Tab": "indentLess",
|
||||
"Shift-Ctrl-K": "deleteLine",
|
||||
"Alt-Q": "wrapLines",
|
||||
"Ctrl-Left": "goSubwordLeft",
|
||||
"Ctrl-Right": "goSubwordRight",
|
||||
"Ctrl-Alt-Up": "scrollLineUp",
|
||||
"Ctrl-Alt-Down": "scrollLineDown",
|
||||
"Cmd-L": "selectLine",
|
||||
"Shift-Cmd-L": "splitSelectionByLine",
|
||||
"Esc": "singleSelectionTop",
|
||||
"Cmd-Enter": "insertLineAfter",
|
||||
"Shift-Cmd-Enter": "insertLineBefore",
|
||||
"Cmd-D": "selectNextOccurrence",
|
||||
"Shift-Cmd-Space": "selectScope",
|
||||
"Shift-Cmd-M": "selectBetweenBrackets",
|
||||
"Cmd-M": "goToBracket",
|
||||
"Cmd-Ctrl-Up": "swapLineUp",
|
||||
"Cmd-Ctrl-Down": "swapLineDown",
|
||||
"Cmd-/": "toggleCommentIndented",
|
||||
"Cmd-J": "joinLines",
|
||||
"Shift-Cmd-D": "duplicateLine",
|
||||
"F5": "sortLines",
|
||||
"Shift-F5": "reverseSortLines",
|
||||
"Cmd-F5": "sortLinesInsensitive",
|
||||
"Shift-Cmd-F5": "reverseSortLinesInsensitive",
|
||||
"F2": "nextBookmark",
|
||||
"Shift-F2": "prevBookmark",
|
||||
"Cmd-F2": "toggleBookmark",
|
||||
"Shift-Cmd-F2": "clearBookmarks",
|
||||
"Alt-F2": "selectBookmarks",
|
||||
"Backspace": "smartBackspace",
|
||||
"Cmd-K Cmd-D": "skipAndSelectNextOccurrence",
|
||||
"Cmd-K Cmd-K": "delLineRight",
|
||||
"Cmd-K Cmd-U": "upcaseAtCursor",
|
||||
"Cmd-K Cmd-L": "downcaseAtCursor",
|
||||
"Cmd-K Cmd-Space": "setSublimeMark",
|
||||
"Cmd-K Cmd-A": "selectToSublimeMark",
|
||||
"Cmd-K Cmd-W": "deleteToSublimeMark",
|
||||
"Cmd-K Cmd-X": "swapWithSublimeMark",
|
||||
"Cmd-K Cmd-Y": "sublimeYank",
|
||||
"Cmd-K Cmd-C": "showInCenter",
|
||||
"Cmd-K Cmd-G": "clearBookmarks",
|
||||
"Cmd-K Cmd-Backspace": "delLineLeft",
|
||||
"Cmd-K Cmd-1": "foldAll",
|
||||
"Cmd-K Cmd-0": "unfoldAll",
|
||||
"Cmd-K Cmd-J": "unfoldAll",
|
||||
"Ctrl-Shift-Up": "addCursorToPrevLine",
|
||||
"Ctrl-Shift-Down": "addCursorToNextLine",
|
||||
"Cmd-F3": "findUnder",
|
||||
"Shift-Cmd-F3": "findUnderPrevious",
|
||||
"Alt-F3": "findAllUnder",
|
||||
"Shift-Cmd-[": "fold",
|
||||
"Shift-Cmd-]": "unfold",
|
||||
"Cmd-I": "findIncremental",
|
||||
"Shift-Cmd-I": "findIncrementalReverse",
|
||||
"Cmd-H": "replace",
|
||||
"F3": "findNext",
|
||||
"Shift-F3": "findPrev",
|
||||
"fallthrough": "macDefault"
|
||||
};
|
||||
CodeMirror.normalizeKeyMap(keyMap.macSublime);
|
||||
|
||||
keyMap.pcSublime = {
|
||||
"Shift-Tab": "indentLess",
|
||||
"Shift-Ctrl-K": "deleteLine",
|
||||
"Alt-Q": "wrapLines",
|
||||
"Ctrl-T": "transposeChars",
|
||||
"Alt-Left": "goSubwordLeft",
|
||||
"Alt-Right": "goSubwordRight",
|
||||
"Ctrl-Up": "scrollLineUp",
|
||||
"Ctrl-Down": "scrollLineDown",
|
||||
"Ctrl-L": "selectLine",
|
||||
"Shift-Ctrl-L": "splitSelectionByLine",
|
||||
"Esc": "singleSelectionTop",
|
||||
"Ctrl-Enter": "insertLineAfter",
|
||||
"Shift-Ctrl-Enter": "insertLineBefore",
|
||||
"Ctrl-D": "selectNextOccurrence",
|
||||
"Shift-Ctrl-Space": "selectScope",
|
||||
"Shift-Ctrl-M": "selectBetweenBrackets",
|
||||
"Ctrl-M": "goToBracket",
|
||||
"Shift-Ctrl-Up": "swapLineUp",
|
||||
"Shift-Ctrl-Down": "swapLineDown",
|
||||
"Ctrl-/": "toggleCommentIndented",
|
||||
"Ctrl-J": "joinLines",
|
||||
"Shift-Ctrl-D": "duplicateLine",
|
||||
"F9": "sortLines",
|
||||
"Shift-F9": "reverseSortLines",
|
||||
"Ctrl-F9": "sortLinesInsensitive",
|
||||
"Shift-Ctrl-F9": "reverseSortLinesInsensitive",
|
||||
"F2": "nextBookmark",
|
||||
"Shift-F2": "prevBookmark",
|
||||
"Ctrl-F2": "toggleBookmark",
|
||||
"Shift-Ctrl-F2": "clearBookmarks",
|
||||
"Alt-F2": "selectBookmarks",
|
||||
"Backspace": "smartBackspace",
|
||||
"Ctrl-K Ctrl-D": "skipAndSelectNextOccurrence",
|
||||
"Ctrl-K Ctrl-K": "delLineRight",
|
||||
"Ctrl-K Ctrl-U": "upcaseAtCursor",
|
||||
"Ctrl-K Ctrl-L": "downcaseAtCursor",
|
||||
"Ctrl-K Ctrl-Space": "setSublimeMark",
|
||||
"Ctrl-K Ctrl-A": "selectToSublimeMark",
|
||||
"Ctrl-K Ctrl-W": "deleteToSublimeMark",
|
||||
"Ctrl-K Ctrl-X": "swapWithSublimeMark",
|
||||
"Ctrl-K Ctrl-Y": "sublimeYank",
|
||||
"Ctrl-K Ctrl-C": "showInCenter",
|
||||
"Ctrl-K Ctrl-G": "clearBookmarks",
|
||||
"Ctrl-K Ctrl-Backspace": "delLineLeft",
|
||||
"Ctrl-K Ctrl-1": "foldAll",
|
||||
"Ctrl-K Ctrl-0": "unfoldAll",
|
||||
"Ctrl-K Ctrl-J": "unfoldAll",
|
||||
"Ctrl-Alt-Up": "addCursorToPrevLine",
|
||||
"Ctrl-Alt-Down": "addCursorToNextLine",
|
||||
"Ctrl-F3": "findUnder",
|
||||
"Shift-Ctrl-F3": "findUnderPrevious",
|
||||
"Alt-F3": "findAllUnder",
|
||||
"Shift-Ctrl-[": "fold",
|
||||
"Shift-Ctrl-]": "unfold",
|
||||
"Ctrl-I": "findIncremental",
|
||||
"Shift-Ctrl-I": "findIncrementalReverse",
|
||||
"Ctrl-H": "replace",
|
||||
"F3": "findNext",
|
||||
"Shift-F3": "findPrev",
|
||||
"fallthrough": "pcDefault"
|
||||
};
|
||||
CodeMirror.normalizeKeyMap(keyMap.pcSublime);
|
||||
|
||||
var mac = keyMap.default == keyMap.macDefault;
|
||||
keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;
|
||||
});
|
|
@ -60,20 +60,13 @@
|
|||
.cm-fat-cursor div.CodeMirror-cursors {
|
||||
z-index: 1;
|
||||
}
|
||||
.cm-fat-cursor-mark {
|
||||
background-color: rgba(20, 255, 20, 0.5);
|
||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||
-moz-animation: blink 1.06s steps(1) infinite;
|
||||
animation: blink 1.06s steps(1) infinite;
|
||||
}
|
||||
.cm-animate-fat-cursor {
|
||||
width: auto;
|
||||
border: 0;
|
||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||
-moz-animation: blink 1.06s steps(1) infinite;
|
||||
animation: blink 1.06s steps(1) infinite;
|
||||
background-color: #7e7;
|
||||
}
|
||||
.cm-fat-cursor .CodeMirror-line::selection,
|
||||
.cm-fat-cursor .CodeMirror-line > span::selection,
|
||||
.cm-fat-cursor .CodeMirror-line > span > span::selection { background: transparent; }
|
||||
.cm-fat-cursor .CodeMirror-line::-moz-selection,
|
||||
.cm-fat-cursor .CodeMirror-line > span::-moz-selection,
|
||||
.cm-fat-cursor .CodeMirror-line > span > span::-moz-selection { background: transparent; }
|
||||
.cm-fat-cursor { caret-color: transparent; }
|
||||
@-moz-keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
|
@ -171,6 +164,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
|
|||
height: 100%;
|
||||
outline: none; /* Prevent dragging from highlighting the element */
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
.CodeMirror-sizer {
|
||||
position: relative;
|
||||
|
@ -184,6 +178,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
|
|||
position: absolute;
|
||||
z-index: 6;
|
||||
display: none;
|
||||
outline: none;
|
||||
}
|
||||
.CodeMirror-vscrollbar {
|
||||
right: 0; top: 0;
|
||||
|
@ -347,3 +342,12 @@ div.CodeMirror-dragcursors {
|
|||
|
||||
/* Help users use markselection to safely style text background */
|
||||
span.CodeMirror-selectedtext { background: none; }
|
||||
.breakpoints {width: .8em;}
|
||||
.breakpoint { color: #822; }
|
||||
.CodeMirror-focused .cm-matchhighlight {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
|
||||
background-position: bottom;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.cm-matchhighlight {background-color: lightgreen}
|
||||
.CodeMirror-selection-highlight-scrollbar {background-color: green}
|
File diff suppressed because it is too large
Load Diff
|
@ -3,9 +3,9 @@
|
|||
// Modified for HAProxy by Roxy-WI
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
mod(require("../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
define(["../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
|
@ -148,7 +148,8 @@ CodeMirror.defineMode("haproxy", function(config) {
|
|||
return state.baseIndent + n * indentUnit;
|
||||
},
|
||||
|
||||
electricChars: "}"
|
||||
electricChars: "}",
|
||||
lineComment: "#"
|
||||
};
|
||||
});
|
||||
CodeMirror.defineMIME("text/x-haproxy-conf", "haproxy");
|
|
@ -148,7 +148,8 @@ CodeMirror.defineMode("modsec", function(config) {
|
|||
return state.baseIndent + n * indentUnit;
|
||||
},
|
||||
|
||||
electricChars: "}"
|
||||
electricChars: "}",
|
||||
lineComment: "#"
|
||||
};
|
||||
});
|
||||
CodeMirror.defineMIME("text/x-modsec-conf", "modsec");
|
File diff suppressed because one or more lines are too long
|
@ -62,7 +62,7 @@ $( function() {
|
|||
type: frm.attr('method'),
|
||||
success: function( data ) {
|
||||
data = data.replace(/\n/g, "<br>");
|
||||
if (data.indexOf(service + ': command not found') != '-1') {
|
||||
if (data.indexOf(service + ': command not found') != '-1') {
|
||||
try {
|
||||
var service = findGetParameter('service');
|
||||
toastr.error('Cannot save config. There is no ' + service);
|
||||
|
@ -73,6 +73,9 @@ $( function() {
|
|||
toastr.clear();
|
||||
returnNiceCheckingConfig(data);
|
||||
}
|
||||
if (data.indexOf('warning: ') != '-1') {
|
||||
toastr.warning(data)
|
||||
}
|
||||
}
|
||||
});
|
||||
event.preventDefault();
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -890,7 +890,8 @@ label {
|
|||
height: 270px;
|
||||
}
|
||||
#logo_span img {
|
||||
margin: auto 40px auto auto;
|
||||
margin: auto 40px auto 100px;
|
||||
width: 330px;
|
||||
}
|
||||
.chart-container, .chart-container_overview {
|
||||
position: relative;
|
||||
|
@ -994,10 +995,14 @@ label {
|
|||
margin-left: 15%;
|
||||
}
|
||||
.wrong-login {
|
||||
margin-right: -150px;
|
||||
margin-left: 44%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 1600px) {
|
||||
#logo_span img {
|
||||
width: 300px;
|
||||
margin: 25px 50px auto 85px;
|
||||
}
|
||||
.ajax-server {
|
||||
clear: both !important;
|
||||
margin-left: 20px !important;
|
||||
|
@ -1006,6 +1011,10 @@ label {
|
|||
}
|
||||
}
|
||||
@media (max-width: 1450px) {
|
||||
#logo_span img {
|
||||
width: 250px;
|
||||
margin: 35px 30px auto 120px;
|
||||
}
|
||||
.ajax-server, .div-backends {
|
||||
clear: both !important;
|
||||
margin-left: 20px !important;
|
||||
|
@ -1031,6 +1040,10 @@ label {
|
|||
}
|
||||
}
|
||||
@media (max-width: 1280px) {
|
||||
#logo_span img {
|
||||
width: 250px;
|
||||
margin: 30px 60px auto 70px;
|
||||
}
|
||||
.div-pannel {
|
||||
height: 430px !important;
|
||||
}
|
||||
|
@ -1052,6 +1065,10 @@ label {
|
|||
#logo_span {
|
||||
margin-left: -5%;
|
||||
}
|
||||
#logo_span img {
|
||||
width: 250px;
|
||||
margin: 35px 0px auto 120px;
|
||||
}
|
||||
.wrong-login {
|
||||
margin-right: -150px;
|
||||
}
|
||||
|
@ -1072,7 +1089,11 @@ label {
|
|||
@media (max-width: 1080px) {
|
||||
#logo_span {
|
||||
margin-left: -5%;
|
||||
}
|
||||
}
|
||||
#logo_span img {
|
||||
width: 250px;
|
||||
margin: 35px 60px auto 120px;
|
||||
}
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 410px;
|
||||
|
@ -1097,9 +1118,13 @@ label {
|
|||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
#logo_span {
|
||||
margin-left: -12%;
|
||||
border: none;
|
||||
#logo_span {
|
||||
margin-left: -12%;
|
||||
border: none;
|
||||
}
|
||||
#logo_span img {
|
||||
width: 250px;
|
||||
margin: 35px 0 auto 120px;
|
||||
}
|
||||
.wrong-login {
|
||||
margin-right: -260px;
|
||||
|
@ -1128,9 +1153,13 @@ label {
|
|||
}
|
||||
}
|
||||
@media (max-width: 667px) {
|
||||
#logo_span {
|
||||
margin-left: -12%;
|
||||
border: none;
|
||||
#logo_span {
|
||||
margin-left: -12%;
|
||||
border: none;
|
||||
}
|
||||
#logo_span img {
|
||||
width: 250px;
|
||||
margin: 35px 0 auto 120px;
|
||||
}
|
||||
.wrong-login {
|
||||
margin-right: -210px;
|
|
@ -37,15 +37,15 @@ table.dataTable {
|
|||
background-repeat: no-repeat;
|
||||
background-position: center right; }
|
||||
table.dataTable thead .sorting {
|
||||
background-image: url("images/sort_both.png"); }
|
||||
background-image: url("../images/sort_both.png"); }
|
||||
table.dataTable thead .sorting_asc {
|
||||
background-image: url("images/sort_asc.png"); }
|
||||
background-image: url("../images/sort_asc.png"); }
|
||||
table.dataTable thead .sorting_desc {
|
||||
background-image: url("images/sort_desc.png"); }
|
||||
background-image: url("../images/sort_desc.png"); }
|
||||
table.dataTable thead .sorting_asc_disabled {
|
||||
background-image: url("images/sort_asc_disabled.png"); }
|
||||
background-image: url("../images/sort_asc_disabled.png"); }
|
||||
table.dataTable thead .sorting_desc_disabled {
|
||||
background-image: url("images/sort_desc_disabled.png"); }
|
||||
background-image: url("../images/sort_desc_disabled.png"); }
|
||||
table.dataTable tbody tr {
|
||||
background-color: white; }
|
||||
table.dataTable tbody tr.selected {
|
|
@ -169,7 +169,11 @@ function ajaxActionNginxServers(action, id) {
|
|||
if (data.indexOf('error:') != '-1') {
|
||||
toastr.error(data);
|
||||
} else if (cur_url[0] == "hapservers.py") {
|
||||
location.reload()
|
||||
if (data.indexOf('warning: ') != '-1') {
|
||||
toastr.warning(data)
|
||||
} else {
|
||||
location.reload()
|
||||
}
|
||||
} else if (cur_url[0] == "waf.py") {
|
||||
setTimeout(showOverviewWaf(ip, hostnamea), 2000)
|
||||
} else {
|
||||
|
@ -590,6 +594,9 @@ function serverSettingsSave(id, name, service, dialog_id) {
|
|||
var haproxy_dockerized = 0;
|
||||
var nginx_dockerized = 0;
|
||||
var apache_dockerized = 0;
|
||||
var haproxy_restart = 0;
|
||||
var nginx_restart = 0;
|
||||
var apache_restart = 0;
|
||||
if ($('#haproxy_enterprise').is(':checked')) {
|
||||
haproxy_enterprise = '1';
|
||||
}
|
||||
|
@ -602,6 +609,15 @@ function serverSettingsSave(id, name, service, dialog_id) {
|
|||
if ($('#apache_dockerized').is(':checked')) {
|
||||
apache_dockerized = '1';
|
||||
}
|
||||
if ($('#haproxy_restart').is(':checked')) {
|
||||
haproxy_restart = '1';
|
||||
}
|
||||
if ($('#nginx_restart').is(':checked')) {
|
||||
nginx_restart = '1';
|
||||
}
|
||||
if ($('#apache_restart').is(':checked')) {
|
||||
apache_restart = '1';
|
||||
}
|
||||
$.ajax({
|
||||
url: "options.py",
|
||||
data: {
|
||||
|
@ -611,6 +627,9 @@ function serverSettingsSave(id, name, service, dialog_id) {
|
|||
serverSettingshaproxy_dockerized: haproxy_dockerized,
|
||||
serverSettingsnginx_dockerized: nginx_dockerized,
|
||||
serverSettingsapache_dockerized: apache_dockerized,
|
||||
serverSettingsHaproxyrestart: haproxy_restart,
|
||||
serverSettingsNginxrestart: nginx_restart,
|
||||
serverSettingsApache_restart: apache_restart,
|
||||
token: $('#token').val()
|
||||
},
|
||||
type: "POST",
|
||||
|
|
|
@ -34,27 +34,43 @@ $( function() {
|
|||
}
|
||||
if (cur_url[0] == link2 && cur_url[1].split('&')[0] != 'service=keepalived' && cur_url[1].split('&')[0] != 'service=nginx' && cur_url[1].split('&')[0] != 'service=apache') {
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'versions.py' && cur_url[1].split('&')[0] == 'service=keepalived' && link2 == 'versions.py?service=keepalived'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'config.py' && cur_url[1].split('&')[0] == 'service=keepalived' && link2 == 'config.py?service=keepalived'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'versions.py' && cur_url[1].split('&')[0] == 'service=nginx' && link2 == 'versions.py?service=nginx'){
|
||||
} else if(cur_url[0] == 'config.py' && cur_url[1].split('&')[0] == 'service=haproxy' && link2 == 'config.py?service=haproxy'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'config.py' && cur_url[1].split('&')[0] == 'service=nginx' && link2 == 'config.py?service=nginx'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'logs.py' && cur_url[1].split('&')[0] == 'service=nginx' && link2 == 'logs.py?service=nginx'){
|
||||
} else if(cur_url[0] == 'config.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'config.py?service=apache'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'logs.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'logs.py?service=apache'){
|
||||
} else if(cur_url[0] == 'versions.py' && cur_url[1].split('&')[0] == 'service=nginx' && link2 == 'versions.py?service=nginx'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'logs.py' && cur_url[1].split('&')[0] == 'service=keepalived' && link2 == 'logs.py?service=keepalived'){
|
||||
} else if(cur_url[0] == 'versions.py' && cur_url[1].split('&')[0] == 'service=haproxy' && link2 == 'versions.py?service=haproxy'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'versions.py' && cur_url[1].split('&')[0] == 'service=keepalived' && link2 == 'versions.py?service=keepalived'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'versions.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'versions.py?service=apache'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'logs.py' && cur_url[1].split('&')[0] == 'service=haproxy' && link2 == 'logs.py?service=haproxy'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'logs.py' && cur_url[1].split('&')[0] == 'service=nginx' && link2 == 'logs.py?service=nginx'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'logs.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'logs.py?service=apache'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'logs.py' && cur_url[1].split('&')[0] == 'service=keepalived' && link2 == 'logs.py?service=keepalived'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'hapservers.py' && cur_url[1].split('&')[0] == 'service=haproxy' && link2 == 'hapservers.py?service=haproxy'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'hapservers.py' && cur_url[1].split('&')[0] == 'service=nginx' && link2 == 'hapservers.py?service=nginx'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'hapservers.py' && cur_url[1].split('&')[0] == 'service=keepalived' && link2 == 'hapservers.py?service=keepalived'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'viewsttats.py' && cur_url[1].split('&')[0] == 'service=nginx' && link2 == 'viewsttats.py?service=nginx'){
|
||||
} else if(cur_url[0] == 'hapservers.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'hapservers.py?service=apache'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'viewsttats.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'viewsttats.py?service=apache'){
|
||||
} else if(cur_url[0] == 'statsview.py' && cur_url[1].split('&')[0] == 'service=haproxy' && link2 == 'statsview.py?service=haproxy'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'statsview.py' && cur_url[1].split('&')[0] == 'service=nginx' && link2 == 'statsview.py?service=nginx'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'statsview.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'statsview.py?service=apache'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'smon.py' && cur_url[1].split('&')[0] == 'action=view' && link2 == 'smon.py?action=view'){
|
||||
show_current_page($(this))
|
||||
|
@ -68,16 +84,12 @@ $( function() {
|
|||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'viewlogs.py' && cur_url[1].split('&')[0] == 'type=2' && link2 == 'viewlogs.py?type=2'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'metrics.py' && cur_url[1].split('&')[0] == 'service=haproxy' && link2 == 'metrics.py?service=haproxy'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'metrics.py' && cur_url[1].split('&')[0] == 'service=nginx' && link2 == 'metrics.py?service=nginx'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'metrics.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'metrics.py?service=apache'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'hapservers.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'hapservers.py?service=apache'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'versions.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'versions.py?service=apache'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'config.py' && cur_url[1].split('&')[0] == 'service=apache' && link2 == 'config.py?service=apache'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'add.py' && cur_url[1].split('&')[0] == 'service=apache#ssl' && link2 == 'add.py?service=apache#ssl'){
|
||||
show_current_page($(this))
|
||||
} else if(cur_url[0] == 'waf.py' && cur_url[1].split('&')[0] == 'service=haproxy' && link2 == 'waf.py?service=haproxy'){
|
||||
|
@ -105,7 +117,7 @@ window.onblur= function() {
|
|||
if(sessionStorage.getItem('auto-refresh-pause') == "0" && sessionStorage.getItem('auto-refresh') > 5000) {
|
||||
if (cur_url[0] == "logs.py") {
|
||||
showLog();
|
||||
} else if (cur_url[0] == "viewsttats.py") {
|
||||
} else if (cur_url[0] == "statsview.py") {
|
||||
showStats()
|
||||
} else if (cur_url[0] == "overview.py") {
|
||||
showOverview();
|
||||
|
@ -207,7 +219,7 @@ function startSetInterval(interval) {
|
|||
if (cur_url[0] == "logs.py") {
|
||||
intervalId = setInterval('showLog()', interval);
|
||||
showLog();
|
||||
} else if (cur_url[0] == "viewsttats.py") {
|
||||
} else if (cur_url[0] == "statsview.py") {
|
||||
intervalId = setInterval('showStats()', interval);
|
||||
showStats()
|
||||
} else if (cur_url[0] == "overview.py") {
|
||||
|
@ -298,9 +310,9 @@ function showStats() {
|
|||
function openStats() {
|
||||
var serv = $("#serv").val();
|
||||
if (cur_url[1].split('&')[0] == "service=nginx") {
|
||||
var url = "viewsttats.py?service=nginx&serv="+serv+"&open=open"
|
||||
var url = "statsview.py?service=nginx&serv="+serv+"&open=open"
|
||||
} else {
|
||||
var url = "viewsttats.py?serv="+serv+"&open=open"
|
||||
var url = "statsview.py?serv="+serv+"&open=open"
|
||||
}
|
||||
var win = window.open(url, '_blank');
|
||||
win.focus();
|
||||
|
|
20
index.html
20
index.html
|
@ -26,32 +26,32 @@
|
|||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="msapplication-TileImage" content="/inc/images/favicon/ms-icon-144x144.png">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<link href="/inc/style.css" rel="stylesheet">
|
||||
<link href="/inc/nprogress.css" rel="stylesheet">
|
||||
<link href="/inc/provisioning.css" rel="stylesheet">
|
||||
<link href="/inc/jquery-ui.min.css" rel="stylesheet">
|
||||
<link href="/inc/jquery-ui.structure.min.css" rel="stylesheet">
|
||||
<link href="/inc/css/style.css" rel="stylesheet">
|
||||
<link href="/inc/css/nprogress.css" rel="stylesheet">
|
||||
<link href="/inc/css/provisioning.css" rel="stylesheet">
|
||||
<link href="/inc/css/jquery-ui.min.css" rel="stylesheet">
|
||||
<link href="/inc/css/jquery-ui.structure.min.css" rel="stylesheet">
|
||||
<script src="/inc/jquery-3.6.0.min.js"></script>
|
||||
<script src="/inc/jquery-ui.min.js"></script>
|
||||
<script src="/inc/nprogress.js"></script>
|
||||
<script defer src="/inc/fa-solid.min.js"></script>
|
||||
<script defer src="/inc/fontawesome.min.js"></script>
|
||||
<link href="/inc/awesome.css" rel="stylesheet">
|
||||
<link href="/inc/chart.min.css" rel="stylesheet">
|
||||
<link href="/inc/css/awesome.css" rel="stylesheet">
|
||||
<link href="/inc/css/chart.min.css" rel="stylesheet">
|
||||
<script src="/inc/metrics.js"></script>
|
||||
<script src="/inc/chart.min.js"></script>
|
||||
<link rel="stylesheet" href="/inc/codemirror/codemirror.css">
|
||||
<script src="/inc/codemirror/codemirror.js"></script>
|
||||
<script src="/inc/codemirror/nginx.js"></script>
|
||||
<script src="/inc/codemirror/haproxy.js"></script>
|
||||
<link href="/inc/toastr.css" rel="stylesheet"/>
|
||||
<link href="/inc/css/toastr.css" rel="stylesheet"/>
|
||||
<script src="/inc/toastr.js"></script>
|
||||
<script defer src="/inc/ion.sound.min.js"></script>
|
||||
<script src="/inc/provisioning.js"></script>
|
||||
<link href="/inc/select2.css" rel="stylesheet" />
|
||||
<link href="/inc/css/select2.css" rel="stylesheet" />
|
||||
<script src="/inc/select2.js"></script>
|
||||
<script src="/inc/reconnecting-websocket.js"></script>
|
||||
<link href="/inc/table.css" rel="stylesheet" type="text/css">
|
||||
<link href="/inc/css/table.css" rel="stylesheet" type="text/css">
|
||||
<script type="text/javascript" charset="utf8" src="/inc/dataTables.min.js"></script>
|
||||
<script src="/inc/js.cookie.min.js"></script>
|
||||
<script src="/inc/script.js"></script>
|
||||
|
|
Loading…
Reference in New Issue