mirror of
https://github.com/YunoHost/yunorunner.git
synced 2024-09-03 20:05:52 +02:00
[enh] split template and DRY using jinja2
This commit is contained in:
parent
10a1bb3ef3
commit
9e24d3e1f7
5 changed files with 476 additions and 475 deletions
1
run.py
1
run.py
|
@ -46,6 +46,7 @@ LOGGING_CONFIG_DEFAULTS["formatters"]["background"] = {
|
|||
task_logger = logging.getLogger("task")
|
||||
|
||||
app = Sanic()
|
||||
app.static('/static', './static/')
|
||||
|
||||
jinja = SanicJinja2(app)
|
||||
|
||||
|
|
0
static/js/app.js
Normal file
0
static/js/app.js
Normal file
16
templates/base.html
Normal file
16
templates/base.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title><% block title %>YunoRunner for CI<% endblock %></title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css">
|
||||
<script defer src="https://use.fontawesome.com/releases/v5.1.0/js/all.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||||
<script src="/static/js/app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<% block content %><% endblock %>
|
||||
<% block javascript %><% endblock %>
|
||||
</body>
|
||||
</html>
|
|
@ -1,76 +1,70 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>YunoRunner for CI</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css">
|
||||
<script defer src="https://use.fontawesome.com/releases/v5.1.0/js/all.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h1 class="title">Tasks</h1>
|
||||
<div id="jobs">
|
||||
<table class="table is-bordered is-hoverable is-striped is-fullwidth">
|
||||
<thead>
|
||||
<th>App</th>
|
||||
<th>State</th>
|
||||
<th>Revision</th>
|
||||
<th>Ynh Version</th>
|
||||
<th>Created time</th>
|
||||
<th>Started time</th>
|
||||
<th>End time</th>
|
||||
</thead>
|
||||
<tr v-for="(job, index) in jobs" :id="job.id">
|
||||
<td><a v-bind:href="'/job/' + job.id">{{job.name}}</a></td>
|
||||
<td>{{job.state}}</td>
|
||||
<td>{{job.target_revision}}</td>
|
||||
<td>{{job.yunohost_version}}</td>
|
||||
<td>{{timestampToDate(job.created_time)}}</td>
|
||||
<td>{{timestampToDate(job.started_time)}}</td>
|
||||
<td>{{timestampToDate(job.end_time)}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
(function() {
|
||||
var app = new Vue({
|
||||
el: '#jobs',
|
||||
data: {
|
||||
jobs: []
|
||||
},
|
||||
methods: {
|
||||
timestampToDate: function (timestamp) {
|
||||
return new Date(timestamp * 1000).toLocaleString()
|
||||
<% extends "base.html" %>
|
||||
|
||||
<% block content %>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h1 class="title">Tasks</h1>
|
||||
<div id="jobs">
|
||||
<table class="table is-bordered is-hoverable is-striped is-fullwidth">
|
||||
<thead>
|
||||
<th>App</th>
|
||||
<th>State</th>
|
||||
<th>Revision</th>
|
||||
<th>Ynh Version</th>
|
||||
<th>Created time</th>
|
||||
<th>Started time</th>
|
||||
<th>End time</th>
|
||||
</thead>
|
||||
<tr v-for="(job, index) in jobs" :id="job.id">
|
||||
<td><a v-bind:href="'/job/' + job.id">{{job.name}}</a></td>
|
||||
<td>{{job.state}}</td>
|
||||
<td>{{job.target_revision}}</td>
|
||||
<td>{{job.yunohost_version}}</td>
|
||||
<td>{{timestampToDate(job.created_time)}}</td>
|
||||
<td>{{timestampToDate(job.started_time)}}</td>
|
||||
<td>{{timestampToDate(job.end_time)}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<% endblock %>
|
||||
|
||||
<% block javascript %>
|
||||
<script>
|
||||
(function() {
|
||||
var app = new Vue({
|
||||
el: '#jobs',
|
||||
data: {
|
||||
jobs: []
|
||||
},
|
||||
methods: {
|
||||
timestampToDate: function (timestamp) {
|
||||
return new Date(timestamp * 1000).toLocaleString()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ws = new WebSocket('ws://' + document.domain + ':' + location.port + '/index-ws');
|
||||
|
||||
ws.onmessage = function (event) {
|
||||
var message = JSON.parse(event.data);
|
||||
var data = message.data;
|
||||
var action = message.action;
|
||||
|
||||
if (action == "init_jobs") {
|
||||
app.jobs = data;
|
||||
} else if (action == "update_job") {
|
||||
for (var i = 0; i < app.jobs.length; ++i) {
|
||||
if (app.jobs[i].id == data.id) {
|
||||
Vue.set(app.jobs, i, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ws = new WebSocket('ws://' + document.domain + ':' + location.port + '/index-ws');
|
||||
|
||||
ws.onmessage = function (event) {
|
||||
var message = JSON.parse(event.data);
|
||||
var data = message.data;
|
||||
var action = message.action;
|
||||
|
||||
if (action == "init_jobs") {
|
||||
app.jobs = data;
|
||||
} else if (action == "update_job") {
|
||||
for (var i = 0; i < app.jobs.length; ++i) {
|
||||
if (app.jobs[i].id == data.id) {
|
||||
Vue.set(app.jobs, i, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (action == "new_job") {
|
||||
app.jobs.splice(0, 0, data);
|
||||
}
|
||||
};
|
||||
})()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
} else if (action == "new_job") {
|
||||
app.jobs.splice(0, 0, data);
|
||||
}
|
||||
};
|
||||
})()
|
||||
</script>
|
||||
<% endblock %>
|
||||
|
|
|
@ -1,421 +1,411 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>YunoRunner for CI</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css">
|
||||
<script defer src="https://use.fontawesome.com/releases/v5.1.0/js/all.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
|
||||
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css"></script>
|
||||
</head>
|
||||
<body>
|
||||
<section class="section" id="job">
|
||||
<div class="container">
|
||||
<h1 class="title">Job '{{job.name}}'</h1>
|
||||
<% extends "base.html" %>
|
||||
|
||||
<div style="margin-bottom: 15px"><button class="button is-warning" v-on:click="cancelJob">Cancel job</button> </div>
|
||||
<% block content %>
|
||||
<section class="section" id="job">
|
||||
<div class="container">
|
||||
<h1 class="title">Job '{{job.name}}'</h1>
|
||||
|
||||
<table class="table is-bordered">
|
||||
<tr><th>State</th><td>{{job.state}}</td></tr>
|
||||
<tr><th>Target revision</th><td>{{job.target_revision}}</td></tr>
|
||||
<tr><th>YunoHost version</th><td>{{job.yunohost_version}}</td></tr>
|
||||
<tr><th>Created time</th><td>{{timestampToDate(job.created_time)}}</td></tr>
|
||||
<tr><th>Started time</th><td>{{timestampToDate(job.started_time)}}</td></tr>
|
||||
<tr><th>End time</th><td>{{timestampToDate(job.end_time)}}</td></tr>
|
||||
</table>
|
||||
<div style="margin-bottom: 15px"><button class="button is-warning" v-on:click="cancelJob">Cancel job</button> </div>
|
||||
|
||||
<h2 class="subtitle">Excution log:</h2>
|
||||
<pre class="consoleOutput" v-html="logWithColors"></pre>
|
||||
</div>
|
||||
</section>
|
||||
<style>
|
||||
.consoleOutput {
|
||||
max-height: 90vh;
|
||||
background-color: #222;
|
||||
color: #eee;
|
||||
padding-top: 1.25rem;
|
||||
padding-right: 1.5rem;
|
||||
padding-bottom: 1.25rem;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
(function() {
|
||||
/* ansi_up.js
|
||||
* author : Dru Nelson
|
||||
* license : MIT
|
||||
* http://github.com/drudru/ansi_up
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['exports'], factory);
|
||||
} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
|
||||
// CommonJS
|
||||
factory(exports);
|
||||
} else {
|
||||
// Browser globals
|
||||
var exp = {};
|
||||
factory(exp);
|
||||
root.AnsiUp = exp.default;
|
||||
}
|
||||
}(this, function (exports) {
|
||||
"use strict";
|
||||
function rgx(tmplObj) {
|
||||
var subst = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
subst[_i - 1] = arguments[_i];
|
||||
}
|
||||
var regexText = tmplObj.raw[0];
|
||||
var wsrgx = /^\s+|\s+\n|\s+#[\s\S]+?\n/gm;
|
||||
var txt2 = regexText.replace(wsrgx, '');
|
||||
return new RegExp(txt2, 'm');
|
||||
<table class="table is-bordered">
|
||||
<tr><th>State</th><td>{{job.state}}</td></tr>
|
||||
<tr><th>Target revision</th><td>{{job.target_revision}}</td></tr>
|
||||
<tr><th>YunoHost version</th><td>{{job.yunohost_version}}</td></tr>
|
||||
<tr><th>Created time</th><td>{{timestampToDate(job.created_time)}}</td></tr>
|
||||
<tr><th>Started time</th><td>{{timestampToDate(job.started_time)}}</td></tr>
|
||||
<tr><th>End time</th><td>{{timestampToDate(job.end_time)}}</td></tr>
|
||||
</table>
|
||||
|
||||
<h2 class="subtitle">Excution log:</h2>
|
||||
<pre class="consoleOutput" v-html="logWithColors"></pre>
|
||||
</div>
|
||||
</section>
|
||||
<% endblock %>
|
||||
|
||||
<% block javascript %>
|
||||
<style>
|
||||
.consoleOutput {
|
||||
max-height: 90vh;
|
||||
background-color: #222;
|
||||
color: #eee;
|
||||
padding-top: 1.25rem;
|
||||
padding-right: 1.5rem;
|
||||
padding-bottom: 1.25rem;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
(function() {
|
||||
/* ansi_up.js
|
||||
* author : Dru Nelson
|
||||
* license : MIT
|
||||
* http://github.com/drudru/ansi_up
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define(['exports'], factory);
|
||||
} else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
|
||||
// CommonJS
|
||||
factory(exports);
|
||||
} else {
|
||||
// Browser globals
|
||||
var exp = {};
|
||||
factory(exp);
|
||||
root.AnsiUp = exp.default;
|
||||
}
|
||||
var AnsiUp = (function () {
|
||||
function AnsiUp() {
|
||||
this.VERSION = "3.0.0";
|
||||
this.ansi_colors = [
|
||||
[
|
||||
{ rgb: [0, 0, 0], class_name: "ansi-black" },
|
||||
{ rgb: [187, 0, 0], class_name: "ansi-red" },
|
||||
{ rgb: [0, 187, 0], class_name: "ansi-green" },
|
||||
{ rgb: [187, 187, 0], class_name: "ansi-yellow" },
|
||||
{ rgb: [0, 0, 187], class_name: "ansi-blue" },
|
||||
{ rgb: [187, 0, 187], class_name: "ansi-magenta" },
|
||||
{ rgb: [0, 187, 187], class_name: "ansi-cyan" },
|
||||
{ rgb: [255, 255, 255], class_name: "ansi-white" }
|
||||
],
|
||||
[
|
||||
{ rgb: [85, 85, 85], class_name: "ansi-bright-black" },
|
||||
{ rgb: [255, 85, 85], class_name: "ansi-bright-red" },
|
||||
{ rgb: [0, 255, 0], class_name: "ansi-bright-green" },
|
||||
{ rgb: [255, 255, 85], class_name: "ansi-bright-yellow" },
|
||||
{ rgb: [85, 85, 255], class_name: "ansi-bright-blue" },
|
||||
{ rgb: [255, 85, 255], class_name: "ansi-bright-magenta" },
|
||||
{ rgb: [85, 255, 255], class_name: "ansi-bright-cyan" },
|
||||
{ rgb: [255, 255, 255], class_name: "ansi-bright-white" }
|
||||
]
|
||||
];
|
||||
this.htmlFormatter = {
|
||||
transform: function (fragment, instance) {
|
||||
var txt = fragment.text;
|
||||
if (txt.length === 0)
|
||||
return txt;
|
||||
if (instance._escape_for_html)
|
||||
txt = instance.old_escape_for_html(txt);
|
||||
if (!fragment.bold && fragment.fg === null && fragment.bg === null)
|
||||
return txt;
|
||||
var styles = [];
|
||||
var classes = [];
|
||||
var fg = fragment.fg;
|
||||
var bg = fragment.bg;
|
||||
if (fragment.bold)
|
||||
styles.push('font-weight:bold');
|
||||
if (!instance._use_classes) {
|
||||
if (fg)
|
||||
}(this, function (exports) {
|
||||
"use strict";
|
||||
function rgx(tmplObj) {
|
||||
var subst = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
subst[_i - 1] = arguments[_i];
|
||||
}
|
||||
var regexText = tmplObj.raw[0];
|
||||
var wsrgx = /^\s+|\s+\n|\s+#[\s\S]+?\n/gm;
|
||||
var txt2 = regexText.replace(wsrgx, '');
|
||||
return new RegExp(txt2, 'm');
|
||||
}
|
||||
var AnsiUp = (function () {
|
||||
function AnsiUp() {
|
||||
this.VERSION = "3.0.0";
|
||||
this.ansi_colors = [
|
||||
[
|
||||
{ rgb: [0, 0, 0], class_name: "ansi-black" },
|
||||
{ rgb: [187, 0, 0], class_name: "ansi-red" },
|
||||
{ rgb: [0, 187, 0], class_name: "ansi-green" },
|
||||
{ rgb: [187, 187, 0], class_name: "ansi-yellow" },
|
||||
{ rgb: [0, 0, 187], class_name: "ansi-blue" },
|
||||
{ rgb: [187, 0, 187], class_name: "ansi-magenta" },
|
||||
{ rgb: [0, 187, 187], class_name: "ansi-cyan" },
|
||||
{ rgb: [255, 255, 255], class_name: "ansi-white" }
|
||||
],
|
||||
[
|
||||
{ rgb: [85, 85, 85], class_name: "ansi-bright-black" },
|
||||
{ rgb: [255, 85, 85], class_name: "ansi-bright-red" },
|
||||
{ rgb: [0, 255, 0], class_name: "ansi-bright-green" },
|
||||
{ rgb: [255, 255, 85], class_name: "ansi-bright-yellow" },
|
||||
{ rgb: [85, 85, 255], class_name: "ansi-bright-blue" },
|
||||
{ rgb: [255, 85, 255], class_name: "ansi-bright-magenta" },
|
||||
{ rgb: [85, 255, 255], class_name: "ansi-bright-cyan" },
|
||||
{ rgb: [255, 255, 255], class_name: "ansi-bright-white" }
|
||||
]
|
||||
];
|
||||
this.htmlFormatter = {
|
||||
transform: function (fragment, instance) {
|
||||
var txt = fragment.text;
|
||||
if (txt.length === 0)
|
||||
return txt;
|
||||
if (instance._escape_for_html)
|
||||
txt = instance.old_escape_for_html(txt);
|
||||
if (!fragment.bold && fragment.fg === null && fragment.bg === null)
|
||||
return txt;
|
||||
var styles = [];
|
||||
var classes = [];
|
||||
var fg = fragment.fg;
|
||||
var bg = fragment.bg;
|
||||
if (fragment.bold)
|
||||
styles.push('font-weight:bold');
|
||||
if (!instance._use_classes) {
|
||||
if (fg)
|
||||
styles.push("color:rgb(" + fg.rgb.join(',') + ")");
|
||||
if (bg)
|
||||
styles.push("background-color:rgb(" + bg.rgb + ")");
|
||||
}
|
||||
else {
|
||||
if (fg) {
|
||||
if (fg.class_name !== 'truecolor') {
|
||||
classes.push(fg.class_name + "-fg");
|
||||
}
|
||||
else {
|
||||
styles.push("color:rgb(" + fg.rgb.join(',') + ")");
|
||||
if (bg)
|
||||
styles.push("background-color:rgb(" + bg.rgb + ")");
|
||||
}
|
||||
else {
|
||||
if (fg) {
|
||||
if (fg.class_name !== 'truecolor') {
|
||||
classes.push(fg.class_name + "-fg");
|
||||
}
|
||||
else {
|
||||
styles.push("color:rgb(" + fg.rgb.join(',') + ")");
|
||||
}
|
||||
}
|
||||
if (bg) {
|
||||
if (bg.class_name !== 'truecolor') {
|
||||
classes.push(bg.class_name + "-bg");
|
||||
}
|
||||
else {
|
||||
styles.push("background-color:rgb(" + bg.rgb.join(',') + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
var class_string = '';
|
||||
var style_string = '';
|
||||
if (classes.length)
|
||||
class_string = " class=\"" + classes.join(' ') + "\"";
|
||||
if (styles.length)
|
||||
style_string = " style=\"" + styles.join(';') + "\"";
|
||||
return "<span" + style_string + class_string + ">" + txt + "</span>";
|
||||
},
|
||||
compose: function (segments, instance) {
|
||||
return segments.join("");
|
||||
}
|
||||
};
|
||||
this.textFormatter = {
|
||||
transform: function (fragment, instance) {
|
||||
return fragment.text;
|
||||
},
|
||||
compose: function (segments, instance) {
|
||||
return segments.join("");
|
||||
}
|
||||
};
|
||||
this.setup_256_palette();
|
||||
this._use_classes = false;
|
||||
this._escape_for_html = true;
|
||||
this.bold = false;
|
||||
this.fg = this.bg = null;
|
||||
this._buffer = '';
|
||||
}
|
||||
Object.defineProperty(AnsiUp.prototype, "use_classes", {
|
||||
get: function () {
|
||||
return this._use_classes;
|
||||
},
|
||||
set: function (arg) {
|
||||
this._use_classes = arg;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(AnsiUp.prototype, "escape_for_html", {
|
||||
get: function () {
|
||||
return this._escape_for_html;
|
||||
},
|
||||
set: function (arg) {
|
||||
this._escape_for_html = arg;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
AnsiUp.prototype.setup_256_palette = function () {
|
||||
var _this = this;
|
||||
this.palette_256 = [];
|
||||
this.ansi_colors.forEach(function (palette) {
|
||||
palette.forEach(function (rec) {
|
||||
_this.palette_256.push(rec);
|
||||
});
|
||||
});
|
||||
var levels = [0, 95, 135, 175, 215, 255];
|
||||
for (var r = 0; r < 6; ++r) {
|
||||
for (var g = 0; g < 6; ++g) {
|
||||
for (var b = 0; b < 6; ++b) {
|
||||
var col = { rgb: [levels[r], levels[g], levels[b]], class_name: 'truecolor' };
|
||||
this.palette_256.push(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
var grey_level = 8;
|
||||
for (var i = 0; i < 24; ++i, grey_level += 10) {
|
||||
var gry = { rgb: [grey_level, grey_level, grey_level], class_name: 'truecolor' };
|
||||
this.palette_256.push(gry);
|
||||
}
|
||||
};
|
||||
AnsiUp.prototype.old_escape_for_html = function (txt) {
|
||||
return txt.replace(/[&<>]/gm, function (str) {
|
||||
if (str === "&")
|
||||
return "&";
|
||||
if (str === "<")
|
||||
return "<";
|
||||
if (str === ">")
|
||||
return ">";
|
||||
});
|
||||
};
|
||||
AnsiUp.prototype.old_linkify = function (txt) {
|
||||
return txt.replace(/(https?:\/\/[^\s]+)/gm, function (str) {
|
||||
return "<a href=\"" + str + "\">" + str + "</a>";
|
||||
});
|
||||
};
|
||||
AnsiUp.prototype.detect_incomplete_ansi = function (txt) {
|
||||
return !(/.*?[\x40-\x7e]/.test(txt));
|
||||
};
|
||||
AnsiUp.prototype.detect_incomplete_link = function (txt) {
|
||||
var found = false;
|
||||
for (var i = txt.length - 1; i > 0; i--) {
|
||||
if (/\s|\x1B/.test(txt[i])) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
if (/(https?:\/\/[^\s]+)/.test(txt))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
var prefix = txt.substr(i + 1, 4);
|
||||
if (prefix.length === 0)
|
||||
return -1;
|
||||
if ("http".indexOf(prefix) === 0)
|
||||
return (i + 1);
|
||||
};
|
||||
AnsiUp.prototype.ansi_to = function (txt, formatter) {
|
||||
var pkt = this._buffer + txt;
|
||||
this._buffer = '';
|
||||
var raw_text_pkts = pkt.split(/\x1B\[/);
|
||||
if (raw_text_pkts.length === 1)
|
||||
raw_text_pkts.push('');
|
||||
this.handle_incomplete_sequences(raw_text_pkts);
|
||||
var first_chunk = this.with_state(raw_text_pkts.shift());
|
||||
var blocks = new Array(raw_text_pkts.length);
|
||||
for (var i = 0, len = raw_text_pkts.length; i < len; ++i) {
|
||||
blocks[i] = (formatter.transform(this.process_ansi(raw_text_pkts[i]), this));
|
||||
}
|
||||
if (first_chunk.text.length > 0)
|
||||
blocks.unshift(formatter.transform(first_chunk, this));
|
||||
return formatter.compose(blocks, this);
|
||||
};
|
||||
AnsiUp.prototype.ansi_to_html = function (txt) {
|
||||
return this.ansi_to(txt, this.htmlFormatter);
|
||||
};
|
||||
AnsiUp.prototype.ansi_to_text = function (txt) {
|
||||
return this.ansi_to(txt, this.textFormatter);
|
||||
};
|
||||
AnsiUp.prototype.with_state = function (text) {
|
||||
return { bold: this.bold, fg: this.fg, bg: this.bg, text: text };
|
||||
};
|
||||
AnsiUp.prototype.handle_incomplete_sequences = function (chunks) {
|
||||
var last_chunk = chunks[chunks.length - 1];
|
||||
if ((last_chunk.length > 0) && this.detect_incomplete_ansi(last_chunk)) {
|
||||
this._buffer = "\x1B[" + last_chunk;
|
||||
chunks.pop();
|
||||
chunks.push('');
|
||||
}
|
||||
else {
|
||||
if (last_chunk.slice(-1) === "\x1B") {
|
||||
this._buffer = "\x1B";
|
||||
console.log("raw", chunks);
|
||||
chunks.pop();
|
||||
chunks.push(last_chunk.substr(0, last_chunk.length - 1));
|
||||
console.log(chunks);
|
||||
console.log(last_chunk);
|
||||
}
|
||||
if (chunks.length === 2 &&
|
||||
chunks[1] === "" &&
|
||||
chunks[0].slice(-1) === "\x1B") {
|
||||
this._buffer = "\x1B";
|
||||
last_chunk = chunks.shift();
|
||||
chunks.unshift(last_chunk.substr(0, last_chunk.length - 1));
|
||||
}
|
||||
}
|
||||
};
|
||||
AnsiUp.prototype.process_ansi = function (block) {
|
||||
if (!this._sgr_regex) {
|
||||
this._sgr_regex = (_a = ["\n ^ # beginning of line\n ([!<-?]?) # a private-mode char (!, <, =, >, ?)\n ([d;]*) # any digits or semicolons\n ([ -/]? # an intermediate modifier\n [@-~]) # the command\n ([sS]*) # any text following this CSI sequence\n "], _a.raw = ["\n ^ # beginning of line\n ([!\\x3c-\\x3f]?) # a private-mode char (!, <, =, >, ?)\n ([\\d;]*) # any digits or semicolons\n ([\\x20-\\x2f]? # an intermediate modifier\n [\\x40-\\x7e]) # the command\n ([\\s\\S]*) # any text following this CSI sequence\n "], rgx(_a));
|
||||
}
|
||||
var matches = block.match(this._sgr_regex);
|
||||
if (!matches) {
|
||||
return this.with_state(block);
|
||||
}
|
||||
var orig_txt = matches[4];
|
||||
if (matches[1] !== '' || matches[3] !== 'm') {
|
||||
return this.with_state(orig_txt);
|
||||
}
|
||||
var sgr_cmds = matches[2].split(';');
|
||||
while (sgr_cmds.length > 0) {
|
||||
var sgr_cmd_str = sgr_cmds.shift();
|
||||
var num = parseInt(sgr_cmd_str, 10);
|
||||
if (isNaN(num) || num === 0) {
|
||||
this.fg = this.bg = null;
|
||||
this.bold = false;
|
||||
}
|
||||
else if (num === 1) {
|
||||
this.bold = true;
|
||||
}
|
||||
else if (num === 22) {
|
||||
this.bold = false;
|
||||
}
|
||||
else if (num === 39) {
|
||||
this.fg = null;
|
||||
}
|
||||
else if (num === 49) {
|
||||
this.bg = null;
|
||||
}
|
||||
else if ((num >= 30) && (num < 38)) {
|
||||
this.fg = this.ansi_colors[0][(num - 30)];
|
||||
}
|
||||
else if ((num >= 40) && (num < 48)) {
|
||||
this.bg = this.ansi_colors[0][(num - 40)];
|
||||
}
|
||||
else if ((num >= 90) && (num < 98)) {
|
||||
this.fg = this.ansi_colors[1][(num - 90)];
|
||||
}
|
||||
else if ((num >= 100) && (num < 108)) {
|
||||
this.bg = this.ansi_colors[1][(num - 100)];
|
||||
}
|
||||
else if (num === 38 || num === 48) {
|
||||
if (sgr_cmds.length > 0) {
|
||||
var is_foreground = (num === 38);
|
||||
var mode_cmd = sgr_cmds.shift();
|
||||
if (mode_cmd === '5' && sgr_cmds.length > 0) {
|
||||
var palette_index = parseInt(sgr_cmds.shift(), 10);
|
||||
if (palette_index >= 0 && palette_index <= 255) {
|
||||
if (is_foreground)
|
||||
this.fg = this.palette_256[palette_index];
|
||||
else
|
||||
this.bg = this.palette_256[palette_index];
|
||||
}
|
||||
if (bg) {
|
||||
if (bg.class_name !== 'truecolor') {
|
||||
classes.push(bg.class_name + "-bg");
|
||||
}
|
||||
if (mode_cmd === '2' && sgr_cmds.length > 2) {
|
||||
var r = parseInt(sgr_cmds.shift(), 10);
|
||||
var g = parseInt(sgr_cmds.shift(), 10);
|
||||
var b = parseInt(sgr_cmds.shift(), 10);
|
||||
if ((r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)) {
|
||||
var c = { rgb: [r, g, b], class_name: 'truecolor' };
|
||||
if (is_foreground)
|
||||
this.fg = c;
|
||||
else
|
||||
this.bg = c;
|
||||
}
|
||||
else {
|
||||
styles.push("background-color:rgb(" + bg.rgb.join(',') + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
var class_string = '';
|
||||
var style_string = '';
|
||||
if (classes.length)
|
||||
class_string = " class=\"" + classes.join(' ') + "\"";
|
||||
if (styles.length)
|
||||
style_string = " style=\"" + styles.join(';') + "\"";
|
||||
return "<span" + style_string + class_string + ">" + txt + "</span>";
|
||||
},
|
||||
compose: function (segments, instance) {
|
||||
return segments.join("");
|
||||
}
|
||||
return this.with_state(orig_txt);
|
||||
var _a;
|
||||
};
|
||||
return AnsiUp;
|
||||
}());
|
||||
//# sourceMappingURL=ansi_up.js.map
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = AnsiUp;
|
||||
}));
|
||||
|
||||
var app = new Vue({
|
||||
el: '#job',
|
||||
data: {
|
||||
job: {}
|
||||
this.textFormatter = {
|
||||
transform: function (fragment, instance) {
|
||||
return fragment.text;
|
||||
},
|
||||
compose: function (segments, instance) {
|
||||
return segments.join("");
|
||||
}
|
||||
};
|
||||
this.setup_256_palette();
|
||||
this._use_classes = false;
|
||||
this._escape_for_html = true;
|
||||
this.bold = false;
|
||||
this.fg = this.bg = null;
|
||||
this._buffer = '';
|
||||
}
|
||||
Object.defineProperty(AnsiUp.prototype, "use_classes", {
|
||||
get: function () {
|
||||
return this._use_classes;
|
||||
},
|
||||
methods: {
|
||||
timestampToDate: function (timestamp) {
|
||||
return new Date(timestamp * 1000).toLocaleString()
|
||||
},
|
||||
cancelJob: function() {
|
||||
$.post("/api/job/" + this.job.id + "/stop")
|
||||
}
|
||||
set: function (arg) {
|
||||
this._use_classes = arg;
|
||||
},
|
||||
computed: {
|
||||
logWithColors: function() {
|
||||
if (this.job.log != undefined) {
|
||||
var ansiup = new AnsiUp;
|
||||
return ansiup.ansi_to_html(this.job.log);
|
||||
} else {
|
||||
return "";
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty(AnsiUp.prototype, "escape_for_html", {
|
||||
get: function () {
|
||||
return this._escape_for_html;
|
||||
},
|
||||
set: function (arg) {
|
||||
this._escape_for_html = arg;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
AnsiUp.prototype.setup_256_palette = function () {
|
||||
var _this = this;
|
||||
this.palette_256 = [];
|
||||
this.ansi_colors.forEach(function (palette) {
|
||||
palette.forEach(function (rec) {
|
||||
_this.palette_256.push(rec);
|
||||
});
|
||||
});
|
||||
var levels = [0, 95, 135, 175, 215, 255];
|
||||
for (var r = 0; r < 6; ++r) {
|
||||
for (var g = 0; g < 6; ++g) {
|
||||
for (var b = 0; b < 6; ++b) {
|
||||
var col = { rgb: [levels[r], levels[g], levels[b]], class_name: 'truecolor' };
|
||||
this.palette_256.push(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ws = new WebSocket('ws://' + document.domain + ':' + location.port + '/job-<{ job.id }>-ws');
|
||||
|
||||
ws.onmessage = function (event) {
|
||||
var message = JSON.parse(event.data);
|
||||
var data = message.data;
|
||||
var action = message.action;
|
||||
|
||||
if (action == "init_job" || action == "update_job") {
|
||||
app.job = data;
|
||||
var grey_level = 8;
|
||||
for (var i = 0; i < 24; ++i, grey_level += 10) {
|
||||
var gry = { rgb: [grey_level, grey_level, grey_level], class_name: 'truecolor' };
|
||||
this.palette_256.push(gry);
|
||||
}
|
||||
};
|
||||
})()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
AnsiUp.prototype.old_escape_for_html = function (txt) {
|
||||
return txt.replace(/[&<>]/gm, function (str) {
|
||||
if (str === "&")
|
||||
return "&";
|
||||
if (str === "<")
|
||||
return "<";
|
||||
if (str === ">")
|
||||
return ">";
|
||||
});
|
||||
};
|
||||
AnsiUp.prototype.old_linkify = function (txt) {
|
||||
return txt.replace(/(https?:\/\/[^\s]+)/gm, function (str) {
|
||||
return "<a href=\"" + str + "\">" + str + "</a>";
|
||||
});
|
||||
};
|
||||
AnsiUp.prototype.detect_incomplete_ansi = function (txt) {
|
||||
return !(/.*?[\x40-\x7e]/.test(txt));
|
||||
};
|
||||
AnsiUp.prototype.detect_incomplete_link = function (txt) {
|
||||
var found = false;
|
||||
for (var i = txt.length - 1; i > 0; i--) {
|
||||
if (/\s|\x1B/.test(txt[i])) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
if (/(https?:\/\/[^\s]+)/.test(txt))
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
var prefix = txt.substr(i + 1, 4);
|
||||
if (prefix.length === 0)
|
||||
return -1;
|
||||
if ("http".indexOf(prefix) === 0)
|
||||
return (i + 1);
|
||||
};
|
||||
AnsiUp.prototype.ansi_to = function (txt, formatter) {
|
||||
var pkt = this._buffer + txt;
|
||||
this._buffer = '';
|
||||
var raw_text_pkts = pkt.split(/\x1B\[/);
|
||||
if (raw_text_pkts.length === 1)
|
||||
raw_text_pkts.push('');
|
||||
this.handle_incomplete_sequences(raw_text_pkts);
|
||||
var first_chunk = this.with_state(raw_text_pkts.shift());
|
||||
var blocks = new Array(raw_text_pkts.length);
|
||||
for (var i = 0, len = raw_text_pkts.length; i < len; ++i) {
|
||||
blocks[i] = (formatter.transform(this.process_ansi(raw_text_pkts[i]), this));
|
||||
}
|
||||
if (first_chunk.text.length > 0)
|
||||
blocks.unshift(formatter.transform(first_chunk, this));
|
||||
return formatter.compose(blocks, this);
|
||||
};
|
||||
AnsiUp.prototype.ansi_to_html = function (txt) {
|
||||
return this.ansi_to(txt, this.htmlFormatter);
|
||||
};
|
||||
AnsiUp.prototype.ansi_to_text = function (txt) {
|
||||
return this.ansi_to(txt, this.textFormatter);
|
||||
};
|
||||
AnsiUp.prototype.with_state = function (text) {
|
||||
return { bold: this.bold, fg: this.fg, bg: this.bg, text: text };
|
||||
};
|
||||
AnsiUp.prototype.handle_incomplete_sequences = function (chunks) {
|
||||
var last_chunk = chunks[chunks.length - 1];
|
||||
if ((last_chunk.length > 0) && this.detect_incomplete_ansi(last_chunk)) {
|
||||
this._buffer = "\x1B[" + last_chunk;
|
||||
chunks.pop();
|
||||
chunks.push('');
|
||||
}
|
||||
else {
|
||||
if (last_chunk.slice(-1) === "\x1B") {
|
||||
this._buffer = "\x1B";
|
||||
console.log("raw", chunks);
|
||||
chunks.pop();
|
||||
chunks.push(last_chunk.substr(0, last_chunk.length - 1));
|
||||
console.log(chunks);
|
||||
console.log(last_chunk);
|
||||
}
|
||||
if (chunks.length === 2 &&
|
||||
chunks[1] === "" &&
|
||||
chunks[0].slice(-1) === "\x1B") {
|
||||
this._buffer = "\x1B";
|
||||
last_chunk = chunks.shift();
|
||||
chunks.unshift(last_chunk.substr(0, last_chunk.length - 1));
|
||||
}
|
||||
}
|
||||
};
|
||||
AnsiUp.prototype.process_ansi = function (block) {
|
||||
if (!this._sgr_regex) {
|
||||
this._sgr_regex = (_a = ["\n ^ # beginning of line\n ([!<-?]?) # a private-mode char (!, <, =, >, ?)\n ([d;]*) # any digits or semicolons\n ([ -/]? # an intermediate modifier\n [@-~]) # the command\n ([sS]*) # any text following this CSI sequence\n "], _a.raw = ["\n ^ # beginning of line\n ([!\\x3c-\\x3f]?) # a private-mode char (!, <, =, >, ?)\n ([\\d;]*) # any digits or semicolons\n ([\\x20-\\x2f]? # an intermediate modifier\n [\\x40-\\x7e]) # the command\n ([\\s\\S]*) # any text following this CSI sequence\n "], rgx(_a));
|
||||
}
|
||||
var matches = block.match(this._sgr_regex);
|
||||
if (!matches) {
|
||||
return this.with_state(block);
|
||||
}
|
||||
var orig_txt = matches[4];
|
||||
if (matches[1] !== '' || matches[3] !== 'm') {
|
||||
return this.with_state(orig_txt);
|
||||
}
|
||||
var sgr_cmds = matches[2].split(';');
|
||||
while (sgr_cmds.length > 0) {
|
||||
var sgr_cmd_str = sgr_cmds.shift();
|
||||
var num = parseInt(sgr_cmd_str, 10);
|
||||
if (isNaN(num) || num === 0) {
|
||||
this.fg = this.bg = null;
|
||||
this.bold = false;
|
||||
}
|
||||
else if (num === 1) {
|
||||
this.bold = true;
|
||||
}
|
||||
else if (num === 22) {
|
||||
this.bold = false;
|
||||
}
|
||||
else if (num === 39) {
|
||||
this.fg = null;
|
||||
}
|
||||
else if (num === 49) {
|
||||
this.bg = null;
|
||||
}
|
||||
else if ((num >= 30) && (num < 38)) {
|
||||
this.fg = this.ansi_colors[0][(num - 30)];
|
||||
}
|
||||
else if ((num >= 40) && (num < 48)) {
|
||||
this.bg = this.ansi_colors[0][(num - 40)];
|
||||
}
|
||||
else if ((num >= 90) && (num < 98)) {
|
||||
this.fg = this.ansi_colors[1][(num - 90)];
|
||||
}
|
||||
else if ((num >= 100) && (num < 108)) {
|
||||
this.bg = this.ansi_colors[1][(num - 100)];
|
||||
}
|
||||
else if (num === 38 || num === 48) {
|
||||
if (sgr_cmds.length > 0) {
|
||||
var is_foreground = (num === 38);
|
||||
var mode_cmd = sgr_cmds.shift();
|
||||
if (mode_cmd === '5' && sgr_cmds.length > 0) {
|
||||
var palette_index = parseInt(sgr_cmds.shift(), 10);
|
||||
if (palette_index >= 0 && palette_index <= 255) {
|
||||
if (is_foreground)
|
||||
this.fg = this.palette_256[palette_index];
|
||||
else
|
||||
this.bg = this.palette_256[palette_index];
|
||||
}
|
||||
}
|
||||
if (mode_cmd === '2' && sgr_cmds.length > 2) {
|
||||
var r = parseInt(sgr_cmds.shift(), 10);
|
||||
var g = parseInt(sgr_cmds.shift(), 10);
|
||||
var b = parseInt(sgr_cmds.shift(), 10);
|
||||
if ((r >= 0 && r <= 255) && (g >= 0 && g <= 255) && (b >= 0 && b <= 255)) {
|
||||
var c = { rgb: [r, g, b], class_name: 'truecolor' };
|
||||
if (is_foreground)
|
||||
this.fg = c;
|
||||
else
|
||||
this.bg = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.with_state(orig_txt);
|
||||
var _a;
|
||||
};
|
||||
return AnsiUp;
|
||||
}());
|
||||
//# sourceMappingURL=ansi_up.js.map
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.default = AnsiUp;
|
||||
}));
|
||||
|
||||
var app = new Vue({
|
||||
el: '#job',
|
||||
data: {
|
||||
job: {}
|
||||
},
|
||||
methods: {
|
||||
timestampToDate: function (timestamp) {
|
||||
return new Date(timestamp * 1000).toLocaleString()
|
||||
},
|
||||
cancelJob: function() {
|
||||
$.post("/api/job/" + this.job.id + "/stop")
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
logWithColors: function() {
|
||||
if (this.job.log != undefined) {
|
||||
var ansiup = new AnsiUp;
|
||||
return ansiup.ansi_to_html(this.job.log);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ws = new WebSocket('ws://' + document.domain + ':' + location.port + '/job-<{ job.id }>-ws');
|
||||
|
||||
ws.onmessage = function (event) {
|
||||
var message = JSON.parse(event.data);
|
||||
var data = message.data;
|
||||
var action = message.action;
|
||||
|
||||
if (action == "init_job" || action == "update_job") {
|
||||
app.job = data;
|
||||
}
|
||||
};
|
||||
})()
|
||||
</script>
|
||||
<% endblock %>
|
||||
|
|
Loading…
Add table
Reference in a new issue