/**
_enyo.FittableLayout_ provides the base positioning and boundary logic for
the fittable layout strategy. The fittable layout strategy is based on
laying out items in either a set of rows or a set of columns, with most of
the items having natural size, but one item expanding to fill the remaining
space. The item that expands is labeled with the attribute _fit: true_.
For example, in the following kind, the second component fills the available
space in the container between the first and third components.
enyo.kind({
kind: "FittableRows",
components: [
{content: "1"},
{content: "2", fit:true},
{content: "3"}
]
});
enyo.FittableColumnsLayout and
enyo.FittableRowsLayout (or their
subkinds) are used for layout rather than _enyo.FittableLayout_ because they
specify properties that _enyo.FittableLayout_ expects to be available when
laying items out.
*/
enyo.kind({
name: "enyo.FittableLayout",
kind: "Layout",
//* @protected
calcFitIndex: function() {
for (var i=0, c$=this.container.children, c; c=c$[i]; i++) {
if (c.fit && c.showing) {
return i;
}
}
},
getFitControl: function() {
var c$=this.container.children;
var f = c$[this.fitIndex];
if (!(f && f.fit && f.showing)) {
this.fitIndex = this.calcFitIndex();
f = c$[this.fitIndex];
}
return f;
},
getLastControl: function() {
var c$=this.container.children;
var i = c$.length-1;
var c = c$[i];
while ((c=c$[i]) && !c.showing) {
i--;
}
return c;
},
_reflow: function(measure, cMeasure, mAttr, nAttr) {
this.container.addRemoveClass("enyo-stretch", !this.container.noStretch);
var f = this.getFitControl();
// no sizing if nothing is fit.
if (!f) {
return;
}
//
// determine container size, available space
var s=0, a=0, b=0, p;
var n = this.container.hasNode();
// calculate available space
if (n) {
// measure 1
p = enyo.FittableLayout.calcPaddingExtents(n);
// measure 2
s = n[cMeasure] - (p[mAttr] + p[nAttr]);
//console.log("overall size", s);
}
//
// calculate space above fitting control
// measure 3
var fb = f.getBounds();
// offset - container padding.
a = fb[mAttr] - ((p && p[mAttr]) || 0);
//console.log("above", a);
//
// calculate space below fitting control
var l = this.getLastControl();
if (l) {
// measure 4
var mb = enyo.FittableLayout.getComputedStyleValue(l.hasNode(), "margin", nAttr) || 0;
if (l != f) {
// measure 5
var lb = l.getBounds();
// fit offset + size
var bf = fb[mAttr] + fb[measure];
// last offset + size + ending margin
var bl = lb[mAttr] + lb[measure] + mb;
// space below is bottom of last item - bottom of fit item.
b = bl - bf;
} else {
b = mb;
}
}
// calculate appropriate size for fit control
var fs = s - (a + b);
//console.log(f.id, fs);
// note: must be border-box;
f.applyStyle(measure, fs + "px");
},
//* @public
/**
Updates the layout to reflect any changes to contained components or the
layout container.
*/
reflow: function() {
if (this.orient == "h") {
this._reflow("width", "clientWidth", "left", "right");
} else {
this._reflow("height", "clientHeight", "top", "bottom");
}
},
statics: {
//* @protected
_ieCssToPixelValue: function(inNode, inValue) {
var v = inValue;
// From the awesome hack by Dean Edwards
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
var s = inNode.style;
// store style and runtime style values
var l = s.left;
var rl = inNode.runtimeStyle && inNode.runtimeStyle.left;
// then put current style in runtime style.
if (rl) {
inNode.runtimeStyle.left = inNode.currentStyle.left;
}
// apply given value and measure its pixel value
s.left = v;
v = s.pixelLeft;
// finally restore previous state
s.left = l;
if (rl) {
s.runtimeStyle.left = rl;
}
return v;
},
_pxMatch: /px/i,
getComputedStyleValue: function(inNode, inProp, inBoundary, inComputedStyle) {
var s = inComputedStyle || enyo.dom.getComputedStyle(inNode);
if (s) {
return parseInt(s.getPropertyValue(inProp + "-" + inBoundary));
} else if (inNode && inNode.currentStyle) {
var v = inNode.currentStyle[inProp + enyo.cap(inBoundary)];
if (!v.match(this._pxMatch)) {
v = this._ieCssToPixelValue(inNode, v);
}
return parseInt(v);
}
return 0;
},
//* Gets the boundaries of a node's margin or padding box.
calcBoxExtents: function(inNode, inBox) {
var s = enyo.dom.getComputedStyle(inNode);
return {
top: this.getComputedStyleValue(inNode, inBox, "top", s),
right: this.getComputedStyleValue(inNode, inBox, "right", s),
bottom: this.getComputedStyleValue(inNode, inBox, "bottom", s),
left: this.getComputedStyleValue(inNode, inBox, "left", s)
};
},
//* Gets the calculated padding of a node.
calcPaddingExtents: function(inNode) {
return this.calcBoxExtents(inNode, "padding");
},
//* Gets the calculated margin of a node.
calcMarginExtents: function(inNode) {
return this.calcBoxExtents(inNode, "margin");
}
}
});
/**
_enyo.FittableColumnsLayout_ provides a container in which items are laid
out in a set of vertical columns, with most of the items having natural
size, but one expanding to fill the remaining space. The one that expands is
labeled with the attribute _fit: true_.
_enyo.FittableColumnsLayout_ is meant to be used as a value for the
_layoutKind_ property of other kinds. _layoutKind_ provides a way to add
layout behavior in a pluggable fashion while retaining the ability to use a
specific base kind.
For example, the following code will align three components as columns, with
the second filling the available container space between the first and third.
enyo.kind({
kind: enyo.Control,
layoutKind: "FittableColumnsLayout",
components: [
{content: "1"},
{content: "2", fit:true},
{content: "3"}
]
});
Alternatively, if a specific base kind is not needed, then instead of
setting the _layoutKind_ attribute, you can set the base kind to
enyo.FittableColumns:
enyo.kind({
kind: "FittableColumns",
components: [
{content: "1"},
{content: "2", fit:true},
{content: "3"}
]
});
*/
enyo.kind({
name: "enyo.FittableColumnsLayout",
kind: "FittableLayout",
orient: "h",
layoutClass: "enyo-fittable-columns-layout"
});
/**
_enyo.FittableRowsLayout_ provides a container in which items are laid out
in a set of horizontal rows, with most of the items having natural size, but
one expanding to fill the remaining space. The one that expands is labeled
with the attribute _fit: true_.
_enyo.FittableRowsLayout_ is meant to be used as a value for the
_layoutKind_ property of other kinds. _layoutKind_ provides a way to add
layout behavior in a pluggable fashion while retaining the ability to use a
specific base kind.
For example, the following code will align three components as rows, with
the second filling the available container space between the first and third.
enyo.kind({
kind: enyo.Control,
layoutKind: "FittableRowsLayout",
components: [
{content: "1"},
{content: "2", fit:true},
{content: "3"}
]
});
Alternatively, if a specific base kind is not needed, then instead of
setting the _layoutKind_ attribute, you can set the base kind to
enyo.FittableRows:
enyo.kind({
kind: "FittableRows",
components: [
{content: "1"},
{content: "2", fit:true},
{content: "3"}
]
});
*/
enyo.kind({
name: "enyo.FittableRowsLayout",
kind: "FittableLayout",
layoutClass: "enyo-fittable-rows-layout",
orient: "v"
});