radio/lorgar/views/gridLayout.js

501 lines
21 KiB
JavaScript

"use strict";
(function gridLayout_js() {
var moduleName = "views/gridLayout";
var defineArray = [];
defineArray.push("views/view");
defineArray.push("views/layout");
define(moduleName, defineArray, function gridLayout_module() {
var View = require("views/view");
var Layout = require("views/layout");
var GridLayout = Layout.inherit({
"className": "GridLayout",
"constructor": function(controller, options) {
var base = {
};
W.extend(base, options);
Layout.fn.constructor.call(this, controller, base);
this._lay = [[[]]];
this._cols = [{}];
this._rows = [{}];
},
"append": function(child, row, col, rowSpan, colSpan, aligment) {
aligment = aligment || 5;
this._addChild(child, aligment);
rowSpan = rowSpan || 1;
colSpan = colSpan || 1;
var tRow = row + rowSpan;
var tCol = col + colSpan;
while (this._lay.length < tRow) {
this._lay.push([]);
}
for (var i = 0; i < this._lay.length; ++i) {
while (this._lay[i].length < tCol) {
this._lay[i].push([]);
}
}
var obj = {
child: child,
colspan: colSpan,
rowspan: rowSpan,
a: aligment
}
for (i = row; i < tRow; ++i) {
for (var j = col; j < tCol; ++j) {
this._lay[i][j].push(obj);
}
}
this._recountLimits();
if (this._w !== undefined && this._h !== undefined) {
this.refreshLay();
}
},
"_cleanupLay": function() {
var i;
var rowsC = false;
var colsC = false;
while (!rowsC) {
for (i = 0; i < this._lay[this._lay.length - 1].length; ++i) {
if (this._lay[this._lay.length - 1][i].length) {
rowsC = true;
break;
}
}
if (!rowsC) {
this._lay.pop()
rowsC = !this._lay.length;
colsC = !this._lay.length;
}
}
while (!colsC) {
for (i = 0; i < this._lay.length; ++i) {
if (this._lay[i][this._lay[i].length - 1].length) {
colsC = true;
break;
}
}
if (!colsC) {
for (i = 0; i < this._lay.length; ++i) {
this._lay[i].pop();
}
}
}
},
"clear": function() {
Layout.fn.clear.call(this);
this._lay = [[[]]];
this._cols = [{}];
this._rows = [{}];
},
"_onChildChangeLimits": function() {
var notification = this._recountLimits();
if (!notification) {
this.refreshLay();
}
},
"_positionElements": function() {
var shiftW = 0;
var shiftH = 0;
var positioned = [];
for (var i = 0; i < this._lay.length; ++i) {
shiftW = 0;
for (var j = 0; j < this._lay[i].length; ++j) {
for (var k = 0; k < this._lay[i][j].length; ++k) {
var e = this._lay[i][j][k];
var child = e.child;
if (positioned.indexOf(child) === -1) {
var tWidth = 0;
var tHeight = 0;
var s;
for (s = 0; s < e.colspan; ++s) {
tWidth += this._cols[j + s].cur;
}
for (s = 0; s < e.rowspan; ++s) {
tHeight += this._rows[i + s].cur;
}
child.setSize(tWidth, tHeight);
switch (e.a) {
case Layout.Aligment.LeftTop:
child.setTop(shiftH);
child.setLeft(shiftW);
break;
case Layout.Aligment.LeftCenter:
child.setTop(shiftH + (tHeight - child._h) / 2);
child.setLeft(shiftW);
break;
case Layout.Aligment.LeftBottom:
child.setTop(shiftH + (tHeight - child._h));
child.setLeft(shiftW);
break;
case Layout.Aligment.CenterTop:
child.setTop(shiftH);
child.setLeft(shiftW + (tWidth - child._w) / 2);
break;
case Layout.Aligment.CenterCenter:
child.setTop(shiftH + (tHeight - child._h) / 2);
child.setLeft(shiftW + (tWidth - child._w) / 2);
break;
case Layout.Aligment.CenterBottom:
child.setTop(shiftH + (tHeight - child._h));
child.setLeft((tWidth - child._w) / 2);
break;
case Layout.Aligment.RightTop:
child.setTop(shiftH);
child.setLeft(shiftW + (tWidth - child._h));
break;
case Layout.Aligment.RightCenter:
child.setTop((tHeight - child._h) / 2);
child.setLeft(shiftW + (tWidth - child._h));
break;
case Layout.Aligment.RightBottom:
child.setTop(shiftH + (tHeight - child._h));
child.setLeft(shiftW + (tWidth - child._h));
break;
}
positioned.push(child);
}
}
shiftW += this._cols[j].cur;
}
shiftH += this._rows[i].cur;
}
},
"_recountLimits": function() {
this._cols = [];
this._rows = [];
var i, j;
var multiCols = [];
var multiRows = [];
var proccessed = Object.create(null);
for (i = 0; i < this._lay.length; ++i) {
while (!this._rows[i]) {
this._rows.push({});
}
for (j = 0; j < this._lay[i].length; ++j) {
while (!this._cols[j]) {
this._cols.push({});
}
for (var k = 0; k < this._lay[i][j].length; ++k) {
var e = this._lay[i][j][k];
if (proccessed[e.child._id]) {
this._cols[j].min = this._cols[j].min || 0;
this._rows[i].min = this._rows[i].min || 0;
this._cols[j].max = this._cols[j].max || 0;
this._rows[i].max = this._rows[i].max || 0;
continue;
}
var colMinW = this._cols[j].min || 0;
var rowMinH = this._rows[i].min || 0;
var colMaxW = this._cols[j].max || Infinity;
var rowMaxH = this._rows[i].max || Infinity;
if (e.colspan === 1) {
this._cols[j].min = Math.max(colMinW, e.child._o.minWidth);
this._cols[j].max = Math.min(colMaxW, e.child._o.maxWidth);
} else {
this._cols[j].min = colMinW;
this._cols[j].max = colMaxW;
multiCols.push({
p: j,
e: e
});
}
if (e.rowspan === 1) {
this._rows[i].min = Math.max(rowMinH, e.child._o.minHeight);
this._rows[i].max = Math.min(rowMaxH, e.child._o.maxHeight);
} else {
this._rows[i].min = rowMinH;
this._rows[i].max = rowMaxH;
multiRows.push({
p: i,
e: e
});
}
proccessed[e.child._id] = true;
}
if (!this._lay[i][j].length) {
this._cols[j].min = this._cols[j].min || 0;
this._rows[i].min = this._rows[i].min || 0;
this._cols[j].max = this._cols[j].max || 0;
this._rows[i].max = this._rows[i].max || 0;
}
}
}
for (i = 0; i < multiCols.length; ++i) {
var e = multiCols[i].e;
var pos = multiCols[i].p;
var span = e.colspan;
var target = pos + span;
var minSize = 0;
var maxSize = 0;
var flexibleColls = [];
for (j = pos; j < target; ++j) {
minSize += this._cols[j].min;
maxSize += this._cols[j].max;
if (this._cols[j].min < this._cols[j].max) {
flexibleColls.push(this._cols[j]);
}
}
var leftMin = e.child._o.minWidth - minSize;
if (leftMin > 0) {
while (leftMin > 0 && flexibleColls.length > 0) {
var portion = leftMin / flexibleColls.length;
for (j = 0; j < flexibleColls.length; ++j) {
var lPortion = Math.min(flexibleColls[j].max, portion);
flexibleColls[j].min += lPortion;
leftMin -= lPortion;
if (flexibleColls[j].min === flexibleColls[j].max) {
flexibleColls.splice(j, 1);
break;
}
}
if (leftMin < 1) {
leftMin = 0;
}
}
}
var leftMax = maxSize - e.child._o.maxWidth
if (leftMax > 0) {
while (leftMax > 0 && flexibleColls.length > 0) {
var portion = leftMax / flexibleColls.length;
for (j = 0; j < flexibleColls.length; ++j) {
var lPortion = Math.max(flexibleColls[j].min, portion);
flexibleColls[j].max -= lPortion;
leftMax -= lPortion;
if (flexibleColls[j].min === flexibleColls[j].max) {
flexibleColls.splice(j, 1);
break;
}
}
if (leftMax < 1) {
leftMax = 0;
}
}
}
}
for (i = 0; i < multiRows.length; ++i) {
var e = multiRows[i].e;
var pos = multiRows[i].p;
var span = e.rowspan;
var target = pos + span;
var minSize = 0;
var maxSize = 0;
var flexibleRows = [];
for (j = pos; j < target; ++j) {
minSize += this._rows[j].min;
maxSize += this._rows[j].max;
if (this._rows[j].min < this._rows[j].max) {
flexibleRows.push(this._rows[j]);
}
}
var leftMin = e.child._o.minHeigh - minSize;
if (leftMin > 0) {
while (leftMin > 0 && flexibleRows.length > 0) {
var portion = leftMin / flexibleRows.length;
for (j = 0; j < flexibleRows.length; ++j) {
var lPortion = Math.min(flexibleRows[j].max, portion);
flexibleRows[j].min += lPortion;
leftMin -= lPortion;
if (flexibleRows[j].min === flexibleRows[j].max) {
flexibleRows.splice(j, 1);
break;
}
}
if (leftMin < 1) {
leftMin = 0;
}
}
}
var leftMax = maxSize - e.child._o.maxHeigh
if (leftMax > 0) {
while (leftMax > 0 && flexibleRows.length > 0) {
var portion = leftMax / flexibleRows.length;
for (j = 0; j < flexibleRows.length; ++j) {
var lPortion = Math.max(flexibleRows[j].min, portion);
flexibleRows[j].max -= lPortion;
leftMax -= lPortion;
if (flexibleRows[j].min === flexibleRows[j].max) {
flexibleRows.splice(j, 1);
break;
}
}
if (leftMax < 1) {
leftMax = 0;
}
}
}
}
var minWidth = 0;
var minHeight = 0;
var maxWidth = 0;
var maxHeight = 0;
for (i = 0; i < this._rows.length; ++i) {
minHeight += this._rows[i].min;
this._rows[i].max = Math.max(this._rows[i].max, this._rows[i].min);
maxHeight += this._rows[i].max;
}
for (i = 0; i < this._cols.length; ++i) {
minWidth += this._cols[i].min;
this._cols[i].max = Math.max(this._cols[i].max, this._cols[i].min);
maxWidth += this._cols[i].max;
}
return this._setLimits(minWidth, minHeight, maxWidth, maxHeight);
},
"refreshLay": function() {
var totalMaxW = 0;
var totalMaxH = 0;
var totalMinW = 0;
var totalMinH = 0;
var i;
for (i = 0; i < this._cols.length; ++i) {
totalMaxW += this._cols[i].max;
totalMinW += this._cols[i].min
}
for (i = 0; i < this._rows.length; ++i) {
totalMaxH += this._rows[i].max;
totalMinH += this._rows[i].min;
}
if (this._w <= totalMinW || this._w >= totalMaxW) {
var kW;
var keyW;
if (this._w <= totalMinW) {
kW = this._w / totalMinW;
keyW = "min";
} else {
kW = this._w / totalMaxW;
keyW = "max";
}
for (i = 0; i < this._cols.length; ++i) {
this._cols[i].cur = this._cols[i][keyW] * kW;
}
} else {
distribute(this._w, this._cols);
}
if (this._h <= totalMinH || this._h >= totalMaxH) {
var kH;
var keyH;
if (this._h <= totalMinH) {
kH = this._h / totalMinH;
keyH = "min";
} else {
kH = this._h / totalMaxH;
keyH = "max";
}
for (i = 0; i < this._rows.length; ++i) {
this._rows[i].cur = this._rows[i][keyH] * kH;
}
} else {
distribute(this._h, this._rows);
}
this._positionElements();
},
"removeChild": function(child) {
Layout.fn.removeChild.call(this, child);
for (var i = 0; i < this._lay.length; ++i) {
for (var j = 0; j < this._lay[i].length; ++j) {
for (var k = 0; k < this._lay[i][j].length; ++k) {
if (child === this._lay[i][j][k].child) {
this._lay[i][j].splice(k, 1);
}
}
}
}
this._cleanupLay();
this._recountLimits();
this.refreshLay();
},
"setSize": function(w, h) {
View.fn.setSize.call(this, w, h);
if (this._c.length) {
this.refreshLay();
}
}
});
function distribute (size, array) {
var i, portion;
var cMax = [];
for (i = 0; i < array.length; ++i) {
array[i].cur = array[i].min;
size -= array[i].min;
if (array[i].cur < array[i].max) {
cMax.push(array[i]);
}
}
cMax.sort(GridLayout._candidatesSortMax);
while (cMax.length && size) {
portion = size / cMax.length;
var last = cMax[cMax.length -1];
if (portion >= last.max) {
size -= last.max - last.cur;
last.cur = last.max;
cMax.pop();
} else {
for (i = 0; i < cMax.length; ++i) {
cMax[i].cur += portion;
}
size = 0;
}
}
if (size) {
portion = size / array.length;
for (i = 0; i < array.length; ++i) {
array.cur += portion;
}
}
}
GridLayout._candidatesSortMax = function(a, b) {
return b.max - a.max;
}
return GridLayout;
});
})();