diff options
Diffstat (limited to 'node_modules/materialize-css/js/materialbox.js')
-rw-r--r-- | node_modules/materialize-css/js/materialbox.js | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/node_modules/materialize-css/js/materialbox.js b/node_modules/materialize-css/js/materialbox.js new file mode 100644 index 0000000000..f682db54ff --- /dev/null +++ b/node_modules/materialize-css/js/materialbox.js @@ -0,0 +1,453 @@ +(function($, anim) { + 'use strict'; + + let _defaults = { + inDuration: 275, + outDuration: 200, + onOpenStart: null, + onOpenEnd: null, + onCloseStart: null, + onCloseEnd: null + }; + + /** + * @class + * + */ + class Materialbox extends Component { + /** + * Construct Materialbox instance + * @constructor + * @param {Element} el + * @param {Object} options + */ + constructor(el, options) { + super(Materialbox, el, options); + + this.el.M_Materialbox = this; + + /** + * Options for the modal + * @member Materialbox#options + * @prop {Number} [inDuration=275] - Length in ms of enter transition + * @prop {Number} [outDuration=200] - Length in ms of exit transition + * @prop {Function} onOpenStart - Callback function called before materialbox is opened + * @prop {Function} onOpenEnd - Callback function called after materialbox is opened + * @prop {Function} onCloseStart - Callback function called before materialbox is closed + * @prop {Function} onCloseEnd - Callback function called after materialbox is closed + */ + this.options = $.extend({}, Materialbox.defaults, options); + + this.overlayActive = false; + this.doneAnimating = true; + this.placeholder = $('<div></div>').addClass('material-placeholder'); + this.originalWidth = 0; + this.originalHeight = 0; + this.originInlineStyles = this.$el.attr('style'); + this.caption = this.el.getAttribute('data-caption') || ''; + + // Wrap + this.$el.before(this.placeholder); + this.placeholder.append(this.$el); + + this._setupEventHandlers(); + } + + static get defaults() { + return _defaults; + } + + static init(els, options) { + return super.init(this, els, options); + } + + /** + * Get Instance + */ + static getInstance(el) { + let domElem = !!el.jquery ? el[0] : el; + return domElem.M_Materialbox; + } + + /** + * Teardown component + */ + destroy() { + this._removeEventHandlers(); + this.el.M_Materialbox = undefined; + + // Unwrap image + $(this.placeholder) + .after(this.el) + .remove(); + + this.$el.removeAttr('style'); + } + + /** + * Setup Event Handlers + */ + _setupEventHandlers() { + this._handleMaterialboxClickBound = this._handleMaterialboxClick.bind(this); + this.el.addEventListener('click', this._handleMaterialboxClickBound); + } + + /** + * Remove Event Handlers + */ + _removeEventHandlers() { + this.el.removeEventListener('click', this._handleMaterialboxClickBound); + } + + /** + * Handle Materialbox Click + * @param {Event} e + */ + _handleMaterialboxClick(e) { + // If already modal, return to original + if (this.doneAnimating === false || (this.overlayActive && this.doneAnimating)) { + this.close(); + } else { + this.open(); + } + } + + /** + * Handle Window Scroll + */ + _handleWindowScroll() { + if (this.overlayActive) { + this.close(); + } + } + + /** + * Handle Window Resize + */ + _handleWindowResize() { + if (this.overlayActive) { + this.close(); + } + } + + /** + * Handle Window Resize + * @param {Event} e + */ + _handleWindowEscape(e) { + // ESC key + if (e.keyCode === 27 && this.doneAnimating && this.overlayActive) { + this.close(); + } + } + + /** + * Find ancestors with overflow: hidden; and make visible + */ + _makeAncestorsOverflowVisible() { + this.ancestorsChanged = $(); + let ancestor = this.placeholder[0].parentNode; + while (ancestor !== null && !$(ancestor).is(document)) { + let curr = $(ancestor); + if (curr.css('overflow') !== 'visible') { + curr.css('overflow', 'visible'); + if (this.ancestorsChanged === undefined) { + this.ancestorsChanged = curr; + } else { + this.ancestorsChanged = this.ancestorsChanged.add(curr); + } + } + ancestor = ancestor.parentNode; + } + } + + /** + * Animate image in + */ + _animateImageIn() { + let animOptions = { + targets: this.el, + height: [this.originalHeight, this.newHeight], + width: [this.originalWidth, this.newWidth], + left: + M.getDocumentScrollLeft() + + this.windowWidth / 2 - + this.placeholder.offset().left - + this.newWidth / 2, + top: + M.getDocumentScrollTop() + + this.windowHeight / 2 - + this.placeholder.offset().top - + this.newHeight / 2, + duration: this.options.inDuration, + easing: 'easeOutQuad', + complete: () => { + this.doneAnimating = true; + + // onOpenEnd callback + if (typeof this.options.onOpenEnd === 'function') { + this.options.onOpenEnd.call(this, this.el); + } + } + }; + + // Override max-width or max-height if needed + this.maxWidth = this.$el.css('max-width'); + this.maxHeight = this.$el.css('max-height'); + if (this.maxWidth !== 'none') { + animOptions.maxWidth = this.newWidth; + } + if (this.maxHeight !== 'none') { + animOptions.maxHeight = this.newHeight; + } + + anim(animOptions); + } + + /** + * Animate image out + */ + _animateImageOut() { + let animOptions = { + targets: this.el, + width: this.originalWidth, + height: this.originalHeight, + left: 0, + top: 0, + duration: this.options.outDuration, + easing: 'easeOutQuad', + complete: () => { + this.placeholder.css({ + height: '', + width: '', + position: '', + top: '', + left: '' + }); + + // Revert to width or height attribute + if (this.attrWidth) { + this.$el.attr('width', this.attrWidth); + } + if (this.attrHeight) { + this.$el.attr('height', this.attrHeight); + } + + this.$el.removeAttr('style'); + this.originInlineStyles && this.$el.attr('style', this.originInlineStyles); + + // Remove class + this.$el.removeClass('active'); + this.doneAnimating = true; + + // Remove overflow overrides on ancestors + if (this.ancestorsChanged.length) { + this.ancestorsChanged.css('overflow', ''); + } + + // onCloseEnd callback + if (typeof this.options.onCloseEnd === 'function') { + this.options.onCloseEnd.call(this, this.el); + } + } + }; + + anim(animOptions); + } + + /** + * Update open and close vars + */ + _updateVars() { + this.windowWidth = window.innerWidth; + this.windowHeight = window.innerHeight; + this.caption = this.el.getAttribute('data-caption') || ''; + } + + /** + * Open Materialbox + */ + open() { + this._updateVars(); + this.originalWidth = this.el.getBoundingClientRect().width; + this.originalHeight = this.el.getBoundingClientRect().height; + + // Set states + this.doneAnimating = false; + this.$el.addClass('active'); + this.overlayActive = true; + + // onOpenStart callback + if (typeof this.options.onOpenStart === 'function') { + this.options.onOpenStart.call(this, this.el); + } + + // Set positioning for placeholder + this.placeholder.css({ + width: this.placeholder[0].getBoundingClientRect().width + 'px', + height: this.placeholder[0].getBoundingClientRect().height + 'px', + position: 'relative', + top: 0, + left: 0 + }); + + this._makeAncestorsOverflowVisible(); + + // Set css on origin + this.$el.css({ + position: 'absolute', + 'z-index': 1000, + 'will-change': 'left, top, width, height' + }); + + // Change from width or height attribute to css + this.attrWidth = this.$el.attr('width'); + this.attrHeight = this.$el.attr('height'); + if (this.attrWidth) { + this.$el.css('width', this.attrWidth + 'px'); + this.$el.removeAttr('width'); + } + if (this.attrHeight) { + this.$el.css('width', this.attrHeight + 'px'); + this.$el.removeAttr('height'); + } + + // Add overlay + this.$overlay = $('<div id="materialbox-overlay"></div>') + .css({ + opacity: 0 + }) + .one('click', () => { + if (this.doneAnimating) { + this.close(); + } + }); + + // Put before in origin image to preserve z-index layering. + this.$el.before(this.$overlay); + + // Set dimensions if needed + let overlayOffset = this.$overlay[0].getBoundingClientRect(); + this.$overlay.css({ + width: this.windowWidth + 'px', + height: this.windowHeight + 'px', + left: -1 * overlayOffset.left + 'px', + top: -1 * overlayOffset.top + 'px' + }); + + anim.remove(this.el); + anim.remove(this.$overlay[0]); + + // Animate Overlay + anim({ + targets: this.$overlay[0], + opacity: 1, + duration: this.options.inDuration, + easing: 'easeOutQuad' + }); + + // Add and animate caption if it exists + if (this.caption !== '') { + if (this.$photocaption) { + anim.remove(this.$photoCaption[0]); + } + this.$photoCaption = $('<div class="materialbox-caption"></div>'); + this.$photoCaption.text(this.caption); + $('body').append(this.$photoCaption); + this.$photoCaption.css({ display: 'inline' }); + + anim({ + targets: this.$photoCaption[0], + opacity: 1, + duration: this.options.inDuration, + easing: 'easeOutQuad' + }); + } + + // Resize Image + let ratio = 0; + let widthPercent = this.originalWidth / this.windowWidth; + let heightPercent = this.originalHeight / this.windowHeight; + this.newWidth = 0; + this.newHeight = 0; + + if (widthPercent > heightPercent) { + ratio = this.originalHeight / this.originalWidth; + this.newWidth = this.windowWidth * 0.9; + this.newHeight = this.windowWidth * 0.9 * ratio; + } else { + ratio = this.originalWidth / this.originalHeight; + this.newWidth = this.windowHeight * 0.9 * ratio; + this.newHeight = this.windowHeight * 0.9; + } + + this._animateImageIn(); + + // Handle Exit triggers + this._handleWindowScrollBound = this._handleWindowScroll.bind(this); + this._handleWindowResizeBound = this._handleWindowResize.bind(this); + this._handleWindowEscapeBound = this._handleWindowEscape.bind(this); + + window.addEventListener('scroll', this._handleWindowScrollBound); + window.addEventListener('resize', this._handleWindowResizeBound); + window.addEventListener('keyup', this._handleWindowEscapeBound); + } + + /** + * Close Materialbox + */ + close() { + this._updateVars(); + this.doneAnimating = false; + + // onCloseStart callback + if (typeof this.options.onCloseStart === 'function') { + this.options.onCloseStart.call(this, this.el); + } + + anim.remove(this.el); + anim.remove(this.$overlay[0]); + + if (this.caption !== '') { + anim.remove(this.$photoCaption[0]); + } + + // disable exit handlers + window.removeEventListener('scroll', this._handleWindowScrollBound); + window.removeEventListener('resize', this._handleWindowResizeBound); + window.removeEventListener('keyup', this._handleWindowEscapeBound); + + anim({ + targets: this.$overlay[0], + opacity: 0, + duration: this.options.outDuration, + easing: 'easeOutQuad', + complete: () => { + this.overlayActive = false; + this.$overlay.remove(); + } + }); + + this._animateImageOut(); + + // Remove Caption + reset css settings on image + if (this.caption !== '') { + anim({ + targets: this.$photoCaption[0], + opacity: 0, + duration: this.options.outDuration, + easing: 'easeOutQuad', + complete: () => { + this.$photoCaption.remove(); + } + }); + } + } + } + + M.Materialbox = Materialbox; + + if (M.jQueryLoaded) { + M.initializeJqueryWrapper(Materialbox, 'materialbox', 'M_Materialbox'); + } +})(cash, M.anime); |