Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(322)

Unified Diff: third_party/polymer/components-chromium/core-overlay/core-overlay-extracted.js

Issue 592593002: Inline scripts were extracted from Polymer elements. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: s/echo ""/echo/ Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/polymer/components-chromium/core-overlay/core-overlay-extracted.js
diff --git a/third_party/polymer/components-chromium/core-overlay/core-overlay-extracted.js b/third_party/polymer/components-chromium/core-overlay/core-overlay-extracted.js
new file mode 100644
index 0000000000000000000000000000000000000000..589952944df063799f6dfd2902cdd58730875ac4
--- /dev/null
+++ b/third_party/polymer/components-chromium/core-overlay/core-overlay-extracted.js
@@ -0,0 +1,588 @@
+
+(function() {
+
+ Polymer('core-overlay', {
+
+ publish: {
+ /**
+ * The target element that will be shown when the overlay is
+ * opened. If unspecified, the core-overlay itself is the target.
+ *
+ * @attribute target
+ * @type Object
+ * @default the overlay element
+ */
+ target: null,
+
+
+ /**
+ * A `core-overlay`'s size is guaranteed to be
+ * constrained to the window size. To achieve this, the sizingElement
+ * is sized with a max-height/width. By default this element is the
+ * target element, but it can be specifically set to a specific element
+ * inside the target if that is more appropriate. This is useful, for
+ * example, when a region inside the overlay should scroll if needed.
+ *
+ * @attribute sizingTarget
+ * @type Object
+ * @default the target element
+ */
+ sizingTarget: null,
+
+ /**
+ * Set opened to true to show an overlay and to false to hide it.
+ * A `core-overlay` may be made initially opened by setting its
+ * `opened` attribute.
+ * @attribute opened
+ * @type boolean
+ * @default false
+ */
+ opened: false,
+
+ /**
+ * If true, the overlay has a backdrop darkening the rest of the screen.
+ * The backdrop element is attached to the document body and may be styled
+ * with the class `core-overlay-backdrop`. When opened the `core-opened`
+ * class is applied.
+ *
+ * @attribute backdrop
+ * @type boolean
+ * @default false
+ */
+ backdrop: false,
+
+ /**
+ * If true, the overlay is guaranteed to display above page content.
+ *
+ * @attribute layered
+ * @type boolean
+ * @default false
+ */
+ layered: false,
+
+ /**
+ * By default an overlay will close automatically if the user
+ * taps outside it or presses the escape key. Disable this
+ * behavior by setting the `autoCloseDisabled` property to true.
+ * @attribute autoCloseDisabled
+ * @type boolean
+ * @default false
+ */
+ autoCloseDisabled: false,
+
+ /**
+ * This property specifies an attribute on elements that should
+ * close the overlay on tap. Should not set `closeSelector` if this
+ * is set.
+ *
+ * @attribute closeAttribute
+ * @type string
+ * @default "core-overlay-toggle"
+ */
+ closeAttribute: 'core-overlay-toggle',
+
+ /**
+ * This property specifies a selector matching elements that should
+ * close the overlay on tap. Should not set `closeAttribute` if this
+ * is set.
+ *
+ * @attribute closeSelector
+ * @type string
+ * @default ""
+ */
+ closeSelector: '',
+
+ /**
+ * A `core-overlay` target's size is constrained to the window size.
+ * The `margin` property specifies a pixel amount around the overlay
+ * that will be reserved. It's useful for ensuring that, for example,
+ * a shadow displayed outside the target will always be visible.
+ *
+ * @attribute margin
+ * @type number
+ * @default 0
+ */
+ margin: 0,
+
+ /**
+ * The transition property specifies a string which identifies a
+ * <a href="../core-transition/">`core-transition`</a> element that
+ * will be used to help the overlay open and close. The default
+ * `core-transition-fade` will cause the overlay to fade in and out.
+ *
+ * @attribute transition
+ * @type string
+ * @default 'core-transition-fade'
+ */
+ transition: 'core-transition-fade'
+
+ },
+
+ captureEventName: 'tap',
+ targetListeners: {
+ 'tap': 'tapHandler',
+ 'keydown': 'keydownHandler',
+ 'core-transitionend': 'transitionend'
+ },
+
+ registerCallback: function(element) {
+ this.layer = document.createElement('core-overlay-layer');
+ this.keyHelper = document.createElement('core-key-helper');
+ this.meta = document.createElement('core-transition');
+ this.scrim = document.createElement('div');
+ this.scrim.className = 'core-overlay-backdrop';
+ },
+
+ ready: function() {
+ this.target = this.target || this;
+ // flush to ensure styles are installed before paint
+ Platform.flush();
+ },
+
+ /**
+ * Toggle the opened state of the overlay.
+ * @method toggle
+ */
+ toggle: function() {
+ this.opened = !this.opened;
+ },
+
+ /**
+ * Open the overlay. This is equivalent to setting the `opened`
+ * property to true.
+ * @method open
+ */
+ open: function() {
+ this.opened = true;
+ },
+
+ /**
+ * Close the overlay. This is equivalent to setting the `opened`
+ * property to false.
+ * @method close
+ */
+ close: function() {
+ this.opened = false;
+ },
+
+ domReady: function() {
+ this.ensureTargetSetup();
+ },
+
+ targetChanged: function(old) {
+ if (this.target) {
+ // really make sure tabIndex is set
+ if (this.target.tabIndex < 0) {
+ this.target.tabIndex = -1;
+ }
+ this.addElementListenerList(this.target, this.targetListeners);
+ this.target.style.display = 'none';
+ }
+ if (old) {
+ this.removeElementListenerList(old, this.targetListeners);
+ var transition = this.getTransition();
+ if (transition) {
+ transition.teardown(old);
+ } else {
+ old.style.position = '';
+ old.style.outline = '';
+ }
+ old.style.display = '';
+ }
+ },
+
+ // NOTE: wait to call this until we're as sure as possible that target
+ // is styled.
+ ensureTargetSetup: function() {
+ if (!this.target || this.target.__overlaySetup) {
+ return;
+ }
+ this.target.__overlaySetup = true;
+ this.target.style.display = '';
+ var transition = this.getTransition();
+ if (transition) {
+ transition.setup(this.target);
+ }
+ var computed = getComputedStyle(this.target);
+ this.targetStyle = {
+ position: computed.position === 'static' ? 'fixed' :
+ computed.position
+ }
+ if (!transition) {
+ this.target.style.position = this.targetStyle.position;
+ this.target.style.outline = 'none';
+ }
+ this.target.style.display = 'none';
+ },
+
+ openedChanged: function() {
+ this.transitioning = true;
+ this.ensureTargetSetup();
+ this.prepareRenderOpened();
+ // continue styling after delay so display state can change
+ // without aborting transitions
+ // note: we wait a full frame so that transition changes executed
+ // during measuring do not cause transition
+ this.async(function() {
+ this.target.style.display = '';
+ this.async('renderOpened');
+ });
+ this.fire('core-overlay-open', this.opened);
+ },
+
+ // tasks which must occur before opening; e.g. making the element visible
+ prepareRenderOpened: function() {
+ if (this.opened) {
+ addOverlay(this);
+ }
+ this.prepareBackdrop();
+ // async so we don't auto-close immediately via a click.
+ this.async(function() {
+ if (!this.autoCloseDisabled) {
+ this.enableElementListener(this.opened, document,
+ this.captureEventName, 'captureHandler', true);
+ }
+ });
+ this.enableElementListener(this.opened, window, 'resize',
+ 'resizeHandler');
+
+ if (this.opened) {
+ // TODO(sorvell): force SD Polyfill to render
+ forcePolyfillRender(this.target);
+ if (!this._shouldPosition) {
+ this.target.style.position = 'absolute';
+ var computed = getComputedStyle(this.target);
+ var t = (computed.top === 'auto' && computed.bottom === 'auto');
+ var l = (computed.left === 'auto' && computed.right === 'auto');
+ this.target.style.position = this.targetStyle.position;
+ this._shouldPosition = {top: t, left: l};
+ }
+ // if we are showing, then take care when measuring
+ this.prepareMeasure(this.target);
+ this.updateTargetDimensions();
+ this.finishMeasure(this.target);
+ if (this.layered) {
+ this.layer.addElement(this.target);
+ this.layer.opened = this.opened;
+ }
+ }
+ },
+
+ // tasks which cause the overlay to actually open; typically play an
+ // animation
+ renderOpened: function() {
+ var transition = this.getTransition();
+ if (transition) {
+ transition.go(this.target, {opened: this.opened});
+ } else {
+ this.transitionend();
+ }
+ this.renderBackdropOpened();
+ },
+
+ // finishing tasks; typically called via a transition
+ transitionend: function(e) {
+ // make sure this is our transition event.
+ if (e && e.target !== this.target) {
+ return;
+ }
+ this.transitioning = false;
+ if (!this.opened) {
+ this.resetTargetDimensions();
+ this.target.style.display = 'none';
+ this.completeBackdrop();
+ removeOverlay(this);
+ if (this.layered) {
+ if (!currentOverlay()) {
+ this.layer.opened = this.opened;
+ }
+ this.layer.removeElement(this.target);
+ }
+ }
+ this.applyFocus();
+ },
+
+ prepareBackdrop: function() {
+ if (this.backdrop && this.opened) {
+ if (!this.scrim.parentNode) {
+ document.body.appendChild(this.scrim);
+ this.scrim.style.zIndex = currentOverlayZ() - 1;
+ }
+ trackBackdrop(this);
+ }
+ },
+
+ renderBackdropOpened: function() {
+ if (this.backdrop && getBackdrops().length < 2) {
+ this.scrim.classList.toggle('core-opened', this.opened);
+ }
+ },
+
+ completeBackdrop: function() {
+ if (this.backdrop) {
+ trackBackdrop(this);
+ if (getBackdrops().length === 0) {
+ this.scrim.parentNode.removeChild(this.scrim);
+ }
+ }
+ },
+
+ prepareMeasure: function(target) {
+ target.style.transition = target.style.webkitTransition = 'none';
+ target.style.transform = target.style.webkitTransform = 'none';
+ target.style.display = '';
+ },
+
+ finishMeasure: function(target) {
+ target.style.display = 'none';
+ target.style.transform = target.style.webkitTransform = '';
+ target.style.transition = target.style.webkitTransition = '';
+ },
+
+ getTransition: function() {
+ return this.meta.byId(this.transition);
+ },
+
+ getFocusNode: function() {
+ return this.target.querySelector('[autofocus]') || this.target;
+ },
+
+ applyFocus: function() {
+ var focusNode = this.getFocusNode();
+ if (this.opened) {
+ focusNode.focus();
+ } else {
+ focusNode.blur();
+ if (currentOverlay() == this) {
+ console.warn('Current core-overlay is attempting to focus itself as next! (bug)');
+ } else {
+ focusOverlay();
+ }
+ }
+ },
+
+ updateTargetDimensions: function() {
+ this.positionTarget();
+ this.sizeTarget();
+ //
+ if (this.layered) {
+ var rect = this.target.getBoundingClientRect();
+ this.target.style.top = rect.top + 'px';
+ this.target.style.left = rect.left + 'px';
+ this.target.style.right = this.target.style.bottom = 'auto';
+ }
+ },
+
+ sizeTarget: function() {
+ var sizer = this.sizingTarget || this.target;
+ var rect = sizer.getBoundingClientRect();
+ var mt = rect.top === this.margin ? this.margin : this.margin * 2;
+ var ml = rect.left === this.margin ? this.margin : this.margin * 2;
+ var h = window.innerHeight - rect.top - mt;
+ var w = window.innerWidth - rect.left - ml;
+ sizer.style.maxHeight = h + 'px';
+ sizer.style.maxWidth = w + 'px';
+ sizer.style.boxSizing = 'border-box';
+ },
+
+ positionTarget: function() {
+ // vertically and horizontally center if not positioned
+ if (this._shouldPosition.top) {
+ var t = Math.max((window.innerHeight -
+ this.target.offsetHeight - this.margin*2) / 2, this.margin);
+ this.target.style.top = t + 'px';
+ }
+ if (this._shouldPosition.left) {
+ var l = Math.max((window.innerWidth -
+ this.target.offsetWidth - this.margin*2) / 2, this.margin);
+ this.target.style.left = l + 'px';
+ }
+ },
+
+ resetTargetDimensions: function() {
+ this.target.style.top = this.target.style.left = '';
+ this.target.style.right = this.target.style.bottom = '';
+ this.target.style.width = this.target.style.height = '';
+ this._shouldPosition = null;
+ },
+
+ tapHandler: function(e) {
+ // closeSelector takes precedence since closeAttribute has a default non-null value.
+ if (e.target &&
+ (this.closeSelector && e.target.matches(this.closeSelector)) ||
+ (this.closeAttribute && e.target.hasAttribute(this.closeAttribute))) {
+ this.toggle();
+ } else {
+ if (this.autoCloseJob) {
+ this.autoCloseJob.stop();
+ this.autoCloseJob = null;
+ }
+ }
+ },
+
+ // We use the traditional approach of capturing events on document
+ // to to determine if the overlay needs to close. However, due to
+ // ShadowDOM event retargeting, the event target is not useful. Instead
+ // of using it, we attempt to close asynchronously and prevent the close
+ // if a tap event is immediately heard on the target.
+ // TODO(sorvell): This approach will not work with modal. For
+ // this we need a scrim.
+ captureHandler: function(e) {
+ if (!this.autoCloseDisabled && (currentOverlay() == this)) {
+ this.autoCloseJob = this.job(this.autoCloseJob, function() {
+ this.close();
+ });
+ }
+ },
+
+ keydownHandler: function(e) {
+ if (!this.autoCloseDisabled && (e.keyCode == this.keyHelper.ESCAPE_KEY)) {
+ this.close();
+ e.stopPropagation();
+ }
+ },
+
+ /**
+ * Extensions of core-overlay should implement the `resizeHandler`
+ * method to adjust the size and position of the overlay when the
+ * browser window resizes.
+ * @method resizeHandler
+ */
+ resizeHandler: function() {
+ this.updateTargetDimensions();
+ },
+
+ // TODO(sorvell): these utility methods should not be here.
+ addElementListenerList: function(node, events) {
+ for (var i in events) {
+ this.addElementListener(node, i, events[i]);
+ }
+ },
+
+ removeElementListenerList: function(node, events) {
+ for (var i in events) {
+ this.removeElementListener(node, i, events[i]);
+ }
+ },
+
+ enableElementListener: function(enable, node, event, methodName, capture) {
+ if (enable) {
+ this.addElementListener(node, event, methodName, capture);
+ } else {
+ this.removeElementListener(node, event, methodName, capture);
+ }
+ },
+
+ addElementListener: function(node, event, methodName, capture) {
+ var fn = this._makeBoundListener(methodName);
+ if (node && fn) {
+ Polymer.addEventListener(node, event, fn, capture);
+ }
+ },
+
+ removeElementListener: function(node, event, methodName, capture) {
+ var fn = this._makeBoundListener(methodName);
+ if (node && fn) {
+ Polymer.removeEventListener(node, event, fn, capture);
+ }
+ },
+
+ _makeBoundListener: function(methodName) {
+ var self = this, method = this[methodName];
+ if (!method) {
+ return;
+ }
+ var bound = '_bound' + methodName;
+ if (!this[bound]) {
+ this[bound] = function(e) {
+ method.call(self, e);
+ }
+ }
+ return this[bound];
+ },
+ });
+
+ function forcePolyfillRender(target) {
+ if (window.ShadowDOMPolyfill) {
+ target.offsetHeight;
+ }
+ }
+
+ // TODO(sorvell): This should be an element with private state so it can
+ // be independent of overlay.
+ // track overlays for z-index and focus managemant
+ var overlays = [];
+ function addOverlay(overlay) {
+ var z0 = currentOverlayZ();
+ overlays.push(overlay);
+ var z1 = currentOverlayZ();
+ if (z1 <= z0) {
+ applyOverlayZ(overlay, z0);
+ }
+ }
+
+ function removeOverlay(overlay) {
+ var i = overlays.indexOf(overlay);
+ if (i >= 0) {
+ overlays.splice(i, 1);
+ setZ(overlay, '');
+ }
+ }
+
+ function applyOverlayZ(overlay, aboveZ) {
+ setZ(overlay.target, aboveZ + 2);
+ }
+
+ function setZ(element, z) {
+ element.style.zIndex = z;
+ }
+
+ function currentOverlay() {
+ return overlays[overlays.length-1];
+ }
+
+ var DEFAULT_Z = 10;
+
+ function currentOverlayZ() {
+ var z;
+ var current = currentOverlay();
+ if (current) {
+ var z1 = window.getComputedStyle(current.target).zIndex;
+ if (!isNaN(z1)) {
+ z = Number(z1);
+ }
+ }
+ return z || DEFAULT_Z;
+ }
+
+ function focusOverlay() {
+ var current = currentOverlay();
+ // We have to be careful to focus the next overlay _after_ any current
+ // transitions are complete (due to the state being toggled prior to the
+ // transition). Otherwise, we risk infinite recursion when a transitioning
+ // (closed) overlay becomes the current overlay.
+ //
+ // NOTE: We make the assumption that any overlay that completes a transition
+ // will call into focusOverlay to kick the process back off. Currently:
+ // transitionend -> applyFocus -> focusOverlay.
+ if (current && !current.transitioning) {
+ current.applyFocus();
+ }
+ }
+
+ var backdrops = [];
+ function trackBackdrop(element) {
+ if (element.opened) {
+ backdrops.push(element);
+ } else {
+ var i = backdrops.indexOf(element);
+ if (i >= 0) {
+ backdrops.splice(i, 1);
+ }
+ }
+ }
+
+ function getBackdrops() {
+ return backdrops;
+ }
+})();

Powered by Google App Engine
This is Rietveld 408576698