mirror of
https://github.com/futurepress/epub.js.git
synced 2025-10-05 15:32:55 +02:00
288 lines
9.3 KiB
HTML
288 lines
9.3 KiB
HTML
<!--
|
|
Copyright 2013 The Polymer Authors. All rights reserved.
|
|
Use of this source code is governed by a BSD-style
|
|
license that can be found in the LICENSE file.
|
|
-->
|
|
<!--
|
|
/**
|
|
* @module Polymer Elements
|
|
*/
|
|
/**
|
|
* g-overlay displays overlayed on top of other content. It starts out
|
|
* hidden and is displayed by setting it's opened property
|
|
* to true. A g-overlay's opened state can be toggled by calling the toggle
|
|
* method.
|
|
*
|
|
* It's common to want a g-overlay to animate to its opened position. A number
|
|
* of helper css classes provide some basic open/close animations. For example,
|
|
* assigning the class g-overlay-fade to a g-overlay will make it fade into
|
|
* and out of view as it opens and closes. Note, if multiple g-overlay's are
|
|
* opened, they should stack on top of each other.
|
|
*
|
|
* Styling: The size and position of a g-overlay should be setup via css.
|
|
* g-overlay is natually sized around its content. When a g-overlay is opened
|
|
* it is shown and the 'opened' class is added to it. This is typically where
|
|
* css transitions and animations are applied. When the g-overlay is closed,
|
|
* the 'opened' class is removed and a 'closing' class is added. Use 'closing'
|
|
* to customize the closing animation.
|
|
*
|
|
* Classes for animating g-overlay:
|
|
*
|
|
* * g-overlay-fade: fade in/out when opened/closed
|
|
* * g-overlay-scale-slideup: open: fade in and shrink; close: slide up
|
|
* * g-overlay-shake: open: fly in and shake; close: shake and fly out.
|
|
*
|
|
* It's common to use g-overlay to gather user input, for example a login
|
|
* dialog. To facilitate this, g-overlay supports automatic focusing of a
|
|
* specific element when it's opened. The element to be focused should be given
|
|
* an autofocus attribute.
|
|
*
|
|
* An element that should close the g-overlay will automatically do so if it
|
|
* is given the overlay-toggle attribute. Please note that g-overlay will
|
|
* close whenever the user taps outside it or presses the escape key. The
|
|
* behavior can be turned off via the autoCloseDisabled property.
|
|
*
|
|
* <g-overlay class="g-overlay-scale-slideup">
|
|
* <h2>Dialog</h2>
|
|
* <input placeholder="say something..." autofocus"></input>
|
|
* <div>I agree with this wholeheartedly.</div>
|
|
* <button overlay-toggle>OK</button>
|
|
* </g-overlay>
|
|
*
|
|
*
|
|
* @class g-overlay
|
|
*/
|
|
/**
|
|
* Fired when the g-overlay opened property is set.
|
|
*
|
|
* @event open
|
|
* @param {Object} inDetail
|
|
* @param {Object} inDetail.opened the opened state
|
|
*/
|
|
-->
|
|
<polymer-element name="g-overlay"
|
|
attributes="opened autoCloseDisabled"
|
|
on-webkitAnimationStart="openedAnimationStart"
|
|
on-animationStart="openedAnimationStart"
|
|
on-webkitAnimationEnd="openedAnimationEnd"
|
|
on-animationEnd="openedAnimationEnd"
|
|
on-webkitTransitionEnd="openedTransitionEnd"
|
|
on-transitionEnd="openedTransitionEnd"
|
|
on-tap="tapHandler"
|
|
on-keydown="keydownHandler"
|
|
>
|
|
|
|
<template>
|
|
<link rel="stylesheet" href="css/g-overlay.css">
|
|
<link rel="stylesheet" polymer-scope="global" href="css/g-overlay-global.css">
|
|
<content></content>
|
|
</template>
|
|
<script>
|
|
(function() {
|
|
// TODO(sorvell): need keyhelper component.
|
|
var ESCAPE_KEY = 27;
|
|
|
|
// track overlays for z-index and focus managemant
|
|
var overlays = [];
|
|
var trackOverlays = function(inOverlay) {
|
|
if (inOverlay.opened) {
|
|
var z0 = currentOverlayZ();
|
|
overlays.push(inOverlay);
|
|
var z1 = currentOverlayZ();
|
|
if (z1 <= z0) {
|
|
applyOverlayZ(inOverlay, z0);
|
|
}
|
|
} else {
|
|
var i = overlays.indexOf(inOverlay);
|
|
if (i >= 0) {
|
|
overlays.splice(i, 1);
|
|
setZ(inOverlay, null);
|
|
}
|
|
}
|
|
}
|
|
|
|
var applyOverlayZ = function(inOverlay, inAboveZ) {
|
|
setZ(inOverlay, inAboveZ + 2);
|
|
}
|
|
|
|
var setZ = function(inNode, inZ) {
|
|
inNode.style.zIndex = inZ;
|
|
}
|
|
|
|
var currentOverlay = function() {
|
|
return overlays[overlays.length-1];
|
|
}
|
|
|
|
var DEFAULT_Z = 10;
|
|
|
|
var currentOverlayZ = function() {
|
|
var z;
|
|
var current = currentOverlay();
|
|
if (current) {
|
|
var z1 = window.getComputedStyle(current).zIndex;
|
|
if (!isNaN(z1)) {
|
|
z = Number(z1);
|
|
}
|
|
}
|
|
return z || DEFAULT_Z;
|
|
}
|
|
|
|
var focusOverlay = function() {
|
|
var current = currentOverlay();
|
|
if (current) {
|
|
current.applyFocus();
|
|
}
|
|
}
|
|
|
|
Polymer('g-overlay', {
|
|
/**
|
|
* Set opened to true to show an overlay and to false to hide it.
|
|
* A g-overlay may be made intially opened by setting its opened
|
|
* attribute.
|
|
* @attribute opened
|
|
* @type boolean
|
|
* @default false
|
|
*/
|
|
opened: 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,
|
|
timeout: 1000,
|
|
captureEventType: 'tap',
|
|
ready: function() {
|
|
if (this.tabIndex === undefined) {
|
|
this.tabIndex = -1;
|
|
}
|
|
this.setAttribute('touch-action', 'none');
|
|
},
|
|
/**
|
|
* Toggle the opened state of the overlay.
|
|
* @method toggle
|
|
*/
|
|
toggle: function() {
|
|
this.opened = !this.opened;
|
|
},
|
|
openedChanged: function() {
|
|
this.renderOpened();
|
|
trackOverlays(this);
|
|
if (!this.autoCloseDisabled) {
|
|
this.enableCaptureHandler(this.opened);
|
|
}
|
|
this.enableResizeHandler(this.opened);
|
|
this.fire('opened', this.opened);
|
|
},
|
|
enableHandler: function(inEnable, inMethodName, inNode, inEventName, inCapture) {
|
|
var m = 'bound' + inMethodName;
|
|
this[m] = this[m] || this[inMethodName].bind(this);
|
|
|
|
inNode[inEnable ? 'addEventListener' : 'removeEventListener'](
|
|
inEventName, this[m], inCapture);
|
|
},
|
|
enableResizeHandler: function(inEnable) {
|
|
this.enableHandler(inEnable, 'resizeHandler', window,
|
|
'resize');
|
|
},
|
|
enableCaptureHandler: function(inEnable) {
|
|
this.enableHandler(inEnable, 'captureHandler', document,
|
|
this.captureEventType, true);
|
|
},
|
|
getFocusNode: function() {
|
|
return this.querySelector('[autofocus]') || this;
|
|
},
|
|
// TODO(sorvell): nodes stay focused when they become un-focusable due to
|
|
// an ancestory becoming display: none; file bug.
|
|
applyFocus: function() {
|
|
var focusNode = this.getFocusNode();
|
|
if (this.opened) {
|
|
focusNode.focus();
|
|
} else {
|
|
focusNode.blur();
|
|
focusOverlay();
|
|
}
|
|
},
|
|
renderOpened: function() {
|
|
this.classList.remove('closing');
|
|
this.classList.add('revealed');
|
|
// continue styling after delay so display state can change without
|
|
// aborting transitions
|
|
this.asyncMethod('continueRenderOpened');
|
|
},
|
|
continueRenderOpened: function() {
|
|
this.classList.toggle('opened', this.opened);
|
|
this.classList.toggle('closing', !this.opened);
|
|
//this.animating = this.asyncMethod('completeOpening', null, this.timeout);
|
|
},
|
|
completeOpening: function() {
|
|
//clearTimeout(this.animating);
|
|
this.animating = null;
|
|
this.classList.remove('closing');
|
|
this.classList.toggle('revealed', this.opened);
|
|
this.applyFocus();
|
|
},
|
|
openedAnimationEnd: function(e) {
|
|
if (!this.opened) {
|
|
this.classList.remove('animation');
|
|
}
|
|
// same steps as when a transition ends
|
|
this.openedTransitionEnd(e);
|
|
},
|
|
openedTransitionEnd: function(e) {
|
|
// TODO(sorvell): Necessary due to
|
|
// https://bugs.webkit.org/show_bug.cgi?id=107892
|
|
// Remove when that bug is addressed.
|
|
if (e.target == this) {
|
|
this.completeOpening();
|
|
e.stopPropagation();
|
|
e.cancelBubble = true;
|
|
}
|
|
},
|
|
openedAnimationStart: function(e) {
|
|
this.classList.add('animation');
|
|
e.stopPropagation();
|
|
e.cancelBubble = true;
|
|
},
|
|
tapHandler: function(e) {
|
|
if (e.target && e.target.hasAttribute('overlay-toggle')) {
|
|
this.toggle();
|
|
} else {
|
|
if (this.autoCloseJob) {
|
|
this.autoCloseJob.stop();
|
|
this.autoCloseJob = null;
|
|
}
|
|
}
|
|
},
|
|
// TODO(sorvell): This approach will not work with modal. For this we need a
|
|
// scrim.
|
|
captureHandler: function(e) {
|
|
if (!this.autoCloseDisabled && (currentOverlay() == this) && (this
|
|
!= e.target) && !(this.contains(e.target))) {
|
|
this.autoCloseJob = this.job(this.autoCloseJob, function() {
|
|
this.opened = false;
|
|
});
|
|
}
|
|
},
|
|
keydownHandler: function(e) {
|
|
if (!this.autoCloseDisabled && (e.keyCode == ESCAPE_KEY)) {
|
|
this.opened = false;
|
|
e.stopPropagation();
|
|
e.cancelBubble = true;
|
|
}
|
|
},
|
|
/**
|
|
* Extensions of g-overlay should implement the resizeHandler
|
|
* method to adjust the size and position of the overlay when the
|
|
* browser window resizes.
|
|
* @method resizeHandler
|
|
*/
|
|
resizeHandler: function() {
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
</polymer-element>
|