Changeset View
Changeset View
Standalone View
Standalone View
webroot/rsrc/js/application/diff/ScrollObjectiveList.js
- This file was added.
/** | |||||
* @provides phabricator-scroll-objective-list | |||||
* @requires javelin-dom | |||||
* javelin-util | |||||
* javelin-stratcom | |||||
* javelin-install | |||||
* javelin-workflow | |||||
* phabricator-scroll-objective | |||||
* @javelin | |||||
*/ | |||||
JX.install('ScrollObjectiveList', { | |||||
construct : function() { | |||||
this._objectives = []; | |||||
var onresize = JX.bind(this, this._dirty); | |||||
JX.Stratcom.listen('resize', null, onresize); | |||||
}, | |||||
members: { | |||||
_objectives: null, | |||||
_visible: false, | |||||
_trigger: null, | |||||
newObjective: function() { | |||||
var objective = new JX.ScrollObjective() | |||||
.setObjectiveList(this); | |||||
this._objectives.push(objective); | |||||
this._getNode().appendChild(objective.getNode()); | |||||
this._dirty(); | |||||
return objective; | |||||
}, | |||||
show: function() { | |||||
this._visible = true; | |||||
this._dirty(); | |||||
return this; | |||||
}, | |||||
hide: function() { | |||||
this._visible = false; | |||||
this._dirty(); | |||||
return this; | |||||
}, | |||||
_getNode: function() { | |||||
if (!this._node) { | |||||
var node = new JX.$N('div', {className: 'scroll-objective-list'}); | |||||
this._node = node; | |||||
} | |||||
return this._node; | |||||
}, | |||||
_dirty: function() { | |||||
if (this._trigger !== null) { | |||||
return; | |||||
} | |||||
this._trigger = setTimeout(JX.bind(this, this._redraw), 0); | |||||
}, | |||||
_redraw: function() { | |||||
this._trigger = null; | |||||
var node = this._getNode(); | |||||
var is_visible = | |||||
(this._visible) && | |||||
(JX.Device.getDevice() == 'desktop') && | |||||
(this._objectives.length); | |||||
if (!is_visible) { | |||||
JX.DOM.remove(node); | |||||
return; | |||||
} | |||||
document.body.appendChild(node); | |||||
var d = JX.Vector.getDocument(); | |||||
var list_dimensions = JX.Vector.getDim(node); | |||||
var icon_height = 16; | |||||
var list_y = (list_dimensions.y - icon_height); | |||||
var ii; | |||||
var offset; | |||||
// First, build a list of all the items we're going to show. | |||||
var items = []; | |||||
for (ii = 0; ii < this._objectives.length; ii++) { | |||||
var objective = this._objectives[ii]; | |||||
var objective_node = objective.getNode(); | |||||
var anchor = objective.getAnchor(); | |||||
if (!anchor || !objective.isVisible()) { | |||||
JX.DOM.remove(objective_node); | |||||
continue; | |||||
} | |||||
offset = (JX.$V(anchor).y / d.y) * (list_y); | |||||
items.push({ | |||||
offset: offset, | |||||
node: objective_node | |||||
}); | |||||
} | |||||
// Now, sort it from top to bottom. | |||||
items.sort(function(u, v) { | |||||
return u.offset - v.offset; | |||||
}); | |||||
// Lay out the items in the objective list, leaving a minimum amount | |||||
// of space between them so they do not overlap. | |||||
var min = null; | |||||
for (ii = 0; ii < items.length; ii++) { | |||||
var item = items[ii]; | |||||
offset = item.offset; | |||||
if (min !== null) { | |||||
offset = Math.max(offset, min); | |||||
} | |||||
min = offset + 15; | |||||
item.node.style.top = offset + 'px'; | |||||
node.appendChild(item.node); | |||||
} | |||||
} | |||||
} | |||||
}); |