Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/html/lib/layout/panels/source/Panels.js
diff options
context:
space:
mode:
Diffstat (limited to 'html/lib/layout/panels/source/Panels.js')
-rw-r--r--html/lib/layout/panels/source/Panels.js386
1 files changed, 386 insertions, 0 deletions
diff --git a/html/lib/layout/panels/source/Panels.js b/html/lib/layout/panels/source/Panels.js
new file mode 100644
index 0000000..d2e2ecc
--- /dev/null
+++ b/html/lib/layout/panels/source/Panels.js
@@ -0,0 +1,386 @@
+/**
+The enyo.Panels kind is designed to satisfy a variety of common use cases for
+application layout. Using enyo.Panels, controls may be arranged as (among other
+things) a carousel, a set of collapsing panels, a card stack that fades between
+panels, or a grid.
+
+Any Enyo control may be placed inside an enyo.Panels, but by convention we refer
+to each of these controls as a "panel." From the set of panels in an enyo.Panels,
+one is considered active. The active panel is set by index using the *setIndex*
+method. The actual layout of the panels typically changes each time the active
+panel is set, such that the new active panel has the most prominent position.
+
+For more information, see the [Panels documentation](https://github.com/enyojs/enyo/wiki/Panels)
+in the Enyo Developer Guide.
+*/
+enyo.kind({
+ name: "enyo.Panels",
+ classes: "enyo-panels",
+ published: {
+ /**
+ The index of the active panel. The layout of panels is controlled by
+ the layoutKind, but as a rule, the active panel is displayed in the
+ most prominent position. For example, in the (default) CardArranger
+ layout, the active panel is shown and the other panels are hidden.
+ */
+ index: 0,
+ //* Controls whether the user can drag between panels.
+ draggable: true,
+ //* Controls whether the panels animate when transitioning; for example,
+ //* when _setIndex_ is called.
+ animate: true,
+ //* Controls whether panels "wrap around" when moving past the end. Actual effect depends upon the arranger in use.
+ wrap: false,
+ //* Sets the arranger kind to be used for dynamic layout.
+ arrangerKind: "CardArranger",
+ //* By default, each panel will be sized to fit the Panels' width when
+ //* the screen size is narrow enough (less than ~800px). Set to false
+ //* to avoid this behavior.
+ narrowFit: true
+ },
+ events: {
+ /**
+ Fires at the start of a panel transition.
+ This event fires when _setIndex_ is called and also during dragging.
+ */
+ onTransitionStart: "",
+ /**
+ Fires at the end of a panel transition.
+ This event fires when _setIndex_ is called and also during dragging.
+ */
+ onTransitionFinish: ""
+ },
+ //* @protected
+ handlers: {
+ ondragstart: "dragstart",
+ ondrag: "drag",
+ ondragfinish: "dragfinish"
+ },
+ tools: [
+ {kind: "Animator", onStep: "step", onEnd: "completed"}
+ ],
+ fraction: 0,
+ create: function() {
+ this.transitionPoints = [];
+ this.inherited(arguments);
+ this.arrangerKindChanged();
+ this.avoidFitChanged();
+ this.indexChanged();
+ },
+ initComponents: function() {
+ this.createChrome(this.tools);
+ this.inherited(arguments);
+ },
+ arrangerKindChanged: function() {
+ this.setLayoutKind(this.arrangerKind);
+ },
+ avoidFitChanged: function() {
+ this.addRemoveClass("enyo-panels-fit-narrow", this.narrowFit);
+ },
+ removeControl: function(inControl) {
+ this.inherited(arguments);
+ if (this.controls.length > 1 && this.isPanel(inControl)) {
+ this.setIndex(Math.max(this.index - 1, 0));
+ this.flow();
+ this.reflow();
+ }
+ },
+ isPanel: function() {
+ // designed to be overridden in kinds derived from Panels that have
+ // non-panel client controls
+ return true;
+ },
+ flow: function() {
+ this.arrangements = [];
+ this.inherited(arguments);
+ },
+ reflow: function() {
+ this.arrangements = [];
+ this.inherited(arguments);
+ this.refresh();
+ },
+ //* @public
+ /**
+ Returns an array of contained panels.
+ Subclasses can override this if they don't want the arranger to layout all of their children
+ */
+ getPanels: function() {
+ var p = this.controlParent || this;
+ return p.children;
+ },
+ //* Returns a reference to the active panel--i.e., the panel at the specified index.
+ getActive: function() {
+ var p$ = this.getPanels();
+ return p$[this.index];
+ },
+ /**
+ Returns a reference to the <a href="#enyo.Animator">enyo.Animator</a>
+ instance used to animate panel transitions. The Panels' animator can be used
+ to set the duration of panel transitions, e.g.:
+
+ this.getAnimator().setDuration(1000);
+ */
+ getAnimator: function() {
+ return this.$.animator;
+ },
+ /**
+ Sets the active panel to the panel specified by the given index.
+ Note that if the _animate_ property is set to true, the active panel
+ will animate into view.
+ */
+ setIndex: function(inIndex) {
+ // override setIndex so that indexChanged is called
+ // whether this.index has actually changed or not
+ this.setPropertyValue("index", inIndex, "indexChanged");
+ },
+ /**
+ Sets the active panel to the panel specified by the given index.
+ Regardless of the value of the _animate_ property, the transition to the
+ next panel will not animate and will be immediate.
+ */
+ setIndexDirect: function(inIndex) {
+ this.setIndex(inIndex);
+ this.completed();
+ },
+ //* Transitions to the previous panel--i.e., the panel whose index value is
+ //* one less than that of the current active panel.
+ previous: function() {
+ this.setIndex(this.index-1);
+ },
+ //* Transitions to the next panel--i.e., the panel whose index value is one
+ //* greater than that of the current active panel.
+ next: function() {
+ this.setIndex(this.index+1);
+ },
+ //* @protected
+ clamp: function(inValue) {
+ var l = this.getPanels().length-1;
+ if (this.wrap) {
+ // FIXME: dragging makes assumptions about direction and from->start indexes.
+ //return inValue < 0 ? l : (inValue > l ? 0 : inValue);
+ return inValue;
+ } else {
+ return Math.max(0, Math.min(inValue, l));
+ }
+ },
+ indexChanged: function(inOld) {
+ this.lastIndex = inOld;
+ this.index = this.clamp(this.index);
+ if (!this.dragging) {
+ if (this.$.animator.isAnimating()) {
+ this.completed();
+ }
+ this.$.animator.stop();
+ if (this.hasNode()) {
+ if (this.animate) {
+ this.startTransition();
+ this.$.animator.play({
+ startValue: this.fraction
+ });
+ } else {
+ this.refresh();
+ }
+ }
+ }
+ },
+ step: function(inSender) {
+ this.fraction = inSender.value;
+ this.stepTransition();
+ },
+ completed: function() {
+ if (this.$.animator.isAnimating()) {
+ this.$.animator.stop();
+ }
+ this.fraction = 1;
+ this.stepTransition();
+ this.finishTransition();
+ },
+ dragstart: function(inSender, inEvent) {
+ if (this.draggable && this.layout && this.layout.canDragEvent(inEvent)) {
+ inEvent.preventDefault();
+ this.dragstartTransition(inEvent);
+ this.dragging = true;
+ this.$.animator.stop();
+ return true;
+ }
+ },
+ drag: function(inSender, inEvent) {
+ if (this.dragging) {
+ inEvent.preventDefault();
+ this.dragTransition(inEvent);
+ }
+ },
+ dragfinish: function(inSender, inEvent) {
+ if (this.dragging) {
+ this.dragging = false;
+ inEvent.preventTap();
+ this.dragfinishTransition(inEvent);
+ }
+ },
+ dragstartTransition: function(inEvent) {
+ if (!this.$.animator.isAnimating()) {
+ var f = this.fromIndex = this.index;
+ this.toIndex = f - (this.layout ? this.layout.calcDragDirection(inEvent) : 0);
+ } else {
+ this.verifyDragTransition(inEvent);
+ }
+ this.fromIndex = this.clamp(this.fromIndex);
+ this.toIndex = this.clamp(this.toIndex);
+ //this.log(this.fromIndex, this.toIndex);
+ this.fireTransitionStart();
+ if (this.layout) {
+ this.layout.start();
+ }
+ },
+ dragTransition: function(inEvent) {
+ // note: for simplicity we choose to calculate the distance directly between
+ // the first and last transition point.
+ var d = this.layout ? this.layout.calcDrag(inEvent) : 0;
+ var t$ = this.transitionPoints, s = t$[0], f = t$[t$.length-1];
+ var as = this.fetchArrangement(s);
+ var af = this.fetchArrangement(f);
+ var dx = this.layout ? this.layout.drag(d, s, as, f, af) : 0;
+ var dragFail = d && !dx;
+ if (dragFail) {
+ //this.log(dx, s, as, f, af);
+ }
+ this.fraction += dx;
+ var fr = this.fraction;
+ if (fr > 1 || fr < 0 || dragFail) {
+ if (fr > 0 || dragFail) {
+ this.dragfinishTransition(inEvent);
+ }
+ this.dragstartTransition(inEvent);
+ this.fraction = 0;
+ // FIXME: account for lost fraction
+ //this.dragTransition(inEvent);
+ }
+ this.stepTransition();
+ },
+ dragfinishTransition: function(inEvent) {
+ this.verifyDragTransition(inEvent);
+ this.setIndex(this.toIndex);
+ // note: if we're still dragging, then we're at a transition boundary
+ // and should fire the finish event
+ if (this.dragging) {
+ this.fireTransitionFinish();
+ }
+ },
+ verifyDragTransition: function(inEvent) {
+ var d = this.layout ? this.layout.calcDragDirection(inEvent) : 0;
+ var f = Math.min(this.fromIndex, this.toIndex);
+ var t = Math.max(this.fromIndex, this.toIndex);
+ if (d > 0) {
+ var s = f;
+ f = t;
+ t = s;
+ }
+ if (f != this.fromIndex) {
+ this.fraction = 1 - this.fraction;
+ }
+ //this.log("old", this.fromIndex, this.toIndex, "new", f, t);
+ this.fromIndex = f;
+ this.toIndex = t;
+ },
+ refresh: function() {
+ if (this.$.animator.isAnimating()) {
+ this.$.animator.stop();
+ }
+ this.startTransition();
+ this.fraction = 1;
+ this.stepTransition();
+ this.finishTransition();
+ },
+ startTransition: function() {
+ this.fromIndex = this.fromIndex != null ? this.fromIndex : this.lastIndex || 0;
+ this.toIndex = this.toIndex != null ? this.toIndex : this.index;
+ //this.log(this.id, this.fromIndex, this.toIndex);
+ if (this.layout) {
+ this.layout.start();
+ }
+ this.fireTransitionStart();
+ },
+ finishTransition: function() {
+ if (this.layout) {
+ this.layout.finish();
+ }
+ this.transitionPoints = [];
+ this.fraction = 0;
+ this.fromIndex = this.toIndex = null;
+ this.fireTransitionFinish();
+ },
+ fireTransitionStart: function() {
+ var t = this.startTransitionInfo;
+ if (this.hasNode() && (!t || (t.fromIndex != this.fromIndex || t.toIndex != this.toIndex))) {
+ this.startTransitionInfo = {fromIndex: this.fromIndex, toIndex: this.toIndex};
+ this.doTransitionStart(enyo.clone(this.startTransitionInfo));
+ }
+ },
+ fireTransitionFinish: function() {
+ var t = this.finishTransitionInfo;
+ if (this.hasNode() && (!t || (t.fromIndex != this.lastIndex || t.toIndex != this.index))) {
+ this.finishTransitionInfo = {fromIndex: this.lastIndex, toIndex: this.index};
+ this.doTransitionFinish(enyo.clone(this.finishTransitionInfo));
+ }
+ this.lastIndex=this.index;
+ },
+ // gambit: we interpolate between arrangements as needed.
+ stepTransition: function() {
+ if (this.hasNode()) {
+ // select correct transition points and normalize fraction.
+ var t$ = this.transitionPoints;
+ var r = (this.fraction || 0) * (t$.length-1);
+ var i = Math.floor(r);
+ r = r - i;
+ var s = t$[i], f = t$[i+1];
+ // get arrangements and lerp between them
+ var s0 = this.fetchArrangement(s);
+ var s1 = this.fetchArrangement(f);
+ this.arrangement = s0 && s1 ? enyo.Panels.lerp(s0, s1, r) : (s0 || s1);
+ if (this.arrangement && this.layout) {
+ this.layout.flowArrangement();
+ }
+ }
+ },
+ fetchArrangement: function(inName) {
+ if ((inName != null) && !this.arrangements[inName] && this.layout) {
+ this.layout._arrange(inName);
+ this.arrangements[inName] = this.readArrangement(this.getPanels());
+ }
+ return this.arrangements[inName];
+ },
+ readArrangement: function(inC) {
+ var r = [];
+ for (var i=0, c$=inC, c; (c=c$[i]); i++) {
+ r.push(enyo.clone(c._arranger));
+ }
+ return r;
+ },
+ statics: {
+ isScreenNarrow: function() {
+ return enyo.dom.getWindowWidth() <= 800;
+ },
+ lerp: function(inA0, inA1, inFrac) {
+ var r = [];
+ for (var i=0, k$=enyo.keys(inA0), k; (k=k$[i]); i++) {
+ r.push(this.lerpObject(inA0[k], inA1[k], inFrac));
+ }
+ return r;
+ },
+ lerpObject: function(inNew, inOld, inFrac) {
+ var b = enyo.clone(inNew), n, o;
+ // inOld might be undefined when deleting panels
+ if (inOld) {
+ for (var i in inNew) {
+ n = inNew[i];
+ o = inOld[i];
+ if (n != o) {
+ b[i] = n - (n - o) * inFrac;
+ }
+ }
+ }
+ return b;
+ }
+ }
+});
+