mirror of
https://bitbucket.org/bellord13/kit.git
synced 2026-11-13 19:06:11 +03:00
585 lines
18 KiB
JavaScript
585 lines
18 KiB
JavaScript
/** @module align */
|
|
kit.PluginManager.register('align',function(module, exports, require) {
|
|
|
|
/**
|
|
* @class Rect
|
|
* @param node
|
|
*/
|
|
var Rect = function (node) {
|
|
this.node = node;
|
|
this.get();
|
|
};
|
|
Rect.prototype.get = function () {
|
|
function getBoundingClientRect(node) {
|
|
var rect = node.getBoundingClientRect();
|
|
return {
|
|
top: rect.top,
|
|
right: rect.right,
|
|
bottom: rect.bottom,
|
|
left: rect.left,
|
|
width: rect.width,
|
|
height: rect.height,
|
|
x: rect.x,
|
|
y: rect.y
|
|
};
|
|
}
|
|
var metrics = {};
|
|
if(this.node instanceof HTMLElement) {
|
|
metrics = {
|
|
offsetHeight: this.node.offsetHeight,
|
|
offsetWidth: this.node.offsetWidth,
|
|
clientHeight: this.node.clientHeight,
|
|
clientWidth: this.node.clientWidth,
|
|
offsetLeft: this.node.offsetLeft,
|
|
offsetTop: this.node.offsetTop,
|
|
clientLeft: this.node.clientLeft,
|
|
clientTop: this.node.clientTop,
|
|
clientRight: this.node.offsetWidth - this.node.clientWidth,
|
|
clientBottom: this.node.offsetHeight - this.node.clientHeight,
|
|
|
|
offsetRight: document.body.offsetWidth - this.node.offsetLeft - this.node.offsetWidth,
|
|
offsetBottom: window.innerHeight - this.node.offsetTop - this.node.offsetHeight,
|
|
};
|
|
metrics = Object.assign({}, getBoundingClientRect(this.node), metrics);
|
|
}
|
|
if(this.node instanceof Window) {
|
|
metrics = {
|
|
offsetHeight: window.innerHeight,
|
|
offsetWidth: document.body.offsetWidth,
|
|
clientHeight: window.innerHeight,
|
|
clientWidth: document.body.offsetWidth,
|
|
offsetLeft: 0,
|
|
offsetTop: 0,
|
|
clientLeft: 0,
|
|
clientTop: 0,
|
|
clientRight: 0,
|
|
clientBottom: 0,
|
|
|
|
offsetRight: 0,
|
|
offsetBottom: 0,
|
|
};
|
|
metrics = Object.assign({
|
|
left: 0,
|
|
top: 0,
|
|
right: document.body.offsetWidth,
|
|
bottom: window.innerHeight,
|
|
x: 0,
|
|
y: 0,
|
|
width: document.body.offsetWidth,
|
|
height: window.innerHeight,
|
|
}, metrics)
|
|
}
|
|
Object.assign(this, metrics);
|
|
return this;
|
|
};
|
|
Rect.prototype.getLine = function (axis = 'horizontal') {
|
|
var line = new Line(null, axis);
|
|
line.node = this.node;
|
|
line.rect = this;
|
|
line.setAxis(axis);
|
|
return line;
|
|
};
|
|
|
|
var viewLineDriver = {
|
|
horizontal: {
|
|
left: 'start',
|
|
right: 'end',
|
|
x: 'a',
|
|
width: 'length',
|
|
|
|
offsetWidth: 'offset',
|
|
clientWidth: 'client',
|
|
offsetLeft: 'offsetStart',
|
|
clientLeft: 'clientStart',
|
|
offsetRight: 'offsetEnd',
|
|
clientRight: 'clientEnd',
|
|
},
|
|
vertical: {
|
|
top: 'start',
|
|
bottom: 'end',
|
|
y: 'a',
|
|
height: 'length',
|
|
|
|
offsetHeight: 'offset',
|
|
clientHeight: 'client',
|
|
offsetTop: 'offsetStart',
|
|
clientTop: 'clientStart',
|
|
offsetBottom: 'offsetEnd',
|
|
clientBottom: 'clientEnd',
|
|
},
|
|
};
|
|
|
|
|
|
/**
|
|
* @param node
|
|
* @param axis
|
|
* @property start
|
|
* @property end
|
|
* @property a
|
|
* @property length
|
|
* @property offset
|
|
* @property client
|
|
* @property offsetStart
|
|
* @property clientStart
|
|
* @property offsetEnd
|
|
* @property clientEnd
|
|
*
|
|
*/
|
|
var Line = function (node, axis = 'horizontal') {
|
|
this.axis = axis;
|
|
this.node = node;
|
|
this.update();
|
|
};
|
|
Line.prototype.setAxis = function(axis){
|
|
this.axis = axis;
|
|
|
|
for (var key in viewLineDriver[this.axis]) {
|
|
this[viewLineDriver[this.axis][key]] = this.rect[key];
|
|
}
|
|
};
|
|
Line.prototype.update = function () {
|
|
if(!this.node) {
|
|
return this;
|
|
}
|
|
this.rect = new Rect(this.node);
|
|
this.setAxis(this.axis);
|
|
return this;
|
|
};
|
|
|
|
var metric = {
|
|
Rect: Rect,
|
|
Line: Line,
|
|
};
|
|
|
|
/**
|
|
*
|
|
* @class {AlignCalc}
|
|
* @param limiterNode {HTMLElement}
|
|
* @param parentNode {HTMLElement}
|
|
* @param node {HTMLElement}
|
|
*/
|
|
var AlignCalc = function (node, parentNode, limiterNode = window) {
|
|
|
|
var _this = this;
|
|
this.axis = 'horizontal';
|
|
this.direction = 'bottom';
|
|
this.align = 'bottom';
|
|
this.offset = [0, 0];
|
|
this.setLimiterNode(limiterNode);
|
|
this.setParentNode(parentNode);
|
|
this.setNode(node);
|
|
|
|
this.style = {
|
|
start: function (extra) {
|
|
this.nodeMetricsLine.node.style[this.map.style.start()] = '' + ((!this.valid && extra)?this.shiftExtra:this.shift) + 'px';
|
|
}.bind(this),
|
|
end: function (extra) {
|
|
this.nodeMetricsLine.node.style[this.map.style.end()] = 'unset';
|
|
}.bind(this),
|
|
};
|
|
|
|
this.map = {
|
|
style: {
|
|
start: function(){
|
|
switch (_this.axis) {
|
|
case 'vertical':
|
|
return 'top';
|
|
case 'horizontal':
|
|
return 'left';
|
|
}
|
|
},
|
|
end: function(){
|
|
switch (_this.axis) {
|
|
case 'vertical':
|
|
return 'bottom';
|
|
case 'horizontal':
|
|
return 'right';
|
|
}
|
|
},
|
|
},
|
|
direction: {
|
|
get: function () {
|
|
switch (_this.direction) {
|
|
case 'start':
|
|
case 'left':
|
|
case 'top':
|
|
return 'start';
|
|
case 'end':
|
|
case 'right':
|
|
case 'bottom':
|
|
return 'end';
|
|
}
|
|
}
|
|
},
|
|
align: {
|
|
get: function () {
|
|
switch (_this.align) {
|
|
case 'start':
|
|
case 'left':
|
|
case 'top':
|
|
return 'start';
|
|
case 'end':
|
|
case 'right':
|
|
case 'bottom':
|
|
return 'end';
|
|
}
|
|
}
|
|
},
|
|
axisNumber: function () {
|
|
return _this.axis === 'horizontal' ? 0 : 1;
|
|
}
|
|
};
|
|
|
|
var getShiftWhenHidingNodeUnderLimiterStart = function(shift) {
|
|
var d = this.parentMetricsLine.start + shift - this.limiterMetricsLine.start;
|
|
if(d < 0) {
|
|
shift = shift - d;
|
|
}
|
|
return shift;
|
|
}.bind(this);
|
|
|
|
this.core = {
|
|
direction: {
|
|
start: {
|
|
shift: function () {
|
|
return - this.nodeMetricsLine.offset - this.offset[this.map.axisNumber()];
|
|
}.bind(this),
|
|
applicability: function () {
|
|
return (this.parentMetricsLine.start + this.shift) - this.limiterMetricsLine.start;
|
|
}.bind(this),
|
|
valid: function () {
|
|
return (this.parentMetricsLine.start + this.shift) >= this.limiterMetricsLine.start;
|
|
}.bind(this),
|
|
shiftExtra: function () {
|
|
return getShiftWhenHidingNodeUnderLimiterStart(this.shift - this.applicability);
|
|
}.bind(this),
|
|
},
|
|
end: {
|
|
shift: function () {
|
|
return this.offset[this.map.axisNumber()];
|
|
}.bind(this),
|
|
applicability: function () {
|
|
return this.limiterMetricsLine.end - (this.parentMetricsLine.start + this.nodeMetricsLine.length + this.shift);
|
|
}.bind(this),
|
|
valid: function () {
|
|
return this.limiterMetricsLine.end >= (this.parentMetricsLine.start + this.nodeMetricsLine.length + this.shift);
|
|
}.bind(this),
|
|
shiftExtra: function () {
|
|
return getShiftWhenHidingNodeUnderLimiterStart(this.shift + this.applicability);
|
|
}.bind(this),
|
|
},
|
|
},
|
|
align: {
|
|
start: {
|
|
shift: function () {
|
|
return this.nodeMetricsLine.clientStart;
|
|
}.bind(this),
|
|
},
|
|
end: {
|
|
shift: function () {
|
|
return this.parentMetricsLine.offset - this.parentMetricsLine.clientStart;
|
|
}.bind(this),
|
|
},
|
|
},
|
|
};
|
|
|
|
return this;
|
|
};
|
|
/**
|
|
* @param node
|
|
* @return {AlignCalc}
|
|
*/
|
|
AlignCalc.prototype.setLimiterNode = function (node) {
|
|
if(!this.limiterMetricsLine || (this.limiterMetricsLine.node !== node)) {
|
|
this.limiterMetricsLine = new Line(node, this.axis);
|
|
}
|
|
return this;
|
|
};
|
|
/**
|
|
*
|
|
* @param node
|
|
* @return {AlignCalc}
|
|
*/
|
|
AlignCalc.prototype.setParentNode = function (node) {
|
|
if(!this.parentMetricsLine || (this.parentMetricsLine.node !== node)) {
|
|
this.parentMetricsLine = new Line(node, this.axis);
|
|
}
|
|
return this;
|
|
};
|
|
/**
|
|
*
|
|
* @param node
|
|
* @return {AlignCalc}
|
|
*/
|
|
AlignCalc.prototype.setNode = function (node) {
|
|
if(!this.nodeMetricsLine || (this.nodeMetricsLine.node !== node)) {
|
|
this.nodeMetricsLine = new Line(node, this.axis);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
*
|
|
* @param extra
|
|
* @return {AlignCalc}
|
|
*/
|
|
AlignCalc.prototype.render = function (extra = false) {
|
|
for (var key in this.style) {
|
|
if(this.style.hasOwnProperty(key)) {
|
|
this.style[key](extra);
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
/**
|
|
*
|
|
* @return {AlignCalc}
|
|
*/
|
|
AlignCalc.prototype.updateMetrics = function(){
|
|
this.limiterMetricsLine.update();
|
|
this.parentMetricsLine.update();
|
|
this.nodeMetricsLine.update();
|
|
return this;
|
|
};
|
|
/**
|
|
*
|
|
* @param direction
|
|
* @return {AlignCalc}
|
|
*/
|
|
AlignCalc.prototype.setDirection = function(direction){
|
|
this.direction = direction;
|
|
return this;
|
|
};
|
|
/**
|
|
*
|
|
* @param align
|
|
* @return {AlignCalc}
|
|
*/
|
|
AlignCalc.prototype.setAlign = function(align){
|
|
this.align = align;
|
|
return this;
|
|
};
|
|
/**
|
|
*
|
|
* @param axis
|
|
* @return {AlignCalc}
|
|
*/
|
|
AlignCalc.prototype.setAxis = function(axis){
|
|
this.axis = axis;
|
|
this.limiterMetricsLine.setAxis(axis);
|
|
this.parentMetricsLine.setAxis(axis);
|
|
this.nodeMetricsLine.setAxis(axis);
|
|
return this;
|
|
};
|
|
/**
|
|
*
|
|
* @param align
|
|
* @param direction
|
|
* @return {AlignCalc}
|
|
*/
|
|
AlignCalc.prototype.set = function(align, direction){
|
|
this.align = align;
|
|
this.direction = direction;
|
|
return this;
|
|
};
|
|
/**
|
|
*
|
|
* @return {AlignCalc}
|
|
*/
|
|
AlignCalc.prototype.calc = function () {
|
|
this.shift =
|
|
this.core.direction[this.map.direction.get()].shift() +
|
|
this.core.align[this.map.align.get()].shift();
|
|
|
|
this.applicability = this.core.direction[this.map.direction.get()].applicability();
|
|
|
|
this.valid = this.core.direction[this.map.direction.get()].valid();
|
|
|
|
this.shiftExtra = this.core.direction[this.map.direction.get()].shiftExtra();
|
|
|
|
return this;
|
|
};
|
|
/**
|
|
*
|
|
* @return {{valid: *, shiftExtra: *, shift: *, applicability: *}}
|
|
*/
|
|
AlignCalc.prototype.getData = function () {
|
|
return {
|
|
shift: this.shift,
|
|
applicability: this.applicability,
|
|
valid: this.valid,
|
|
shiftExtra: this.shiftExtra,
|
|
};
|
|
};
|
|
|
|
|
|
/**
|
|
* @param node {HTMLElement}
|
|
* @param parentNode {HTMLElement}
|
|
* @param limiterNode {HTMLElement}
|
|
* @constructor
|
|
*/
|
|
var AlignClass = function(node, parentNode, limiterNode = window)
|
|
{
|
|
this.changeOfDirection = true;
|
|
this.withinTheScreen = true;
|
|
this.offset = [0, 0];
|
|
|
|
this.status = {vertical: {}, horizontal: {},};
|
|
|
|
this.setNode(node);
|
|
this.setParentNode(parentNode);
|
|
this.setLimiterNode(limiterNode);
|
|
|
|
};
|
|
|
|
AlignClass.prototype.setNode = function(node){
|
|
this.unset();
|
|
this.node = node;
|
|
this.reset();
|
|
if(node instanceof HTMLElement) {
|
|
this.node.kit = this.node.kit || {};
|
|
this.node.kit.align = this;
|
|
}
|
|
if(this.calc) {
|
|
this.calc.setNode(this.node);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
AlignClass.prototype.setParentNode = function(node){
|
|
this.parentNode = node;
|
|
if(this.calc) {
|
|
this.calc.setParentNode(this.parentNode);
|
|
}
|
|
return this;
|
|
};
|
|
AlignClass.prototype.setLimiterNode = function(node){
|
|
this.limiterNode = node;
|
|
if(this.calc) {
|
|
this.calc.setLimiterNode(this.limiterNode);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
AlignClass.prototype.unset = function(){
|
|
if(this.node instanceof HTMLElement) {
|
|
if(this.node.kit && this.node.kit.align){
|
|
delete this.node.kit.align;
|
|
}
|
|
this.node.removeAttribute('data-kit-align-horizontal-direction');
|
|
this.node.removeAttribute('data-kit-align-horizontal-align');
|
|
this.node.removeAttribute('data-kit-align-vertical-direction');
|
|
this.node.removeAttribute('data-kit-align-vertical-align');
|
|
}
|
|
};
|
|
AlignClass.prototype.reset = function(bHorizontal = true, bVertical = true){
|
|
if(bHorizontal) {
|
|
this.status.horizontal = {};
|
|
if(this.node instanceof HTMLElement) {
|
|
this.node.style.left = '';
|
|
this.node.style.right = '';
|
|
this.node.removeAttribute('data-kit-align-horizontal-direction');
|
|
this.node.removeAttribute('data-kit-align-horizontal-align');
|
|
}
|
|
}
|
|
if(bVertical) {
|
|
this.status.vertical = {};
|
|
if(this.node instanceof HTMLElement) {
|
|
this.node.style.top = '';
|
|
this.node.style.bottom = '';
|
|
this.node.removeAttribute('data-kit-align-vertical-direction');
|
|
this.node.removeAttribute('data-kit-align-vertical-align');
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
|
|
AlignClass.prototype.set = function(hAlign, vAlign, hDirection, vDirection){
|
|
this.setEx('vertical', vDirection, vAlign);
|
|
this.setEx('horizontal', hDirection, hAlign);
|
|
return this;
|
|
};
|
|
AlignClass.prototype.setVertical = function(direction, align){
|
|
this.setEx('vertical', direction, align);
|
|
return this;
|
|
};
|
|
|
|
AlignClass.prototype.setHorizontal = function(direction, align){
|
|
this.setEx('horizontal', direction, align);
|
|
return this;
|
|
};
|
|
|
|
AlignClass.prototype.setEx = function(axis, direction, align){
|
|
|
|
this.reset(axis === 'horizontal', axis === 'vertical');
|
|
|
|
this.status[axis].direction = direction;
|
|
this.status[axis].align = align;
|
|
|
|
if(!this.calc) {
|
|
this.calc = new AlignCalc(this.node, this.parentNode, this.limiterNode);
|
|
}
|
|
|
|
this.calc.offset = this.offset;
|
|
this.node.setAttribute('data-kit-align-'+axis+'-direction', direction);
|
|
this.node.setAttribute('data-kit-align-'+axis+'-align', align);
|
|
var defAlignResult = this.calc.updateMetrics().setAxis(axis).setDirection(direction).setAlign(align).calc().getData();
|
|
// console.log('defAlignResult', axis, align, direction, defAlignResult);
|
|
if(this.changeOfDirection) {
|
|
if(!defAlignResult.valid) {
|
|
this.node.setAttribute('data-kit-align-'+axis+'-direction', this.getInverse(direction));
|
|
this.node.setAttribute('data-kit-align-'+axis+'-align', this.getInverse(align));
|
|
var inverseAlignResult = this.calc.updateMetrics().setDirection(this.getInverse(direction)).setAlign(this.getInverse(align)).calc().getData();
|
|
// console.log('inverseAlignResult', axis, this.getInverse(align), this.getInverse(direction), inverseAlignResult);
|
|
this.status[axis].direction = this.getInverse(direction);
|
|
this.status[axis].align = this.getInverse(align);
|
|
if(!inverseAlignResult.valid && (defAlignResult.applicability > inverseAlignResult.applicability) ) {
|
|
this.node.setAttribute('data-kit-align-'+axis+'-direction', direction);
|
|
this.node.setAttribute('data-kit-align-'+axis+'-align', align);
|
|
this.status[axis].direction = direction;
|
|
this.status[axis].align = align;
|
|
this.calc.updateMetrics().setDirection(direction).setAlign(align).calc();
|
|
}
|
|
}
|
|
}
|
|
|
|
this.calc.render(this.withinTheScreen);
|
|
return this;
|
|
};
|
|
|
|
AlignClass.prototype.getInverse = function(value){
|
|
var inverseConfig = {
|
|
start: 'end',
|
|
left: 'right',
|
|
top: 'bottom',
|
|
end: 'start',
|
|
right: 'left',
|
|
bottom: 'top',
|
|
};
|
|
if(inverseConfig.hasOwnProperty(value)) {
|
|
return inverseConfig[value];
|
|
}
|
|
return value;
|
|
};
|
|
|
|
|
|
/**
|
|
* @memberOf kit
|
|
*/
|
|
var align = {
|
|
Class: AlignClass,
|
|
getInstance: function (node, parentNode) {
|
|
if(node instanceof HTMLElement && node.kit && node.kit.align) {
|
|
return node.kit.align;
|
|
}
|
|
return new AlignClass(node, parentNode);
|
|
},
|
|
metric: metric,
|
|
};
|
|
|
|
module.exports = align;
|
|
|
|
return module.exports;
|
|
});
|