Index: pkg/polymer/lib/elements/polymer-anchor-point/polymer-anchor-point.html |
diff --git a/pkg/polymer/lib/elements/polymer-anchor-point/polymer-anchor-point.html b/pkg/polymer/lib/elements/polymer-anchor-point/polymer-anchor-point.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7df6e1d25c97e8ab5a799636599ef192f039cf5d |
--- /dev/null |
+++ b/pkg/polymer/lib/elements/polymer-anchor-point/polymer-anchor-point.html |
@@ -0,0 +1,196 @@ |
+<!-- |
+/** |
+ * @module Polymer Elements |
+ */ |
+/** |
+ * polymer-anchor-point can be used to align two nodes. The node to |
+ * use as the reference position is the anchor node, and the node to |
+ * be positioned is the target node. |
+ * |
+ * Both the anchor and target nodes should have an anchor-point |
+ * attribute. The target node is positioned such that its anchor-point |
+ * aligns with the anchor node's anchor-point. |
+ * |
+ * Note: The target node is positioned with position: fixed, and will not |
+ * scroll with the page. |
+ * |
+ * Note: This is meant to polyfill the `<dialog>` positioning behavior when |
+ * an anchor is provided. Spec: http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#the-dialog-element |
+ * |
+ * Example: |
+ * |
+ * <div id="anchor" anchor-point="bottom left"></div> |
+ * <div id="target" anchor-point="top left"></div> |
+ * <polymer-anchor-point id="anchor-helper"></polymer-anchor-point> |
+ * <script> |
+ * var helper = document.querySelector('#anchor-helper'); |
+ * helper.anchor = document.querySelector('#anchor'); |
+ * helper.target = document.querySelector('#target'); |
+ * helper.apply(); |
+ * </script> |
+ * |
+ * @class polymer-anchor-point |
+ */ |
+ --> |
+<link rel="import" href="../polymer/polymer.html"> |
+ |
+<polymer-element name="polymer-anchor-point" attributes="target anchor"> |
+ <script> |
+ (function() { |
+ var DEFAULT_ANCHOR_POINT = 'center center'; |
+ |
+ function getAnchorPoint(node) { |
+ var anchorPt = node.getAttribute('anchor-point'); |
+ if (!anchorPt || anchorPt === 'none') { |
+ anchorPt = DEFAULT_ANCHOR_POINT; |
+ } |
+ return anchorPt; |
+ }; |
+ |
+ function lengthIsPx(length) { |
+ return length.slice(-2) === 'px'; |
+ }; |
+ |
+ function lengthIsPercent(length) { |
+ return length.slice(-1) === '%'; |
+ }; |
+ |
+ function computeLength(length, ref) { |
+ var computed = 0; |
+ if (lengthIsPx(length)) { |
+ computed = length.slice(0, -2); |
+ } else if (lengthIsPercent(length)) { |
+ computed = ref * length.slice(0, -1) / 100; |
+ } |
+ return computed; |
+ }; |
+ |
+ function partIsX(part) { |
+ return part === 'left' || part === 'right' || part === 'center'; |
+ }; |
+ |
+ function partIsY(part) { |
+ return part === 'top' || part === 'bottom' || part === 'center'; |
+ }; |
+ |
+ function partIsKeyword(part) { |
+ return part.slice(-1) !== '%' && part.slice(-2) !== 'px'; |
+ }; |
+ |
+ function parsePosition(position) { |
+ var parsed = {}; |
+ var parts = position.split(' '); |
+ var i = 0; |
+ var lastEdgeX = true; |
+ do { |
+ if (partIsX(parts[i])) { |
+ parsed.x = parts[i]; |
+ lastEdgeX = parts[i] !== 'center'; |
+ } else if (partIsY(parts[i])) { |
+ parsed.y = parts[i]; |
+ lastEdgeX = false; |
+ } else if (lastEdgeX) { |
+ parsed.xOffset = parts[i]; |
+ lastEdgeX = false; |
+ } else { |
+ parsed.yOffset = parts[i]; |
+ } |
+ } while (++i < parts.length); |
+ return parsed; |
+ }; |
+ |
+ function computeAnchorOffset(rect, anchorPt) { |
+ var offset = { |
+ left: 0, |
+ top: 0 |
+ }; |
+ var parsed = parsePosition(anchorPt); |
+ if (!parsed.x && !parsed.xOffset) { |
+ offset.left = rect.width / 2; |
+ } else if (parsed.x && !parsed.xOffset) { |
+ switch (parsed.x) { |
+ case 'left': |
+ offset.left = 0; |
+ break; |
+ case 'right': |
+ offset.left = rect.width; |
+ break; |
+ case 'center': |
+ offset.left = rect.width / 2; |
+ break; |
+ } |
+ } else { |
+ var computed = computeLength(parsed.xOffset, rect.width); |
+ if (parsed.x === 'right') { |
+ offset.left = rect.width - computed; |
+ } else if (!parsed.x || parsed.x === 'left') { |
+ offset.left = computed; |
+ } |
+ } |
+ if (!parsed.y && !parsed.yOffset) { |
+ offset.top = rect.height / 2; |
+ } else if (parsed.y && !parsed.yOffset) { |
+ switch (parsed.y) { |
+ case 'top': |
+ offset.top = 0; |
+ break; |
+ case 'bottom': |
+ offset.top = rect.height; |
+ break; |
+ case 'center': |
+ offset.top = rect.height / 2; |
+ break; |
+ } |
+ } else { |
+ var computed = computeLength(parsed.yOffset, rect.height); |
+ if (parsed.y === 'bottom') { |
+ offset.top = rect.height - computed; |
+ } else if (!parsed.y || parsed.y === 'top') { |
+ offset.top = computed; |
+ } |
+ } |
+ return offset; |
+ }; |
+ |
+ Polymer('polymer-anchor-point', { |
+ /** |
+ * The node to be positioned. |
+ * @attribute target |
+ * @type Node |
+ */ |
+ target: null, |
+ /** |
+ * The node to align the target to. |
+ * @attribute anchor |
+ * @type node |
+ */ |
+ anchor: null, |
+ canPosition: function() { |
+ return this.target && this.anchor; |
+ }, |
+ apply: function() { |
+ if (!this.canPosition()) { |
+ return; |
+ } |
+ var pos = this.computePosition(); |
+ this.target.style.position = 'fixed'; |
+ this.target.style.top = pos.top + 'px'; |
+ this.target.style.left = pos.left + 'px'; |
+ }, |
+ computePosition: function() { |
+ var rect = this.anchor.getBoundingClientRect(); |
+ var anchorPt = getAnchorPoint(this.anchor); |
+ var anchorOffset = computeAnchorOffset(rect, anchorPt); |
+ var targetRect = this.target.getBoundingClientRect(); |
+ var targetAnchorPt = getAnchorPoint(this.target); |
+ var targetOffset = computeAnchorOffset(targetRect, targetAnchorPt); |
+ var pos = { |
+ left: rect.left + anchorOffset.left - targetOffset.left, |
+ top: rect.top + anchorOffset.top - targetOffset.top |
+ }; |
+ return pos; |
+ } |
+ }); |
+ })(); |
+ </script> |
+</polymer-element> |