Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/html/lib/layout/slideable/source/Slideable.js
diff options
context:
space:
mode:
Diffstat (limited to 'html/lib/layout/slideable/source/Slideable.js')
-rw-r--r--html/lib/layout/slideable/source/Slideable.js312
1 files changed, 312 insertions, 0 deletions
diff --git a/html/lib/layout/slideable/source/Slideable.js b/html/lib/layout/slideable/source/Slideable.js
new file mode 100644
index 0000000..4ef0a08
--- /dev/null
+++ b/html/lib/layout/slideable/source/Slideable.js
@@ -0,0 +1,312 @@
+/**
+ Slideable is a control that can be dragged either horizontally or vertically
+ between a minimum and a maximum value. When released from dragging, a
+ Slideable will animate to its minimum or maximum position, depending on the
+ direction of the drag.
+
+ The *min* value specifies a position left of or above the initial position,
+ to which the Slideable may be dragged.
+ The *max* value specifies a position right of or below the initial position,
+ to which the Slideable may be dragged.
+ The *value* property specifies the current position of the Slideable,
+ between the minimum and maximum positions.
+
+ *min*, *max*, and *value* may be specified in units of "px" or "%".
+
+ The *axis* property controls whether the Slideable slides left-right (h) or
+ up-down (v).
+
+ The following control is placed 90% off the screen to the right, and slides
+ to its natural position.
+
+ {kind: "enyo.Slideable", value: -90, min: -90, unit: "%",
+ classes: "enyo-fit", style: "width: 300px;",
+ components: [
+ {content: "stuff"}
+ ]
+ }
+*/
+enyo.kind({
+ name: "enyo.Slideable",
+ kind: "Control",
+ published: {
+ //* Direction of sliding; can be "h" or "v"
+ axis: "h",
+ //* A value between min and max to position the Slideable
+ value: 0,
+ //* Unit for min, max, and value; can be "px" or "%"
+ unit: "px",
+ //* A minimum value to slide to
+ min: 0,
+ //* A maximum value to slide to
+ max: 0,
+ accelerated: "auto",
+ //* Set to false to prevent the Slideable from dragging with elasticity past its min/max value.
+ overMoving: true,
+ //* Set to false to disable dragging.
+ draggable: true
+ },
+ events: {
+ //* Fires when the Slideable finishes animating.
+ onAnimateFinish: "",
+ onChange: ""
+ },
+ // Set to true to prevent a drag from bubbling beyond the Slideable.
+ preventDragPropagation: false,
+ //* @protected
+ tools: [
+ {kind: "Animator", onStep: "animatorStep", onEnd: "animatorComplete"}
+ ],
+ handlers: {
+ ondragstart: "dragstart",
+ ondrag: "drag",
+ ondragfinish: "dragfinish"
+ },
+ kDragScalar: 1,
+ dragEventProp: "dx",
+ unitModifier: false,
+ canTransform: false,
+ //* @protected
+ create: function() {
+ this.inherited(arguments);
+ this.acceleratedChanged();
+ this.transformChanged();
+ this.axisChanged();
+ this.valueChanged();
+ this.addClass("enyo-slideable");
+ },
+ initComponents: function() {
+ this.createComponents(this.tools);
+ this.inherited(arguments);
+ },
+ rendered: function() {
+ this.inherited(arguments);
+ this.canModifyUnit();
+ this.updateDragScalar();
+ },
+ resizeHandler: function() {
+ this.inherited(arguments);
+ this.updateDragScalar();
+ },
+ canModifyUnit: function() {
+ if (!this.canTransform) {
+ var b = this.getInitialStyleValue(this.hasNode(), this.boundary);
+ // If inline style of "px" exists, while unit is "%"
+ if (b.match(/px/i) && (this.unit === "%")) {
+ // Set unitModifier - used to over-ride "%"
+ this.unitModifier = this.getBounds()[this.dimension];
+ }
+ }
+ },
+ getInitialStyleValue: function(inNode, inBoundary) {
+ var s = enyo.dom.getComputedStyle(inNode);
+ if (s) {
+ return s.getPropertyValue(inBoundary);
+ } else if (inNode && inNode.currentStyle) {
+ return inNode.currentStyle[inBoundary];
+ }
+ return "0";
+ },
+ updateBounds: function(inValue, inDimensions) {
+ var inBounds = {};
+ inBounds[this.boundary] = inValue;
+ this.setBounds(inBounds, this.unit);
+
+ this.setInlineStyles(inValue, inDimensions);
+ },
+ updateDragScalar: function() {
+ if (this.unit == "%") {
+ var d = this.getBounds()[this.dimension];
+ this.kDragScalar = d ? 100 / d : 1;
+
+ if (!this.canTransform) {
+ this.updateBounds(this.value, 100);
+ }
+ }
+ },
+ transformChanged: function() {
+ this.canTransform = enyo.dom.canTransform();
+ },
+ acceleratedChanged: function() {
+ if (!(enyo.platform.android > 2)) {
+ enyo.dom.accelerate(this, this.accelerated);
+ }
+ },
+ axisChanged: function() {
+ var h = this.axis == "h";
+ this.dragMoveProp = h ? "dx" : "dy";
+ this.shouldDragProp = h ? "horizontal" : "vertical";
+ this.transform = h ? "translateX" : "translateY";
+ this.dimension = h ? "width" : "height";
+ this.boundary = h ? "left" : "top";
+ },
+ setInlineStyles: function(inValue, inDimensions) {
+ var inBounds = {};
+
+ if (this.unitModifier) {
+ inBounds[this.boundary] = this.percentToPixels(inValue, this.unitModifier);
+ inBounds[this.dimension] = this.unitModifier;
+ this.setBounds(inBounds);
+ } else {
+ if (inDimensions) {
+ inBounds[this.dimension] = inDimensions;
+ } else {
+ inBounds[this.boundary] = inValue;
+ }
+ this.setBounds(inBounds, this.unit);
+ }
+ },
+ valueChanged: function(inLast) {
+ var v = this.value;
+ if (this.isOob(v) && !this.isAnimating()) {
+ this.value = this.overMoving ? this.dampValue(v) : this.clampValue(v);
+ }
+ // FIXME: android cannot handle nested compositing well so apply acceleration only if needed
+ // desktop chrome doesn't like this code path so avoid...
+ if (enyo.platform.android > 2) {
+ if (this.value) {
+ if (inLast === 0 || inLast === undefined) {
+ enyo.dom.accelerate(this, this.accelerated);
+ }
+ } else {
+ enyo.dom.accelerate(this, false);
+ }
+ }
+
+ // If platform supports transforms
+ if (this.canTransform) {
+ enyo.dom.transformValue(this, this.transform, this.value + this.unit);
+ // else update inline styles
+ } else {
+ this.setInlineStyles(this.value, false);
+ }
+ this.doChange();
+ },
+ getAnimator: function() {
+ return this.$.animator;
+ },
+ isAtMin: function() {
+ return this.value <= this.calcMin();
+ },
+ isAtMax: function() {
+ return this.value >= this.calcMax();
+ },
+ calcMin: function() {
+ return this.min;
+ },
+ calcMax: function() {
+ return this.max;
+ },
+ clampValue: function(inValue) {
+ var min = this.calcMin();
+ var max = this.calcMax();
+ return Math.max(min, Math.min(inValue, max));
+ },
+ dampValue: function(inValue) {
+ return this.dampBound(this.dampBound(inValue, this.min, 1), this.max, -1);
+ },
+ dampBound: function(inValue, inBoundary, inSign) {
+ var v = inValue;
+ if (v * inSign < inBoundary * inSign) {
+ v = inBoundary + (v - inBoundary) / 4;
+ }
+ return v;
+ },
+ percentToPixels: function(value, dimension) {
+ return Math.floor((dimension / 100) * value);
+ },
+ pixelsToPercent: function(value) {
+ var boundary = this.unitModifier ? this.getBounds()[this.dimension] : this.container.getBounds()[this.dimension];
+ return (value / boundary) * 100;
+ },
+ // dragging
+ shouldDrag: function(inEvent) {
+ return this.draggable && inEvent[this.shouldDragProp];
+ },
+ isOob: function(inValue) {
+ return inValue > this.calcMax() || inValue < this.calcMin();
+ },
+ dragstart: function(inSender, inEvent) {
+ if (this.shouldDrag(inEvent)) {
+ inEvent.preventDefault();
+ this.$.animator.stop();
+ inEvent.dragInfo = {};
+ this.dragging = true;
+ this.drag0 = this.value;
+ this.dragd0 = 0;
+ return this.preventDragPropagation;
+ }
+ },
+ drag: function(inSender, inEvent) {
+ if (this.dragging) {
+ inEvent.preventDefault();
+ var d = this.canTransform ? inEvent[this.dragMoveProp] * this.kDragScalar : this.pixelsToPercent(inEvent[this.dragMoveProp]);
+ var v = this.drag0 + d;
+ var dd = d - this.dragd0;
+ this.dragd0 = d;
+ if (dd) {
+ inEvent.dragInfo.minimizing = dd < 0;
+ }
+ this.setValue(v);
+ return this.preventDragPropagation;
+ }
+ },
+ dragfinish: function(inSender, inEvent) {
+ if (this.dragging) {
+ this.dragging = false;
+ this.completeDrag(inEvent);
+ inEvent.preventTap();
+ return this.preventDragPropagation;
+ }
+ },
+ completeDrag: function(inEvent) {
+ if (this.value !== this.calcMax() && this.value != this.calcMin()) {
+ this.animateToMinMax(inEvent.dragInfo.minimizing);
+ }
+ },
+ // animation
+ isAnimating: function() {
+ return this.$.animator.isAnimating();
+ },
+ play: function(inStart, inEnd) {
+ this.$.animator.play({
+ startValue: inStart,
+ endValue: inEnd,
+ node: this.hasNode()
+ });
+ },
+ //* @public
+ //* Animates to the given value.
+ animateTo: function(inValue) {
+ this.play(this.value, inValue);
+ },
+ //* Animates to the minimum value.
+ animateToMin: function() {
+ this.animateTo(this.calcMin());
+ },
+ //* Animates to the maximum value.
+ animateToMax: function() {
+ this.animateTo(this.calcMax());
+ },
+ //* @protected
+ animateToMinMax: function(inMin) {
+ if (inMin) {
+ this.animateToMin();
+ } else {
+ this.animateToMax();
+ }
+ },
+ animatorStep: function(inSender) {
+ this.setValue(inSender.value);
+ return true;
+ },
+ animatorComplete: function(inSender) {
+ this.doAnimateFinish(inSender);
+ return true;
+ },
+ //* @public
+ //* Toggles between min and max with animation.
+ toggleMinMax: function() {
+ this.animateToMinMax(!this.isAtMin());
+ }
+});