diff --git a/assets/scripts/app/transforms/object.coffee b/assets/scripts/app/transforms/object.coffee
index 1b7d6e55..13fba95d 100644
--- a/assets/scripts/app/transforms/object.coffee
+++ b/assets/scripts/app/transforms/object.coffee
@@ -1,6 +1,8 @@
-Travis.ObjectTransform = DS.Transform.extend
+Transform = DS.Transform.extend
deserialize: (serialized) ->
serialized
serialize: (deserialized) ->
deserialized
+
+Travis.ObjectTransform = Transform
diff --git a/assets/scripts/app/utils/lines-selector.coffee b/assets/scripts/app/utils/lines-selector.coffee
index 1f81002c..a70008ad 100644
--- a/assets/scripts/app/utils/lines-selector.coffee
+++ b/assets/scripts/app/utils/lines-selector.coffee
@@ -1,4 +1,4 @@
-class Travis.LinesSelector
+class LinesSelector
Location:
getHash: ->
window.location.hash
@@ -71,3 +71,5 @@ class Travis.LinesSelector
first = match[1]
last = match[3] || match[1]
{first: first, last: last}
+
+Travis.LinesSelector = LinesSelector
diff --git a/assets/scripts/app/utils/log-chunks.coffee b/assets/scripts/app/utils/log-chunks.coffee
index 9570ad4a..3689ba50 100644
--- a/assets/scripts/app/utils/log-chunks.coffee
+++ b/assets/scripts/app/utils/log-chunks.coffee
@@ -1,4 +1,4 @@
-Travis.LogChunks = Em.ArrayProxy.extend
+LogChunks = Ember.ArrayProxy.extend
timeout: 30000
init: ->
@@ -80,3 +80,5 @@ Travis.LogChunks = Em.ArrayProxy.extend
Ember.run.once this, ->
@tryFinalizing()
@resetTimeout()
+
+Travis.LogChunks = LogChunks
diff --git a/assets/scripts/app/utils/log-folder.coffee b/assets/scripts/app/utils/log-folder.coffee
index 1d1bb1be..22b7e86e 100644
--- a/assets/scripts/app/utils/log-folder.coffee
+++ b/assets/scripts/app/utils/log-folder.coffee
@@ -1,4 +1,4 @@
-class Travis.LogFolder
+class LogFolder
constructor: (@element) ->
@element.on 'click', '.fold', (event) =>
folder = @getFolderFromLine $(event.target)
@@ -22,3 +22,5 @@ class Travis.LogFolder
getFolderFromLine: (line) ->
line.parent('.fold')
+
+ Travis.LogFolder = LogFolder
diff --git a/assets/scripts/app/vendor/log.js b/assets/scripts/app/vendor/log.js
new file mode 100644
index 00000000..496e44d1
--- /dev/null
+++ b/assets/scripts/app/vendor/log.js
@@ -0,0 +1,1075 @@
+var Log = function() {
+ this.autoCloseFold = true;
+ this.listeners = [];
+ this.renderer = new Log.Renderer;
+ this.children = new Log.Nodes(this);
+ this.parts = {};
+ this.folds = new Log.Folds(this);
+ this.times = new Log.Times(this);
+ return this;
+};
+
+Log.extend = function(one, other) {
+ var name;
+ for (name in other) {
+ one[name] = other[name];
+ }
+ return one;
+};
+
+Log.extend(Log, {
+ DEBUG: true,
+ SLICE: 500,
+ TIMEOUT: 25,
+ FOLD: /fold:(start|end):([\w_\-\.]+)/,
+ TIME: /time:(start|end):([\w_\-\.]+):?([\w_\-\.\=\,]*)/,
+ create: function(options) {
+ var listener, log, _i, _len, _ref;
+ options || (options = {});
+ log = new Log();
+ if (options.limit) {
+ log.listeners.push(log.limit = new Log.Limit(options.limit));
+ }
+ _ref = options.listeners || [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ listener = _ref[_i];
+ log.listeners.push(listener);
+ }
+ return log;
+ }
+});
+
+var newLineAtTheEndRegexp, newLineRegexp, rRegexp, removeCarriageReturns;
+
+Log.Node = function(id, num) {
+ this.id = id;
+ this.num = num;
+ this.key = Log.Node.key(this.id);
+ this.children = new Log.Nodes(this);
+ return this;
+};
+
+Log.extend(Log.Node, {
+ key: function(id) {
+ if (id) {
+ return id.split('-').map(function(i) {
+ return '000000'.concat(i).slice(-6);
+ }).join('');
+ }
+ }
+});
+
+Log.extend(Log.Node.prototype, {
+ addChild: function(node) {
+ return this.children.add(node);
+ },
+ remove: function() {
+ this.log.remove(this.element);
+ return this.parent.children.remove(this);
+ }
+});
+
+Object.defineProperty(Log.Node.prototype, 'log', {
+ get: function() {
+ var _ref;
+ return this._log || (this._log = ((_ref = this.parent) != null ? _ref.log : void 0) || this.parent);
+ }
+});
+
+Object.defineProperty(Log.Node.prototype, 'firstChild', {
+ get: function() {
+ return this.children.first;
+ }
+});
+
+Object.defineProperty(Log.Node.prototype, 'lastChild', {
+ get: function() {
+ return this.children.last;
+ }
+});
+
+Log.Nodes = function(parent) {
+ if (parent) {
+ this.parent = parent;
+ }
+ this.items = [];
+ this.index = {};
+ return this;
+};
+
+Log.extend(Log.Nodes.prototype, {
+ add: function(item) {
+ var ix, next, prev, _ref, _ref1;
+ ix = this.position(item) || 0;
+ this.items.splice(ix, 0, item);
+ if (this.parent) {
+ item.parent = this.parent;
+ }
+ prev = function(item) {
+ while (item && !item.children.last) {
+ item = item.prev;
+ }
+ return item != null ? item.children.last : void 0;
+ };
+ next = function(item) {
+ while (item && !item.children.first) {
+ item = item.next;
+ }
+ return item != null ? item.children.first : void 0;
+ };
+ if (item.prev = this.items[ix - 1] || prev((_ref = this.parent) != null ? _ref.prev : void 0)) {
+ item.prev.next = item;
+ }
+ if (item.next = this.items[ix + 1] || next((_ref1 = this.parent) != null ? _ref1.next : void 0)) {
+ item.next.prev = item;
+ }
+ return item;
+ },
+ remove: function(item) {
+ this.items.splice(this.items.indexOf(item), 1);
+ if (item.next) {
+ item.next.prev = item.prev;
+ }
+ if (item.prev) {
+ item.prev.next = item.next;
+ }
+ if (this.items.length === 0) {
+ return this.parent.remove();
+ }
+ },
+ position: function(item) {
+ var ix, _i, _ref;
+ for (ix = _i = _ref = this.items.length - 1; _i >= 0; ix = _i += -1) {
+ if (this.items[ix].key < item.key) {
+ return ix + 1;
+ }
+ }
+ },
+ indexOf: function() {
+ return this.items.indexOf.apply(this.items, arguments);
+ },
+ slice: function() {
+ return this.items.slice.apply(this.items, arguments);
+ },
+ each: function(func) {
+ return this.items.slice().forEach(func);
+ },
+ map: function(func) {
+ return this.items.map(func);
+ }
+});
+
+Object.defineProperty(Log.Nodes.prototype, 'first', {
+ get: function() {
+ return this.items[0];
+ }
+});
+
+Object.defineProperty(Log.Nodes.prototype, 'last', {
+ get: function() {
+ return this.items[this.length - 1];
+ }
+});
+
+Object.defineProperty(Log.Nodes.prototype, 'length', {
+ get: function() {
+ return this.items.length;
+ }
+});
+
+Log.Part = function(id, num, string) {
+ Log.Node.apply(this, arguments);
+ this.string = string || '';
+ this.string = this.string.replace(/\033\[1000D/gm, '\r');
+ this.string = this.string.replace(/\r+\n/gm, '\n');
+ this.strings = this.string.split(/^/gm) || [];
+ this.slices = ((function() {
+ var _results;
+ _results = [];
+ while (this.strings.length > 0) {
+ _results.push(this.strings.splice(0, Log.SLICE));
+ }
+ return _results;
+ }).call(this));
+ return this;
+};
+
+Log.extend(Log.Part, {
+ create: function(log, num, string) {
+ var part;
+ part = new Log.Part(num.toString(), num, string);
+ log.addChild(part);
+ return part.process(0, -1);
+ }
+});
+
+Log.Part.prototype = Log.extend(new Log.Node, {
+ remove: function() {},
+ process: function(slice, num) {
+ var node, span, spans, string, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3, _ref4,
+ _this = this;
+ _ref = this.slices[slice] || [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ string = _ref[_i];
+ if ((_ref1 = this.log.limit) != null ? _ref1.limited : void 0) {
+ return;
+ }
+ spans = [];
+ _ref2 = Log.Deansi.apply(string);
+ for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
+ node = _ref2[_j];
+ span = Log.Span.create(this, "" + this.id + "-" + (num += 1), num, node.text, node["class"]);
+ span.render();
+ spans.push(span);
+ }
+ if ((_ref3 = spans[0]) != null ? (_ref4 = _ref3.line) != null ? _ref4.cr : void 0 : void 0) {
+ spans[0].line.clear();
+ }
+ }
+ if (!(slice >= this.slices.length - 1)) {
+ return setTimeout((function() {
+ return _this.process(slice + 1, num);
+ }), Log.TIMEOUT);
+ }
+ }
+});
+
+newLineAtTheEndRegexp = new RegExp("\n$");
+
+newLineRegexp = new RegExp("\n");
+
+rRegexp = new RegExp("\r");
+
+removeCarriageReturns = function(string) {
+ var index;
+ index = string.lastIndexOf("\r");
+ if (index === -1) {
+ return string;
+ }
+ return string.substr(index + 1);
+};
+
+Log.Span = function(id, num, text, classes) {
+ var fold, time, _ref;
+ Log.Node.apply(this, arguments);
+ if (fold = text.match(Log.FOLD)) {
+ this.fold = true;
+ this.event = fold[1];
+ this.text = this.name = fold[2];
+ } else if (time = text.match(Log.TIME)) {
+ this.time = true;
+ this.event = time[1];
+ this.name = time[2];
+ this.stats = time[3];
+ } else {
+ this.text = text;
+ this.text = removeCarriageReturns(this.text);
+ this.text = this.text.replace(newLineAtTheEndRegexp, '');
+ this.nl = !!((_ref = text[text.length - 1]) != null ? _ref.match(newLineRegexp) : void 0);
+ this.cr = !!text.match(rRegexp);
+ this["class"] = this.cr && ['clears'] || classes;
+ }
+ return this;
+};
+
+Log.extend(Log.Span, {
+ create: function(parent, id, num, text, classes) {
+ var span;
+ span = new Log.Span(id, num, text, classes);
+ parent.addChild(span);
+ return span;
+ },
+ render: function(parent, id, num, text, classes) {
+ var span;
+ span = this.create(parent, id, num, text, classes);
+ return span.render();
+ }
+});
+
+Log.Span.prototype = Log.extend(new Log.Node, {
+ render: function() {
+ var tail;
+ if (this.time && this.event === 'end' && this.prev) {
+ if (Log.DEBUG) {
+ console.log("S.0 insert " + this.id + " after prev " + this.prev.id);
+ }
+ this.nl = this.prev.nl;
+ this.log.insert(this.data, {
+ after: this.prev.element
+ });
+ this.line = this.prev.line;
+ } else if (!this.fold && this.prev && !this.prev.fold && !this.prev.nl) {
+ if (Log.DEBUG) {
+ console.log("S.1 insert " + this.id + " after prev " + this.prev.id);
+ }
+ this.log.insert(this.data, {
+ after: this.prev.element
+ });
+ this.line = this.prev.line;
+ } else if (!this.fold && this.next && !this.next.fold && !this.next.time) {
+ if (Log.DEBUG) {
+ console.log("S.2 insert " + this.id + " before next " + this.next.id);
+ }
+ this.log.insert(this.data, {
+ before: this.next.element
+ });
+ this.line = this.next.line;
+ } else {
+ this.line = Log.Line.create(this.log, [this]);
+ this.line.render();
+ }
+ if (this.nl && (tail = this.tail).length > 0) {
+ this.split(tail);
+ }
+ if (this.time) {
+ return this.log.times.add(this);
+ }
+ },
+ remove: function() {
+ Log.Node.prototype.remove.apply(this);
+ if (this.line) {
+ return this.line.remove(this);
+ }
+ },
+ split: function(spans) {
+ var line, span, _i, _len;
+ if (Log.DEBUG) {
+ console.log("S.4 split [" + (spans.map(function(span) {
+ return span.id;
+ }).join(', ')) + "]");
+ }
+ for (_i = 0, _len = spans.length; _i < _len; _i++) {
+ span = spans[_i];
+ this.log.remove(span.element);
+ }
+ line = Log.Line.create(this.log, spans);
+ line.render();
+ if (line.cr) {
+ return line.clear();
+ }
+ },
+ clear: function() {
+ if (this.prev && this.isSibling(this.prev) && this.isSequence(this.prev)) {
+ this.prev.clear();
+ return this.prev.remove();
+ }
+ },
+ isSequence: function(other) {
+ return this.parent.num - other.parent.num === this.log.children.indexOf(this.parent) - this.log.children.indexOf(other.parent);
+ },
+ isSibling: function(other) {
+ var _ref, _ref1;
+ return ((_ref = this.element) != null ? _ref.parentNode : void 0) === ((_ref1 = other.element) != null ? _ref1.parentNode : void 0);
+ },
+ siblings: function(type) {
+ var siblings, span;
+ siblings = [];
+ while ((span = (span || this)[type]) && this.isSibling(span)) {
+ siblings.push(span);
+ }
+ return siblings;
+ }
+});
+
+Object.defineProperty(Log.Span.prototype, 'data', {
+ get: function() {
+ return {
+ id: this.id,
+ type: 'span',
+ text: this.text,
+ "class": this["class"],
+ time: this.time
+ };
+ }
+});
+
+Object.defineProperty(Log.Span.prototype, 'line', {
+ get: function() {
+ return this._line;
+ },
+ set: function(line) {
+ if (this.line) {
+ this.line.remove(this);
+ }
+ this._line = line;
+ if (this.line) {
+ return this.line.add(this);
+ }
+ }
+});
+
+Object.defineProperty(Log.Span.prototype, 'element', {
+ get: function() {
+ return document.getElementById(this.id);
+ }
+});
+
+Object.defineProperty(Log.Span.prototype, 'head', {
+ get: function() {
+ return this.siblings('prev').reverse();
+ }
+});
+
+Object.defineProperty(Log.Span.prototype, 'tail', {
+ get: function() {
+ return this.siblings('next');
+ }
+});
+
+Log.Line = function(log) {
+ this.log = log;
+ this.spans = [];
+ return this;
+};
+
+Log.extend(Log.Line, {
+ create: function(log, spans) {
+ var line, span, _i, _len;
+ if ((span = spans[0]) && span.fold) {
+ line = new Log.Fold(log, span.event, span.name);
+ } else {
+ line = new Log.Line(log);
+ }
+ for (_i = 0, _len = spans.length; _i < _len; _i++) {
+ span = spans[_i];
+ span.line = line;
+ }
+ return line;
+ }
+});
+
+Log.extend(Log.Line.prototype, {
+ add: function(span) {
+ var ix;
+ if (span.cr) {
+ this.cr = true;
+ }
+ if (this.spans.indexOf(span) > -1) {
+
+ } else if ((ix = this.spans.indexOf(span.prev)) > -1) {
+ return this.spans.splice(ix + 1, 0, span);
+ } else if ((ix = this.spans.indexOf(span.next)) > -1) {
+ return this.spans.splice(ix, 0, span);
+ } else {
+ return this.spans.push(span);
+ }
+ },
+ remove: function(span) {
+ var ix;
+ if ((ix = this.spans.indexOf(span)) > -1) {
+ return this.spans.splice(ix, 1);
+ }
+ },
+ render: function() {
+ var fold;
+ if ((fold = this.prev) && fold.event === 'start' && fold.active) {
+ if (this.next && !this.next.fold) {
+ if (Log.DEBUG) {
+ console.log("L.0 insert " + this.id + " before next " + this.next.id);
+ }
+ return this.element = this.log.insert(this.data, {
+ before: this.next.element
+ });
+ } else {
+ if (Log.DEBUG) {
+ console.log("L.0 insert " + this.id + " into fold " + fold.id);
+ }
+ fold = this.log.folds.folds[fold.name].fold;
+ return this.element = this.log.insert(this.data, {
+ into: fold
+ });
+ }
+ } else if (this.prev) {
+ if (Log.DEBUG) {
+ console.log("L.1 insert " + this.spans[0].id + " after prev " + this.prev.id);
+ }
+ return this.element = this.log.insert(this.data, {
+ after: this.prev.element
+ });
+ } else if (this.next) {
+ if (Log.DEBUG) {
+ console.log("L.2 insert " + this.spans[0].id + " before next " + this.next.id);
+ }
+ return this.element = this.log.insert(this.data, {
+ before: this.next.element
+ });
+ } else {
+ if (Log.DEBUG) {
+ console.log("L.3 insert " + this.spans[0].id + " into #log");
+ }
+ return this.element = this.log.insert(this.data);
+ }
+ },
+ clear: function() {
+ var cr, _i, _len, _ref, _results;
+ _ref = this.crs;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ cr = _ref[_i];
+ _results.push(cr.clear());
+ }
+ return _results;
+ }
+});
+
+Object.defineProperty(Log.Line.prototype, 'id', {
+ get: function() {
+ var _ref;
+ return (_ref = this.spans[0]) != null ? _ref.id : void 0;
+ }
+});
+
+Object.defineProperty(Log.Line.prototype, 'data', {
+ get: function() {
+ return {
+ type: 'paragraph',
+ nodes: this.nodes
+ };
+ }
+});
+
+Object.defineProperty(Log.Line.prototype, 'nodes', {
+ get: function() {
+ return this.spans.map(function(span) {
+ return span.data;
+ });
+ }
+});
+
+Object.defineProperty(Log.Line.prototype, 'prev', {
+ get: function() {
+ var _ref;
+ return (_ref = this.spans[0].prev) != null ? _ref.line : void 0;
+ }
+});
+
+Object.defineProperty(Log.Line.prototype, 'next', {
+ get: function() {
+ var _ref;
+ return (_ref = this.spans[this.spans.length - 1].next) != null ? _ref.line : void 0;
+ }
+});
+
+Object.defineProperty(Log.Line.prototype, 'crs', {
+ get: function() {
+ return this.spans.filter(function(span) {
+ return span.cr;
+ });
+ }
+});
+
+Log.Fold = function(log, event, name) {
+ Log.Line.apply(this, arguments);
+ this.fold = true;
+ this.event = event;
+ this.name = name;
+ return this;
+};
+
+Log.Fold.prototype = Log.extend(new Log.Line, {
+ render: function() {
+ var element, _ref;
+ if (this.prev && this.prev.element) {
+ if (Log.DEBUG) {
+ console.log("F.1 insert " + this.id + " after prev " + this.prev.id);
+ }
+ element = this.prev.element;
+ this.element = this.log.insert(this.data, {
+ after: element
+ });
+ } else if (this.next) {
+ if (Log.DEBUG) {
+ console.log("F.2 insert " + this.id + " before next " + this.next.id);
+ }
+ element = this.next.element || this.next.element.parentNode;
+ this.element = this.log.insert(this.data, {
+ before: element
+ });
+ } else {
+ if (Log.DEBUG) {
+ console.log("F.3 insert " + this.id);
+ }
+ this.element = this.log.insert(this.data);
+ }
+ if (this.span.next && ((_ref = this.span.prev) != null ? _ref.isSibling(this.span.next) : void 0)) {
+ this.span.prev.split([this.span.next].concat(this.span.next.tail));
+ }
+ return this.active = this.log.folds.add(this.data);
+ }
+});
+
+Object.defineProperty(Log.Fold.prototype, 'id', {
+ get: function() {
+ return "fold-" + this.event + "-" + this.name;
+ }
+});
+
+Object.defineProperty(Log.Fold.prototype, 'span', {
+ get: function() {
+ return this.spans[0];
+ }
+});
+
+Object.defineProperty(Log.Fold.prototype, 'data', {
+ get: function() {
+ return {
+ type: 'fold',
+ id: this.id,
+ event: this.event,
+ name: this.name
+ };
+ }
+});
+
+Log.prototype = Log.extend(new Log.Node, {
+ set: function(num, string) {
+ if (this.parts[num]) {
+ return console.log("part " + num + " exists");
+ } else {
+ this.parts[num] = true;
+ return Log.Part.create(this, num, string);
+ }
+ },
+ insert: function(data, pos) {
+ this.trigger('insert', data, pos);
+ return this.renderer.insert(data, pos);
+ },
+ remove: function(node) {
+ this.trigger('remove', node);
+ return this.renderer.remove(node);
+ },
+ hide: function(node) {
+ this.trigger('hide', node);
+ return this.renderer.hide(node);
+ },
+ trigger: function() {
+ var args, ix, listener, _i, _len, _ref, _results;
+ args = [this].concat(Array.prototype.slice.apply(arguments));
+ _ref = this.listeners;
+ _results = [];
+ for (ix = _i = 0, _len = _ref.length; _i < _len; ix = ++_i) {
+ listener = _ref[ix];
+ _results.push(listener.notify.apply(listener, args));
+ }
+ return _results;
+ }
+});
+
+Log.Listener = function() {};
+
+Log.extend(Log.Listener.prototype, {
+ notify: function(log, event) {
+ if (this[event]) {
+ return this[event].apply(this, [log].concat(Array.prototype.slice.call(arguments, 2)));
+ }
+ }
+});
+
+Log.Folds = function(log) {
+ this.log = log;
+ this.folds = {};
+ return this;
+};
+
+Log.extend(Log.Folds.prototype, {
+ add: function(data) {
+ var fold, _base, _name;
+ fold = (_base = this.folds)[_name = data.name] || (_base[_name] = new Log.Folds.Fold);
+ fold.receive(data, {
+ autoCloseFold: this.log.autoCloseFold
+ });
+ return fold.active;
+ }
+});
+
+Log.Folds.Fold = function() {
+ return this;
+};
+
+Log.extend(Log.Folds.Fold.prototype, {
+ receive: function(data, options) {
+ this[data.event] = data.id;
+ if (this.start && this.end && !this.active) {
+ return this.activate(options);
+ }
+ },
+ activate: function(options) {
+ var fragment, nextSibling, node, parentNode, toRemove, _i, _len, _ref;
+ options || (options = {});
+ if (Log.DEBUG) {
+ console.log("F.n - activate " + this.start);
+ }
+ toRemove = this.fold.parentNode;
+ parentNode = toRemove.parentNode;
+ nextSibling = toRemove.nextSibling;
+ parentNode.removeChild(toRemove);
+ fragment = document.createDocumentFragment();
+ _ref = this.nodes;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ node = _ref[_i];
+ fragment.appendChild(node);
+ }
+ this.fold.appendChild(fragment);
+ parentNode.insertBefore(toRemove, nextSibling);
+ this.fold.setAttribute('class', this.classes(options['autoCloseFold']));
+ return this.active = true;
+ },
+ classes: function(autoCloseFold) {
+ var classes;
+ classes = this.fold.getAttribute('class').split(' ');
+ classes.push('fold');
+ if (!autoCloseFold) {
+ classes.push('open');
+ }
+ if (this.fold.childNodes.length > 2) {
+ classes.push('active');
+ }
+ return classes.join(' ');
+ }
+});
+
+Object.defineProperty(Log.Folds.Fold.prototype, 'fold', {
+ get: function() {
+ return this._fold || (this._fold = document.getElementById(this.start));
+ }
+});
+
+Object.defineProperty(Log.Folds.Fold.prototype, 'nodes', {
+ get: function() {
+ var node, nodes;
+ node = this.fold;
+ nodes = [];
+ while ((node = node.nextSibling) && node.id !== this.end) {
+ nodes.push(node);
+ }
+ return nodes;
+ }
+});
+
+Log.Times = function(log) {
+ this.log = log;
+ this.times = {};
+ return this;
+};
+
+Log.extend(Log.Times.prototype, {
+ add: function(node) {
+ var time, _base, _name;
+ time = (_base = this.times)[_name = node.name] || (_base[_name] = new Log.Times.Time);
+ return time.receive(node);
+ },
+ duration: function(name) {
+ if (this.times[name]) {
+ return this.times[name].duration;
+ }
+ }
+});
+
+Log.Times.Time = function() {
+ return this;
+};
+
+Log.extend(Log.Times.Time.prototype, {
+ receive: function(node) {
+ this[node.event] = node;
+ if (Log.DEBUG) {
+ console.log("T.0 - " + node.event + " " + node.name);
+ }
+ if (this.start && this.end) {
+ return this.finish();
+ }
+ },
+ finish: function() {
+ var element;
+ if (Log.DEBUG) {
+ console.log("T.1 - finish " + this.start.name);
+ }
+ element = document.getElementById(this.start.id);
+ if (element) {
+ return this.update(element);
+ }
+ },
+ update: function(element) {
+ element.setAttribute('class', 'duration');
+ element.setAttribute('title', "This command finished after " + this.duration + " seconds.");
+ return element.lastChild.nodeValue = "" + this.duration + "s";
+ }
+});
+
+Object.defineProperty(Log.Times.Time.prototype, 'duration', {
+ get: function() {
+ var duration;
+ duration = this.stats.duration / 1000 / 1000 / 1000;
+ return duration.toFixed(2);
+ }
+});
+
+Object.defineProperty(Log.Times.Time.prototype, 'stats', {
+ get: function() {
+ var stat, stats, _i, _len, _ref;
+ if (!(this.end && this.end.stats)) {
+ return {};
+ }
+ stats = {};
+ _ref = this.end.stats.split(',');
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ stat = _ref[_i];
+ stat = stat.split('=');
+ stats[stat[0]] = stat[1];
+ }
+ return stats;
+ }
+});
+
+Log.Deansi = {
+ CLEAR_ANSI: /(?:\033)(?:\[0?c|\[[0356]n|\[7[lh]|\[\?25[lh]|\(B|H|\[(?:\d+(;\d+){,2})?G|\[(?:[12])?[JK]|[DM]|\[0K)/gm,
+ apply: function(string) {
+ var nodes,
+ _this = this;
+ if (!string) {
+ return [];
+ }
+ string = string.replace(this.CLEAR_ANSI, '');
+ nodes = ansiparse(string).map(function(part) {
+ return _this.node(part);
+ });
+ return nodes;
+ },
+ node: function(part) {
+ var classes, node;
+ node = {
+ type: 'span',
+ text: part.text
+ };
+ if (classes = this.classes(part)) {
+ node["class"] = classes.join(' ');
+ }
+ return node;
+ },
+ classes: function(part) {
+ var result;
+ result = [];
+ result = result.concat(this.colors(part));
+ if (result.length > 0) {
+ return result;
+ }
+ },
+ colors: function(part) {
+ var colors;
+ colors = [];
+ if (part.foreground) {
+ colors.push(part.foreground);
+ }
+ if (part.background) {
+ colors.push("bg-" + part.background);
+ }
+ if (part.bold) {
+ colors.push('bold');
+ }
+ if (part.italic) {
+ colors.push('italic');
+ }
+ if (part.underline) {
+ colors.push('underline');
+ }
+ return colors;
+ },
+ hidden: function(part) {
+ if (part.text.match(/\r/)) {
+ part.text = part.text.replace(/^.*\r/gm, '');
+ return true;
+ }
+ }
+};
+
+Log.Limit = function(max_lines) {
+ this.max_lines = max_lines || 1000;
+ return this;
+};
+
+Log.Limit.prototype = Log.extend(new Log.Listener, {
+ count: 0,
+ insert: function(log, node, pos) {
+ if (node.type === 'paragraph' && !node.hidden) {
+ return this.count += 1;
+ }
+ }
+});
+
+Object.defineProperty(Log.Limit.prototype, 'limited', {
+ get: function() {
+ return this.count >= this.max_lines;
+ }
+});
+
+Log.Renderer = function() {
+ this.frag = document.createDocumentFragment();
+ this.para = this.createParagraph();
+ this.span = this.createSpan();
+ this.text = document.createTextNode('');
+ this.fold = this.createFold();
+ return this;
+};
+
+Log.extend(Log.Renderer.prototype, {
+ insert: function(data, pos) {
+ var after, before, into, node;
+ node = this.render(data);
+ if (into = pos != null ? pos.into : void 0) {
+ if (typeof into === 'String') {
+ into = document.getElementById(pos != null ? pos.into : void 0);
+ }
+ if (pos != null ? pos.prepend : void 0) {
+ this.prependTo(node, into);
+ } else {
+ this.appendTo(node, into);
+ }
+ } else if (after = pos != null ? pos.after : void 0) {
+ if (typeof after === 'String') {
+ after = document.getElementById(pos);
+ }
+ this.insertAfter(node, after);
+ } else if (before = pos != null ? pos.before : void 0) {
+ if (typeof before === 'String') {
+ before = document.getElementById(pos != null ? pos.before : void 0);
+ }
+ this.insertBefore(node, before);
+ } else {
+ this.insertBefore(node);
+ }
+ return node;
+ },
+ hide: function(node) {
+ node.setAttribute('class', this.addClass(node.getAttribute('class'), 'hidden'));
+ return node;
+ },
+ remove: function(node) {
+ if (node) {
+ node.parentNode.removeChild(node);
+ }
+ return node;
+ },
+ render: function(data) {
+ var frag, node, type, _i, _len;
+ if (data instanceof Array) {
+ frag = this.frag.cloneNode(true);
+ for (_i = 0, _len = data.length; _i < _len; _i++) {
+ node = data[_i];
+ node = this.render(node);
+ if (node) {
+ frag.appendChild(node);
+ }
+ }
+ return frag;
+ } else {
+ data.type || (data.type = 'paragraph');
+ type = data.type[0].toUpperCase() + data.type.slice(1);
+ return this["render" + type](data);
+ }
+ },
+ renderParagraph: function(data) {
+ var node, para, type, _i, _len, _ref;
+ para = this.para.cloneNode(true);
+ if (data.id) {
+ para.setAttribute('id', data.id);
+ }
+ if (data.hidden) {
+ para.setAttribute('style', 'display: none;');
+ }
+ _ref = data.nodes || [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ node = _ref[_i];
+ type = node.type[0].toUpperCase() + node.type.slice(1);
+ node = this["render" + type](node);
+ para.appendChild(node);
+ }
+ return para;
+ },
+ renderFold: function(data) {
+ var fold;
+ fold = this.fold.cloneNode(true);
+ fold.setAttribute('id', data.id || ("fold-" + data.event + "-" + data.name));
+ fold.setAttribute('class', "fold-" + data.event);
+ if (data.event === 'start') {
+ fold.lastChild.lastChild.nodeValue = data.name;
+ } else {
+ fold.removeChild(fold.lastChild);
+ }
+ return fold;
+ },
+ renderSpan: function(data) {
+ var span;
+ span = this.span.cloneNode(true);
+ if (data.id) {
+ span.setAttribute('id', data.id);
+ }
+ if (data["class"]) {
+ span.setAttribute('class', data["class"]);
+ }
+ span.lastChild.nodeValue = data.text || '';
+ return span;
+ },
+ renderText: function(data) {
+ var text;
+ text = this.text.cloneNode(true);
+ text.nodeValue = data.text;
+ return text;
+ },
+ createParagraph: function() {
+ var para;
+ para = document.createElement('p');
+ para.appendChild(document.createElement('a'));
+ return para;
+ },
+ createFold: function() {
+ var fold;
+ fold = document.createElement('div');
+ fold.appendChild(this.createSpan());
+ fold.lastChild.setAttribute('class', 'fold-name');
+ return fold;
+ },
+ createSpan: function() {
+ var span;
+ span = document.createElement('span');
+ span.appendChild(document.createTextNode(' '));
+ return span;
+ },
+ insertBefore: function(node, other) {
+ var log;
+ if (other) {
+ return other.parentNode.insertBefore(node, other);
+ } else {
+ log = document.getElementById('log');
+ return log.insertBefore(node, log.firstChild);
+ }
+ },
+ insertAfter: function(node, other) {
+ if (other.nextSibling) {
+ return this.insertBefore(node, other.nextSibling);
+ } else {
+ return this.appendTo(node, other.parentNode);
+ }
+ },
+ prependTo: function(node, other) {
+ if (other.firstChild) {
+ return other.insertBefore(node, other.firstChild);
+ } else {
+ return appendTo(node, other);
+ }
+ },
+ appendTo: function(node, other) {
+ return other.appendChild(node);
+ },
+ addClass: function(classes, string) {
+ if (classes != null ? classes.indexOf(string) : void 0) {
+ return;
+ }
+ if (classes) {
+ return "" + classes + " " + string;
+ } else {
+ return string;
+ }
+ }
+});
+
+window.Log = Log;
diff --git a/assets/scripts/app/views/accounts-info.coffee b/assets/scripts/app/views/accounts-info.coffee
new file mode 100644
index 00000000..e2b8a067
--- /dev/null
+++ b/assets/scripts/app/views/accounts-info.coffee
@@ -0,0 +1,11 @@
+BasicView = Travis.BasicView
+
+View = BasicView.extend
+ templateName: 'profile/tabs/user'
+ userBinding: 'controller.user'
+
+ gravatarUrl: (->
+ "#{location.protocol}//www.gravatar.com/avatar/#{@get('user.gravatarId')}?s=200&d=mm"
+ ).property('user.gravatarId')
+
+AccountsInfoView = View
diff --git a/assets/scripts/app/views/accounts-list.coffee b/assets/scripts/app/views/accounts-list.coffee
new file mode 100644
index 00000000..6a0ee89e
--- /dev/null
+++ b/assets/scripts/app/views/accounts-list.coffee
@@ -0,0 +1,30 @@
+accountUrl = Travis.Urls.account
+
+View = Ember.CollectionView.extend
+ elementId: 'accounts'
+ accountBinding: 'content'
+ tagName: 'ul'
+
+ emptyView: Ember.View.extend
+ template: Ember.Handlebars.compile('
Loading
')
+
+ itemViewClass: Ember.View.extend
+ accountBinding: 'content'
+ typeBinding: 'content.type'
+ selectedBinding: 'account.selected'
+
+ classNames: ['account']
+ classNameBindings: ['type', 'selected']
+
+ name: (->
+ @get('content.name') || @get('content.login')
+ ).property('content.login', 'content.name')
+
+ urlAccount: (->
+ accountUrl(@get('account.login'))
+ ).property('account.login')
+
+ click: ->
+ @get('controller').transitionToRoute("account", @get('account.login'))
+
+AccountsListView = View
diff --git a/assets/scripts/app/views/accounts.coffee b/assets/scripts/app/views/accounts.coffee
deleted file mode 100644
index 5d479445..00000000
--- a/assets/scripts/app/views/accounts.coffee
+++ /dev/null
@@ -1,34 +0,0 @@
-@Travis.reopen
- ProfileAccountsView: Travis.BasicView.extend
- tabBinding: 'controller.tab'
- templateName: 'profile/accounts'
- classAccounts: (->
- 'active' if @get('tab') == 'accounts'
- ).property('tab')
-
- AccountsListView: Em.CollectionView.extend
- elementId: 'accounts'
- accountBinding: 'content'
- tagName: 'ul'
-
- emptyView: Ember.View.extend
- template: Ember.Handlebars.compile('Loading
')
-
- itemViewClass: Travis.BasicView.extend
- accountBinding: 'content'
- typeBinding: 'content.type'
- selectedBinding: 'account.selected'
-
- classNames: ['account']
- classNameBindings: ['type', 'selected']
-
- name: (->
- @get('content.name') || @get('content.login')
- ).property('content.login', 'content.name')
-
- urlAccount: (->
- Travis.Urls.account(@get('account.login'))
- ).property('account.login')
-
- click: ->
- @get('controller').transitionToRoute("account", @get('account.login'))
diff --git a/assets/scripts/app/views/annotation.coffee b/assets/scripts/app/views/annotation.coffee
index b3afe483..3ae89392 100644
--- a/assets/scripts/app/views/annotation.coffee
+++ b/assets/scripts/app/views/annotation.coffee
@@ -1,4 +1,6 @@
-Travis.reopen
- AnnotationsView: Travis.BasicView.extend
- templateName: 'annotations/list'
+BasicView = Travis.BasicView
+View = BasicView.extend
+ templateName: 'annotations/list'
+
+Travis.AnnotationsView = View
diff --git a/assets/scripts/app/views/annotations.coffee b/assets/scripts/app/views/annotations.coffee
index ac8376a0..5045bd7d 100644
--- a/assets/scripts/app/views/annotations.coffee
+++ b/assets/scripts/app/views/annotations.coffee
@@ -1,2 +1,3 @@
-Travis.reopen
- AnnotationsView: Travis.BasicView.extend
+View = Ember.View.extend()
+
+Travis.AnnotationsView = View
diff --git a/assets/scripts/app/views/application.coffee b/assets/scripts/app/views/application.coffee
index fd595908..f6581f30 100644
--- a/assets/scripts/app/views/application.coffee
+++ b/assets/scripts/app/views/application.coffee
@@ -1,17 +1,19 @@
-Travis.reopen
- ApplicationView: Travis.BasicView.extend
- classNames: ['application']
- click: (event) ->
- # TODO: this solves the case of closing menus and popups,
- # but I would like to rewrite it later, not sure how
- # yet, but this does not seem optimal
- targetAndParents = $(event.target).parents().andSelf()
- if ! ( targetAndParents.hasClass('open-popup') || targetAndParents.hasClass('popup') )
- @popupCloseAll()
+BasicView = Travis.BasicView
- # TODO: I needed to add second check to this conditional, because for some reason
- # event.stopPropagation() in menu() function in RepoShowToolsView does
- # not prevent calling following code
- if ! targetAndParents.hasClass('menu') && !targetAndParents.is('#tools > a')
- $('.menu').removeClass('display')
+View = BasicView.extend
+ classNames: ['application']
+ click: (event) ->
+ # TODO: this solves the case of closing menus and popups,
+ # but I would like to rewrite it later, not sure how
+ # yet, but this does not seem optimal
+ targetAndParents = $(event.target).parents().andSelf()
+ if ! ( targetAndParents.hasClass('open-popup') || targetAndParents.hasClass('popup') )
+ @popupCloseAll()
+ # TODO: I needed to add second check to this conditional, because for some reason
+ # event.stopPropagation() in menu() function in RepoShowToolsView does
+ # not prevent calling following code
+ if ! targetAndParents.hasClass('menu') && !targetAndParents.is('#tools > a')
+ $('.menu').removeClass('display')
+
+ApplicationView = View
diff --git a/assets/scripts/app/views/build.coffee b/assets/scripts/app/views/build.coffee
index 1ef531c9..1e062f38 100644
--- a/assets/scripts/app/views/build.coffee
+++ b/assets/scripts/app/views/build.coffee
@@ -1,12 +1,13 @@
colorForState = Travis.Helpers.colorForState
+BasicView = Travis.BasicView
-View = Travis.BasicView.extend
- classNameBindings: ['color', 'loading']
- buildBinding: 'controller.build'
- loadingBinding: 'controller.loading'
+View = BasicView.extend
+ classNameBindings: ['color', 'loading']
+ buildBinding: 'controller.build'
+ loadingBinding: 'controller.loading'
- color: (->
- colorForState(@get('build.state'))
- ).property('build.state')
+ color: (->
+ colorForState(@get('build.state'))
+ ).property('build.state')
Travis.BuildView = View
diff --git a/assets/scripts/app/views/dashboard.coffee b/assets/scripts/app/views/dashboard.coffee
index 3cc467af..09081a55 100644
--- a/assets/scripts/app/views/dashboard.coffee
+++ b/assets/scripts/app/views/dashboard.coffee
@@ -1,8 +1,6 @@
require 'views/basic'
-TravisView = Travis.BasicView
-
-View = TravisView.extend
+View = Ember.View.extend
layoutName: 'layouts/dashboard'
classNames: ['dashboard']
diff --git a/assets/scripts/app/views/first-sync.coffee b/assets/scripts/app/views/first-sync.coffee
index 88c61a0f..18934520 100644
--- a/assets/scripts/app/views/first-sync.coffee
+++ b/assets/scripts/app/views/first-sync.coffee
@@ -1,4 +1,6 @@
-View = Travis.BasicView.extend
+BasicView = Travis.BasicView
+
+View = BasicView.extend
layoutName: 'layouts/simple'
Travis.FirstSyncView = View
diff --git a/assets/scripts/app/views/flash-item.coffee b/assets/scripts/app/views/flash-item.coffee
new file mode 100644
index 00000000..7ae2bc66
--- /dev/null
+++ b/assets/scripts/app/views/flash-item.coffee
@@ -0,0 +1,15 @@
+BasicView = Travis.BasicView
+
+View = BasicView.extend
+ tagName: 'li'
+ classNameBindings: ['type']
+
+ type: (->
+ @get('flash.type') || 'broadcast'
+ ).property('flash.type')
+
+ actions:
+ close: ->
+ @get('controller').close(@get('flash'))
+
+FlashItemView = View
diff --git a/assets/scripts/app/views/flash.coffee b/assets/scripts/app/views/flash.coffee
index b1fe7451..7bad9961 100644
--- a/assets/scripts/app/views/flash.coffee
+++ b/assets/scripts/app/views/flash.coffee
@@ -1,17 +1,8 @@
-@Travis.reopen
- FlashView: Travis.BasicView.extend
- classNames: ['flash']
- tagName: 'ul'
- templateName: 'layouts/flash'
+BasicView = Travis.BasicView
- FlashItemView: Travis.BasicView.extend
- tagName: 'li'
- classNameBindings: ['type']
+View = BasicView.extend
+ classNames: ['flash']
+ tagName: 'ul'
+ templateName: 'layouts/flash'
- type: (->
- @get('flash.type') || 'broadcast'
- ).property('flash.type')
-
- actions:
- close: ->
- @get('controller').close(@get('flash'))
+Travis.FlashView = View
diff --git a/assets/scripts/app/views/hooks.coffee b/assets/scripts/app/views/hooks.coffee
new file mode 100644
index 00000000..bdca93a9
--- /dev/null
+++ b/assets/scripts/app/views/hooks.coffee
@@ -0,0 +1,12 @@
+BasicView = Travis.BasicView
+githubAdminUrl = Travis.Urls.githubAdmin
+
+View = BasicView.extend
+ templateName: 'profile/tabs/hooks'
+ userBinding: 'controller.user'
+
+ urlGithubAdmin: (->
+ githubAdminUrl(@get('hook.slug'))
+ ).property('hook.slug')
+
+HooksView = View
diff --git a/assets/scripts/app/views/jobs-item.coffee b/assets/scripts/app/views/jobs-item.coffee
index 77fff83d..e9ca8e8d 100644
--- a/assets/scripts/app/views/jobs-item.coffee
+++ b/assets/scripts/app/views/jobs-item.coffee
@@ -1,11 +1,14 @@
-View = Travis.BasicView.extend
+colorForState = Travis.Helpers.colorForState
+BasicView = Travis.BasicView
+
+View = BasicView.extend
tagName: 'tr'
classNameBindings: ['color']
repoBinding: 'context.repo'
jobBinding: 'context'
color: (->
- Travis.Helpers.colorForState(@get('job.state'))
+ colorForState(@get('job.state'))
).property('job.state')
Travis.JobsItemView = View
diff --git a/assets/scripts/app/views/left.coffee b/assets/scripts/app/views/left.coffee
deleted file mode 100644
index 02c9eba9..00000000
--- a/assets/scripts/app/views/left.coffee
+++ /dev/null
@@ -1,20 +0,0 @@
-@Travis.reopen
- ReposView: Travis.BasicView.extend
- templateName: 'repos/list'
- tabBinding: 'controller.tab'
-
- classRecent: (->
- 'active' if @get('tab') == 'recent'
- ).property('tab')
-
- classOwned: (->
- classes = []
- classes.push('active') if @get('tab') == 'owned'
- classes.push('display') if @get('controller.currentUser')
- classes.join(' ')
- ).property('tab', 'controller.currentUser')
-
- classSearch: (->
- 'active' if @get('tab') == 'search'
- ).property('tab')
-
diff --git a/assets/scripts/app/views/log.coffee b/assets/scripts/app/views/log.coffee
index c0241462..9b6a0a36 100644
--- a/assets/scripts/app/views/log.coffee
+++ b/assets/scripts/app/views/log.coffee
@@ -1,153 +1,35 @@
-require 'log'
-require 'utils/lines-selector'
-require 'utils/log-folder'
+BasicView = Travis.BasicView
Log.DEBUG = false
Log.LIMIT = 10000
-
config = ENV.config
-Travis.reopen
- LogView: Travis.BasicView.extend
- templateName: 'jobs/log'
- logBinding: 'job.log'
+View = BasicView.extend
+ templateName: 'jobs/log'
+ logBinding: 'job.log'
- didInsertElement: ->
- @setupLog()
+ didInsertElement: ->
+ @setupLog()
- logDidChange: (->
- @setupLog()
- ).observes('log')
+ logDidChange: (->
+ @setupLog()
+ ).observes('log')
- logWillChange: (->
- @teardownLog()
- ).observesBefore('log')
+ logWillChange: (->
+ @teardownLog()
+ ).observesBefore('log')
- willDestroyElement: ->
- @teardownLog()
+ willDestroyElement: ->
+ @teardownLog()
- teardownLog: ->
- job = @get('job')
- job.unsubscribe() if job
+ teardownLog: ->
+ job = @get('job')
+ job.unsubscribe() if job
- setupLog: ->
- job = @get('job')
- if job
- job.get('log').fetch()
- job.subscribe()
+ setupLog: ->
+ job = @get('job')
+ if job
+ job.get('log').fetch()
+ job.subscribe()
- PreView: Em.View.extend
- templateName: 'jobs/pre'
-
- logWillChange: (->
- console.log 'log view: log will change' if Log.DEBUG
- @teardownLog()
- ).observesBefore('log')
-
- didInsertElement: ->
- console.log 'log view: did insert' if Log.DEBUG
- @_super.apply this, arguments
- @createEngine()
-
- willDestroyElement: ->
- console.log 'log view: will destroy' if Log.DEBUG
- @teardownLog()
-
- versionDidChange: (->
- @rerender() if @get('_state') == 'inDOM'
- ).observes('log.version')
-
- logDidChange: (->
- console.log 'log view: log did change: rerender' if Log.DEBUG
-
- if @get('log')
- @createEngine()
- @rerender() if @get('_state') == 'inDOM'
- ).observes('log')
-
- teardownLog: ->
- if log = @get('log')
- parts = log.get('parts')
- parts.removeArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
- parts.destroy()
- log.notifyPropertyChange('parts')
- @lineSelector?.willDestroy()
-
- createEngine: ->
- if @get('log')
- console.log 'log view: create engine' if Log.DEBUG
- @scroll = new Log.Scroll beforeScroll: =>
- @unfoldHighlight()
- @engine = Log.create(limit: Log.LIMIT, listeners: [@scroll])
- @logFolder = new Travis.LogFolder(@$().find('#log'))
- @lineSelector = new Travis.LinesSelector(@$().find('#log'), @scroll, @logFolder)
- @observeParts()
-
- unfoldHighlight: ->
- @lineSelector.unfoldLines()
-
- observeParts: ->
- if log = @get('log')
- parts = log.get('parts')
- parts.addArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
- parts = parts.slice(0)
- @partsDidChange(parts, 0, null, parts.length)
-
- partsDidChange: (parts, start, _, added) ->
- console.log 'log view: parts did change' if Log.DEBUG
- for part, i in parts.slice(start, start + added)
- # console.log "limit in log view: #{@get('limited')}"
- break if @get('limited')
- @engine.set(part.number, part.content)
- @propertyDidChange('limited')
-
- limited: (->
- @engine?.limit?.limited
- ).property()
-
- plainTextLogUrl: (->
- if id = @get('log.job.id')
- url = Travis.Urls.plainTextLog(id)
- if config.pro
- url += "&access_token=#{@get('job.log.token')}"
- url
- ).property('job.log.id', 'job.log.token')
-
- actions:
- toTop: () ->
- $(window).scrollTop(0)
-
- toggleTailing: ->
- Travis.tailing.toggle()
- @engine.autoCloseFold = !Travis.tailing.isActive()
- event.preventDefault()
-
- noop: -> # TODO required?
-
-Log.Scroll = (options) ->
- options ||= {}
- @beforeScroll = options.beforeScroll
- this
-Log.Scroll.prototype = $.extend new Log.Listener,
- insert: (log, data, pos) ->
- @tryScroll() if @numbers
- true
-
- tryScroll: ->
- if element = $("#log p:visible.highlight:first")
- if @beforeScroll
- @beforeScroll()
- $('#main').scrollTop(0)
- $('html, body').scrollTop(element.offset()?.top - (window.innerHeight / 3)) # weird, html works in chrome, body in firefox
-
-# Log.Logger = ->
-# Log.Logger.prototype = $.extend new Log.Listener,
-# receive: (log, num, string) ->
-# @log("rcv #{num} #{JSON.stringify(string)}")
-# true
-# insert: (log, element, pos) ->
-# @log("ins #{element.id}, #{if pos.before then 'before' else 'after'}: #{pos.before || pos.after || '?'}, #{JSON.stringify(element)}")
-# remove: (log, element) ->
-# @log("rem #{element.id}")
-# log: (line) ->
-# console.log(line)
+Travis.LogView = View
diff --git a/assets/scripts/app/views/pre.coffee b/assets/scripts/app/views/pre.coffee
new file mode 100644
index 00000000..d7bb8d48
--- /dev/null
+++ b/assets/scripts/app/views/pre.coffee
@@ -0,0 +1,113 @@
+require 'utils/lines-selector'
+require 'utils/log-folder'
+
+config = ENV.config
+plainTextLogUrl = Travis.Urls.plainTextLog
+LogFolder = Travis.LogFolder
+LinesSelector = Travis.LinesSelector
+
+Log.Scroll = (options) ->
+ options ||= {}
+ @beforeScroll = options.beforeScroll
+ this
+Log.Scroll.prototype = $.extend new Log.Listener,
+ insert: (log, data, pos) ->
+ @tryScroll() if @numbers
+ true
+
+ tryScroll: ->
+ if element = $("#log p:visible.highlight:first")
+ if @beforeScroll
+ @beforeScroll()
+ $('#main').scrollTop(0)
+ $('html, body').scrollTop(element.offset()?.top - (window.innerHeight / 3)) # weird, html works in chrome, body in firefox
+
+View = Ember.View.extend
+ templateName: 'jobs/pre'
+
+ logWillChange: (->
+ console.log 'log view: log will change' if Log.DEBUG
+ @teardownLog()
+ ).observesBefore('log')
+
+ didInsertElement: ->
+ console.log 'log view: did insert' if Log.DEBUG
+ @_super.apply this, arguments
+ @createEngine()
+
+ willDestroyElement: ->
+ console.log 'log view: will destroy' if Log.DEBUG
+ @teardownLog()
+
+ versionDidChange: (->
+ @rerender() if @get('_state') == 'inDOM'
+ ).observes('log.version')
+
+ logDidChange: (->
+ console.log 'log view: log did change: rerender' if Log.DEBUG
+
+ if @get('log')
+ @createEngine()
+ @rerender() if @get('_state') == 'inDOM'
+ ).observes('log')
+
+ teardownLog: ->
+ if log = @get('log')
+ parts = log.get('parts')
+ parts.removeArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
+ parts.destroy()
+ log.notifyPropertyChange('parts')
+ @lineSelector?.willDestroy()
+
+ createEngine: ->
+ if @get('log')
+ console.log 'log view: create engine' if Log.DEBUG
+ @scroll = new Log.Scroll beforeScroll: =>
+ @unfoldHighlight()
+ @engine = Log.create(limit: Log.LIMIT, listeners: [@scroll])
+ @logFolder = new LogFolder(@$().find('#log'))
+ @lineSelector = new LinesSelector(@$().find('#log'), @scroll, @logFolder)
+ @observeParts()
+
+ unfoldHighlight: ->
+ @lineSelector.unfoldLines()
+
+ observeParts: ->
+ if log = @get('log')
+ parts = log.get('parts')
+ parts.addArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
+ parts = parts.slice(0)
+ @partsDidChange(parts, 0, null, parts.length)
+
+ partsDidChange: (parts, start, _, added) ->
+ console.log 'log view: parts did change' if Log.DEBUG
+ for part, i in parts.slice(start, start + added)
+ # console.log "limit in log view: #{@get('limited')}"
+ break if @get('limited')
+ @engine.set(part.number, part.content)
+ @propertyDidChange('limited')
+
+ limited: (->
+ @engine?.limit?.limited
+ ).property()
+
+ plainTextLogUrl: (->
+ if id = @get('log.job.id')
+ url = plainTextLogUrl(id)
+ if config.pro
+ url += "&access_token=#{@get('job.log.token')}"
+ url
+ ).property('job.log.id', 'job.log.token')
+
+ actions:
+ toTop: () ->
+ $(window).scrollTop(0)
+
+ toggleTailing: ->
+ Travis.tailing.toggle()
+ @engine.autoCloseFold = !Travis.tailing.isActive()
+ event.preventDefault()
+
+ noop: -> # TODO required?
+
+Travis.PreView = View
diff --git a/assets/scripts/app/views/profile-accounts.coffee b/assets/scripts/app/views/profile-accounts.coffee
new file mode 100644
index 00000000..c76b8e47
--- /dev/null
+++ b/assets/scripts/app/views/profile-accounts.coffee
@@ -0,0 +1,11 @@
+BasicView = Travis.BasicView
+
+View = BasicView.extend
+ tabBinding: 'controller.tab'
+ templateName: 'profile/accounts'
+ classAccounts: (->
+ 'active' if @get('tab') == 'accounts'
+ ).property('tab')
+
+
+ProfileAccountsView = View
diff --git a/assets/scripts/app/views/profile-tabs.coffee b/assets/scripts/app/views/profile-tabs.coffee
new file mode 100644
index 00000000..fda52920
--- /dev/null
+++ b/assets/scripts/app/views/profile-tabs.coffee
@@ -0,0 +1,22 @@
+BasicView = Travis.BasicView
+
+View = BasicView.extend
+ templateName: 'profile/tabs'
+ tabBinding: 'controller.tab'
+
+ activate: ->
+ @get('controller').activate(event.target.name)
+
+ classHooks: (->
+ 'active' if @get('tab') == 'hooks'
+ ).property('tab')
+
+ classUser: (->
+ 'active' if @get('tab') == 'user'
+ ).property('tab')
+
+ displayUser: (->
+ @get('controller.account.login') == @get('controller.user.login')
+ ).property('controller.account.login', 'controller.user.login')
+
+ProfileTabsView = View
diff --git a/assets/scripts/app/views/profile.coffee b/assets/scripts/app/views/profile.coffee
index dadf0518..cb59eb84 100644
--- a/assets/scripts/app/views/profile.coffee
+++ b/assets/scripts/app/views/profile.coffee
@@ -1,47 +1,15 @@
-Travis.reopen
- ProfileView: Travis.BasicView.extend
- templateName: 'profile/show'
- layoutName: 'layouts/profile'
- classNames: ['profile-view']
- accountBinding: 'controller.account'
- subscribedBinding: 'account.subscribed'
- educationBinding: 'account.education'
+BasicView = Travis.BasicView
- name: (->
- @get('account.name') || @get('account.login')
- ).property('account.name', 'account.login')
+View = BasicView.extend
+ templateName: 'profile/show'
+ layoutName: 'layouts/profile'
+ classNames: ['profile-view']
+ accountBinding: 'controller.account'
+ subscribedBinding: 'account.subscribed'
+ educationBinding: 'account.education'
- ProfileTabsView: Travis.BasicView.extend
- templateName: 'profile/tabs'
- tabBinding: 'controller.tab'
+ name: (->
+ @get('account.name') || @get('account.login')
+ ).property('account.name', 'account.login')
- activate: ->
- @get('controller').activate(event.target.name)
-
- classHooks: (->
- 'active' if @get('tab') == 'hooks'
- ).property('tab')
-
- classUser: (->
- 'active' if @get('tab') == 'user'
- ).property('tab')
-
- displayUser: (->
- @get('controller.account.login') == @get('controller.user.login')
- ).property('controller.account.login', 'controller.user.login')
-
- HooksView: Travis.BasicView.extend
- templateName: 'profile/tabs/hooks'
- userBinding: 'controller.user'
-
- urlGithubAdmin: (->
- Travis.Urls.githubAdmin(@get('hook.slug'))
- ).property('hook.slug')
-
- AccountsInfoView: Travis.BasicView.extend
- templateName: 'profile/tabs/user'
- userBinding: 'controller.user'
-
- gravatarUrl: (->
- "#{location.protocol}//www.gravatar.com/avatar/#{@get('user.gravatarId')}?s=200&d=mm"
- ).property('user.gravatarId')
+ProfileView = View
diff --git a/assets/scripts/app/views/repo.coffee b/assets/scripts/app/views/repo.coffee
index 9f8c309d..58ce2672 100644
--- a/assets/scripts/app/views/repo.coffee
+++ b/assets/scripts/app/views/repo.coffee
@@ -1,8 +1,9 @@
statusImage = Travis.Urls.statusImage
StatusImagesView = Travis.StatusImagesView
config = ENV.config
+BasicView = Travis.BasicView
-View = Travis.BasicView.extend
+View = BasicView.extend
reposBinding: 'controllers.repos'
repoBinding: 'controller.repo'
buildBinding: 'controller.build'
@@ -23,11 +24,11 @@ View = Travis.BasicView.extend
statusImages: () ->
@popupCloseAll()
view = StatusImagesView.create(toolsView: this)
- Travis.BasicView.currentPopupView = view
+ BasicView.currentPopupView = view
view.appendTo($('body'))
return false
- ReposEmptyView: Travis.BasicView.extend
+ ReposEmptyView: BasicView.extend
template: (->
if config.pro
'pro/repos/show/empty'
diff --git a/assets/scripts/app/views/repos-list.coffee b/assets/scripts/app/views/repos-list.coffee
index 4d38d6ba..a4e7c6ee 100644
--- a/assets/scripts/app/views/repos-list.coffee
+++ b/assets/scripts/app/views/repos-list.coffee
@@ -1,6 +1,6 @@
colorForState = Travis.Helpers.colorForState
-View = Em.CollectionView.extend
+View = Ember.CollectionView.extend
elementId: 'repos'
tagName: 'ul'
diff --git a/assets/scripts/app/views/signin.coffee b/assets/scripts/app/views/signin.coffee
index 3365fcfa..b6e975db 100644
--- a/assets/scripts/app/views/signin.coffee
+++ b/assets/scripts/app/views/signin.coffee
@@ -1,3 +1,6 @@
-@Travis.reopen
- SigninView: Travis.BasicView.extend
- templateName: 'auth/signin'
+BasicView = Travis.BasicView
+
+View = BasicView.extend
+ templateName: 'auth/signin'
+
+Travis.SigninView = View
diff --git a/assets/scripts/app/views/stats.coffee b/assets/scripts/app/views/stats.coffee
deleted file mode 100644
index 7043e232..00000000
--- a/assets/scripts/app/views/stats.coffee
+++ /dev/null
@@ -1,73 +0,0 @@
-@Travis.reopen
- StatsView: Travis.BasicView.extend
- templateName: 'stats/show'
- didInsertElement: ->
- # @renderChart(config) for name, config of @CHARTS
-
- renderChart: (config) ->
- chart = new Highcharts.Chart(config)
- @fetch config.source, (data) ->
- stats = (config.map(stats) for stats in data.stats)
- chart.series[0].setData(stats)
-
- fetch: (url, callback) ->
- $.ajax
- type: 'GET'
- url: url
- accepts: { json: 'application/vnd.travis-ci.2+json' }
- success: callback
-
- CHARTS:
- repos:
- source: '/api/stats/repos'
- total: 0
- map: (data) ->
- [Date.parse(data.date), @total += parseInt(data.count)]
- chart:
- renderTo: "repos_stats"
- title:
- text: "Total Projects/Repositories"
- xAxis:
- type: "datetime"
- dateTimeLabelFormats: # don't display the dummy year
- month: "%e. %b"
- year: "%b"
- yAxis:
- title:
- text: "Count"
- min: 0
- tooltip:
- formatter: ->
- Highcharts.dateFormat("%e. %b", @x) + ": " + @y + " repos"
- series: [
- name: "Repository Growth"
- data: []
- ]
-
- builds:
- source: '/api/stats/tests'
- map: (data) ->
- [Date.parse(data.date), parseInt(data.count)]
- chart:
- renderTo: "tests_stats"
- type: "column"
- title:
- text: "Build Count"
- subtitle:
- text: "last month"
- xAxis:
- type: "datetime"
- dateTimeLabelFormats: # don't display the dummy year
- month: "%e. %b"
- year: "%b"
- yAxis:
- title:
- text: "Count"
- min: 0
- tooltip:
- formatter: ->
- Highcharts.dateFormat("%e. %b", @x) + ": " + @y + " builds"
- series: [
- name: "Total Builds"
- data: []
- ]
diff --git a/assets/scripts/app/views/status-image-input.coffee b/assets/scripts/app/views/status-image-input.coffee
index dd8c4530..69b77117 100644
--- a/assets/scripts/app/views/status-image-input.coffee
+++ b/assets/scripts/app/views/status-image-input.coffee
@@ -1,3 +1,5 @@
-Travis.StatusImageInput = Em.TextArea.extend
+View = Ember.TextArea.extend
click: ->
@get('element').select()
+
+Travis.StatusImageInput = View
diff --git a/assets/scripts/app/views/status-images.coffee b/assets/scripts/app/views/status-images.coffee
index f3e733e5..f851ad21 100644
--- a/assets/scripts/app/views/status-images.coffee
+++ b/assets/scripts/app/views/status-images.coffee
@@ -1,4 +1,6 @@
-Travis.StatusImagesView = Em.View.extend
+format = Travis.StatusImageFormats.format
+
+View = Ember.View.extend
templateName: 'status_images'
elementId: 'status-images'
classNames: ['popup']
@@ -46,6 +48,7 @@ Travis.StatusImagesView = Em.View.extend
).observes('repo.branches', 'repo.branches.isLoaded', 'build.commit.branch')
statusString: (->
- Travis.StatusImageFormatter.format(@get('statusImageFormat'), @get('repo.slug'), @get('statusImageBranch.commit.branch'))
+ format(@get('statusImageFormat'), @get('repo.slug'), @get('statusImageBranch.commit.branch'))
).property('statusImageFormat', 'repo.slug', 'statusImageBranch.commit.branch')
+Travis.StatusImagesView = View
diff --git a/assets/scripts/app/views/top.coffee b/assets/scripts/app/views/top.coffee
index 7ddcb7ce..be1fcc39 100644
--- a/assets/scripts/app/views/top.coffee
+++ b/assets/scripts/app/views/top.coffee
@@ -1,26 +1,28 @@
-@Travis.reopen
- TopView: Travis.BasicView.extend
- tabBinding: 'controller.tab'
+BasicView = Travis.BasicView
- # hrm. how to parametrize bind-attr?
- classHome: (->
- 'active' if @get('tab') == 'home'
- ).property('tab')
+View = BasicView.extend
+ tabBinding: 'controller.tab'
- classStats: (->
- 'active' if @get('tab') == 'stats'
- ).property('tab')
+ # hrm. how to parametrize bind-attr?
+ classHome: (->
+ 'active' if @get('tab') == 'home'
+ ).property('tab')
- classProfile: (->
- classes = ['profile menu']
- classes.push('active') if @get('tab') == 'profile'
- classes.push(@get('controller.auth.state') || 'signed-out')
- classes.join(' ')
- ).property('tab', 'controller.auth.state')
+ classStats: (->
+ 'active' if @get('tab') == 'stats'
+ ).property('tab')
- showProfile: ->
- $('#top .profile ul').show()
+ classProfile: (->
+ classes = ['profile menu']
+ classes.push('active') if @get('tab') == 'profile'
+ classes.push(@get('controller.auth.state') || 'signed-out')
+ classes.join(' ')
+ ).property('tab', 'controller.auth.state')
- hideProfile: ->
- $('#top .profile ul').hide()
+ showProfile: ->
+ $('#top .profile ul').show()
+ hideProfile: ->
+ $('#top .profile ul').hide()
+
+Travis.TopView = View
diff --git a/assets/scripts/travis.coffee b/assets/scripts/travis.coffee
index c16a518f..dfcb1c21 100644
--- a/assets/scripts/travis.coffee
+++ b/assets/scripts/travis.coffee
@@ -182,8 +182,13 @@ require 'models/user'
require 'models/env-var'
require 'models/ssh-key'
+require 'vendor/log'
+
require 'views/basic'
-require 'views/accounts'
+require 'views/flash-item'
+require 'views/accounts-info'
+require 'views/hooks'
+require 'views/pre'
require 'views/annotation'
require 'views/application'
require 'views/build'
@@ -199,7 +204,7 @@ require 'views/repo-show-tools'
require 'views/repo-show-tabs'
require 'views/repo-actions'
require 'views/profile'
-require 'views/stats'
+require 'views/profile-tabs'
require 'views/signin'
require 'views/top'
require 'views/status-images'
diff --git a/assets/scripts/vendor/log.js b/assets/scripts/vendor/log.js
deleted file mode 100644
index 715e47b5..00000000
--- a/assets/scripts/vendor/log.js
+++ /dev/null
@@ -1,2 +0,0 @@
-minispade.register('log', "(function() {(function() {\n\n this.Log = function() {\n this.autoCloseFold = true;\n this.listeners = [];\n this.renderer = new Log.Renderer;\n this.children = new Log.Nodes(this);\n this.parts = {};\n this.folds = new Log.Folds(this);\n this.times = new Log.Times(this);\n return this;\n };\n\n Log.extend = function(one, other) {\n var name;\n for (name in other) {\n one[name] = other[name];\n }\n return one;\n };\n\n Log.extend(Log, {\n DEBUG: true,\n SLICE: 500,\n TIMEOUT: 25,\n FOLD: /fold:(start|end):([\\w_\\-\\.]+)/,\n TIME: /time:(start|end):([\\w_\\-\\.]+):?([\\w_\\-\\.\\=\\,]*)/,\n create: function(options) {\n var listener, log, _i, _len, _ref;\n options || (options = {});\n log = new Log();\n if (options.limit) {\n log.listeners.push(log.limit = new Log.Limit(options.limit));\n }\n _ref = options.listeners || [];\n for (_i = 0, _len = _ref.length; _i < _len; _i++) {\n listener = _ref[_i];\n log.listeners.push(listener);\n }\n return log;\n }\n });\nminispade.require('log/nodes');\n\n Log.prototype = Log.extend(new Log.Node, {\n set: function(num, string) {\n if (this.parts[num]) {\n return console.log(\"part \" + num + \" exists\");\n } else {\n this.parts[num] = true;\n return Log.Part.create(this, num, string);\n }\n },\n insert: function(data, pos) {\n this.trigger('insert', data, pos);\n return this.renderer.insert(data, pos);\n },\n remove: function(node) {\n this.trigger('remove', node);\n return this.renderer.remove(node);\n },\n hide: function(node) {\n this.trigger('hide', node);\n return this.renderer.hide(node);\n },\n trigger: function() {\n var args, ix, listener, _i, _len, _ref, _results;\n args = [this].concat(Array.prototype.slice.apply(arguments));\n _ref = this.listeners;\n _results = [];\n for (ix = _i = 0, _len = _ref.length; _i < _len; ix = ++_i) {\n listener = _ref[ix];\n _results.push(listener.notify.apply(listener, args));\n }\n return _results;\n }\n });\n\n Log.Listener = function() {};\n\n Log.extend(Log.Listener.prototype, {\n notify: function(log, event) {\n if (this[event]) {\n return this[event].apply(this, [log].concat(Array.prototype.slice.call(arguments, 2)));\n }\n }\n });\nminispade.require('log/folds');\nminispade.require('log/times');\nminispade.require('log/deansi');\nminispade.require('log/limit');\nminispade.require('log/renderer');\n\n}).call(this);\n\n})();\n//@ sourceURL=log");minispade.register('log/deansi', "(function() {(function() {\n\n Log.Deansi = {\n CLEAR_ANSI: /(?:\\033)(?:\\[0?c|\\[[0356]n|\\[7[lh]|\\[\\?25[lh]|\\(B|H|\\[(?:\\d+(;\\d+){,2})?G|\\[(?:[12])?[JK]|[DM]|\\[0K)/gm,\n apply: function(string) {\n var nodes,\n _this = this;\n if (!string) {\n return [];\n }\n string = string.replace(this.CLEAR_ANSI, '');\n nodes = ansiparse(string).map(function(part) {\n return _this.node(part);\n });\n return nodes;\n },\n node: function(part) {\n var classes, node;\n node = {\n type: 'span',\n text: part.text\n };\n if (classes = this.classes(part)) {\n node[\"class\"] = classes.join(' ');\n }\n return node;\n },\n classes: function(part) {\n var result;\n result = [];\n result = result.concat(this.colors(part));\n if (result.length > 0) {\n return result;\n }\n },\n colors: function(part) {\n var colors;\n colors = [];\n if (part.foreground) {\n colors.push(part.foreground);\n }\n if (part.background) {\n colors.push(\"bg-\" + part.background);\n }\n if (part.bold) {\n colors.push('bold');\n }\n if (part.italic) {\n colors.push('italic');\n }\n if (part.underline) {\n colors.push('underline');\n }\n return colors;\n },\n hidden: function(part) {\n if (part.text.match(/\\r/)) {\n part.text = part.text.replace(/^.*\\r/gm, '');\n return true;\n }\n }\n };\n\n}).call(this);\n\n})();\n//@ sourceURL=log/deansi");minispade.register('log/folds', "(function() {(function() {\n\n Log.Folds = function(log) {\n this.log = log;\n this.folds = {};\n return this;\n };\n\n Log.extend(Log.Folds.prototype, {\n add: function(data) {\n var fold, _base, _name;\n fold = (_base = this.folds)[_name = data.name] || (_base[_name] = new Log.Folds.Fold);\n fold.receive(data, {\n autoCloseFold: this.log.autoCloseFold\n });\n return fold.active;\n }\n });\n\n Log.Folds.Fold = function() {\n return this;\n };\n\n Log.extend(Log.Folds.Fold.prototype, {\n receive: function(data, options) {\n this[data.event] = data.id;\n if (this.start && this.end && !this.active) {\n return this.activate(options);\n }\n },\n activate: function(options) {\n var fragment, nextSibling, node, parentNode, toRemove, _i, _len, _ref;\n options || (options = {});\n if (Log.DEBUG) {\n console.log(\"F.n - activate \" + this.start);\n }\n toRemove = this.fold.parentNode;\n parentNode = toRemove.parentNode;\n nextSibling = toRemove.nextSibling;\n parentNode.removeChild(toRemove);\n fragment = document.createDocumentFragment();\n _ref = this.nodes;\n for (_i = 0, _len = _ref.length; _i < _len; _i++) {\n node = _ref[_i];\n fragment.appendChild(node);\n }\n this.fold.appendChild(fragment);\n parentNode.insertBefore(toRemove, nextSibling);\n this.fold.setAttribute('class', this.classes(options['autoCloseFold']));\n return this.active = true;\n },\n classes: function(autoCloseFold) {\n var classes;\n classes = this.fold.getAttribute('class').split(' ');\n classes.push('fold');\n if (!autoCloseFold) {\n classes.push('open');\n }\n if (this.fold.childNodes.length > 2) {\n classes.push('active');\n }\n return classes.join(' ');\n }\n });\n\n Object.defineProperty(Log.Folds.Fold.prototype, 'fold', {\n get: function() {\n return this._fold || (this._fold = document.getElementById(this.start));\n }\n });\n\n Object.defineProperty(Log.Folds.Fold.prototype, 'nodes', {\n get: function() {\n var node, nodes;\n node = this.fold;\n nodes = [];\n while ((node = node.nextSibling) && node.id !== this.end) {\n nodes.push(node);\n }\n return nodes;\n }\n });\n\n}).call(this);\n\n})();\n//@ sourceURL=log/folds");minispade.register('log/limit', "(function() {(function() {\n\n Log.Limit = function(max_lines) {\n this.max_lines = max_lines || 1000;\n return this;\n };\n\n Log.Limit.prototype = Log.extend(new Log.Listener, {\n count: 0,\n insert: function(log, node, pos) {\n if (node.type === 'paragraph' && !node.hidden) {\n return this.count += 1;\n }\n }\n });\n\n Object.defineProperty(Log.Limit.prototype, 'limited', {\n get: function() {\n return this.count >= this.max_lines;\n }\n });\n\n}).call(this);\n\n})();\n//@ sourceURL=log/limit");minispade.register('log/nodes', "(function() {(function() {\n var newLineAtTheEndRegexp, newLineRegexp, rRegexp, removeCarriageReturns;\n\n Log.Node = function(id, num) {\n this.id = id;\n this.num = num;\n this.key = Log.Node.key(this.id);\n this.children = new Log.Nodes(this);\n return this;\n };\n\n Log.extend(Log.Node, {\n key: function(id) {\n if (id) {\n return id.split('-').map(function(i) {\n return '000000'.concat(i).slice(-6);\n }).join('');\n }\n }\n });\n\n Log.extend(Log.Node.prototype, {\n addChild: function(node) {\n return this.children.add(node);\n },\n remove: function() {\n this.log.remove(this.element);\n return this.parent.children.remove(this);\n }\n });\n\n Object.defineProperty(Log.Node.prototype, 'log', {\n get: function() {\n var _ref;\n return this._log || (this._log = ((_ref = this.parent) != null ? _ref.log : void 0) || this.parent);\n }\n });\n\n Object.defineProperty(Log.Node.prototype, 'firstChild', {\n get: function() {\n return this.children.first;\n }\n });\n\n Object.defineProperty(Log.Node.prototype, 'lastChild', {\n get: function() {\n return this.children.last;\n }\n });\n\n Log.Nodes = function(parent) {\n if (parent) {\n this.parent = parent;\n }\n this.items = [];\n this.index = {};\n return this;\n };\n\n Log.extend(Log.Nodes.prototype, {\n add: function(item) {\n var ix, next, prev, _ref, _ref1;\n ix = this.position(item) || 0;\n this.items.splice(ix, 0, item);\n if (this.parent) {\n item.parent = this.parent;\n }\n prev = function(item) {\n while (item && !item.children.last) {\n item = item.prev;\n }\n return item != null ? item.children.last : void 0;\n };\n next = function(item) {\n while (item && !item.children.first) {\n item = item.next;\n }\n return item != null ? item.children.first : void 0;\n };\n if (item.prev = this.items[ix - 1] || prev((_ref = this.parent) != null ? _ref.prev : void 0)) {\n item.prev.next = item;\n }\n if (item.next = this.items[ix + 1] || next((_ref1 = this.parent) != null ? _ref1.next : void 0)) {\n item.next.prev = item;\n }\n return item;\n },\n remove: function(item) {\n this.items.splice(this.items.indexOf(item), 1);\n if (item.next) {\n item.next.prev = item.prev;\n }\n if (item.prev) {\n item.prev.next = item.next;\n }\n if (this.items.length === 0) {\n return this.parent.remove();\n }\n },\n position: function(item) {\n var ix, _i, _ref;\n for (ix = _i = _ref = this.items.length - 1; _i >= 0; ix = _i += -1) {\n if (this.items[ix].key < item.key) {\n return ix + 1;\n }\n }\n },\n indexOf: function() {\n return this.items.indexOf.apply(this.items, arguments);\n },\n slice: function() {\n return this.items.slice.apply(this.items, arguments);\n },\n each: function(func) {\n return this.items.slice().forEach(func);\n },\n map: function(func) {\n return this.items.map(func);\n }\n });\n\n Object.defineProperty(Log.Nodes.prototype, 'first', {\n get: function() {\n return this.items[0];\n }\n });\n\n Object.defineProperty(Log.Nodes.prototype, 'last', {\n get: function() {\n return this.items[this.length - 1];\n }\n });\n\n Object.defineProperty(Log.Nodes.prototype, 'length', {\n get: function() {\n return this.items.length;\n }\n });\n\n Log.Part = function(id, num, string) {\n Log.Node.apply(this, arguments);\n this.string = string || '';\n this.string = this.string.replace(/\\033\\[1000D/gm, '\\r');\n this.string = this.string.replace(/\\r+\\n/gm, '\\n');\n this.strings = this.string.split(/^/gm) || [];\n this.slices = ((function() {\n var _results;\n _results = [];\n while (this.strings.length > 0) {\n _results.push(this.strings.splice(0, Log.SLICE));\n }\n return _results;\n }).call(this));\n return this;\n };\n\n Log.extend(Log.Part, {\n create: function(log, num, string) {\n var part;\n part = new Log.Part(num.toString(), num, string);\n log.addChild(part);\n return part.process(0, -1);\n }\n });\n\n Log.Part.prototype = Log.extend(new Log.Node, {\n remove: function() {},\n process: function(slice, num) {\n var node, span, spans, string, _i, _j, _len, _len1, _ref, _ref1, _ref2, _ref3, _ref4,\n _this = this;\n _ref = this.slices[slice] || [];\n for (_i = 0, _len = _ref.length; _i < _len; _i++) {\n string = _ref[_i];\n if ((_ref1 = this.log.limit) != null ? _ref1.limited : void 0) {\n return;\n }\n spans = [];\n _ref2 = Log.Deansi.apply(string);\n for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {\n node = _ref2[_j];\n span = Log.Span.create(this, \"\" + this.id + \"-\" + (num += 1), num, node.text, node[\"class\"]);\n span.render();\n spans.push(span);\n }\n if ((_ref3 = spans[0]) != null ? (_ref4 = _ref3.line) != null ? _ref4.cr : void 0 : void 0) {\n spans[0].line.clear();\n }\n }\n if (!(slice >= this.slices.length - 1)) {\n return setTimeout((function() {\n return _this.process(slice + 1, num);\n }), Log.TIMEOUT);\n }\n }\n });\n\n newLineAtTheEndRegexp = new RegExp(\"\\n$\");\n\n newLineRegexp = new RegExp(\"\\n\");\n\n rRegexp = new RegExp(\"\\r\");\n\n removeCarriageReturns = function(string) {\n var index;\n index = string.lastIndexOf(\"\\r\");\n if (index === -1) {\n return string;\n }\n return string.substr(index + 1);\n };\n\n Log.Span = function(id, num, text, classes) {\n var fold, time, _ref;\n Log.Node.apply(this, arguments);\n if (fold = text.match(Log.FOLD)) {\n this.fold = true;\n this.event = fold[1];\n this.text = this.name = fold[2];\n } else if (time = text.match(Log.TIME)) {\n this.time = true;\n this.event = time[1];\n this.name = time[2];\n this.stats = time[3];\n } else {\n this.text = text;\n this.text = removeCarriageReturns(this.text);\n this.text = this.text.replace(newLineAtTheEndRegexp, '');\n this.nl = !!((_ref = text[text.length - 1]) != null ? _ref.match(newLineRegexp) : void 0);\n this.cr = !!text.match(rRegexp);\n this[\"class\"] = this.cr && ['clears'] || classes;\n }\n return this;\n };\n\n Log.extend(Log.Span, {\n create: function(parent, id, num, text, classes) {\n var span;\n span = new Log.Span(id, num, text, classes);\n parent.addChild(span);\n return span;\n },\n render: function(parent, id, num, text, classes) {\n var span;\n span = this.create(parent, id, num, text, classes);\n return span.render();\n }\n });\n\n Log.Span.prototype = Log.extend(new Log.Node, {\n render: function() {\n var tail;\n if (this.time && this.event === 'end' && this.prev) {\n if (Log.DEBUG) {\n console.log(\"S.0 insert \" + this.id + \" after prev \" + this.prev.id);\n }\n this.nl = this.prev.nl;\n this.log.insert(this.data, {\n after: this.prev.element\n });\n this.line = this.prev.line;\n } else if (!this.fold && this.prev && !this.prev.fold && !this.prev.nl) {\n if (Log.DEBUG) {\n console.log(\"S.1 insert \" + this.id + \" after prev \" + this.prev.id);\n }\n this.log.insert(this.data, {\n after: this.prev.element\n });\n this.line = this.prev.line;\n } else if (!this.fold && this.next && !this.next.fold && !this.next.time) {\n if (Log.DEBUG) {\n console.log(\"S.2 insert \" + this.id + \" before next \" + this.next.id);\n }\n this.log.insert(this.data, {\n before: this.next.element\n });\n this.line = this.next.line;\n } else {\n this.line = Log.Line.create(this.log, [this]);\n this.line.render();\n }\n if (this.nl && (tail = this.tail).length > 0) {\n this.split(tail);\n }\n if (this.time) {\n return this.log.times.add(this);\n }\n },\n remove: function() {\n Log.Node.prototype.remove.apply(this);\n if (this.line) {\n return this.line.remove(this);\n }\n },\n split: function(spans) {\n var line, span, _i, _len;\n if (Log.DEBUG) {\n console.log(\"S.4 split [\" + (spans.map(function(span) {\n return span.id;\n }).join(', ')) + \"]\");\n }\n for (_i = 0, _len = spans.length; _i < _len; _i++) {\n span = spans[_i];\n this.log.remove(span.element);\n }\n line = Log.Line.create(this.log, spans);\n line.render();\n if (line.cr) {\n return line.clear();\n }\n },\n clear: function() {\n if (this.prev && this.isSibling(this.prev) && this.isSequence(this.prev)) {\n this.prev.clear();\n return this.prev.remove();\n }\n },\n isSequence: function(other) {\n return this.parent.num - other.parent.num === this.log.children.indexOf(this.parent) - this.log.children.indexOf(other.parent);\n },\n isSibling: function(other) {\n var _ref, _ref1;\n return ((_ref = this.element) != null ? _ref.parentNode : void 0) === ((_ref1 = other.element) != null ? _ref1.parentNode : void 0);\n },\n siblings: function(type) {\n var siblings, span;\n siblings = [];\n while ((span = (span || this)[type]) && this.isSibling(span)) {\n siblings.push(span);\n }\n return siblings;\n }\n });\n\n Object.defineProperty(Log.Span.prototype, 'data', {\n get: function() {\n return {\n id: this.id,\n type: 'span',\n text: this.text,\n \"class\": this[\"class\"],\n time: this.time\n };\n }\n });\n\n Object.defineProperty(Log.Span.prototype, 'line', {\n get: function() {\n return this._line;\n },\n set: function(line) {\n if (this.line) {\n this.line.remove(this);\n }\n this._line = line;\n if (this.line) {\n return this.line.add(this);\n }\n }\n });\n\n Object.defineProperty(Log.Span.prototype, 'element', {\n get: function() {\n return document.getElementById(this.id);\n }\n });\n\n Object.defineProperty(Log.Span.prototype, 'head', {\n get: function() {\n return this.siblings('prev').reverse();\n }\n });\n\n Object.defineProperty(Log.Span.prototype, 'tail', {\n get: function() {\n return this.siblings('next');\n }\n });\n\n Log.Line = function(log) {\n this.log = log;\n this.spans = [];\n return this;\n };\n\n Log.extend(Log.Line, {\n create: function(log, spans) {\n var line, span, _i, _len;\n if ((span = spans[0]) && span.fold) {\n line = new Log.Fold(log, span.event, span.name);\n } else {\n line = new Log.Line(log);\n }\n for (_i = 0, _len = spans.length; _i < _len; _i++) {\n span = spans[_i];\n span.line = line;\n }\n return line;\n }\n });\n\n Log.extend(Log.Line.prototype, {\n add: function(span) {\n var ix;\n if (span.cr) {\n this.cr = true;\n }\n if (this.spans.indexOf(span) > -1) {\n\n } else if ((ix = this.spans.indexOf(span.prev)) > -1) {\n return this.spans.splice(ix + 1, 0, span);\n } else if ((ix = this.spans.indexOf(span.next)) > -1) {\n return this.spans.splice(ix, 0, span);\n } else {\n return this.spans.push(span);\n }\n },\n remove: function(span) {\n var ix;\n if ((ix = this.spans.indexOf(span)) > -1) {\n return this.spans.splice(ix, 1);\n }\n },\n render: function() {\n var fold;\n if ((fold = this.prev) && fold.event === 'start' && fold.active) {\n if (this.next && !this.next.fold) {\n if (Log.DEBUG) {\n console.log(\"L.0 insert \" + this.id + \" before next \" + this.next.id);\n }\n return this.element = this.log.insert(this.data, {\n before: this.next.element\n });\n } else {\n if (Log.DEBUG) {\n console.log(\"L.0 insert \" + this.id + \" into fold \" + fold.id);\n }\n fold = this.log.folds.folds[fold.name].fold;\n return this.element = this.log.insert(this.data, {\n into: fold\n });\n }\n } else if (this.prev) {\n if (Log.DEBUG) {\n console.log(\"L.1 insert \" + this.spans[0].id + \" after prev \" + this.prev.id);\n }\n return this.element = this.log.insert(this.data, {\n after: this.prev.element\n });\n } else if (this.next) {\n if (Log.DEBUG) {\n console.log(\"L.2 insert \" + this.spans[0].id + \" before next \" + this.next.id);\n }\n return this.element = this.log.insert(this.data, {\n before: this.next.element\n });\n } else {\n if (Log.DEBUG) {\n console.log(\"L.3 insert \" + this.spans[0].id + \" into #log\");\n }\n return this.element = this.log.insert(this.data);\n }\n },\n clear: function() {\n var cr, _i, _len, _ref, _results;\n _ref = this.crs;\n _results = [];\n for (_i = 0, _len = _ref.length; _i < _len; _i++) {\n cr = _ref[_i];\n _results.push(cr.clear());\n }\n return _results;\n }\n });\n\n Object.defineProperty(Log.Line.prototype, 'id', {\n get: function() {\n var _ref;\n return (_ref = this.spans[0]) != null ? _ref.id : void 0;\n }\n });\n\n Object.defineProperty(Log.Line.prototype, 'data', {\n get: function() {\n return {\n type: 'paragraph',\n nodes: this.nodes\n };\n }\n });\n\n Object.defineProperty(Log.Line.prototype, 'nodes', {\n get: function() {\n return this.spans.map(function(span) {\n return span.data;\n });\n }\n });\n\n Object.defineProperty(Log.Line.prototype, 'prev', {\n get: function() {\n var _ref;\n return (_ref = this.spans[0].prev) != null ? _ref.line : void 0;\n }\n });\n\n Object.defineProperty(Log.Line.prototype, 'next', {\n get: function() {\n var _ref;\n return (_ref = this.spans[this.spans.length - 1].next) != null ? _ref.line : void 0;\n }\n });\n\n Object.defineProperty(Log.Line.prototype, 'crs', {\n get: function() {\n return this.spans.filter(function(span) {\n return span.cr;\n });\n }\n });\n\n Log.Fold = function(log, event, name) {\n Log.Line.apply(this, arguments);\n this.fold = true;\n this.event = event;\n this.name = name;\n return this;\n };\n\n Log.Fold.prototype = Log.extend(new Log.Line, {\n render: function() {\n var element, _ref;\n if (this.prev && this.prev.element) {\n if (Log.DEBUG) {\n console.log(\"F.1 insert \" + this.id + \" after prev \" + this.prev.id);\n }\n element = this.prev.element;\n this.element = this.log.insert(this.data, {\n after: element\n });\n } else if (this.next) {\n if (Log.DEBUG) {\n console.log(\"F.2 insert \" + this.id + \" before next \" + this.next.id);\n }\n element = this.next.element || this.next.element.parentNode;\n this.element = this.log.insert(this.data, {\n before: element\n });\n } else {\n if (Log.DEBUG) {\n console.log(\"F.3 insert \" + this.id);\n }\n this.element = this.log.insert(this.data);\n }\n if (this.span.next && ((_ref = this.span.prev) != null ? _ref.isSibling(this.span.next) : void 0)) {\n this.span.prev.split([this.span.next].concat(this.span.next.tail));\n }\n return this.active = this.log.folds.add(this.data);\n }\n });\n\n Object.defineProperty(Log.Fold.prototype, 'id', {\n get: function() {\n return \"fold-\" + this.event + \"-\" + this.name;\n }\n });\n\n Object.defineProperty(Log.Fold.prototype, 'span', {\n get: function() {\n return this.spans[0];\n }\n });\n\n Object.defineProperty(Log.Fold.prototype, 'data', {\n get: function() {\n return {\n type: 'fold',\n id: this.id,\n event: this.event,\n name: this.name\n };\n }\n });\n\n}).call(this);\n\n})();\n//@ sourceURL=log/nodes");minispade.register('log/renderer', "(function() {(function() {\n\n Log.Renderer = function() {\n this.frag = document.createDocumentFragment();\n this.para = this.createParagraph();\n this.span = this.createSpan();\n this.text = document.createTextNode('');\n this.fold = this.createFold();\n return this;\n };\n\n Log.extend(Log.Renderer.prototype, {\n insert: function(data, pos) {\n var after, before, into, node;\n node = this.render(data);\n if (into = pos != null ? pos.into : void 0) {\n if (typeof into === 'String') {\n into = document.getElementById(pos != null ? pos.into : void 0);\n }\n if (pos != null ? pos.prepend : void 0) {\n this.prependTo(node, into);\n } else {\n this.appendTo(node, into);\n }\n } else if (after = pos != null ? pos.after : void 0) {\n if (typeof after === 'String') {\n after = document.getElementById(pos);\n }\n this.insertAfter(node, after);\n } else if (before = pos != null ? pos.before : void 0) {\n if (typeof before === 'String') {\n before = document.getElementById(pos != null ? pos.before : void 0);\n }\n this.insertBefore(node, before);\n } else {\n this.insertBefore(node);\n }\n return node;\n },\n hide: function(node) {\n node.setAttribute('class', this.addClass(node.getAttribute('class'), 'hidden'));\n return node;\n },\n remove: function(node) {\n if (node) {\n node.parentNode.removeChild(node);\n }\n return node;\n },\n render: function(data) {\n var frag, node, type, _i, _len;\n if (data instanceof Array) {\n frag = this.frag.cloneNode(true);\n for (_i = 0, _len = data.length; _i < _len; _i++) {\n node = data[_i];\n node = this.render(node);\n if (node) {\n frag.appendChild(node);\n }\n }\n return frag;\n } else {\n data.type || (data.type = 'paragraph');\n type = data.type[0].toUpperCase() + data.type.slice(1);\n return this[\"render\" + type](data);\n }\n },\n renderParagraph: function(data) {\n var node, para, type, _i, _len, _ref;\n para = this.para.cloneNode(true);\n if (data.id) {\n para.setAttribute('id', data.id);\n }\n if (data.hidden) {\n para.setAttribute('style', 'display: none;');\n }\n _ref = data.nodes || [];\n for (_i = 0, _len = _ref.length; _i < _len; _i++) {\n node = _ref[_i];\n type = node.type[0].toUpperCase() + node.type.slice(1);\n node = this[\"render\" + type](node);\n para.appendChild(node);\n }\n return para;\n },\n renderFold: function(data) {\n var fold;\n fold = this.fold.cloneNode(true);\n fold.setAttribute('id', data.id || (\"fold-\" + data.event + \"-\" + data.name));\n fold.setAttribute('class', \"fold-\" + data.event);\n if (data.event === 'start') {\n fold.lastChild.lastChild.nodeValue = data.name;\n } else {\n fold.removeChild(fold.lastChild);\n }\n return fold;\n },\n renderSpan: function(data) {\n var span;\n span = this.span.cloneNode(true);\n if (data.id) {\n span.setAttribute('id', data.id);\n }\n if (data[\"class\"]) {\n span.setAttribute('class', data[\"class\"]);\n }\n span.lastChild.nodeValue = data.text || '';\n return span;\n },\n renderText: function(data) {\n var text;\n text = this.text.cloneNode(true);\n text.nodeValue = data.text;\n return text;\n },\n createParagraph: function() {\n var para;\n para = document.createElement('p');\n para.appendChild(document.createElement('a'));\n return para;\n },\n createFold: function() {\n var fold;\n fold = document.createElement('div');\n fold.appendChild(this.createSpan());\n fold.lastChild.setAttribute('class', 'fold-name');\n return fold;\n },\n createSpan: function() {\n var span;\n span = document.createElement('span');\n span.appendChild(document.createTextNode(' '));\n return span;\n },\n insertBefore: function(node, other) {\n var log;\n if (other) {\n return other.parentNode.insertBefore(node, other);\n } else {\n log = document.getElementById('log');\n return log.insertBefore(node, log.firstChild);\n }\n },\n insertAfter: function(node, other) {\n if (other.nextSibling) {\n return this.insertBefore(node, other.nextSibling);\n } else {\n return this.appendTo(node, other.parentNode);\n }\n },\n prependTo: function(node, other) {\n if (other.firstChild) {\n return other.insertBefore(node, other.firstChild);\n } else {\n return appendTo(node, other);\n }\n },\n appendTo: function(node, other) {\n return other.appendChild(node);\n },\n addClass: function(classes, string) {\n if (classes != null ? classes.indexOf(string) : void 0) {\n return;\n }\n if (classes) {\n return \"\" + classes + \" \" + string;\n } else {\n return string;\n }\n }\n });\n\n}).call(this);\n\n})();\n//@ sourceURL=log/renderer");minispade.register('log/times', "(function() {(function() {\n\n Log.Times = function(log) {\n this.log = log;\n this.times = {};\n return this;\n };\n\n Log.extend(Log.Times.prototype, {\n add: function(node) {\n var time, _base, _name;\n time = (_base = this.times)[_name = node.name] || (_base[_name] = new Log.Times.Time);\n return time.receive(node);\n },\n duration: function(name) {\n if (this.times[name]) {\n return this.times[name].duration;\n }\n }\n });\n\n Log.Times.Time = function() {\n return this;\n };\n\n Log.extend(Log.Times.Time.prototype, {\n receive: function(node) {\n this[node.event] = node;\n if (Log.DEBUG) {\n console.log(\"T.0 - \" + node.event + \" \" + node.name);\n }\n if (this.start && this.end) {\n return this.finish();\n }\n },\n finish: function() {\n var element;\n if (Log.DEBUG) {\n console.log(\"T.1 - finish \" + this.start.name);\n }\n element = document.getElementById(this.start.id);\n if (element) {\n return this.update(element);\n }\n },\n update: function(element) {\n element.setAttribute('class', 'duration');\n element.setAttribute('title', \"This command finished after \" + this.duration + \" seconds.\");\n return element.lastChild.nodeValue = \"\" + this.duration + \"s\";\n }\n });\n\n Object.defineProperty(Log.Times.Time.prototype, 'duration', {\n get: function() {\n var duration;\n duration = this.stats.duration / 1000 / 1000 / 1000;\n return duration.toFixed(2);\n }\n });\n\n Object.defineProperty(Log.Times.Time.prototype, 'stats', {\n get: function() {\n var stat, stats, _i, _len, _ref;\n if (!(this.end && this.end.stats)) {\n return {};\n }\n stats = {};\n _ref = this.end.stats.split(',');\n for (_i = 0, _len = _ref.length; _i < _len; _i++) {\n stat = _ref[_i];\n stat = stat.split('=');\n stats[stat[0]] = stat[1];\n }\n return stats;\n }\n });\n\n}).call(this);\n\n})();\n//@ sourceURL=log/times");
-