2018-03-19 18:55:02 +01:00
|
|
|
/**
|
|
|
|
* @provides javelin-behavior-document-engine
|
|
|
|
* @requires javelin-behavior
|
|
|
|
* javelin-dom
|
|
|
|
* javelin-stratcom
|
|
|
|
*/
|
|
|
|
|
2018-03-23 21:44:14 +01:00
|
|
|
JX.behavior('document-engine', function(config, statics) {
|
|
|
|
|
2018-03-19 18:55:02 +01:00
|
|
|
function onmenu(e) {
|
|
|
|
var node = e.getNode('document-engine-view-dropdown');
|
|
|
|
var data = JX.Stratcom.getData(node);
|
|
|
|
|
|
|
|
if (data.menu) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
e.prevent();
|
|
|
|
|
|
|
|
var menu = new JX.PHUIXDropdownMenu(node);
|
|
|
|
var list = new JX.PHUIXActionListView();
|
|
|
|
|
|
|
|
var view;
|
2018-03-23 21:44:14 +01:00
|
|
|
var engines = [];
|
2018-03-19 18:55:02 +01:00
|
|
|
for (var ii = 0; ii < data.views.length; ii++) {
|
|
|
|
var spec = data.views[ii];
|
|
|
|
|
|
|
|
view = new JX.PHUIXActionView()
|
|
|
|
.setName(spec.name)
|
|
|
|
.setIcon(spec.icon)
|
2018-03-19 20:36:39 +01:00
|
|
|
.setIconColor(spec.color)
|
2018-03-19 18:55:02 +01:00
|
|
|
.setHref(spec.engineURI);
|
|
|
|
|
|
|
|
view.setHandler(JX.bind(null, function(spec, e) {
|
|
|
|
if (!e.isNormalClick()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
e.prevent();
|
|
|
|
menu.close();
|
|
|
|
|
2018-03-23 21:44:14 +01:00
|
|
|
onview(data, spec, false);
|
2018-03-19 18:55:02 +01:00
|
|
|
}, spec));
|
|
|
|
|
|
|
|
list.addItem(view);
|
2018-03-23 21:44:14 +01:00
|
|
|
|
|
|
|
engines.push({
|
|
|
|
spec: spec,
|
|
|
|
view: view
|
|
|
|
});
|
2018-03-19 18:55:02 +01:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:02:01 +02:00
|
|
|
list.addItem(
|
|
|
|
new JX.PHUIXActionView()
|
|
|
|
.setDivider(true));
|
|
|
|
|
|
|
|
var encode_item = new JX.PHUIXActionView()
|
|
|
|
.setName(data.encode.name)
|
|
|
|
.setIcon(data.encode.icon);
|
|
|
|
|
|
|
|
var onencode = JX.bind(null, function(data, e) {
|
|
|
|
e.prevent();
|
|
|
|
|
|
|
|
if (encode_item.getDisabled()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
new JX.Workflow(data.encode.uri, {encoding: data.encode.value})
|
|
|
|
.setHandler(function(r) {
|
|
|
|
data.encode.value = r.encoding;
|
|
|
|
onview(data);
|
|
|
|
})
|
|
|
|
.start();
|
|
|
|
|
|
|
|
menu.close();
|
|
|
|
|
|
|
|
}, data);
|
|
|
|
|
|
|
|
encode_item.setHandler(onencode);
|
|
|
|
|
|
|
|
list.addItem(encode_item);
|
|
|
|
|
|
|
|
var highlight_item = new JX.PHUIXActionView()
|
|
|
|
.setName(data.highlight.name)
|
|
|
|
.setIcon(data.highlight.icon);
|
|
|
|
|
|
|
|
var onhighlight = JX.bind(null, function(data, e) {
|
|
|
|
e.prevent();
|
|
|
|
|
|
|
|
if (highlight_item.getDisabled()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
new JX.Workflow(data.highlight.uri, {highlight: data.highlight.value})
|
|
|
|
.setHandler(function(r) {
|
|
|
|
data.highlight.value = r.highlight;
|
|
|
|
onview(data);
|
|
|
|
})
|
|
|
|
.start();
|
|
|
|
|
|
|
|
menu.close();
|
|
|
|
}, data);
|
|
|
|
|
|
|
|
highlight_item.setHandler(onhighlight);
|
|
|
|
|
|
|
|
list.addItem(highlight_item);
|
|
|
|
|
2018-03-19 18:55:02 +01:00
|
|
|
menu.setContent(list.getNode());
|
|
|
|
|
2018-03-23 21:44:14 +01:00
|
|
|
menu.listen('open', function() {
|
|
|
|
for (var ii = 0; ii < engines.length; ii++) {
|
|
|
|
var engine = engines[ii];
|
|
|
|
|
|
|
|
// Highlight the current rendering engine.
|
|
|
|
var is_selected = (engine.spec.viewKey == data.viewKey);
|
|
|
|
engine.view.setSelected(is_selected);
|
2018-03-30 20:02:01 +02:00
|
|
|
|
|
|
|
if (is_selected) {
|
|
|
|
encode_item.setDisabled(!engine.spec.canEncode);
|
|
|
|
highlight_item.setDisabled(!engine.spec.canHighlight);
|
|
|
|
}
|
2018-03-23 21:44:14 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-03-19 18:55:02 +01:00
|
|
|
data.menu = menu;
|
|
|
|
menu.open();
|
|
|
|
}
|
|
|
|
|
2018-03-30 20:02:01 +02:00
|
|
|
function add_params(uri, data) {
|
|
|
|
uri = JX.$U(uri);
|
|
|
|
|
|
|
|
if (data.highlight.value) {
|
|
|
|
uri.setQueryParam('highlight', data.highlight.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data.encode.value) {
|
|
|
|
uri.setQueryParam('encode', data.encode.value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return uri.toString();
|
|
|
|
}
|
|
|
|
|
2018-03-23 21:44:14 +01:00
|
|
|
function onview(data, spec, immediate) {
|
2018-03-30 20:02:01 +02:00
|
|
|
if (!spec) {
|
|
|
|
for (var ii = 0; ii < data.views.length; ii++) {
|
|
|
|
if (data.views[ii].viewKey == data.viewKey) {
|
|
|
|
spec = data.views[ii];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-23 21:44:14 +01:00
|
|
|
data.sequence = (data.sequence || 0) + 1;
|
2018-04-09 14:25:35 +02:00
|
|
|
var handler = JX.bind(null, onrender, data, data.sequence, spec);
|
2018-03-23 21:44:14 +01:00
|
|
|
|
|
|
|
data.viewKey = spec.viewKey;
|
2018-03-19 18:55:02 +01:00
|
|
|
|
2018-03-30 20:02:01 +02:00
|
|
|
var uri = add_params(spec.engineURI, data);
|
|
|
|
|
|
|
|
new JX.Request(uri, handler)
|
2018-03-19 18:55:02 +01:00
|
|
|
.send();
|
2018-03-23 21:44:14 +01:00
|
|
|
|
|
|
|
if (data.loadingView) {
|
|
|
|
// If we're already showing "Loading...", immediately change it to
|
|
|
|
// show the new document type.
|
|
|
|
onloading(data, spec);
|
|
|
|
} else if (!immediate) {
|
|
|
|
// Otherwise, grey out the document and show "Loading..." after a
|
|
|
|
// short delay. This prevents the content from flickering when rendering
|
|
|
|
// is fast.
|
|
|
|
var viewport = JX.$(data.viewportID);
|
|
|
|
JX.DOM.alterClass(viewport, 'document-engine-in-flight', true);
|
|
|
|
|
|
|
|
var load = JX.bind(null, onloading, data, spec);
|
|
|
|
data.loadTimer = setTimeout(load, 333);
|
2018-03-26 16:17:59 +02:00
|
|
|
|
|
|
|
// Replace the URI with the URI for the specific rendering the user
|
|
|
|
// has selected.
|
2018-03-30 20:02:01 +02:00
|
|
|
|
|
|
|
var view_uri = add_params(spec.viewURI, data);
|
|
|
|
JX.History.replace(view_uri);
|
2018-03-23 21:44:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function onloading(data, spec) {
|
|
|
|
data.loadingView = true;
|
|
|
|
|
|
|
|
var viewport = JX.$(data.viewportID);
|
|
|
|
JX.DOM.alterClass(viewport, 'document-engine-in-flight', false);
|
|
|
|
JX.DOM.setContent(viewport, JX.$H(spec.loadingMarkup));
|
2018-03-19 18:55:02 +01:00
|
|
|
}
|
|
|
|
|
2018-04-09 14:25:35 +02:00
|
|
|
function onrender(data, sequence, spec, r) {
|
2018-03-23 21:44:14 +01:00
|
|
|
// If this isn't the most recent request we sent, throw it away. This can
|
|
|
|
// happen if the user makes multiple selections from the menu while we are
|
|
|
|
// still rendering the first view.
|
|
|
|
if (sequence != data.sequence) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data.loadTimer) {
|
|
|
|
clearTimeout(data.loadTimer);
|
|
|
|
data.loadTimer = null;
|
|
|
|
}
|
|
|
|
|
2018-03-19 18:55:02 +01:00
|
|
|
var viewport = JX.$(data.viewportID);
|
|
|
|
|
2018-03-23 21:44:14 +01:00
|
|
|
JX.DOM.alterClass(viewport, 'document-engine-in-flight', false);
|
|
|
|
data.loadingView = false;
|
|
|
|
|
2018-03-19 18:55:02 +01:00
|
|
|
JX.DOM.setContent(viewport, JX.$H(r.markup));
|
2018-04-09 14:25:35 +02:00
|
|
|
|
|
|
|
// If this engine supports rendering blame, populate or draw it.
|
|
|
|
if (spec.canBlame) {
|
|
|
|
blame(data);
|
|
|
|
}
|
2018-03-19 18:55:02 +01:00
|
|
|
}
|
|
|
|
|
2018-04-08 20:38:19 +02:00
|
|
|
function blame(data) {
|
2018-04-09 14:25:35 +02:00
|
|
|
// If the rendering engine can't handle blame, bail.
|
2018-04-08 20:38:19 +02:00
|
|
|
if (!data.blame.uri) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-04-09 14:25:35 +02:00
|
|
|
// If we already have an outstanding request for blame data, bail.
|
|
|
|
if (data.blame.request) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we don't have blame data yet, request it and then try rendering
|
|
|
|
// again later.
|
2018-04-08 20:38:19 +02:00
|
|
|
if (!data.blame.value) {
|
2018-04-09 14:25:35 +02:00
|
|
|
var req = new JX.Request(data.blame.uri, JX.bind(null, onblame, data));
|
|
|
|
data.blame.request = req;
|
|
|
|
req.send();
|
2018-04-08 20:38:19 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-04-09 14:25:35 +02:00
|
|
|
// We're ready to render.
|
2018-04-08 20:38:19 +02:00
|
|
|
var viewport = JX.$(data.viewportID);
|
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
var row_nodes = JX.DOM.scry(viewport, 'tr');
|
|
|
|
var row_list = [];
|
|
|
|
var ii;
|
2018-04-08 20:38:19 +02:00
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
for (ii = 0; ii < row_nodes.length; ii++) {
|
|
|
|
var row = {};
|
|
|
|
var keep = false;
|
|
|
|
var node = row_nodes[ii];
|
2018-04-08 20:38:19 +02:00
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
for (var jj = 0; jj < node.childNodes.length; jj++) {
|
|
|
|
var child = node.childNodes[jj];
|
2018-04-08 20:38:19 +02:00
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
if (!JX.DOM.isType(child, 'th')) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-04-08 20:38:19 +02:00
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
var spec = child.getAttribute('data-blame');
|
|
|
|
if (spec) {
|
|
|
|
row[spec] = child;
|
|
|
|
keep = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (spec === 'info') {
|
|
|
|
row.lines = child.getAttribute('data-blame-lines');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (keep) {
|
|
|
|
row_list.push(row);
|
2018-04-08 20:38:19 +02:00
|
|
|
}
|
2018-04-09 14:38:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var last = null;
|
|
|
|
for (ii = 0; ii < row_list.length; ii++) {
|
|
|
|
var commit = data.blame.value.blame[row_list[ii].lines - 1];
|
|
|
|
row_list[ii].commit = commit;
|
|
|
|
row_list[ii].last = last;
|
|
|
|
last = commit;
|
|
|
|
}
|
2018-04-08 20:38:19 +02:00
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
for (ii = 0; ii < row_list.length; ii++) {
|
|
|
|
renderBlame(row_list[ii], data.blame.value);
|
2018-04-08 20:38:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function onblame(data, r) {
|
2018-04-09 14:25:35 +02:00
|
|
|
data.blame.request = null;
|
2018-04-08 20:38:19 +02:00
|
|
|
data.blame.value = r;
|
|
|
|
blame(data);
|
|
|
|
}
|
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
function renderBlame(row, blame) {
|
|
|
|
var spec = blame.map[row.commit];
|
2018-04-08 20:38:19 +02:00
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
var info = null;
|
|
|
|
var skip = null;
|
2018-04-08 20:38:19 +02:00
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
if (spec && (row.commit != row.last)) {
|
|
|
|
skip = JX.$H(spec.skip);
|
|
|
|
info = JX.$H(spec.info);
|
2018-04-08 20:38:19 +02:00
|
|
|
}
|
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
if (row.skip) {
|
|
|
|
JX.DOM.setContent(row.skip, skip);
|
|
|
|
}
|
2018-04-08 20:38:19 +02:00
|
|
|
|
2018-04-09 14:38:06 +02:00
|
|
|
if (row.info) {
|
|
|
|
JX.DOM.setContent(row.info, info);
|
|
|
|
}
|
2018-04-09 15:05:40 +02:00
|
|
|
|
|
|
|
var epoch_range = (blame.epoch.max - blame.epoch.min);
|
|
|
|
|
|
|
|
var epoch_value;
|
|
|
|
if (!epoch_range) {
|
|
|
|
epoch_value = 1;
|
|
|
|
} else {
|
|
|
|
epoch_value = (spec.epoch - blame.epoch.min) / epoch_range;
|
|
|
|
}
|
|
|
|
|
|
|
|
var h_min = 0.04;
|
|
|
|
var h_max = 0.44;
|
|
|
|
var h = h_min + ((h_max - h_min) * epoch_value);
|
|
|
|
|
|
|
|
var s = 0.44;
|
|
|
|
|
|
|
|
var v_min = 0.92;
|
|
|
|
var v_max = 1.00;
|
|
|
|
var v = v_min + ((v_max - v_min) * epoch_value);
|
|
|
|
|
|
|
|
row.info.style.background = getHSV(h, s, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getHSV(h, s, v) {
|
|
|
|
var r, g, b, i, f, p, q, t;
|
|
|
|
|
|
|
|
i = Math.floor(h * 6);
|
|
|
|
f = h * 6 - i;
|
|
|
|
p = v * (1 - s);
|
|
|
|
q = v * (1 - f * s);
|
|
|
|
t = v * (1 - (1 - f) * s);
|
|
|
|
|
|
|
|
switch (i % 6) {
|
|
|
|
case 0: r = v, g = t, b = p; break;
|
|
|
|
case 1: r = q, g = v, b = p; break;
|
|
|
|
case 2: r = p, g = v, b = t; break;
|
|
|
|
case 3: r = p, g = q, b = v; break;
|
|
|
|
case 4: r = t, g = p, b = v; break;
|
|
|
|
case 5: r = v, g = p, b = q; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = Math.round(r * 255);
|
|
|
|
g = Math.round(g * 255);
|
|
|
|
b = Math.round(b * 255);
|
|
|
|
|
|
|
|
|
|
|
|
return 'rgb(' + r + ', ' + g + ', ' + b + ')';
|
2018-04-08 20:38:19 +02:00
|
|
|
}
|
|
|
|
|
2018-03-23 21:44:14 +01:00
|
|
|
if (!statics.initialized) {
|
|
|
|
JX.Stratcom.listen('click', 'document-engine-view-dropdown', onmenu);
|
|
|
|
statics.initialized = true;
|
|
|
|
}
|
|
|
|
|
2018-04-08 20:38:19 +02:00
|
|
|
if (config && config.controlID) {
|
|
|
|
var control = JX.$(config.controlID);
|
2018-03-23 21:44:14 +01:00
|
|
|
var data = JX.Stratcom.getData(control);
|
2018-04-08 20:38:19 +02:00
|
|
|
|
|
|
|
switch (config.next) {
|
|
|
|
case 'render':
|
|
|
|
onview(data, null, true);
|
|
|
|
break;
|
|
|
|
case 'blame':
|
|
|
|
blame(data);
|
|
|
|
break;
|
|
|
|
}
|
2018-03-23 21:44:14 +01:00
|
|
|
}
|
2018-03-19 18:55:02 +01:00
|
|
|
|
|
|
|
});
|