From b2965bf4322eaeb6f73f8785332e40f485f4f4f8 Mon Sep 17 00:00:00 2001 From: Michael Nutt Date: Sun, 2 Mar 2014 00:43:20 -0500 Subject: [PATCH] extract commit update js into a separate file --- Makefile | 5 +- cmd/droned/assets/js/commit_updates.js | 83 ++++++++++++++ cmd/droned/assets/js/line_formatter.js | 66 +++++++++++ cmd/droned/assets/js/main.js | 149 +++++++++++++++++++++++++ cmd/droned/drone.go | 1 + pkg/template/pages/base.html | 5 +- pkg/template/pages/repo_commit.html | 118 +++----------------- pkg/template/template.go | 14 +++ 8 files changed, 337 insertions(+), 104 deletions(-) create mode 100644 cmd/droned/assets/js/commit_updates.js create mode 100644 cmd/droned/assets/js/line_formatter.js create mode 100644 cmd/droned/assets/js/main.js diff --git a/Makefile b/Makefile index 83898753c..4d431273e 100644 --- a/Makefile +++ b/Makefile @@ -26,10 +26,13 @@ deps: go get github.com/mattn/go-sqlite3 go get github.com/russross/meddler -embed: +embed: js cd cmd/droned && rice embed cd pkg/template && rice embed +js: + cd cmd/droned/assets && find js -name "*.js" ! -name '.*' ! -name "main.js" -exec cat {} \; > js/main.js + build: cd cmd/drone && go build -o ../../bin/drone cd cmd/droned && go build -o ../../bin/droned diff --git a/cmd/droned/assets/js/commit_updates.js b/cmd/droned/assets/js/commit_updates.js new file mode 100644 index 000000000..70868e22f --- /dev/null +++ b/cmd/droned/assets/js/commit_updates.js @@ -0,0 +1,83 @@ +;// Live commit updates + +if(typeof(Drone) === 'undefined') { Drone = {}; } + +(function () { + Drone.CommitUpdates = function(socket) { + if(typeof(socket) === "string") { + var url = [(window.location.protocol == 'https:' ? 'wss' : 'ws'), + '://', + window.location.host, + '/', + socket].join('') + this.socket = new WebSocket(url); + } else { + this.socket = socket; + } + + this.lineFormatter = new Drone.LineFormatter(); + this.attach(); + } + + Drone.CommitUpdates.prototype = { + lineBuffer: "", + autoFollow: false, + + startOutput: function(el) { + if(typeof(el) === 'string') { + this.el = document.getElementById(el); + } else { + this.el = el; + } + + this.updateScreen(); + }, + + attach: function() { + this.socket.onopen = this.onOpen; + this.socket.onerror = this.onError; + this.socket.onmessage = this.onMessage.bind(this); + this.socket.onclose = this.onClose; + }, + + updateScreen: function() { + if(this.lineBuffer.length > 0) { + this.el.innerHTML += this.lineBuffer; + this.lineBuffer = ''; + + if (this.autoFollow) { + window.scrollTo(0, document.body.scrollHeight); + } + } + + requestAnimationFrame(this.updateScreen.bind(this)); + }, + + onOpen: function() { + console.log('output websocket open'); + }, + + onError: function(e) { + console.log('websocket error: ' + e); + }, + + onMessage: function(e) { + this.lineBuffer += this.lineFormatter.format(e.data); + }, + + onClose: function(e) { + console.log('output websocket closed: ' + JSON.stringify(e)); + //window.location.reload(); + } + }; + + // Polyfill rAF for older browsers + window.requestAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + function(callback, element) { + return window.setTimeout(function() { + callback(+new Date()); + }, 1000 / 60); + }; + +})(); diff --git a/cmd/droned/assets/js/line_formatter.js b/cmd/droned/assets/js/line_formatter.js new file mode 100644 index 000000000..3b9e9e4fa --- /dev/null +++ b/cmd/droned/assets/js/line_formatter.js @@ -0,0 +1,66 @@ +;// Format ANSI to HTML + +if(typeof(Drone) === 'undefined') { Drone = {}; } + +(function() { + Drone.LineFormatter = function() {}; + + Drone.LineFormatter.prototype = { + regex: /\u001B\[([0-9]+;?)*[Km]/g, + styles: [], + + format: function(s) { + // Check for newline and early exit? + s = s.replace(//g, ">"); + + var output = ""; + var current = 0; + while (m = this.regex.exec(s)) { + var part = s.substring(current, m.index+1); + current = this.regex.lastIndex; + + var token = s.substr(m.index, this.regex.lastIndex - m.index); + var code = token.substr(2, token.length-2); + + var pre = ""; + var post = ""; + + switch (code) { + case 'm': + case '0m': + var len = styles.length; + for (var i=0; i < len; i++) { + styles.pop(); + post += "" + } + break; + case '30;42m': pre = ''; break; + case '36m': + case '36;1m': pre = ''; break; + case '31m': + case '31;31m': pre = ''; break; + case '33m': + case '33;33m': pre = ''; break; + case '32m': + case '0;32m': pre = ''; break; + case '90m': pre = ''; break; + case 'K': + case '0K': + case '1K': + case '2K': break; + } + + if (pre !== "") { + styles.push(pre); + } + + output += part + pre + post; + } + + var part = s.substring(current, s.length); + output += part; + return output; + } + }; +})(); diff --git a/cmd/droned/assets/js/main.js b/cmd/droned/assets/js/main.js new file mode 100644 index 000000000..d16ae68f0 --- /dev/null +++ b/cmd/droned/assets/js/main.js @@ -0,0 +1,149 @@ +;// Live commit updates + +if(typeof(Drone) === 'undefined') { Drone = {}; } + +(function () { + Drone.CommitUpdates = function(socket) { + if(typeof(socket) === "string") { + var url = [(window.location.protocol == 'https:' ? 'wss' : 'ws'), + '://', + window.location.host, + '/', + socket].join('') + this.socket = new WebSocket(url); + } else { + this.socket = socket; + } + + this.lineFormatter = new Drone.LineFormatter(); + this.attach(); + } + + Drone.CommitUpdates.prototype = { + lineBuffer: "", + autoFollow: false, + + startOutput: function(el) { + if(typeof(el) === 'string') { + this.el = document.getElementById(el); + } else { + this.el = el; + } + + this.updateScreen(); + }, + + attach: function() { + this.socket.onopen = this.onOpen; + this.socket.onerror = this.onError; + this.socket.onmessage = this.onMessage.bind(this); + this.socket.onclose = this.onClose; + }, + + updateScreen: function() { + if(this.lineBuffer.length > 0) { + this.el.innerHTML += this.lineBuffer; + this.lineBuffer = ''; + + if (this.autoFollow) { + window.scrollTo(0, document.body.scrollHeight); + } + } + + requestAnimationFrame(this.updateScreen.bind(this)); + }, + + onOpen: function() { + console.log('output websocket open'); + }, + + onError: function(e) { + console.log('websocket error: ' + e); + }, + + onMessage: function(e) { + this.lineBuffer += this.lineFormatter.format(e.data); + }, + + onClose: function(e) { + console.log('output websocket closed: ' + JSON.stringify(e)); + //window.location.reload(); + } + }; + + // Polyfill rAF for older browsers + window.requestAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + function(callback, element) { + return window.setTimeout(function() { + callback(+new Date()); + }, 1000 / 60); + }; + +})(); +;// Format ANSI to HTML + +if(typeof(Drone) === 'undefined') { Drone = {}; } + +(function() { + Drone.LineFormatter = function() {}; + + Drone.LineFormatter.prototype = { + regex: /\u001B\[([0-9]+;?)*[Km]/g, + styles: [], + + format: function(s) { + // Check for newline and early exit? + s = s.replace(//g, ">"); + + var output = ""; + var current = 0; + while (m = this.regex.exec(s)) { + var part = s.substring(current, m.index+1); + current = this.regex.lastIndex; + + var token = s.substr(m.index, this.regex.lastIndex - m.index); + var code = token.substr(2, token.length-2); + + var pre = ""; + var post = ""; + + switch (code) { + case 'm': + case '0m': + var len = styles.length; + for (var i=0; i < len; i++) { + styles.pop(); + post += "" + } + break; + case '30;42m': pre = ''; break; + case '36m': + case '36;1m': pre = ''; break; + case '31m': + case '31;31m': pre = ''; break; + case '33m': + case '33;33m': pre = ''; break; + case '32m': + case '0;32m': pre = ''; break; + case '90m': pre = ''; break; + case 'K': + case '0K': + case '1K': + case '2K': break; + } + + if (pre !== "") { + styles.push(pre); + } + + output += part + pre + post; + } + + var part = s.substring(current, s.length); + output += part; + return output; + } + }; +})(); diff --git a/cmd/droned/drone.go b/cmd/droned/drone.go index 27f05f7d8..e25a6e2a6 100644 --- a/cmd/droned/drone.go +++ b/cmd/droned/drone.go @@ -111,6 +111,7 @@ func setupDatabase() { func setupStatic() { box := rice.MustFindBox("assets") http.Handle("/css/", http.FileServer(box.HTTPBox())) + http.Handle("/js/", http.FileServer(box.HTTPBox())) // we need to intercept all attempts to serve images // so that we can add a cache-control settings diff --git a/pkg/template/pages/base.html b/pkg/template/pages/base.html index 62bf5e8cb..687f17970 100644 --- a/pkg/template/pages/base.html +++ b/pkg/template/pages/base.html @@ -13,13 +13,14 @@ - + + @@ -59,4 +60,4 @@ {{ template "script" . }} - \ No newline at end of file + diff --git a/pkg/template/pages/repo_commit.html b/pkg/template/pages/repo_commit.html index da32581e0..022020eac 100644 --- a/pkg/template/pages/repo_commit.html +++ b/pkg/template/pages/repo_commit.html @@ -52,120 +52,36 @@ {{ define "script" }} - - -{{ end }} \ No newline at end of file +{{ end }} diff --git a/pkg/template/template.go b/pkg/template/template.go index 61ecbe11a..a965bdbfc 100644 --- a/pkg/template/template.go +++ b/pkg/template/template.go @@ -4,6 +4,9 @@ import ( "errors" "html/template" "io" + "crypto/md5" + "strings" + "fmt" "github.com/GeertJohan/go.rice" ) @@ -87,6 +90,17 @@ func init() { panic(err) } + assets := rice.MustFindBox("../../cmd/droned/assets") + mainjs, err := assets.String("js/main.js") + if err != nil { + panic(err) + } + + h := md5.New() + io.WriteString(h, mainjs) + jshash := fmt.Sprintf("%x", h.Sum(nil)) + base = strings.Replace(base, "main.js", "main.js?h=" + jshash, 1) + // extract the base form template as a string form, err := box.String("form.html") if err != nil {