first commit
This commit is contained in:
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
!function(e){var t={};function o(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.m=e,o.c=t,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(e,t){if(1&t&&(e=o(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)o.d(n,r,function(t){return e[t]}.bind(null,r));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=297)}({297:function(e,t,o){"use strict";(function(e){var t,n=(t=o(68))&&t.__esModule?t:{default:t};window.et_error_modal_shown=!1,window.et_builder_version=window.et_builder_version||"";var r=e("#et-builder-cache-notice-template");et_pb_notice_options.product_version!==window.et_builder_version&&(e("body").addClass("et_pb_stop_scroll").append(r.html()),(0,n.default)(e(".et_pb_prompt_modal")),window.et_error_modal_shown=!0)}).call(this,o(5))},5:function(e,t){e.exports=jQuery},68:function(e,t,o){"use strict";(function(e){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o=function(t,o){var n=e(window),r=e("#wpadminbar"),i=n.height(),u=t.outerHeight(),l=r.outerHeight(),a=0-u/2+l/2;u>i-l?t.css({top:"".concat(l+15,"px"),bottom:15,marginTop:0,minHeight:0}):t.css({top:"50%",marginTop:"".concat(a,"px")}),t.addClass("et_pb_auto_centerize_modal")};t.default=o}).call(this,o(5))}});
|
||||
//# sourceMappingURL=cache_notice.js.map
|
||||
@@ -0,0 +1,2 @@
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=298)}({298:function(e,t,n){"use strict";(function(e){et_modules_wrapper.builderCssContainerPrefix;var t=et_modules_wrapper.builderCssLayoutPrefix,n=e(".et_pb_module:not(".concat(t," .et_pb_module, .et_pb_section .et_pb_module), .et_pb_row:not(").concat(t," .et_pb_row, .et_pb_section .et_pb_row), .et_pb_section:not(").concat(t," .et_pb_section)"));n.length>0&&n.each((function(){var t=e(this);0===t.closest("#et-boc").length&&t.wrap('<div id="et-boc"></div>'),0===t.closest(".et-l").length&&t.wrap('<div class="et-l"></div>')}))}).call(this,n(5))},5:function(e,t){e.exports=jQuery}});
|
||||
//# sourceMappingURL=cpt-modules-wrapper.js.map
|
||||
File diff suppressed because one or more lines are too long
Vendored
+7
File diff suppressed because one or more lines are too long
Vendored
+7
File diff suppressed because one or more lines are too long
Vendored
+2155
File diff suppressed because it is too large
Load Diff
+365
@@ -0,0 +1,365 @@
|
||||
/*!
|
||||
* easyPieChart
|
||||
* Lightweight plugin to render simple, animated and retina optimized pie charts
|
||||
*
|
||||
* @author Robert Fleischmann <rendro87@gmail.com> (http://robert-fleischmann.de)
|
||||
* @version 2.1.5
|
||||
*
|
||||
* Modified to adapt the latest jQuery version (v3 above) included on WordPress 5.6:
|
||||
* - (2020-12-15) - jQuery isFunction method is deprecated.
|
||||
*/
|
||||
|
||||
(function(root, factory) {
|
||||
if(typeof exports === 'object') {
|
||||
module.exports = factory(require('jquery'));
|
||||
}
|
||||
else if(typeof define === 'function' && define.amd) {
|
||||
define(['jquery'], factory);
|
||||
}
|
||||
else {
|
||||
factory(root.jQuery);
|
||||
}
|
||||
}(this, function($) {
|
||||
|
||||
/**
|
||||
* Renderer to render the chart on a canvas object
|
||||
* @param {DOMElement} el DOM element to host the canvas (root of the plugin)
|
||||
* @param {object} options options object of the plugin
|
||||
*/
|
||||
var CanvasRenderer = function(el, options) {
|
||||
var cachedBackground;
|
||||
var canvas = document.createElement('canvas');
|
||||
|
||||
el.appendChild(canvas);
|
||||
|
||||
if (typeof(G_vmlCanvasManager) !== 'undefined') {
|
||||
G_vmlCanvasManager.initElement(canvas);
|
||||
}
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
canvas.width = canvas.height = options.size;
|
||||
|
||||
// canvas on retina devices
|
||||
var scaleBy = 1;
|
||||
if (window.devicePixelRatio > 1) {
|
||||
scaleBy = window.devicePixelRatio;
|
||||
canvas.style.width = canvas.style.height = [options.size, 'px'].join('');
|
||||
canvas.width = canvas.height = options.size * scaleBy;
|
||||
ctx.scale(scaleBy, scaleBy);
|
||||
}
|
||||
|
||||
// move 0,0 coordinates to the center
|
||||
ctx.translate(options.size / 2, options.size / 2);
|
||||
|
||||
// rotate canvas -90deg
|
||||
ctx.rotate((-1 / 2 + options.rotate / 180) * Math.PI);
|
||||
|
||||
var radius = (options.size - options.lineWidth) / 2;
|
||||
if (options.scaleColor && options.scaleLength) {
|
||||
radius -= options.scaleLength + 2; // 2 is the distance between scale and bar
|
||||
}
|
||||
|
||||
// IE polyfill for Date
|
||||
Date.now = Date.now || function() {
|
||||
return +(new Date());
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw a circle around the center of the canvas
|
||||
* @param {strong} color Valid CSS color string
|
||||
* @param {number} lineWidth Width of the line in px
|
||||
* @param {number} percent Percentage to draw (float between -1 and 1)
|
||||
*/
|
||||
var drawCircle = function(color, lineWidth, percent, alpha ) {
|
||||
percent = Math.min(Math.max(-1, percent || 0), 1);
|
||||
var isNegative = percent <= 0 ? true : false;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, radius, 0, Math.PI * 2 * percent, isNegative);
|
||||
|
||||
ctx.strokeStyle = color;
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.lineWidth = lineWidth;
|
||||
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the scale of the chart
|
||||
*/
|
||||
var drawScale = function() {
|
||||
var offset;
|
||||
var length;
|
||||
|
||||
ctx.lineWidth = 1;
|
||||
ctx.fillStyle = options.scaleColor;
|
||||
|
||||
ctx.save();
|
||||
for (var i = 24; i > 0; --i) {
|
||||
if (i % 6 === 0) {
|
||||
length = options.scaleLength;
|
||||
offset = 0;
|
||||
} else {
|
||||
length = options.scaleLength * 0.6;
|
||||
offset = options.scaleLength - length;
|
||||
}
|
||||
ctx.fillRect(-options.size/2 + offset, 0, length, 1);
|
||||
ctx.rotate(Math.PI / 12);
|
||||
}
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
/**
|
||||
* Request animation frame wrapper with polyfill
|
||||
* @return {function} Request animation frame method or timeout fallback
|
||||
*/
|
||||
var reqAnimationFrame = (function() {
|
||||
return window.requestAnimationFrame ||
|
||||
window.webkitRequestAnimationFrame ||
|
||||
window.mozRequestAnimationFrame ||
|
||||
function(callback) {
|
||||
window.setTimeout(callback, 1000 / 60);
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* Draw the background of the plugin including the scale and the track
|
||||
*/
|
||||
var drawBackground = function() {
|
||||
if(options.scaleColor) drawScale();
|
||||
if(options.trackColor) drawCircle(options.trackColor, options.lineWidth, 1, options.trackAlpha );
|
||||
};
|
||||
|
||||
/**
|
||||
* Canvas accessor
|
||||
*/
|
||||
this.getCanvas = function() {
|
||||
return canvas;
|
||||
};
|
||||
|
||||
/**
|
||||
* Canvas 2D context 'ctx' accessor
|
||||
*/
|
||||
this.getCtx = function() {
|
||||
return ctx;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the complete canvas
|
||||
*/
|
||||
this.clear = function() {
|
||||
ctx.clearRect(options.size / -2, options.size / -2, options.size, options.size);
|
||||
};
|
||||
|
||||
/**
|
||||
* Draw the complete chart
|
||||
* @param {number} percent Percent shown by the chart between -100 and 100
|
||||
*/
|
||||
this.draw = function(percent) {
|
||||
// do we need to render a background
|
||||
if (!!options.scaleColor || !!options.trackColor) {
|
||||
// getImageData and putImageData are supported
|
||||
if (ctx.getImageData && ctx.putImageData) {
|
||||
if (!cachedBackground) {
|
||||
drawBackground();
|
||||
cachedBackground = ctx.getImageData(0, 0, options.size * scaleBy, options.size * scaleBy);
|
||||
} else {
|
||||
ctx.putImageData(cachedBackground, 0, 0);
|
||||
}
|
||||
} else {
|
||||
this.clear();
|
||||
drawBackground();
|
||||
}
|
||||
} else {
|
||||
this.clear();
|
||||
}
|
||||
|
||||
ctx.lineCap = options.lineCap;
|
||||
|
||||
// if barcolor is a function execute it and pass the percent as a value
|
||||
var color;
|
||||
if (typeof(options.barColor) === 'function') {
|
||||
color = options.barColor(percent);
|
||||
} else {
|
||||
color = options.barColor;
|
||||
}
|
||||
|
||||
// draw bar
|
||||
drawCircle(color, options.lineWidth, percent / 100, options.barAlpha );
|
||||
}.bind(this);
|
||||
|
||||
/**
|
||||
* Animate from some percent to some other percentage
|
||||
* @param {number} from Starting percentage
|
||||
* @param {number} to Final percentage
|
||||
*/
|
||||
this.animate = function(from, to) {
|
||||
var startTime = Date.now();
|
||||
options.onStart(from, to);
|
||||
var animation = function() {
|
||||
var process = Math.min(Date.now() - startTime, options.animate.duration);
|
||||
var currentValue = options.easing(this, process, from, to - from, options.animate.duration);
|
||||
this.draw(currentValue);
|
||||
options.onStep(from, to, currentValue);
|
||||
if (process >= options.animate.duration) {
|
||||
options.onStop(from, to);
|
||||
} else {
|
||||
reqAnimationFrame(animation);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
reqAnimationFrame(animation);
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
var EasyPieChart = function(el, opts) {
|
||||
var defaultOptions = {
|
||||
barColor: '#ef1e25',
|
||||
barAlpha: 1.0,
|
||||
trackColor: '#f9f9f9',
|
||||
trackAlpha: 1.0,
|
||||
scaleColor: '#dfe0e0',
|
||||
scaleLength: 5,
|
||||
lineCap: 'round',
|
||||
lineWidth: 3,
|
||||
size: 110,
|
||||
rotate: 0,
|
||||
render: true,
|
||||
animate: {
|
||||
duration: 1000,
|
||||
enabled: true
|
||||
},
|
||||
easing: function (x, t, b, c, d) { // more can be found here: http://gsgd.co.uk/sandbox/jquery/easing/
|
||||
t = t / (d/2);
|
||||
if (t < 1) {
|
||||
return c / 2 * t * t + b;
|
||||
}
|
||||
return -c/2 * ((--t)*(t-2) - 1) + b;
|
||||
},
|
||||
onStart: function(from, to) {
|
||||
return;
|
||||
},
|
||||
onStep: function(from, to, currentValue) {
|
||||
return;
|
||||
},
|
||||
onStop: function(from, to) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// detect present renderer
|
||||
if (typeof(CanvasRenderer) !== 'undefined') {
|
||||
defaultOptions.renderer = CanvasRenderer;
|
||||
} else if (typeof(SVGRenderer) !== 'undefined') {
|
||||
defaultOptions.renderer = SVGRenderer;
|
||||
} else {
|
||||
throw new Error('Please load either the SVG- or the CanvasRenderer');
|
||||
}
|
||||
|
||||
var options = {};
|
||||
var currentValue = 0;
|
||||
|
||||
/**
|
||||
* Initialize the plugin by creating the options object and initialize rendering
|
||||
*/
|
||||
var init = function() {
|
||||
this.el = el;
|
||||
this.options = options;
|
||||
|
||||
// merge user options into default options
|
||||
for (var i in defaultOptions) {
|
||||
if (defaultOptions.hasOwnProperty(i)) {
|
||||
options[i] = opts && typeof(opts[i]) !== 'undefined' ? opts[i] : defaultOptions[i];
|
||||
if (typeof(options[i]) === 'function') {
|
||||
options[i] = options[i].bind(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for jQuery easing
|
||||
if (typeof(options.easing) === 'string' && typeof(jQuery) !== 'undefined' && 'function' === typeof jQuery.easing[options.easing]) {
|
||||
options.easing = jQuery.easing[options.easing];
|
||||
} else {
|
||||
options.easing = defaultOptions.easing;
|
||||
}
|
||||
|
||||
// process earlier animate option to avoid bc breaks
|
||||
if (typeof(options.animate) === 'number') {
|
||||
options.animate = {
|
||||
duration: options.animate,
|
||||
enabled: true
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof(options.animate) === 'boolean' && !options.animate) {
|
||||
options.animate = {
|
||||
duration: 1000,
|
||||
enabled: options.animate
|
||||
};
|
||||
}
|
||||
|
||||
// create renderer
|
||||
this.renderer = new options.renderer(el, options);
|
||||
|
||||
// initial draw
|
||||
this.renderer.draw(currentValue);
|
||||
|
||||
// initial update
|
||||
if (el.dataset && el.dataset.percent) {
|
||||
this.update(parseFloat(el.dataset.percent));
|
||||
} else if (el.getAttribute && el.getAttribute('data-percent')) {
|
||||
this.update(parseFloat(el.getAttribute('data-percent')));
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
/**
|
||||
* Update the value of the chart
|
||||
* @param {number} newValue Number between 0 and 100
|
||||
* @return {object} Instance of the plugin for method chaining
|
||||
*/
|
||||
this.update = function(newValue) {
|
||||
newValue = parseFloat(newValue);
|
||||
if (options.animate.enabled) {
|
||||
this.renderer.animate(currentValue, newValue);
|
||||
} else {
|
||||
this.renderer.draw(newValue);
|
||||
}
|
||||
currentValue = newValue;
|
||||
return this;
|
||||
}.bind(this);
|
||||
|
||||
/**
|
||||
* Disable animation
|
||||
* @return {object} Instance of the plugin for method chaining
|
||||
*/
|
||||
this.disableAnimation = function() {
|
||||
options.animate.enabled = false;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable animation
|
||||
* @return {object} Instance of the plugin for method chaining
|
||||
*/
|
||||
this.enableAnimation = function() {
|
||||
options.animate.enabled = true;
|
||||
return this;
|
||||
};
|
||||
|
||||
init();
|
||||
};
|
||||
|
||||
$.fn.easyPieChart = function(options) {
|
||||
return this.each(function() {
|
||||
var instanceOptions;
|
||||
|
||||
if (!$.data(this, 'easyPieChart')) {
|
||||
instanceOptions = $.extend({}, options, $(this).data());
|
||||
$.data(this, 'easyPieChart', new EasyPieChart(this, instanceOptions));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
}));
|
||||
@@ -0,0 +1,46 @@
|
||||
/*global jQuery */
|
||||
/*!
|
||||
* FitText.js 1.2
|
||||
*
|
||||
* Copyright 2011, Dave Rupert http://daverupert.com
|
||||
* Released under the WTFPL license
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
*
|
||||
* Date: Thu May 05 14:23:00 2011 -0600
|
||||
*
|
||||
* Modified to adapt the latest jQuery version (v3 above) included on WordPress 5.6:
|
||||
* - (2021-02-05) - Number type value passed to css method is deprecated.
|
||||
*/
|
||||
|
||||
(function( $ ){
|
||||
|
||||
$.fn.fitText = function( kompressor, options ) {
|
||||
|
||||
// Setup options
|
||||
var compressor = kompressor || 1,
|
||||
settings = $.extend({
|
||||
'minFontSize' : Number.NEGATIVE_INFINITY,
|
||||
'maxFontSize' : $(this).css('font-size')
|
||||
}, options);
|
||||
|
||||
return this.each(function(){
|
||||
|
||||
// Store the object
|
||||
var $this = $(this);
|
||||
|
||||
// Resizer() resizes items based on the object width divided by the compressor * 10
|
||||
var resizer = function () {
|
||||
$this.css('font-size', Math.max(Math.min($this.width() / (compressor*10), parseFloat(settings.maxFontSize)), parseFloat(settings.minFontSize)) + 'px');
|
||||
};
|
||||
|
||||
// Call once to set.
|
||||
resizer();
|
||||
|
||||
// Call on resize. Opera debounces their resize by default.
|
||||
$(window).on('resize.fittext orientationchange.fittext', resizer);
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
})( jQuery );
|
||||
@@ -0,0 +1,87 @@
|
||||
/*jshint browser:true */
|
||||
/*!
|
||||
* FitVids 1.1
|
||||
*
|
||||
* Copyright 2013, Chris Coyier - http://css-tricks.com + Dave Rupert - http://daverupert.com
|
||||
* Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/
|
||||
* Released under the WTFPL license - http://sam.zoy.org/wtfpl/
|
||||
*
|
||||
*/
|
||||
|
||||
;(function( $ ){
|
||||
|
||||
'use strict';
|
||||
|
||||
$.fn.fitVids = function( options ) {
|
||||
var settings = {
|
||||
customSelector: null,
|
||||
ignore: null
|
||||
};
|
||||
|
||||
if(!document.getElementById('fit-vids-style')) {
|
||||
// appendStyles: https://github.com/toddmotto/fluidvids/blob/master/dist/fluidvids.js
|
||||
var head = document.head || document.getElementsByTagName('head')[0];
|
||||
var css = '.fluid-width-video-wrapper{width:100%;position:relative;padding:0;}.fluid-width-video-wrapper iframe,.fluid-width-video-wrapper object,.fluid-width-video-wrapper embed {position:absolute;top:0;left:0;width:100%;height:100%;}';
|
||||
var div = document.createElement("div");
|
||||
div.innerHTML = '<p>x</p><style id="fit-vids-style">' + css + '</style>';
|
||||
head.appendChild(div.childNodes[1]);
|
||||
}
|
||||
|
||||
if ( options ) {
|
||||
$.extend( settings, options );
|
||||
}
|
||||
|
||||
return this.each(function(){
|
||||
var selectors = [
|
||||
'iframe[src*="player.vimeo.com"]',
|
||||
'iframe[src*="youtube.com"]',
|
||||
'iframe[src*="youtube-nocookie.com"]',
|
||||
'iframe[src*="kickstarter.com"][src*="video.html"]',
|
||||
'object',
|
||||
'embed'
|
||||
];
|
||||
|
||||
if (settings.customSelector) {
|
||||
selectors.push(settings.customSelector);
|
||||
}
|
||||
|
||||
var ignoreList = '.fitvidsignore';
|
||||
|
||||
if(settings.ignore) {
|
||||
ignoreList = ignoreList + ', ' + settings.ignore;
|
||||
}
|
||||
|
||||
var $allVideos = $(this).find(selectors.join(','));
|
||||
$allVideos = $allVideos.not('object object'); // SwfObj conflict patch
|
||||
$allVideos = $allVideos.not(ignoreList); // Disable FitVids on this video.
|
||||
|
||||
$allVideos.each(function(){
|
||||
var $this = $(this);
|
||||
if($this.parents(ignoreList).length > 0) {
|
||||
return; // Disable FitVids on this video.
|
||||
}
|
||||
if (this.tagName.toLowerCase() === 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; }
|
||||
if ((!$this.css('height') && !$this.css('width')) && (isNaN($this.attr('height')) || isNaN($this.attr('width'))))
|
||||
{
|
||||
$this.attr('height', 9);
|
||||
$this.attr('width', 16);
|
||||
}
|
||||
var height = ( this.tagName.toLowerCase() === 'object' || ($this.attr('height') && !isNaN(parseInt($this.attr('height'), 10))) ) ? parseInt($this.attr('height'), 10) : $this.height(),
|
||||
width = !isNaN(parseInt($this.attr('width'), 10)) ? parseInt($this.attr('width'), 10) : $this.width(),
|
||||
aspectRatio = height / width;
|
||||
if(!$this.attr('name')){
|
||||
var videoName = 'fitvid' + $.fn.fitVids._count;
|
||||
$this.attr('name', videoName);
|
||||
$.fn.fitVids._count++;
|
||||
}
|
||||
$this.wrap('<div class="fluid-width-video-wrapper"></div>').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+'%');
|
||||
$this.removeAttr('height').removeAttr('width');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Internal counter for unique video names.
|
||||
$.fn.fitVids._count = 0;
|
||||
|
||||
// Works with either jQuery or Zepto
|
||||
})( window.jQuery || window.Zepto );
|
||||
@@ -0,0 +1,393 @@
|
||||
/*!
|
||||
* jQuery hashchange event - v1.3 - 7/21/2010
|
||||
* http://benalman.com/projects/jquery-hashchange-plugin/
|
||||
*
|
||||
* Copyright (c) 2010 "Cowboy" Ben Alman
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* http://benalman.com/about/license/
|
||||
*
|
||||
* Modified to adapt the latest jQuery version (v3 above) included on WordPress 5.6:
|
||||
* - (2021-02-03) - jQuery bind method is deprecated.
|
||||
*/
|
||||
|
||||
// Script: jQuery hashchange event
|
||||
//
|
||||
// *Version: 1.3, Last updated: 7/21/2010*
|
||||
//
|
||||
// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
|
||||
// GitHub - http://github.com/cowboy/jquery-hashchange/
|
||||
// Source - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
|
||||
// (Minified) - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
|
||||
//
|
||||
// About: License
|
||||
//
|
||||
// Copyright (c) 2010 "Cowboy" Ben Alman,
|
||||
// Dual licensed under the MIT and GPL licenses.
|
||||
// http://benalman.com/about/license/
|
||||
//
|
||||
// About: Examples
|
||||
//
|
||||
// These working examples, complete with fully commented code, illustrate a few
|
||||
// ways in which this plugin can be used.
|
||||
//
|
||||
// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
|
||||
// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
|
||||
//
|
||||
// About: Support and Testing
|
||||
//
|
||||
// Information about what version or versions of jQuery this plugin has been
|
||||
// tested with, what browsers it has been tested in, and where the unit tests
|
||||
// reside (so you can test it yourself).
|
||||
//
|
||||
// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
|
||||
// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
|
||||
// Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
|
||||
// Unit Tests - http://benalman.com/code/projects/jquery-hashchange/unit/
|
||||
//
|
||||
// About: Known issues
|
||||
//
|
||||
// While this jQuery hashchange event implementation is quite stable and
|
||||
// robust, there are a few unfortunate browser bugs surrounding expected
|
||||
// hashchange event-based behaviors, independent of any JavaScript
|
||||
// window.onhashchange abstraction. See the following examples for more
|
||||
// information:
|
||||
//
|
||||
// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
|
||||
// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
|
||||
// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
|
||||
// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
|
||||
//
|
||||
// Also note that should a browser natively support the window.onhashchange
|
||||
// event, but not report that it does, the fallback polling loop will be used.
|
||||
//
|
||||
// About: Release History
|
||||
//
|
||||
// 1.3 - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
|
||||
// "removable" for mobile-only development. Added IE6/7 document.title
|
||||
// support. Attempted to make Iframe as hidden as possible by using
|
||||
// techniques from http://www.paciellogroup.com/blog/?p=604. Added
|
||||
// support for the "shortcut" format $(window).hashchange( fn ) and
|
||||
// $(window).hashchange() like jQuery provides for built-in events.
|
||||
// Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
|
||||
// lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
|
||||
// and <jQuery.fn.hashchange.src> properties plus document-domain.html
|
||||
// file to address access denied issues when setting document.domain in
|
||||
// IE6/7.
|
||||
// 1.2 - (2/11/2010) Fixed a bug where coming back to a page using this plugin
|
||||
// from a page on another domain would cause an error in Safari 4. Also,
|
||||
// IE6/7 Iframe is now inserted after the body (this actually works),
|
||||
// which prevents the page from scrolling when the event is first bound.
|
||||
// Event can also now be bound before DOM ready, but it won't be usable
|
||||
// before then in IE6/7.
|
||||
// 1.1 - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
|
||||
// where browser version is incorrectly reported as 8.0, despite
|
||||
// inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
|
||||
// 1.0 - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
|
||||
// window.onhashchange functionality into a separate plugin for users
|
||||
// who want just the basic event & back button support, without all the
|
||||
// extra awesomeness that BBQ provides. This plugin will be included as
|
||||
// part of jQuery BBQ, but also be available separately.
|
||||
|
||||
(function($,window,undefined){
|
||||
'$:nomunge'; // Used by YUI compressor.
|
||||
|
||||
// Reused string.
|
||||
var str_hashchange = 'hashchange',
|
||||
|
||||
// Method / object references.
|
||||
doc = document,
|
||||
fake_onhashchange,
|
||||
special = $.event.special,
|
||||
|
||||
// Does the browser support window.onhashchange? Note that IE8 running in
|
||||
// IE7 compatibility mode reports true for 'onhashchange' in window, even
|
||||
// though the event isn't supported, so also test document.documentMode.
|
||||
doc_mode = doc.documentMode,
|
||||
supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
|
||||
|
||||
// Get location.hash (or what you'd expect location.hash to be) sans any
|
||||
// leading #. Thanks for making this necessary, Firefox!
|
||||
function get_fragment( url ) {
|
||||
url = url || location.href;
|
||||
return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
|
||||
};
|
||||
|
||||
// Method: jQuery.fn.hashchange
|
||||
//
|
||||
// Bind a handler to the window.onhashchange event or trigger all bound
|
||||
// window.onhashchange event handlers. This behavior is consistent with
|
||||
// jQuery's built-in event handlers.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// > jQuery(window).hashchange( [ handler ] );
|
||||
//
|
||||
// Arguments:
|
||||
//
|
||||
// handler - (Function) Optional handler to be bound to the hashchange
|
||||
// event. This is a "shortcut" for the more verbose form:
|
||||
// jQuery(window).on( 'hashchange', handler ). If handler is omitted,
|
||||
// all bound window.onhashchange event handlers will be triggered. This
|
||||
// is a shortcut for the more verbose
|
||||
// jQuery(window).trigger( 'hashchange' ). These forms are described in
|
||||
// the <hashchange event> section.
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// (jQuery) The initial jQuery collection of elements.
|
||||
|
||||
// Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
|
||||
// $(elem).hashchange() for triggering, like jQuery does for built-in events.
|
||||
$.fn[ str_hashchange ] = function( fn ) {
|
||||
return fn ? this.on( str_hashchange, fn ) : this.trigger( str_hashchange );
|
||||
};
|
||||
|
||||
// Property: jQuery.fn.hashchange.delay
|
||||
//
|
||||
// The numeric interval (in milliseconds) at which the <hashchange event>
|
||||
// polling loop executes. Defaults to 50.
|
||||
|
||||
// Property: jQuery.fn.hashchange.domain
|
||||
//
|
||||
// If you're setting document.domain in your JavaScript, and you want hash
|
||||
// history to work in IE6/7, not only must this property be set, but you must
|
||||
// also set document.domain BEFORE jQuery is loaded into the page. This
|
||||
// property is only applicable if you are supporting IE6/7 (or IE8 operating
|
||||
// in "IE7 compatibility" mode).
|
||||
//
|
||||
// In addition, the <jQuery.fn.hashchange.src> property must be set to the
|
||||
// path of the included "document-domain.html" file, which can be renamed or
|
||||
// modified if necessary (note that the document.domain specified must be the
|
||||
// same in both your main JavaScript as well as in this file).
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// jQuery.fn.hashchange.domain = document.domain;
|
||||
|
||||
// Property: jQuery.fn.hashchange.src
|
||||
//
|
||||
// If, for some reason, you need to specify an Iframe src file (for example,
|
||||
// when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
|
||||
// do so using this property. Note that when using this property, history
|
||||
// won't be recorded in IE6/7 until the Iframe src file loads. This property
|
||||
// is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
|
||||
// compatibility" mode).
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// jQuery.fn.hashchange.src = 'path/to/file.html';
|
||||
|
||||
$.fn[ str_hashchange ].delay = 50;
|
||||
/*
|
||||
$.fn[ str_hashchange ].domain = null;
|
||||
$.fn[ str_hashchange ].src = null;
|
||||
*/
|
||||
|
||||
// Event: hashchange event
|
||||
//
|
||||
// Fired when location.hash changes. In browsers that support it, the native
|
||||
// HTML5 window.onhashchange event is used, otherwise a polling loop is
|
||||
// initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
|
||||
// see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
|
||||
// compatibility" mode), a hidden Iframe is created to allow the back button
|
||||
// and hash-based history to work.
|
||||
//
|
||||
// Usage as described in <jQuery.fn.hashchange>:
|
||||
//
|
||||
// > // Bind an event handler.
|
||||
// > jQuery(window).hashchange( function(e) {
|
||||
// > var hash = location.hash;
|
||||
// > ...
|
||||
// > });
|
||||
// >
|
||||
// > // Manually trigger the event handler.
|
||||
// > jQuery(window).hashchange();
|
||||
//
|
||||
// A more verbose usage that allows for event namespacing:
|
||||
//
|
||||
// > // Bind an event handler.
|
||||
// > jQuery(window).on( 'hashchange', function(e) {
|
||||
// > var hash = location.hash;
|
||||
// > ...
|
||||
// > });
|
||||
// >
|
||||
// > // Manually trigger the event handler.
|
||||
// > jQuery(window).trigger( 'hashchange' );
|
||||
//
|
||||
// Additional Notes:
|
||||
//
|
||||
// * The polling loop and Iframe are not created until at least one handler
|
||||
// is actually bound to the 'hashchange' event.
|
||||
// * If you need the bound handler(s) to execute immediately, in cases where
|
||||
// a location.hash exists on page load, via bookmark or page refresh for
|
||||
// example, use jQuery(window).hashchange() or the more verbose
|
||||
// jQuery(window).trigger( 'hashchange' ).
|
||||
// * The event can be bound before DOM ready, but since it won't be usable
|
||||
// before then in IE6/7 (due to the necessary Iframe), recommended usage is
|
||||
// to bind it inside a DOM ready handler.
|
||||
|
||||
// Override existing $.event.special.hashchange methods (allowing this plugin
|
||||
// to be defined after jQuery BBQ in BBQ's source code).
|
||||
special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
|
||||
|
||||
// Called only when the first 'hashchange' event is bound to window.
|
||||
setup: function() {
|
||||
// If window.onhashchange is supported natively, there's nothing to do..
|
||||
if ( supports_onhashchange ) { return false; }
|
||||
|
||||
// Otherwise, we need to create our own. And we don't want to call this
|
||||
// until the user binds to the event, just in case they never do, since it
|
||||
// will create a polling loop and possibly even a hidden Iframe.
|
||||
$( fake_onhashchange.start );
|
||||
},
|
||||
|
||||
// Called only when the last 'hashchange' event is unbound from window.
|
||||
teardown: function() {
|
||||
// If window.onhashchange is supported natively, there's nothing to do..
|
||||
if ( supports_onhashchange ) { return false; }
|
||||
|
||||
// Otherwise, we need to stop ours (if possible).
|
||||
$( fake_onhashchange.stop );
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// fake_onhashchange does all the work of triggering the window.onhashchange
|
||||
// event for browsers that don't natively support it, including creating a
|
||||
// polling loop to watch for hash changes and in IE 6/7 creating a hidden
|
||||
// Iframe to enable back and forward.
|
||||
fake_onhashchange = (function(){
|
||||
var self = {},
|
||||
timeout_id,
|
||||
|
||||
// Remember the initial hash so it doesn't get triggered immediately.
|
||||
last_hash = get_fragment(),
|
||||
|
||||
fn_retval = function(val){ return val; },
|
||||
history_set = fn_retval,
|
||||
history_get = fn_retval;
|
||||
|
||||
// Start the polling loop.
|
||||
self.start = function() {
|
||||
timeout_id || poll();
|
||||
};
|
||||
|
||||
// Stop the polling loop.
|
||||
self.stop = function() {
|
||||
timeout_id && clearTimeout( timeout_id );
|
||||
timeout_id = undefined;
|
||||
};
|
||||
|
||||
// This polling loop checks every $.fn.hashchange.delay milliseconds to see
|
||||
// if location.hash has changed, and triggers the 'hashchange' event on
|
||||
// window when necessary.
|
||||
function poll() {
|
||||
var hash = get_fragment(),
|
||||
history_hash = history_get( last_hash );
|
||||
|
||||
if ( hash !== last_hash ) {
|
||||
history_set( last_hash = hash, history_hash );
|
||||
|
||||
$(window).trigger( str_hashchange );
|
||||
|
||||
} else if ( history_hash !== last_hash ) {
|
||||
location.href = location.href.replace( /#.*/, '' ) + history_hash;
|
||||
}
|
||||
|
||||
timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
|
||||
};
|
||||
|
||||
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||
// vvvvvvvvvvvvvvvvvvv REMOVE IF NOT SUPPORTING IE6/7/8 vvvvvvvvvvvvvvvvvvv
|
||||
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||
typeof $.browser !== 'undefined' && $.browser.msie && !supports_onhashchange && (function(){
|
||||
// Not only do IE6/7 need the "magical" Iframe treatment, but so does IE8
|
||||
// when running in "IE7 compatibility" mode.
|
||||
|
||||
var iframe,
|
||||
iframe_src;
|
||||
|
||||
// When the event is bound and polling starts in IE 6/7, create a hidden
|
||||
// Iframe for history handling.
|
||||
self.start = function(){
|
||||
if ( !iframe ) {
|
||||
iframe_src = $.fn[ str_hashchange ].src;
|
||||
iframe_src = iframe_src && iframe_src + get_fragment();
|
||||
|
||||
// Create hidden Iframe. Attempt to make Iframe as hidden as possible
|
||||
// by using techniques from http://www.paciellogroup.com/blog/?p=604.
|
||||
iframe = $('<iframe tabindex="-1" title="empty"/>').hide()
|
||||
|
||||
// When Iframe has completely loaded, initialize the history and
|
||||
// start polling.
|
||||
.one( 'load', function(){
|
||||
iframe_src || history_set( get_fragment() );
|
||||
poll();
|
||||
})
|
||||
|
||||
// Load Iframe src if specified, otherwise nothing.
|
||||
.attr( 'src', iframe_src || 'javascript:0' )
|
||||
|
||||
// Append Iframe after the end of the body to prevent unnecessary
|
||||
// initial page scrolling (yes, this works).
|
||||
.insertAfter( 'body' )[0].contentWindow;
|
||||
|
||||
// Whenever `document.title` changes, update the Iframe's title to
|
||||
// prettify the back/next history menu entries. Since IE sometimes
|
||||
// errors with "Unspecified error" the very first time this is set
|
||||
// (yes, very useful) wrap this with a try/catch block.
|
||||
doc.onpropertychange = function(){
|
||||
try {
|
||||
if ( event.propertyName === 'title' ) {
|
||||
iframe.document.title = doc.title;
|
||||
}
|
||||
} catch(e) {}
|
||||
};
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Override the "stop" method since an IE6/7 Iframe was created. Even
|
||||
// if there are no longer any bound event handlers, the polling loop
|
||||
// is still necessary for back/next to work at all!
|
||||
self.stop = fn_retval;
|
||||
|
||||
// Get history by looking at the hidden Iframe's location.hash.
|
||||
history_get = function() {
|
||||
return get_fragment( iframe.location.href );
|
||||
};
|
||||
|
||||
// Set a new history item by opening and then closing the Iframe
|
||||
// document, *then* setting its location.hash. If document.domain has
|
||||
// been set, update that as well.
|
||||
history_set = function( hash, history_hash ) {
|
||||
var iframe_doc = iframe.document,
|
||||
domain = $.fn[ str_hashchange ].domain;
|
||||
|
||||
if ( hash !== history_hash ) {
|
||||
// Update Iframe with any initial `document.title` that might be set.
|
||||
iframe_doc.title = doc.title;
|
||||
|
||||
// Opening the Iframe's document after it has been closed is what
|
||||
// actually adds a history entry.
|
||||
iframe_doc.open();
|
||||
|
||||
// Set document.domain for the Iframe document as well, if necessary.
|
||||
domain && iframe_doc.write( '<script>document.domain="' + domain + '"</script>' );
|
||||
|
||||
iframe_doc.close();
|
||||
|
||||
// Update the Iframe's hash, for great justice.
|
||||
iframe.location.hash = hash;
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// ^^^^^^^^^^^^^^^^^^^ REMOVE IF NOT SUPPORTING IE6/7/8 ^^^^^^^^^^^^^^^^^^^
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
return self;
|
||||
})();
|
||||
|
||||
})(jQuery,this);
|
||||
+33
File diff suppressed because one or more lines are too long
+1130
File diff suppressed because it is too large
Load Diff
Vendored
+12
File diff suppressed because one or more lines are too long
+23
File diff suppressed because one or more lines are too long
+1299
File diff suppressed because it is too large
Load Diff
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
Copyright (c) 2012 Digital Fusion, http://teamdf.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
!function(t){var i=t(window);t.fn.visible=function(t,e,o){if(!(this.length<1)){var r=this.length>1?this.eq(0):this,n=r.get(0),f=i.width(),h=i.height(),o=o?o:"both",l=e===!0?n.offsetWidth*n.offsetHeight:!0;if("function"==typeof n.getBoundingClientRect){var g=n.getBoundingClientRect(),u=g.top>=0&&g.top<h,s=g.bottom>0&&g.bottom<=h,c=g.left>=0&&g.left<f,a=g.right>0&&g.right<=f,v=t?u||s:u&&s,b=t?c||a:c&&a;if("both"===o)return l&&v&&b;if("vertical"===o)return l&&v;if("horizontal"===o)return l&&b}else{var d=i.scrollTop(),p=d+h,w=i.scrollLeft(),m=w+f,y=r.offset(),z=y.top,B=z+r.height(),C=y.left,R=C+r.width(),j=t===!0?B:z,q=t===!0?z:B,H=t===!0?R:C,L=t===!0?C:R;if("both"===o)return!!l&&p>=q&&j>=d&&m>=L&&H>=w;if("vertical"===o)return!!l&&p>=q&&j>=d;if("horizontal"===o)return!!l&&m>=L&&H>=w}}}}(jQuery);
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*!
|
||||
* Copyright (c) 2013 Pieroxy <pieroxy@pieroxy.net>
|
||||
* This work is free. You can redistribute it and/or modify it
|
||||
* under the terms of the WTFPL, Version 2
|
||||
* For more information see LICENSE.txt or http://www.wtfpl.net/
|
||||
*
|
||||
* For more information, the home page:
|
||||
* http://pieroxy.net/blog/pages/lz-string/testing.html
|
||||
*
|
||||
* LZ-based compression algorithm, version 1.4.4
|
||||
*/
|
||||
var LZString=function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;n<o.length;n++)t[o][o.charAt(n)]=n}return t[o][r]}var r=String.fromCharCode,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",t={},i={compressToBase64:function(o){if(null==o)return"";var r=i._compress(o,6,function(o){return n.charAt(o)});switch(r.length%4){default:case 0:return r;case 1:return r+"===";case 2:return r+"==";case 3:return r+"="}},decompressFromBase64:function(r){return null==r?"":""==r?null:i._decompress(r.length,32,function(e){return o(n,r.charAt(e))})},compressToUTF16:function(o){return null==o?"":i._compress(o,15,function(o){return r(o+32)})+" "},decompressFromUTF16:function(o){return null==o?"":""==o?null:i._decompress(o.length,16384,function(r){return o.charCodeAt(r)-32})},compressToUint8Array:function(o){for(var r=i.compress(o),n=new Uint8Array(2*r.length),e=0,t=r.length;t>e;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o))}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;i<o.length;i+=1)if(u=o.charAt(i),Object.prototype.hasOwnProperty.call(s,u)||(s[u]=f++,p[u]=!0),c=a+u,Object.prototype.hasOwnProperty.call(s,c))a=c;else{if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}();"function"==typeof define&&define.amd?define(function(){return LZString}):"undefined"!=typeof module&&null!=module&&(module.exports=LZString);
|
||||
@@ -0,0 +1,157 @@
|
||||
/* global wp */
|
||||
|
||||
/**
|
||||
* media-library.js
|
||||
*
|
||||
* Adapted from WordPress
|
||||
*
|
||||
* @copyright 2017 by the WordPress contributors.
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* This program incorporates work covered by the following copyright and
|
||||
* permission notices:
|
||||
*
|
||||
* b2 is (c) 2001, 2002 Michel Valdrighi - m@tidakada.com - http://tidakada.com
|
||||
*
|
||||
* b2 is released under the GPL
|
||||
*
|
||||
* WordPress - Web publishing software
|
||||
*
|
||||
* Copyright 2003-2010 by the contributors
|
||||
*
|
||||
* WordPress is released under the GPL
|
||||
*/
|
||||
|
||||
var Select = wp.media.view.MediaFrame.Select,
|
||||
Library = wp.media.controller.Library,
|
||||
l10n = wp.media.view.l10n;
|
||||
|
||||
wp.media.view.MediaFrame.ETSelect = wp.media.view.MediaFrame.Select.extend({
|
||||
initialize: function() {
|
||||
_.defaults( this.options, {
|
||||
multiple: true,
|
||||
editing: false,
|
||||
embed: true,
|
||||
state: 'insert',
|
||||
metadata: {},
|
||||
title: l10n.insertMediaTitle,
|
||||
button: {
|
||||
text: l10n.insertIntoPost
|
||||
},
|
||||
});
|
||||
|
||||
// Call 'initialize' directly on the parent class.
|
||||
Select.prototype.initialize.apply( this, arguments );
|
||||
this.createIframeStates();
|
||||
},
|
||||
|
||||
/**
|
||||
* Create the default states.
|
||||
*/
|
||||
createStates: function() {
|
||||
var options = this.options;
|
||||
|
||||
var states = [
|
||||
// Main states.
|
||||
new Library({
|
||||
id: 'insert',
|
||||
title: options.title,
|
||||
priority: 20,
|
||||
toolbar: 'main-insert',
|
||||
filterable: 'all',
|
||||
library: wp.media.query( options.library ),
|
||||
multiple: options.multiple ? 'reset' : false,
|
||||
editable: true,
|
||||
allowLocalEdits: true,
|
||||
displaySettings: true,
|
||||
displayUserSettings: true
|
||||
}),
|
||||
];
|
||||
if (options.embed) {
|
||||
// Embed states.
|
||||
states.push(new wp.media.controller.Embed( { metadata: options.metadata } ))
|
||||
}
|
||||
this.states.add(states);
|
||||
},
|
||||
|
||||
bindHandlers: function() {
|
||||
var handlers;
|
||||
|
||||
Select.prototype.bindHandlers.apply( this, arguments );
|
||||
|
||||
this.on( 'toolbar:create:main-insert', this.createToolbar, this );
|
||||
this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
|
||||
|
||||
handlers = {
|
||||
content: {
|
||||
'embed': 'embedContent',
|
||||
},
|
||||
|
||||
toolbar: {
|
||||
'main-insert': 'mainInsertToolbar',
|
||||
}
|
||||
};
|
||||
|
||||
_.each( handlers, function( regionHandlers, region ) {
|
||||
_.each( regionHandlers, function( callback, handler ) {
|
||||
this.on( region + ':render:' + handler, this[ callback ], this );
|
||||
}, this );
|
||||
}, this );
|
||||
},
|
||||
|
||||
// Content
|
||||
embedContent: function() {
|
||||
var view = new wp.media.view.Embed({
|
||||
controller: this,
|
||||
model: this.state()
|
||||
}).render();
|
||||
|
||||
this.content.set( view );
|
||||
|
||||
if ( ! wp.media.isTouchDevice ) {
|
||||
view.url.input.focus();
|
||||
}
|
||||
},
|
||||
|
||||
// Toolbars
|
||||
mainInsertToolbar: function( view ) {
|
||||
var options = this.options;
|
||||
var controller = this;
|
||||
|
||||
view.set( 'insert', {
|
||||
style: 'primary',
|
||||
priority: 80,
|
||||
text: options.button.text,
|
||||
requires: { selection: true },
|
||||
|
||||
/**
|
||||
* @fires wp.media.controller.State#insert
|
||||
*/
|
||||
click: function() {
|
||||
var state = controller.state(),
|
||||
selection = state.get('selection');
|
||||
|
||||
controller.close();
|
||||
state.trigger( 'insert', selection ).reset();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
mainEmbedToolbar: function( toolbar ) {
|
||||
toolbar.view = new wp.media.view.Toolbar.Embed({
|
||||
controller: this
|
||||
});
|
||||
}
|
||||
});
|
||||
+29
File diff suppressed because one or more lines are too long
+10
File diff suppressed because one or more lines are too long
@@ -0,0 +1,92 @@
|
||||
( function($) {
|
||||
|
||||
$(function() {
|
||||
// WP 5.8 above - Widget Block Editor.
|
||||
var is_widgets_block_editor = $('.edit-widgets-block-editor').length > 0;
|
||||
|
||||
// 1.a Appends widget area creator panel.
|
||||
var widget_writing_area = is_widgets_block_editor ? '.block-editor-writing-flow > div' : '.widget-liquid-right';
|
||||
$(widget_writing_area).append(et_pb_options.widget_info);
|
||||
|
||||
var $create_box = $( '#et_pb_widget_area_create' ),
|
||||
$widget_name_input = $create_box.find( '#et_pb_new_widget_area_name' ),
|
||||
$et_pb_sidebars = $( 'div[id^=et_pb_widget_area_]' );
|
||||
|
||||
// 1.b. Handles create widget area action.
|
||||
$create_box.find('.et_pb_create_widget_area').on('click', function(event) {
|
||||
var $this_el = $(this);
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if ( $widget_name_input.val() === '' ) return;
|
||||
|
||||
$.ajax( {
|
||||
type: "POST",
|
||||
url: et_pb_options.ajaxurl,
|
||||
data:
|
||||
{
|
||||
action : 'et_pb_add_widget_area',
|
||||
et_admin_load_nonce : et_pb_options.et_admin_load_nonce,
|
||||
et_widget_area_name : $widget_name_input.val()
|
||||
},
|
||||
success: function( data ){
|
||||
$this_el.closest( '#et_pb_widget_area_create' ).find( '.et_pb_widget_area_result' ).hide().html( data ).slideToggle();
|
||||
}
|
||||
} );
|
||||
});
|
||||
|
||||
// 2.a. Append custom widget area remove button and handles remove action.
|
||||
var et_pb_sidebars_append_delete_button = function() {
|
||||
// 2.a.1. Append custom widget area remove button.
|
||||
var widget_area_id = is_widgets_block_editor ? $(this).data('widget-area-id') : $(this).attr('id');
|
||||
var widget_wrapper = is_widgets_block_editor ? '.block-editor-block-list__block' : '.widgets-holder-wrap';
|
||||
var widget_title = is_widgets_block_editor ? '.components-panel__body-toggle' : '.sidebar-name h2, .sidebar-name h3';
|
||||
|
||||
$(this).closest(widget_wrapper).find(widget_title).before('<a href="#" class="et_pb_widget_area_remove" data-et-widget-area-id="' + widget_area_id + '">' + et_pb_options.delete_string + '</a>');
|
||||
|
||||
// 2.a.1. andles remove widget area action.
|
||||
$('.et_pb_widget_area_remove').on('click', function(event) {
|
||||
var $this_el = $(this);
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
$.ajax( {
|
||||
type: "POST",
|
||||
url: et_pb_options.ajaxurl,
|
||||
data:
|
||||
{
|
||||
action : 'et_pb_remove_widget_area',
|
||||
et_admin_load_nonce : et_pb_options.et_admin_load_nonce,
|
||||
et_widget_area_name : $this_el.data('et-widget-area-id'),
|
||||
},
|
||||
success: function( data ){
|
||||
$('a[data-et-widget-area-id="' + data + '"]').closest(widget_wrapper).remove();
|
||||
}
|
||||
} );
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
// 2.b. Appends custom widget area remove button on each custom sidebar.
|
||||
if (is_widgets_block_editor) {
|
||||
var widgetBlockListMutation = _.debounce(function(mutations, observer) {
|
||||
$('div[data-widget-area-id^=et_pb_widget_area_]').each(et_pb_sidebars_append_delete_button);
|
||||
|
||||
// Disconnect once we know the delete buttons are added.
|
||||
if ($('.et_pb_widget_area_remove').length > 0) {
|
||||
observer.disconnect();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Watch for widget block editor to load Widget Area blocks. There is no event to
|
||||
// know when those blocks are added on the editor, hence we use mutation observer.
|
||||
var widgetBlockListObserver = new MutationObserver(widgetBlockListMutation);
|
||||
|
||||
widgetBlockListObserver.observe(document.querySelector('.block-editor-block-list__layout'), {childList: true});
|
||||
} else {
|
||||
$et_pb_sidebars.each(et_pb_sidebars_append_delete_button);
|
||||
}
|
||||
} );
|
||||
|
||||
} )(jQuery);
|
||||
+424
@@ -0,0 +1,424 @@
|
||||
/**
|
||||
* wp-color-picker-alpha
|
||||
*
|
||||
* Version 1.0
|
||||
* Copyright (c) 2017 Elegant Themes.
|
||||
* Licensed under the GPLv2 license.
|
||||
*
|
||||
* Overwrite Automattic Iris for enabled Alpha Channel in wpColorPicker
|
||||
* Only run in input and is defined data alpha in true
|
||||
* Add custom colorpicker UI
|
||||
*
|
||||
* This is modified version made by Elegant Themes based on the work covered by
|
||||
* the following copyright:
|
||||
*
|
||||
* wp-color-picker-alpha Version: 1.1
|
||||
* https://github.com/23r9i0/wp-color-picker-alpha
|
||||
* Copyright (c) 2015 Sergio P.A. (23r9i0).
|
||||
* Licensed under the GPLv2 license.
|
||||
*/
|
||||
( function( $ ) {
|
||||
// Variable for some backgrounds
|
||||
var image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAAHnlligAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHJJREFUeNpi+P///4EDBxiAGMgCCCAGFB5AADGCRBgYDh48CCRZIJS9vT2QBAggFBkmBiSAogxFBiCAoHogAKIKAlBUYTELAiAmEtABEECk20G6BOmuIl0CIMBQ/IEMkO0myiSSraaaBhZcbkUOs0HuBwDplz5uFJ3Z4gAAAABJRU5ErkJggg==';
|
||||
// html stuff for wpColorPicker copy of the original color-picker.js
|
||||
var _before = '<a tabindex="0" class="wp-color-result" />',
|
||||
_after = '<div class="wp-picker-holder" />',
|
||||
_wrap = '<div class="wp-picker-container" />',
|
||||
_button = '<input type="button" class="button button-small button-clear hidden" />',
|
||||
_close_button = '<button type="button" class="button button-confirm" />',
|
||||
_close_button_icon = '<div style="fill: #3EF400; width: 25px; height: 25px; margin-top: -1px;"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><g><path d="M19.203 9.21a.677.677 0 0 0-.98 0l-5.71 5.9-2.85-2.95a.675.675 0 0 0-.98 0l-1.48 1.523a.737.737 0 0 0 0 1.015l4.82 4.979a.677.677 0 0 0 .98 0l7.68-7.927a.737.737 0 0 0 0-1.015l-1.48-1.525z" fillRule="evenodd" /></g></svg></div>';
|
||||
|
||||
/**
|
||||
* Overwrite Color
|
||||
* for enable support rbga
|
||||
*/
|
||||
Color.fn.toString = function() {
|
||||
if ( this._alpha < 1 )
|
||||
return this.toCSS( 'rgba', this._alpha ).replace( /\s+/g, '' );
|
||||
|
||||
var hex = parseInt( this._color, 10 ).toString( 16 );
|
||||
|
||||
if ( this.error )
|
||||
return '';
|
||||
|
||||
if ( hex.length < 6 ) {
|
||||
for ( var i = 6 - hex.length - 1; i >= 0; i-- ) {
|
||||
hex = '0' + hex;
|
||||
}
|
||||
}
|
||||
|
||||
return '#' + hex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Overwrite wpColorPicker
|
||||
*/
|
||||
$.widget( 'wp.wpColorPicker', $.wp.wpColorPicker, {
|
||||
_create: function() {
|
||||
// bail early for unsupported Iris.
|
||||
if ( ! $.support.iris ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this,
|
||||
el = self.element;
|
||||
|
||||
$.extend( self.options, el.data() );
|
||||
|
||||
// keep close bound so it can be attached to a body listener
|
||||
self.close = $.proxy( self.close, self );
|
||||
|
||||
self.initialValue = el.val();
|
||||
|
||||
// Set up HTML structure, hide things
|
||||
el.addClass( 'wp-color-picker' ).hide().wrap( _wrap );
|
||||
self.wrap = el.parent();
|
||||
self.toggler = $( _before ).insertBefore( el ).css( { backgroundColor: self.initialValue } ).attr( 'title', wpColorPickerL10n.pick ).attr( 'data-current', wpColorPickerL10n.current );
|
||||
self.pickerContainer = $( _after ).insertAfter( el );
|
||||
self.button = $( _button );
|
||||
self.close_button = $( _close_button );
|
||||
|
||||
if ( self.options.defaultColor ) {
|
||||
self.button.addClass( 'wp-picker-default' ).val( wpColorPickerL10n.defaultString );
|
||||
} else {
|
||||
self.button.addClass( 'wp-picker-clear' ).val( wpColorPickerL10n.clear );
|
||||
}
|
||||
|
||||
el.wrap( '<span class="wp-picker-input-wrap" />' ).after(self.button);
|
||||
|
||||
if ( self.options.diviColorpicker ) {
|
||||
self.close_button.html( _close_button_icon );
|
||||
el.after( self.close_button );
|
||||
}
|
||||
|
||||
el.iris( {
|
||||
target: self.pickerContainer,
|
||||
hide: self.options.hide,
|
||||
width: self.options.width,
|
||||
height: self.options.height,
|
||||
diviColorpicker: self.options.diviColorpicker,
|
||||
mode: self.options.mode,
|
||||
palettes: self.options.palettes,
|
||||
change: function( event, ui ) {
|
||||
if ( self.options.alpha ) {
|
||||
self.toggler.css( { 'background-image': 'url(' + image + ')' } ).html('<span />');
|
||||
self.toggler.find('span').css({
|
||||
'width': '100%',
|
||||
'height': '100%',
|
||||
'position': 'absolute',
|
||||
'top': 0,
|
||||
'left': 0,
|
||||
'border-top-left-radius': '3px',
|
||||
'border-bottom-left-radius': '3px',
|
||||
'background': ui.color.toString()
|
||||
});
|
||||
} else {
|
||||
self.toggler.css( { backgroundColor: ui.color.toString() } );
|
||||
}
|
||||
// check for a custom cb
|
||||
if ( $.isFunction( self.options.change ) ) {
|
||||
self.options.change.call( this, event, ui );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
el.val( self.initialValue );
|
||||
self._addListeners();
|
||||
if ( ! self.options.hide ) {
|
||||
self.toggler.click();
|
||||
}
|
||||
},
|
||||
_addListeners: function() {
|
||||
var self = this;
|
||||
|
||||
// prevent any clicks inside this widget from leaking to the top and closing it
|
||||
self.wrap.on( 'click.wpcolorpicker', function( event ) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
self.toggler.click( function(){
|
||||
if ( self.toggler.hasClass( 'wp-picker-open' ) ) {
|
||||
self.close();
|
||||
} else {
|
||||
self.open();
|
||||
}
|
||||
});
|
||||
|
||||
self.element.change( function( event ) {
|
||||
var me = $( this ),
|
||||
val = me.val();
|
||||
// Empty or Error = clear
|
||||
if ( val === '' || self.element.hasClass('iris-error') ) {
|
||||
if ( self.options.alpha ) {
|
||||
self.toggler.css( 'backgroundColor', '' );
|
||||
self.toggler.find('span').css( 'backgroundColor', '' );
|
||||
} else {
|
||||
self.toggler.css( 'backgroundColor', '' );
|
||||
}
|
||||
// fire clear callback if we have one
|
||||
if ( $.isFunction( self.options.clear ) ) {
|
||||
self.options.clear.call( this, event );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// open a keyboard-focused closed picker with space or enter
|
||||
self.toggler.on( 'keyup', function( event ) {
|
||||
if ( event.keyCode === 13 || event.keyCode === 32 ) {
|
||||
event.preventDefault();
|
||||
self.toggler.trigger( 'click' ).next().focus();
|
||||
}
|
||||
});
|
||||
|
||||
self.button.click( function( event ) {
|
||||
var me = $( this );
|
||||
if ( me.hasClass( 'wp-picker-clear' ) ) {
|
||||
self.element.val( '' );
|
||||
if ( self.options.alpha ) {
|
||||
self.toggler.css( 'backgroundColor', '' );
|
||||
self.toggler.find('span').css( 'backgroundColor', '' );
|
||||
} else {
|
||||
self.toggler.css( 'backgroundColor', '' );
|
||||
}
|
||||
if ( $.isFunction( self.options.clear ) ) {
|
||||
self.options.clear.call( this, event );
|
||||
}
|
||||
} else if ( me.hasClass( 'wp-picker-default' ) ) {
|
||||
self.element.val( self.options.defaultColor ).change();
|
||||
}
|
||||
});
|
||||
|
||||
self.close_button.click( function( event ) {
|
||||
event.preventDefault();
|
||||
self.close();
|
||||
});
|
||||
},
|
||||
close: function() {
|
||||
this._super();
|
||||
var self = this;
|
||||
|
||||
if ($.isFunction(self.options.onClose)) {
|
||||
self.options.onClose.call(this);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Overwrite iris
|
||||
*/
|
||||
$.widget( 'a8c.iris', $.a8c.iris, {
|
||||
_create: function() {
|
||||
this._super();
|
||||
|
||||
// Global option for check is mode rbga is enabled
|
||||
this.options.alpha = this.element.data( 'alpha' ) || false;
|
||||
|
||||
// Is not input disabled
|
||||
if ( ! this.element.is( ':input' ) ) {
|
||||
this.options.alpha = false;
|
||||
}
|
||||
|
||||
if ( typeof this.options.alpha !== 'undefined' && this.options.alpha ) {
|
||||
var self = this,
|
||||
el = self.element,
|
||||
_html = '<div class="iris-strip iris-slider iris-alpha-slider"><div class="iris-slider-offset iris-slider-offset-alpha"></div></div>',
|
||||
aContainer = $( _html ).appendTo( self.picker.find( '.iris-picker-inner' ) ),
|
||||
aSlider = aContainer.find( '.iris-slider-offset-alpha' ),
|
||||
controls = {
|
||||
aContainer: aContainer,
|
||||
aSlider: aSlider
|
||||
};
|
||||
|
||||
// Set default width for input reset
|
||||
self.options.defaultWidth = el.width();
|
||||
|
||||
// Update width for input
|
||||
if ( self._color._alpha < 1 || self._color.toString().indexOf('rgb') != 1 ) {
|
||||
el.width( parseInt( self.options.defaultWidth+100 ) );
|
||||
}
|
||||
|
||||
// Push new controls
|
||||
$.each( controls, function( k, v ){
|
||||
self.controls[k] = v;
|
||||
});
|
||||
|
||||
// Change size strip and add margin for sliders
|
||||
self.controls.square.css({'margin-right': '0'});
|
||||
var emptyWidth = ( self.picker.width() - self.controls.square.width() - 20 ),
|
||||
stripsMargin = emptyWidth/6,
|
||||
stripsWidth = (emptyWidth/2) - stripsMargin;
|
||||
|
||||
$.each( [ 'aContainer', 'strip' ], function( k, v ) {
|
||||
self.controls[v].width( stripsWidth ).css({ 'margin-left': stripsMargin + 'px' });
|
||||
});
|
||||
|
||||
// Add new slider
|
||||
self._initControls();
|
||||
|
||||
// For updated widget
|
||||
self._change();
|
||||
}
|
||||
},
|
||||
_initControls: function() {
|
||||
this._super();
|
||||
|
||||
if ( this.options.alpha ) {
|
||||
var self = this,
|
||||
controls = self.controls;
|
||||
|
||||
controls.aSlider.slider({
|
||||
orientation: 'vertical',
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
value: parseInt( self._color._alpha*100 ),
|
||||
slide: function( event, ui ) {
|
||||
// Update alpha value
|
||||
self._color._alpha = parseFloat( ui.value/100 );
|
||||
self._change.apply( self, arguments );
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
_change: function() {
|
||||
this._super();
|
||||
var self = this,
|
||||
el = self.element;
|
||||
|
||||
if ( this.options.alpha ) {
|
||||
var controls = self.controls,
|
||||
alpha = parseInt( self._color._alpha*100 ),
|
||||
color = self._color.toRgb(),
|
||||
gradient = [
|
||||
'rgb(' + color.r + ',' + color.g + ',' + color.b + ') 0%',
|
||||
'rgba(' + color.r + ',' + color.g + ',' + color.b + ', 0) 100%'
|
||||
],
|
||||
defaultWidth = self.options.defaultWidth,
|
||||
target = self.picker.closest('.wp-picker-container').find( '.wp-color-result' );
|
||||
|
||||
// Generate background slider alpha, only for CSS3 old browser fuck!! :)
|
||||
controls.aContainer.css({ 'background': 'linear-gradient(to bottom, ' + gradient.join( ', ' ) + '), url(' + image + ')' });
|
||||
|
||||
if ( target.hasClass('wp-picker-open') ) {
|
||||
// Update alpha value
|
||||
controls.aSlider.slider( 'value', alpha );
|
||||
|
||||
/**
|
||||
* Disabled change opacity in default slider Saturation ( only is alpha enabled )
|
||||
* and change input width for view all value
|
||||
*/
|
||||
if ( self._color._alpha < 1 ) {
|
||||
var style = controls.strip.attr( 'style' ).replace( /rgba\(([0-9]+,)(\s+)?([0-9]+,)(\s+)?([0-9]+)(,(\s+)?[0-9\.]+)\)/g, 'rgb($1$3$5)' );
|
||||
|
||||
controls.strip.attr( 'style', style );
|
||||
|
||||
el.width( parseInt( defaultWidth+100 ) );
|
||||
} else {
|
||||
el.width( defaultWidth );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var reset = el.data('reset-alpha') || false;
|
||||
if ( reset ) {
|
||||
self.picker.find( '.iris-palette-container' ).on( 'click.palette', '.iris-palette', function() {
|
||||
self._color._alpha = 1;
|
||||
self.active = 'external';
|
||||
self._change();
|
||||
});
|
||||
}
|
||||
},
|
||||
_addInputListeners: function( input ) {
|
||||
var self = this,
|
||||
debounceTimeout = 700, // originally set to 100, but some user perceive it as "jumps to random colors at third digit"
|
||||
callback = function( event ){
|
||||
var color = new Color( input.val() ),
|
||||
val = input.val();
|
||||
|
||||
input.removeClass( 'iris-error' );
|
||||
// we gave a bad color
|
||||
if ( color.error ) {
|
||||
// don't error on an empty input
|
||||
if ( val !== '' ) {
|
||||
input.addClass( 'iris-error' );
|
||||
}
|
||||
} else {
|
||||
if ( color.toString() !== self._color.toString() ) {
|
||||
// let's not do this on keyup for hex shortcodes
|
||||
if ( ! ( event.type === 'keyup' && val.match( /^[0-9a-fA-F]{3}$/ ) ) ) {
|
||||
self._setOption( 'color', color.toString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
input.on( 'change', callback ).on( 'keyup', self._debounce( callback, debounceTimeout ) );
|
||||
|
||||
// If we initialized hidden, show on first focus. The rest is up to you.
|
||||
if ( self.options.hide ) {
|
||||
input.one( 'focus', function() {
|
||||
self.show();
|
||||
});
|
||||
}
|
||||
},
|
||||
_dimensions: function( reset ) {
|
||||
// whatever size
|
||||
var self = this,
|
||||
opts = self.options,
|
||||
controls = self.controls,
|
||||
square = controls.square,
|
||||
strip = self.picker.find( '.iris-strip' ),
|
||||
squareWidth = '77.5%',
|
||||
stripWidth = '12%',
|
||||
totalPadding = 20,
|
||||
innerWidth = opts.border ? opts.width - totalPadding : opts.width,
|
||||
controlsHeight,
|
||||
paletteCount = $.isArray( opts.palettes ) ? opts.palettes.length : self._palettes.length,
|
||||
paletteMargin, paletteWidth, paletteContainerWidth;
|
||||
|
||||
if ( reset ) {
|
||||
square.css( 'width', '' );
|
||||
strip.css( 'width', '' );
|
||||
self.picker.css( {width: '', height: ''} );
|
||||
}
|
||||
|
||||
squareWidth = innerWidth * ( parseFloat( squareWidth ) / 100 );
|
||||
stripWidth = innerWidth * ( parseFloat( stripWidth ) / 100 );
|
||||
controlsHeight = opts.border ? squareWidth + totalPadding : squareWidth;
|
||||
|
||||
if (opts.diviColorpicker ) {
|
||||
square.width( opts.width ).height( opts.height );
|
||||
controlsHeight = opts.height;
|
||||
} else {
|
||||
square.width( squareWidth ).height( squareWidth );
|
||||
}
|
||||
|
||||
strip.height( squareWidth ).width( stripWidth );
|
||||
self.picker.css( { width: opts.width, height: controlsHeight } );
|
||||
|
||||
if ( ! opts.palettes ) {
|
||||
return self.picker.css( 'paddingBottom', '' );
|
||||
}
|
||||
|
||||
// single margin at 2%
|
||||
paletteMargin = squareWidth * 2 / 100;
|
||||
paletteContainerWidth = squareWidth - ( ( paletteCount - 1 ) * paletteMargin );
|
||||
paletteWidth = paletteContainerWidth / paletteCount;
|
||||
self.picker.find('.iris-palette').each( function( i ) {
|
||||
var margin = i === 0 ? 0 : paletteMargin;
|
||||
$( this ).css({
|
||||
width: paletteWidth,
|
||||
height: paletteWidth,
|
||||
marginLeft: margin
|
||||
});
|
||||
});
|
||||
self.picker.css( 'paddingBottom', paletteWidth + paletteMargin );
|
||||
strip.height( paletteWidth + paletteMargin + squareWidth );
|
||||
}
|
||||
} );
|
||||
}( jQuery ) );
|
||||
|
||||
// Auto Call plugin is class is color-picker
|
||||
jQuery( document ).ready( function( $ ) {
|
||||
$( '.color-picker' ).wpColorPicker();
|
||||
} );
|
||||
Vendored
+8
File diff suppressed because one or more lines are too long
+529
@@ -0,0 +1,529 @@
|
||||
/**
|
||||
* wp-color-picker-alpha
|
||||
*
|
||||
* Version 1.0
|
||||
* Copyright (c) 2017 Elegant Themes.
|
||||
* Licensed under the GPLv2 license.
|
||||
*
|
||||
* Overwrite Automattic Iris for enabled Alpha Channel in wpColorPicker
|
||||
* Only run in input and is defined data alpha in true
|
||||
* Add custom colorpicker UI
|
||||
*
|
||||
* This is modified version made by Elegant Themes based on the work covered by
|
||||
* the following copyright:
|
||||
*
|
||||
* wp-color-picker-alpha Version: 1.1
|
||||
* https://github.com/23r9i0/wp-color-picker-alpha
|
||||
* Copyright (c) 2015 Sergio P.A. (23r9i0).
|
||||
* Licensed under the GPLv2 license.
|
||||
*/
|
||||
( function( $ ) {
|
||||
// Variable for some backgrounds
|
||||
var image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAAHnlligAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAHJJREFUeNpi+P///4EDBxiAGMgCCCAGFB5AADGCRBgYDh48CCRZIJS9vT2QBAggFBkmBiSAogxFBiCAoHogAKIKAlBUYTELAiAmEtABEECk20G6BOmuIl0CIMBQ/IEMkO0myiSSraaaBhZcbkUOs0HuBwDplz5uFJ3Z4gAAAABJRU5ErkJggg==';
|
||||
// html stuff for wpColorPicker copy of the original color-picker.js
|
||||
var _before = '<button type="button" class="button wp-color-result" aria-expanded="false"><span class="wp-color-result-text"></span></button>',
|
||||
_after = '<div class="wp-picker-holder" />',
|
||||
_wrap = '<div class="wp-picker-container" />',
|
||||
_button = '<input type="button" class="button button-small button-clear hidden" />',
|
||||
_wrappingLabel = '<label></label>',
|
||||
_wrappingLabelText = '<span class="screen-reader-text"></span>',
|
||||
_close_button = '<button type="button" class="button button-confirm" />',
|
||||
_close_button_icon = '<div style="fill: #3EF400; width: 25px; height: 25px; margin-top: -1px;"><svg viewBox="0 0 28 28" preserveAspectRatio="xMidYMid meet" shapeRendering="geometricPrecision"><g><path d="M19.203 9.21a.677.677 0 0 0-.98 0l-5.71 5.9-2.85-2.95a.675.675 0 0 0-.98 0l-1.48 1.523a.737.737 0 0 0 0 1.015l4.82 4.979a.677.677 0 0 0 .98 0l7.68-7.927a.737.737 0 0 0 0-1.015l-1.48-1.525z" fillRule="evenodd" /></g></svg></div>';
|
||||
|
||||
// Color picker label translation. By default, set label manually without translation.
|
||||
var _defaultString = 'Default';
|
||||
var _defaultAriaLabel = 'Select default color';
|
||||
var _clearString = 'Clear';
|
||||
var _clearAriaLabel = 'Clear color';
|
||||
var _colorValue = 'Color value';
|
||||
var _selectColor = 'Select Color';
|
||||
|
||||
if ('undefined' !== typeof wp && 'undefined' !== typeof wp.i18n && 'undefined' !== typeof wp.i18n.__) {
|
||||
// Directly use wp.i18n if it exists. wp.i18n is added on 5.0.
|
||||
var __ = wp.i18n.__;
|
||||
_defaultString = __( 'Default' );
|
||||
_defaultAriaLabel = __( 'Select default color' );
|
||||
_clearString = __( 'Clear' );
|
||||
_clearAriaLabel = __( 'Clear color' );
|
||||
_colorValue = __( 'Color value' );
|
||||
_selectColor = __( 'Select Color' );
|
||||
} else if ('undefined' !== typeof wpColorPickerL10n && 'undefined' !== typeof wpColorPickerL10n.current) {
|
||||
// Or use wpColorPickerL10n if it's still supported. wpColorPickerL10n is deprecated
|
||||
// since 5.5.
|
||||
_defaultString = wpColorPickerL10n.defaultString;
|
||||
_defaultAriaLabel = wpColorPickerL10n.defaultAriaLabel;
|
||||
_clearString = wpColorPickerL10n.clear,
|
||||
_clearAriaLabel = wpColorPickerL10n.clearAriaLabel;
|
||||
_colorValue = wpColorPickerL10n.defaultLabel;
|
||||
_selectColor = wpColorPickerL10n.pick;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite Color
|
||||
* for enable support rbga
|
||||
*/
|
||||
Color.fn.toString = function() {
|
||||
if ( this._alpha < 1 )
|
||||
return this.toCSS( 'rgba', this._alpha ).replace( /\s+/g, '' );
|
||||
|
||||
var hex = parseInt( this._color, 10 ).toString( 16 );
|
||||
|
||||
if ( this.error )
|
||||
return '';
|
||||
|
||||
if ( hex.length < 6 ) {
|
||||
for ( var i = 6 - hex.length - 1; i >= 0; i-- ) {
|
||||
hex = '0' + hex;
|
||||
}
|
||||
}
|
||||
|
||||
return '#' + hex;
|
||||
};
|
||||
|
||||
/**
|
||||
* Overwrite wpColorPicker
|
||||
*/
|
||||
$.widget( 'wp.wpColorPicker', $.wp.wpColorPicker, {
|
||||
|
||||
_create: function() {
|
||||
// Return early if Iris support is missing.
|
||||
if ( ! $.support.iris ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this,
|
||||
el = self.element;
|
||||
|
||||
// Override default options with options bound to the element.
|
||||
$.extend( self.options, el.data() );
|
||||
|
||||
// Create a color picker which only allows adjustments to the hue.
|
||||
if ( self.options.type === 'hue' ) {
|
||||
return self._createHueOnly();
|
||||
}
|
||||
|
||||
// Bind the close event.
|
||||
self.close = self.close.bind(self);
|
||||
|
||||
self.initialValue = el.val();
|
||||
|
||||
// Add a CSS class to the input field.
|
||||
el.addClass( 'wp-color-picker' );
|
||||
|
||||
/*
|
||||
* Check if there's already a wrapping label, e.g. in the Customizer.
|
||||
* If there's no label, add a default one to match the Customizer template.
|
||||
*/
|
||||
if ( ! el.parent( 'label' ).length ) {
|
||||
// Wrap the input field in the default label.
|
||||
el.wrap( _wrappingLabel );
|
||||
// Insert the default label text.
|
||||
self.wrappingLabelText = $( _wrappingLabelText )
|
||||
.insertBefore( el )
|
||||
.text( _colorValue );
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, either it's the standalone version or the Customizer
|
||||
* one, we have a wrapping label to use as hook in the DOM, let's store it.
|
||||
*/
|
||||
self.wrappingLabel = el.parent();
|
||||
|
||||
// Wrap the label in the main wrapper.
|
||||
self.wrappingLabel.wrap( _wrap );
|
||||
// Store a reference to the main wrapper.
|
||||
self.wrap = self.wrappingLabel.parent();
|
||||
// Set up the toggle button and insert it before the wrapping label.
|
||||
self.toggler = $( _before )
|
||||
.insertBefore( self.wrappingLabel )
|
||||
.css( { backgroundColor: self.initialValue } )
|
||||
.attr( 'title', _selectColor )
|
||||
.addClass( 'et-wp-color-result-updated');
|
||||
|
||||
// some strings were changed in recent colorpicker update, but we still need to use legacy strings in some places
|
||||
if ( typeof et_pb_color_picker_strings !== 'undefined' ) {
|
||||
self.toggler.attr( 'data-legacy_title', et_pb_color_picker_strings.legacy_pick ).attr( 'data-current', et_pb_color_picker_strings.legacy_current );
|
||||
}
|
||||
|
||||
// Set the toggle button span element text.
|
||||
self.toggler.find( '.wp-color-result-text' ).text( _selectColor );
|
||||
// Set up the Iris container and insert it after the wrapping label.
|
||||
self.pickerContainer = $( _after ).insertAfter( self.wrappingLabel );
|
||||
// Store a reference to the Clear/Default button.
|
||||
self.button = $( _button );
|
||||
self.close_button = $( _close_button ).html( _close_button_icon );
|
||||
|
||||
if ( self.options.diviColorpicker ) {
|
||||
el.after( self.close_button );
|
||||
}
|
||||
|
||||
// Set up the Clear/Default button.
|
||||
if ( self.options.defaultColor ) {
|
||||
self.button
|
||||
.addClass( 'wp-picker-default' )
|
||||
.val( _defaultString )
|
||||
.attr( 'aria-label', _defaultAriaLabel );
|
||||
} else {
|
||||
self.button
|
||||
.addClass( 'wp-picker-clear' )
|
||||
.val( _clearString )
|
||||
.attr( 'aria-label', _clearAriaLabel );
|
||||
}
|
||||
|
||||
// Wrap the wrapping label in its wrapper and append the Clear/Default button.
|
||||
self.wrappingLabel
|
||||
.wrap( '<span class="wp-picker-input-wrap hidden" />' )
|
||||
.after( self.button );
|
||||
|
||||
/*
|
||||
* The input wrapper now contains the label+input+Clear/Default button.
|
||||
* Store a reference to the input wrapper: we'll use this to toggle
|
||||
* the controls visibility.
|
||||
*/
|
||||
self.inputWrapper = el.closest( '.wp-picker-input-wrap' );
|
||||
|
||||
/*
|
||||
* CSS for support < 4.9
|
||||
*/
|
||||
self.toggler.css({
|
||||
'height': '24px',
|
||||
'margin': '0 6px 6px 0',
|
||||
'padding': '0 0 0 30px',
|
||||
'font-size': '11px'
|
||||
});
|
||||
|
||||
|
||||
el.iris( {
|
||||
target: self.pickerContainer,
|
||||
hide: self.options.hide,
|
||||
width: self.options.width,
|
||||
height: self.options.height,
|
||||
mode: self.options.mode,
|
||||
palettes: self.options.palettes,
|
||||
diviColorpicker: self.options.diviColorpicker,
|
||||
change: function( event, ui ) {
|
||||
if ( self.options.alpha ) {
|
||||
self.toggler.css( {
|
||||
'background-image' : 'url(' + image + ')',
|
||||
'position' : 'relative'
|
||||
} );
|
||||
if ( self.toggler.find('span.color-alpha').length == 0 ) {
|
||||
self.toggler.append('<span class="color-alpha" />');
|
||||
}
|
||||
self.toggler.find( 'span.color-alpha' ).css( {
|
||||
'width' : '100%',
|
||||
'height' : '100%',
|
||||
'position' : 'absolute',
|
||||
'top' : '0px',
|
||||
'left' : '0px',
|
||||
'border-top-left-radius' : '3px',
|
||||
'border-bottom-left-radius' : '3px',
|
||||
'background' : ui.color.toString()
|
||||
} );
|
||||
} else {
|
||||
self.toggler.css( { backgroundColor: ui.color.toString() } );
|
||||
}
|
||||
|
||||
// check for a custom cb
|
||||
if ( 'function' === typeof self.options.change ) {
|
||||
self.options.change.call( this, event, ui );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
el.val( self.initialValue );
|
||||
self._addListeners();
|
||||
|
||||
// Force the color picker to always be closed on initial load.
|
||||
if ( ! self.options.hide ) {
|
||||
self.toggler.trigger('click');
|
||||
}
|
||||
},
|
||||
|
||||
_addListeners: function() {
|
||||
var self = this;
|
||||
|
||||
// Prevent any clicks inside this widget from leaking to the top and closing it.
|
||||
self.wrap.on( 'click.wpcolorpicker', function( event ) {
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
|
||||
self.toggler.on('click', function() {
|
||||
if ( self.toggler.hasClass( 'wp-picker-open' ) ) {
|
||||
self.close();
|
||||
} else {
|
||||
self.open();
|
||||
}
|
||||
});
|
||||
|
||||
self.element.on( 'change', function( event ) {
|
||||
// Empty or Error = clear
|
||||
if ( $( this ).val() === '' || self.element.hasClass( 'iris-error' ) ) {
|
||||
if ( self.options.alpha ) {
|
||||
self.toggler.find( 'span.color-alpha' ).css( 'backgroundColor', '' );
|
||||
} else {
|
||||
self.toggler.css( 'backgroundColor', '' );
|
||||
}
|
||||
|
||||
// fire clear callback if we have one
|
||||
if ( 'function' === typeof self.options.clear )
|
||||
self.options.clear.call( this, event );
|
||||
}
|
||||
} );
|
||||
|
||||
self.button.on( 'click', function( event ) {
|
||||
if ( $( this ).hasClass( 'wp-picker-clear' ) ) {
|
||||
self.element.val( '' );
|
||||
if ( self.options.alpha ) {
|
||||
self.toggler.find( 'span.color-alpha' ).css( 'backgroundColor', '' );
|
||||
} else {
|
||||
self.toggler.css( 'backgroundColor', '' );
|
||||
}
|
||||
|
||||
if ( 'function' === typeof self.options.clear )
|
||||
self.options.clear.call( this, event );
|
||||
|
||||
} else if ( $( this ).hasClass( 'wp-picker-default' ) ) {
|
||||
self.element.val( self.options.defaultColor ).trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
self.close_button.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
self.close();
|
||||
});
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this._super();
|
||||
var self = this;
|
||||
|
||||
if ('function' === typeof self.options.onClose) {
|
||||
self.options.onClose.call(this);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Overwrite iris
|
||||
*/
|
||||
$.widget( 'a8c.iris', $.a8c.iris, {
|
||||
_create: function() {
|
||||
this._super();
|
||||
|
||||
// Global option for check is mode rbga is enabled
|
||||
this.options.alpha = this.element.data( 'alpha' ) || false;
|
||||
|
||||
// Is not input disabled
|
||||
if ( ! this.element.is( ':input' ) ) {
|
||||
this.options.alpha = false;
|
||||
}
|
||||
|
||||
if ( typeof this.options.alpha !== 'undefined' && this.options.alpha ) {
|
||||
var self = this,
|
||||
el = self.element,
|
||||
_html = '<div class="iris-strip iris-slider iris-alpha-slider"><div class="iris-slider-offset iris-slider-offset-alpha"></div></div>',
|
||||
aContainer = $( _html ).appendTo( self.picker.find( '.iris-picker-inner' ) ),
|
||||
aSlider = aContainer.find( '.iris-slider-offset-alpha' ),
|
||||
controls = {
|
||||
aContainer: aContainer,
|
||||
aSlider: aSlider
|
||||
};
|
||||
|
||||
// Set default width for input reset
|
||||
self.options.defaultWidth = el.width();
|
||||
|
||||
// Update width for input
|
||||
if ( self._color._alpha < 1 || self._color.toString().indexOf('rgb') != 1 ) {
|
||||
el.width( parseInt( self.options.defaultWidth+100 ) );
|
||||
}
|
||||
|
||||
// Push new controls
|
||||
$.each( controls, function( k, v ){
|
||||
self.controls[k] = v;
|
||||
});
|
||||
|
||||
// Change size strip and add margin for sliders
|
||||
self.controls.square.css({'margin-right': '0px'});
|
||||
var emptyWidth = ( self.picker.width() - self.controls.square.width() - 20 ),
|
||||
stripsMargin = emptyWidth/6,
|
||||
stripsWidth = (emptyWidth/2) - stripsMargin;
|
||||
|
||||
$.each( [ 'aContainer', 'strip' ], function( k, v ) {
|
||||
self.controls[v].width( stripsWidth ).css({ 'margin-left': stripsMargin + 'px' });
|
||||
});
|
||||
|
||||
// Add new slider
|
||||
self._initControls();
|
||||
|
||||
// For updated widget
|
||||
self._change();
|
||||
}
|
||||
},
|
||||
_initControls: function() {
|
||||
this._super();
|
||||
|
||||
if ( this.options.alpha ) {
|
||||
var self = this,
|
||||
controls = self.controls;
|
||||
|
||||
controls.aSlider.slider({
|
||||
orientation: 'vertical',
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
value: parseInt( self._color._alpha*100 ),
|
||||
slide: function( event, ui ) {
|
||||
// Update alpha value
|
||||
self._color._alpha = parseFloat( ui.value/100 );
|
||||
self._change.apply( self, arguments );
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
_change: function() {
|
||||
this._super();
|
||||
var self = this,
|
||||
el = self.element;
|
||||
|
||||
if ( this.options.alpha ) {
|
||||
var controls = self.controls,
|
||||
alpha = parseInt( self._color._alpha*100 ),
|
||||
color = self._color.toRgb(),
|
||||
gradient = [
|
||||
'rgb(' + color.r + ',' + color.g + ',' + color.b + ') 0%',
|
||||
'rgba(' + color.r + ',' + color.g + ',' + color.b + ', 0) 100%'
|
||||
],
|
||||
defaultWidth = self.options.defaultWidth,
|
||||
target = self.picker.closest('.wp-picker-container').find( '.wp-color-result' );
|
||||
|
||||
// Generate background slider alpha, only for CSS3 old browser fuck!! :)
|
||||
controls.aContainer.css({ 'background': 'linear-gradient(to bottom, ' + gradient.join( ', ' ) + '), url(' + image + ')' });
|
||||
|
||||
if ( target.hasClass('wp-picker-open') ) {
|
||||
// Update alpha value
|
||||
controls.aSlider.slider( 'value', alpha );
|
||||
|
||||
/**
|
||||
* Disabled change opacity in default slider Saturation ( only is alpha enabled )
|
||||
* and change input width for view all value
|
||||
*/
|
||||
if ( self._color._alpha < 1 ) {
|
||||
var style = controls.strip.attr( 'style' ).replace( /rgba\(([0-9]+,)(\s+)?([0-9]+,)(\s+)?([0-9]+)(,(\s+)?[0-9\.]+)\)/g, 'rgb($1$3$5)' );
|
||||
|
||||
controls.strip.attr( 'style', style );
|
||||
|
||||
el.width( parseInt( defaultWidth+100 ) );
|
||||
} else {
|
||||
el.width( defaultWidth );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var reset = el.data('reset-alpha') || false;
|
||||
if ( reset ) {
|
||||
self.picker.find( '.iris-palette-container' ).on( 'click.palette', '.iris-palette', function() {
|
||||
self._color._alpha = 1;
|
||||
self.active = 'external';
|
||||
self._change();
|
||||
});
|
||||
}
|
||||
},
|
||||
_addInputListeners: function( input ) {
|
||||
var self = this,
|
||||
debounceTimeout = 700, // originally set to 100, but some user perceive it as "jumps to random colors at third digit"
|
||||
callback = function( event ){
|
||||
var color = new Color( input.val() ),
|
||||
val = input.val();
|
||||
|
||||
input.removeClass( 'iris-error' );
|
||||
// we gave a bad color
|
||||
if ( color.error ) {
|
||||
// don't error on an empty input
|
||||
if ( val !== '' ) {
|
||||
input.addClass( 'iris-error' );
|
||||
}
|
||||
} else {
|
||||
if ( color.toString() !== self._color.toString() ) {
|
||||
// let's not do this on keyup for hex shortcodes
|
||||
if ( ! ( event.type === 'keyup' && val.match( /^[0-9a-fA-F]{3}$/ ) ) ) {
|
||||
self._setOption( 'color', color.toString() );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
input.on( 'change', callback ).on( 'keyup', self._debounce( callback, debounceTimeout ) );
|
||||
|
||||
// If we initialized hidden, show on first focus. The rest is up to you.
|
||||
if ( self.options.hide ) {
|
||||
input.one( 'focus', function() {
|
||||
self.show();
|
||||
});
|
||||
}
|
||||
},
|
||||
_dimensions: function( reset ) {
|
||||
// whatever size
|
||||
var self = this,
|
||||
opts = self.options,
|
||||
controls = self.controls,
|
||||
square = controls.square,
|
||||
strip = self.picker.find( '.iris-strip' ),
|
||||
squareWidth = '77.5%',
|
||||
stripWidth = '12%',
|
||||
totalPadding = 20,
|
||||
innerWidth = opts.border ? opts.width - totalPadding : opts.width,
|
||||
controlsHeight,
|
||||
paletteCount = Array.isArray( opts.palettes ) ? opts.palettes.length : self._palettes.length,
|
||||
paletteMargin, paletteWidth, paletteContainerWidth;
|
||||
|
||||
if ( reset ) {
|
||||
square.css( 'width', '' );
|
||||
strip.css( 'width', '' );
|
||||
self.picker.css( {width: '', height: ''} );
|
||||
}
|
||||
|
||||
squareWidth = innerWidth * ( parseFloat( squareWidth ) / 100 );
|
||||
stripWidth = innerWidth * ( parseFloat( stripWidth ) / 100 );
|
||||
controlsHeight = opts.border ? squareWidth + totalPadding : squareWidth;
|
||||
|
||||
if (opts.diviColorpicker ) {
|
||||
square.width( opts.width ).height( opts.height );
|
||||
controlsHeight = opts.height;
|
||||
} else {
|
||||
square.width( squareWidth ).height( squareWidth );
|
||||
}
|
||||
|
||||
strip.height( squareWidth ).width( stripWidth );
|
||||
self.picker.css({
|
||||
width: 'number' === typeof opts.width ? opts.width + 'px' : opts.width,
|
||||
height: 'number' === typeof controlsHeight ? controlsHeight + 'px' : controlsHeight,
|
||||
});
|
||||
|
||||
if ( ! opts.palettes ) {
|
||||
return self.picker.css( 'paddingBottom', '' );
|
||||
}
|
||||
|
||||
// single margin at 2%
|
||||
paletteMargin = squareWidth * 2 / 100;
|
||||
paletteContainerWidth = squareWidth - ( ( paletteCount - 1 ) * paletteMargin );
|
||||
paletteWidth = paletteContainerWidth / paletteCount;
|
||||
self.picker.find('.iris-palette').each( function( i ) {
|
||||
var margin = i === 0 ? 0 : paletteMargin;
|
||||
$( this ).css({
|
||||
width: paletteWidth + 'px',
|
||||
height: paletteWidth + 'px',
|
||||
marginLeft: margin + 'px',
|
||||
});
|
||||
});
|
||||
self.picker.css( 'paddingBottom', paletteWidth + paletteMargin + 'px' );
|
||||
strip.height( paletteWidth + paletteMargin + squareWidth );
|
||||
}
|
||||
} );
|
||||
}( jQuery ) );
|
||||
|
||||
// Auto Call plugin is class is color-picker.
|
||||
jQuery(function($) {
|
||||
$('.color-picker').wpColorPicker();
|
||||
});
|
||||
Vendored
+8
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=299)}({299:function(e,t,n){"use strict";(function(e){e((function(){e("body").on("click",".et_pb_prompt_dont_proceed",(function(){var t=e(this).closest(".et_pb_modal_overlay");return e("body").removeClass("et_pb_stop_scroll"),t.addClass("et_pb_modal_closing"),setTimeout((function(){t.remove()}),600),!1}))}))}).call(this,n(5))},5:function(e,t){e.exports=jQuery}});
|
||||
//# sourceMappingURL=failure_notice.js.map
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
// External dependencies
|
||||
import $ from 'jquery';
|
||||
|
||||
/**
|
||||
* Toggle background-layout classname of current module and/or its child modules.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {object} $module
|
||||
* @param {bool} status
|
||||
*/
|
||||
export const toggleAllBackgroundLayoutClassnameOnSticky = ($module, status) => {
|
||||
// Toggle background-layout classname if it is on current $module
|
||||
if ($module.is($('[data-background-layout-sticky]'))) {
|
||||
toggleBackgroundLayoutClassnameOnSticky($module, status);
|
||||
}
|
||||
|
||||
// Toggle background-layout classname if it is child module of current module
|
||||
if ($module.find('[data-background-layout-sticky]').length > 0) {
|
||||
$module.find('[data-background-layout-sticky]').each(function() {
|
||||
toggleBackgroundLayoutClassnameOnSticky($(this), status);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle background-layout classname of current module.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {object} $module
|
||||
* @param {bool} status
|
||||
*/
|
||||
export const toggleBackgroundLayoutClassnameOnSticky = ($module, status) => {
|
||||
const attrPrefix = status ? '-sticky' : '';
|
||||
const allClassnames = 'et_pb_bg_layout_dark et_pb_bg_layout_light';
|
||||
const layoutColor = $module.attr(`data-background-layout${attrPrefix}`);
|
||||
const layoutClassname = `et_pb_bg_layout_${layoutColor}`;
|
||||
|
||||
let $target = $module;
|
||||
|
||||
// Switch the target element for some modules.
|
||||
if ($module.hasClass('et_pb_slide')) {
|
||||
$target = $module.closest('.et_pb_slider');
|
||||
}
|
||||
|
||||
$target.removeClass(allClassnames).addClass(layoutClassname);
|
||||
};
|
||||
+648
@@ -0,0 +1,648 @@
|
||||
// Internal Dependencies
|
||||
import { top_window } from '@core/admin/js/frame-helpers';
|
||||
import { isBuilder } from '../utils/utils';
|
||||
import { getClosestStickyModuleOffsetTop, isTargetStickyState } from '../utils/sticky';
|
||||
|
||||
|
||||
(function($) {
|
||||
const isBlockLayoutPreview = $('body').hasClass('et-block-layout-preview');
|
||||
|
||||
const $tbHeader = $('.et-l--header').first();
|
||||
let tbHeaderMostLengthyFixedSectionHeight = 0;
|
||||
|
||||
// Modification of underscore's _.debounce()
|
||||
// Underscore.js 1.8.3
|
||||
// http://underscorejs.org
|
||||
// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||
// Underscore may be freely distributed under the MIT license.
|
||||
window.et_pb_debounce = function(func, wait, immediate) {
|
||||
let timeout; let args; let context; let timestamp; let
|
||||
result;
|
||||
|
||||
const now = Date.now || new Date().getTime();
|
||||
|
||||
var later = function() {
|
||||
const last = now - timestamp;
|
||||
|
||||
if (last < wait && last >= 0) {
|
||||
timeout = setTimeout(later, wait - last);
|
||||
} else {
|
||||
timeout = null;
|
||||
if (! immediate) {
|
||||
result = func.apply(context, args);
|
||||
if (! timeout) context = args = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return function() {
|
||||
context = this;
|
||||
args = arguments;
|
||||
timestamp = now;
|
||||
const callNow = immediate && ! timeout;
|
||||
if (! timeout) timeout = setTimeout(later, wait);
|
||||
if (callNow) {
|
||||
result = func.apply(context, args);
|
||||
context = args = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
if ($tbHeader.length) {
|
||||
const $tbHeaderSections = $tbHeader.find('.et_builder_inner_content').children('.et_pb_section--fixed');
|
||||
|
||||
// Get the most tall header fixed section height
|
||||
const et_pb_header_most_lengthy_fixed_section_height = et_pb_debounce(e => {
|
||||
tbHeaderMostLengthyFixedSectionHeight = 0;
|
||||
|
||||
$.each($tbHeaderSections, function(index, section) {
|
||||
let $sectionHeight = $(section).outerHeight(true);
|
||||
|
||||
if ($sectionHeight > tbHeaderMostLengthyFixedSectionHeight) {
|
||||
tbHeaderMostLengthyFixedSectionHeight = $sectionHeight;
|
||||
}
|
||||
});
|
||||
}, 300);
|
||||
|
||||
$(window).on('resize', et_pb_header_most_lengthy_fixed_section_height);
|
||||
}
|
||||
|
||||
window.et_pb_smooth_scroll = function($target, $top_section, speed, easing) {
|
||||
const targetOffsetTop = $target.offset().top;
|
||||
const $window_width = $(window).width();
|
||||
let $menu_offset = 0;
|
||||
let $scroll_position = 0;
|
||||
let $menuLeft = '';
|
||||
let $menuRight = '';
|
||||
let $fixedHeaderSection = $tbHeader.find('.et_pb_section');
|
||||
|
||||
// If the target is in sticky state there should be no scroll so we can bail early.
|
||||
if (isTargetStickyState($target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($('body').hasClass('et_fixed_nav') && $window_width > 980) {
|
||||
const topHeaderHeight = $('#top-header').outerHeight() || 0;
|
||||
const mainHeaderHeight = $('#main-header').outerHeight() || 0;
|
||||
$menu_offset = topHeaderHeight + mainHeaderHeight - 1;
|
||||
} else {
|
||||
$menu_offset = 0;
|
||||
}
|
||||
|
||||
if ($('#wpadminbar').length && $window_width > 600) {
|
||||
const wpAdminBarHeight = $('#wpadminbar').outerHeight() || 0;
|
||||
$menu_offset += wpAdminBarHeight;
|
||||
}
|
||||
|
||||
if ($tbHeader.length) {
|
||||
// attach targeted section just under header (if) fixed section
|
||||
if ($fixedHeaderSection.hasClass('et_pb_section--fixed')) {
|
||||
$menuLeft = Math.ceil(parseFloat($fixedHeaderSection.css('left')));
|
||||
$menuRight = Math.ceil(parseFloat($fixedHeaderSection.css('right')));
|
||||
if ($window_width < 980) {
|
||||
$menu_offset += 90;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 === $menuLeft + $menuRight) {
|
||||
$menu_offset += tbHeaderMostLengthyFixedSectionHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate offset that needs to be added due to the existence of sticky module(s).
|
||||
// This avoids smooth scroll to stop beneath sticky module.
|
||||
const closestStickyOffsetTop = getClosestStickyModuleOffsetTop($target);
|
||||
|
||||
if (closestStickyOffsetTop) {
|
||||
// if automatic sticky offset exist, add additional 40px offset for UX reason
|
||||
$menu_offset += (closestStickyOffsetTop + 40);
|
||||
}
|
||||
|
||||
// fix sidenav scroll to top
|
||||
if ($top_section) {
|
||||
$scroll_position = 0;
|
||||
} else {
|
||||
$scroll_position = targetOffsetTop - $menu_offset;
|
||||
}
|
||||
|
||||
// set swing (animate's scrollTop default) as default value
|
||||
if ('undefined' === typeof easing) {
|
||||
easing = 'swing';
|
||||
}
|
||||
|
||||
$('html, body').animate({ scrollTop: $scroll_position }, speed, easing);
|
||||
};
|
||||
|
||||
window.et_pb_form_placeholders_init = function($form) {
|
||||
$form.find('input:text, input[type="email"], input[type="url"], textarea').each((index, domEle) => {
|
||||
const $et_current_input = jQuery(domEle);
|
||||
const $et_comment_label = $et_current_input.siblings('label');
|
||||
let et_comment_label_value = $et_current_input.siblings('label').text();
|
||||
if ($et_comment_label.length) {
|
||||
$et_comment_label.hide();
|
||||
if ($et_current_input.siblings('span.required')) {
|
||||
et_comment_label_value += $et_current_input.siblings('span.required').text();
|
||||
$et_current_input.siblings('span.required').hide();
|
||||
}
|
||||
$et_current_input.val(et_comment_label_value);
|
||||
}
|
||||
}).on('focus', function() {
|
||||
let et_label_text = jQuery(this).siblings('label').text();
|
||||
if (jQuery(this).siblings('span.required').length) et_label_text += jQuery(this).siblings('span.required').text();
|
||||
if (jQuery(this).val() === et_label_text) jQuery(this).val('');
|
||||
}).on('blur', function() {
|
||||
let et_label_text = jQuery(this).siblings('label').text();
|
||||
if (jQuery(this).siblings('span.required').length) et_label_text += jQuery(this).siblings('span.required').text();
|
||||
if ('' === jQuery(this).val()) jQuery(this).val(et_label_text);
|
||||
});
|
||||
};
|
||||
|
||||
window.et_duplicate_menu = function(menu, append_to, menu_id, menu_class, menu_click_event) {
|
||||
append_to.each(function() {
|
||||
const $this_menu = $(this);
|
||||
let $cloned_nav;
|
||||
|
||||
// Bail early if menu has already been duplicated.
|
||||
if ($this_menu.find(`#${menu_id}`).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// make this function work with existing menus, without cloning
|
||||
if ('' !== menu) {
|
||||
menu.clone().attr('id', menu_id).removeClass().attr('class', menu_class)
|
||||
.appendTo($this_menu);
|
||||
}
|
||||
|
||||
$cloned_nav = $this_menu.find('> ul');
|
||||
$cloned_nav.find('.menu_slide').remove();
|
||||
$cloned_nav.find('.et_pb_menu__logo-slot').remove();
|
||||
$cloned_nav.find('li').first().addClass('et_first_mobile_item');
|
||||
|
||||
$cloned_nav.find('a').on('click', function() {
|
||||
$(this).parents('.et_mobile_menu').siblings('.mobile_menu_bar').trigger('click');
|
||||
});
|
||||
|
||||
if ('no_click_event' !== menu_click_event) {
|
||||
if (isBuilder) {
|
||||
$this_menu.off('click');
|
||||
}
|
||||
|
||||
$this_menu.on('click', '.mobile_menu_bar', function() {
|
||||
// Close all other open menus.
|
||||
$('.mobile_nav.opened .mobile_menu_bar').not($(this)).trigger('click');
|
||||
|
||||
if ($this_menu.hasClass('closed')) {
|
||||
$this_menu.removeClass('closed').addClass('opened');
|
||||
$cloned_nav.stop().slideDown(500);
|
||||
} else {
|
||||
$this_menu.removeClass('opened').addClass('closed');
|
||||
$cloned_nav.stop().slideUp(500);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('#mobile_menu .centered-inline-logo-wrap').remove();
|
||||
};
|
||||
|
||||
// remove placeholder text before form submission
|
||||
window.et_pb_remove_placeholder_text = function($form) {
|
||||
$form.find('input:text, textarea').each((index, domEle) => {
|
||||
const $et_current_input = jQuery(domEle);
|
||||
const $et_label = $et_current_input.siblings('label');
|
||||
const et_label_value = $et_current_input.siblings('label').text();
|
||||
|
||||
if ($et_label.length && $et_label.is(':hidden')) {
|
||||
if ($et_label.text() == $et_current_input.val()) $et_current_input.val('');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.et_fix_fullscreen_section = function() {
|
||||
const $et_window = isBlockLayoutPreview ? $(top_window) : $(window);
|
||||
|
||||
$('section.et_pb_fullscreen').each(function() {
|
||||
const $this_section = $(this);
|
||||
|
||||
et_calc_fullscreen_section.bind($this_section);
|
||||
|
||||
$et_window.on('resize', et_calc_fullscreen_section.bind($this_section));
|
||||
});
|
||||
};
|
||||
|
||||
window.et_bar_counters_init = function($bar_item) {
|
||||
if (! $bar_item.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
$bar_item.css({
|
||||
width: `${parseFloat($bar_item.attr('data-width'))}%`,
|
||||
});
|
||||
};
|
||||
|
||||
window.et_fix_pricing_currency_position = function($pricing_table) {
|
||||
setTimeout(() => {
|
||||
const $all_pricing_tables = typeof $pricing_table !== 'undefined' ? $pricing_table : $('.et_pb_pricing_table');
|
||||
|
||||
if (! $all_pricing_tables.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
$all_pricing_tables.each(function() {
|
||||
const $this_table = $(this);
|
||||
const $price_container = $this_table.find('.et_pb_et_price');
|
||||
const $currency = $price_container.length ? $price_container.find('.et_pb_dollar_sign') : false;
|
||||
const $price = $price_container.length ? $price_container.find('.et_pb_sum') : false;
|
||||
|
||||
if (! $currency || ! $price) {
|
||||
return;
|
||||
}
|
||||
|
||||
// adjust the margin of currency sign to make sure it doesn't overflow the price
|
||||
$currency.css({ marginLeft: `${- $currency.width()}px` });
|
||||
});
|
||||
}, 1);
|
||||
};
|
||||
|
||||
window.et_pb_set_responsive_grid = function($grid_items_container, single_item_selector) {
|
||||
setTimeout(() => {
|
||||
const container_width = $grid_items_container.innerWidth();
|
||||
const $grid_items = $grid_items_container.find(single_item_selector);
|
||||
const item_width = $grid_items.outerWidth(true);
|
||||
const last_item_margin = item_width - $grid_items.outerWidth();
|
||||
const columns_count = Math.round((container_width + last_item_margin) / item_width);
|
||||
let counter = 1;
|
||||
let first_in_row = 1;
|
||||
let $first_in_last_row = $();
|
||||
|
||||
$grid_items.removeClass('last_in_row first_in_row on_last_row');
|
||||
$grid_items.filter(':visible').each(function() {
|
||||
const $this_element = $(this);
|
||||
|
||||
if (! $this_element.hasClass('inactive')) {
|
||||
if (first_in_row === counter) {
|
||||
$this_element.addClass('first_in_row');
|
||||
$first_in_last_row = $this_element;
|
||||
} else if (0 === counter % columns_count) {
|
||||
$this_element.addClass('last_in_row');
|
||||
first_in_row = counter + 1;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
if ($first_in_last_row.length) {
|
||||
const $module = $first_in_last_row.parents('.et_pb_module');
|
||||
|
||||
// set margin bottom to 0 if the gallery is the last module on the column
|
||||
if ($module.is(':last-child')) {
|
||||
const column = $first_in_last_row.parents('.et_pb_column')[0];
|
||||
$(column).find('.et_pb_grid_item').removeClass('on_last_row');
|
||||
|
||||
// keep gutter margin if gallery has pagination
|
||||
let pagination = $module.find('.et_pb_gallery_pagination');
|
||||
if (0 === pagination.length) {
|
||||
pagination = $module.find('.et_pb_portofolio_pagination');
|
||||
}
|
||||
if (0 === pagination.length || (pagination.length > 0 && ! pagination.is(':visible'))) {
|
||||
if (columns_count > 1) {
|
||||
$first_in_last_row.addClass('on_last_row');
|
||||
}
|
||||
|
||||
$first_in_last_row.nextAll().addClass('on_last_row');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1); // need this timeout to make sure all the css applied before calculating sizes
|
||||
};
|
||||
|
||||
window.et_pb_set_tabs_height = function($tabs_module) {
|
||||
if ('undefined' === typeof $tabs_module) {
|
||||
$tabs_module = $('.et_pb_tabs');
|
||||
}
|
||||
|
||||
if (! $tabs_module.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
$tabs_module.each(function() {
|
||||
const $tab_controls = $(this).find('.et_pb_tabs_controls');
|
||||
const $all_tabs = $tab_controls.find('li');
|
||||
let max_height = 0;
|
||||
const small_columns = '.et_pb_column_1_3, .et_pb_column_1_4, .et_pb_column_3_8';
|
||||
const in_small_column = $(this).parents(small_columns).length > 0;
|
||||
const on_small_screen = parseFloat($(window).width()) < 768;
|
||||
const vertically_stacked = in_small_column || on_small_screen;
|
||||
|
||||
if (vertically_stacked) {
|
||||
$(this).addClass('et_pb_tabs_vertically_stacked');
|
||||
}
|
||||
|
||||
// determine the height of the tallest tab
|
||||
if ($all_tabs.length) {
|
||||
// remove the height attribute if it was added to calculate the height correctly
|
||||
$tab_controls.children('li').removeAttr('style');
|
||||
|
||||
$all_tabs.each(function() {
|
||||
const tab_height = $(this).outerHeight();
|
||||
|
||||
if (vertically_stacked) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab_height > max_height) {
|
||||
max_height = tab_height;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (0 !== max_height) {
|
||||
// set the height of tabs container based on the height of the tallest tab
|
||||
$tab_controls.children('li').css('height', `${max_height}px`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.et_pb_box_shadow_apply_overlay = function(el) {
|
||||
const pointerEventsSupport = document.body.style.pointerEvents !== undefined
|
||||
|
||||
// For some reasons IE 10 tells that supports pointer-events, but it doesn't
|
||||
&& (document.documentMode === undefined || document.documentMode >= 11);
|
||||
|
||||
if (pointerEventsSupport) {
|
||||
$(el).each(function() {
|
||||
if (! $(this).children('.box-shadow-overlay').length) {
|
||||
$(this)
|
||||
.addClass('has-box-shadow-overlay')
|
||||
.prepend('<div class="box-shadow-overlay"></div>');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$(el).addClass('.et-box-shadow-no-overlay');
|
||||
}
|
||||
};
|
||||
|
||||
window.et_pb_init_nav_menu = function($et_menus) {
|
||||
$et_menus.each(function() {
|
||||
const $et_menu = $(this);
|
||||
|
||||
// don't attach event handlers several times to the same menu
|
||||
if ($et_menu.data('et-is-menu-ready')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$et_menu.find('li').on('mouseenter', function() {
|
||||
window.et_pb_toggle_nav_menu($(this), 'open');
|
||||
}).on('mouseleave', function() {
|
||||
window.et_pb_toggle_nav_menu($(this), 'close');
|
||||
});
|
||||
|
||||
// close all opened menus on touch outside the menu
|
||||
$('body').on('touchend', event => {
|
||||
if ($(event.target).closest('ul.nav, ul.menu').length < 1 && $('.et-hover').length > 0) {
|
||||
window.et_pb_toggle_nav_menu($('.et-hover'), 'close');
|
||||
}
|
||||
});
|
||||
|
||||
// Dropdown menu adjustment for touch screen
|
||||
$et_menu.find('li.menu-item-has-children').on('touchend', function(event) {
|
||||
const $closest_li = $(event.target).closest('.menu-item');
|
||||
|
||||
// no need special processing if parent li doesn't have hidden child elements
|
||||
if (! $closest_li.hasClass('menu-item-has-children')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $this_el = $(this);
|
||||
const is_mega_menu_opened = $closest_li.closest('.mega-menu-parent.et-touch-hover').length > 0;
|
||||
|
||||
// open submenu on 1st tap
|
||||
// open link on second tap
|
||||
if ($this_el.hasClass('et-touch-hover') || is_mega_menu_opened) {
|
||||
const href = $this_el.find('>a').attr('href');
|
||||
|
||||
if (typeof href !== 'undefined') { // if parent link is not empty then open the link
|
||||
window.location = $this_el.find('>a').attr('href');
|
||||
}
|
||||
} else {
|
||||
const $opened_menu = $(event.target);
|
||||
const $already_opened_menus = $opened_menu.closest('.menu-item').siblings('.et-touch-hover');
|
||||
|
||||
// close the menu before opening new one
|
||||
if ($opened_menu.closest('.et-touch-hover').length < 1) {
|
||||
window.et_pb_toggle_nav_menu($('.et-hover'), 'close', 0);
|
||||
}
|
||||
|
||||
$this_el.addClass('et-touch-hover');
|
||||
|
||||
if ($already_opened_menus.length > 0) {
|
||||
const $submenus_in_already_opened = $already_opened_menus.find('.et-touch-hover');
|
||||
|
||||
// close already opened submenus to avoid overlaps
|
||||
window.et_pb_toggle_nav_menu($already_opened_menus, 'close');
|
||||
window.et_pb_toggle_nav_menu($submenus_in_already_opened, 'close');
|
||||
}
|
||||
|
||||
// open new submenu
|
||||
window.et_pb_toggle_nav_menu($this_el, 'open');
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
$et_menu.find('li.mega-menu').each(function() {
|
||||
const $li_mega_menu = $(this);
|
||||
const $li_mega_menu_item = $li_mega_menu.children('ul').children('li');
|
||||
const li_mega_menu_item_count = $li_mega_menu_item.length;
|
||||
|
||||
if (li_mega_menu_item_count < 4) {
|
||||
$li_mega_menu.addClass(`mega-menu-parent mega-menu-parent-${li_mega_menu_item_count}`);
|
||||
}
|
||||
});
|
||||
|
||||
// mark the menu as ready
|
||||
$et_menu.data('et-is-menu-ready', 'ready');
|
||||
});
|
||||
};
|
||||
|
||||
window.et_pb_toggle_nav_menu = function($element, state, delay) {
|
||||
if ('open' === state) {
|
||||
if (! $element.closest('li.mega-menu').length || $element.hasClass('mega-menu')) {
|
||||
$element.addClass('et-show-dropdown');
|
||||
$element.removeClass('et-hover').addClass('et-hover');
|
||||
}
|
||||
} else {
|
||||
const closeDelay = typeof delay !== 'undefined' ? delay : 200;
|
||||
$element.removeClass('et-show-dropdown');
|
||||
$element.removeClass('et-touch-hover');
|
||||
|
||||
setTimeout(() => {
|
||||
if (! $element.hasClass('et-show-dropdown')) {
|
||||
$element.removeClass('et-hover');
|
||||
}
|
||||
}, closeDelay);
|
||||
}
|
||||
};
|
||||
|
||||
window.et_pb_apply_sticky_image_effect = function($sticky_image_el) {
|
||||
const $row = $sticky_image_el.closest('.et_pb_row');
|
||||
const $section = $row.closest('.et_pb_section');
|
||||
const $column = $sticky_image_el.closest('.et_pb_column');
|
||||
const sticky_class = 'et_pb_section_sticky';
|
||||
const sticky_mobile_class = 'et_pb_section_sticky_mobile';
|
||||
const $lastRowInSection = $section.children('.et_pb_row').last();
|
||||
const $lastColumnInRow = $row.children('.et_pb_column').last();
|
||||
const $lastModuleInColumn = $column.children('.et_pb_module').last();
|
||||
|
||||
// If it is not in the last row, continue
|
||||
if (! $row.is($lastRowInSection)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$lastRowInSection.addClass('et-last-child');
|
||||
|
||||
// Make sure sticky image is the last element in the column
|
||||
if (! $sticky_image_el.is($lastModuleInColumn)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it is in the last row, find the parent section and attach new class to it
|
||||
if (! $section.hasClass(sticky_class)) {
|
||||
$section.addClass(sticky_class);
|
||||
}
|
||||
|
||||
$column.addClass('et_pb_row_sticky');
|
||||
|
||||
if (! $section.hasClass(sticky_mobile_class) && $column.is($lastColumnInRow)) {
|
||||
$section.addClass(sticky_mobile_class);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject a <li> element in the middle of a menu for the purposes of the menu module's
|
||||
* inline centered logo style.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
* @param {object} menu
|
||||
*
|
||||
* @returns {object|null}
|
||||
*/
|
||||
window.et_pb_menu_inject_inline_centered_logo = function(menu) {
|
||||
const $listItems = $(menu).find('nav > ul > li');
|
||||
const index = Math.round($listItems.length / 2);
|
||||
const li = window.et_pb_menu_inject_item(menu, index, true);
|
||||
|
||||
if (li) {
|
||||
$(li).addClass('et_pb_menu__logo-slot');
|
||||
}
|
||||
|
||||
return li;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject a <li> element at the start of a menu for the purposes of the menu module's
|
||||
* additional icons.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
* @param {object} menu
|
||||
* @param {number} index
|
||||
* @param {boolean} fromTheBeginning
|
||||
*
|
||||
* @returns {object|null}
|
||||
*/
|
||||
window.et_pb_menu_inject_item = function(menu, index, fromTheBeginning) {
|
||||
fromTheBeginning = undefined === fromTheBeginning ? true : fromTheBeginning;
|
||||
index = Math.max(index, 0);
|
||||
const $list = $(menu).find('nav > ul').first();
|
||||
|
||||
if (0 === $list.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const $listItems = $list.find('> li');
|
||||
const $li = $('<li></li>');
|
||||
|
||||
if (0 === $listItems.length) {
|
||||
$list.append($li);
|
||||
} else {
|
||||
let action = fromTheBeginning ? 'before' : 'after';
|
||||
let $sibling = fromTheBeginning
|
||||
? $listItems.eq(index)
|
||||
: $listItems.eq(($listItems.length - 1) - index);
|
||||
|
||||
if (0 === $sibling.length) {
|
||||
action = fromTheBeginning ? 'after' : 'before';
|
||||
$sibling = fromTheBeginning ? $listItems.last() : $listItems.first();
|
||||
}
|
||||
|
||||
$sibling[action]($li);
|
||||
}
|
||||
|
||||
return $li.get(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Reposition menu module dropdowns.
|
||||
* This is necessary due to mega menus relying on an upper wrapper's width but
|
||||
* still needing to be position relative to their parent li.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
window.et_pb_reposition_menu_module_dropdowns = et_pb_debounce(menus => {
|
||||
const $menus = menus ? $(menus) : $('.et_pb_menu, .et_pb_fullwidth_menu');
|
||||
|
||||
$menus.each(function() {
|
||||
const $row = $(this).find('.et_pb_row').first();
|
||||
|
||||
if (0 === $row.length) {
|
||||
return true; // = continue.
|
||||
}
|
||||
|
||||
const offset = $row.offset().top;
|
||||
const moduleClass = $(this).attr('class').replace(/^.*?(et_pb(?:_fullwidth)?_menu_\d+[^\s]*).*$/i, '$1');
|
||||
const isUpwards = $(this).find('.et_pb_menu__menu ul').first().hasClass('upwards');
|
||||
const selector = '.et_pb_menu__menu > nav > ul > li.mega-menu.menu-item-has-children';
|
||||
let css = '';
|
||||
|
||||
$(this).find(selector).each(function() {
|
||||
const $li = $(this);
|
||||
const liId = $li.attr('class').replace(/^.*?(menu-item-\d+).*$/i, '$1');
|
||||
const selector = `.${moduleClass} li.${liId} > .sub-menu`;
|
||||
|
||||
if (isUpwards) {
|
||||
// Offset by 1px to ensure smooth mouse hover.
|
||||
var linkOffset = Math.floor(offset + $row.outerHeight() - $li.offset().top) - 1;
|
||||
|
||||
css += `${selector}{ bottom: ${linkOffset.toString()}px !important; }`;
|
||||
} else {
|
||||
// Offset by 1px to ensure smooth mouse hover.
|
||||
var linkOffset = Math.floor($li.offset().top + $li.outerHeight() - offset) - 1;
|
||||
|
||||
css += `${selector}{ top: ${linkOffset.toString()}px !important; }`;
|
||||
}
|
||||
});
|
||||
|
||||
let $style = $(`style.et-menu-style-${moduleClass}`).first();
|
||||
|
||||
if (0 === $style.length) {
|
||||
$style = $('<style></style>');
|
||||
$style.addClass('et-menu-style');
|
||||
$style.addClass(`et-menu-style-${moduleClass}`);
|
||||
$style.appendTo($('head'));
|
||||
}
|
||||
|
||||
const oldCss = $style.html();
|
||||
|
||||
if (css !== oldCss) {
|
||||
$style.html(css);
|
||||
}
|
||||
});
|
||||
}, 200);
|
||||
})(jQuery);
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
// External dependencies
|
||||
import debounce from 'lodash/debounce';
|
||||
import endsWith from 'lodash/endsWith';
|
||||
import filter from 'lodash/filter';
|
||||
import forEach from 'lodash/forEach';
|
||||
import get from 'lodash/get';
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import isObject from 'lodash/isObject';
|
||||
import map from 'lodash/map';
|
||||
import size from 'lodash/size';
|
||||
|
||||
// Internal dependencies
|
||||
import * as DOM from '@frontend-builder/features/scroll-effects/fe/io/dom';
|
||||
import { ETBuilderScrollEffects } from '@frontend-builder/features/scroll-effects';
|
||||
import { convertTransforms } from '@frontend-builder/features/scroll-effects/vb/helpers';
|
||||
import { fromObject } from '@frontend-builder/features/scroll-effects/entities/scroll-effect';
|
||||
import ETBuilderOffsetsConst from '@frontend-builder/constants/et-builder-offsets-const';
|
||||
import { Window } from '@frontend-builder/features/scroll-effects/stores/window';
|
||||
import { top_window } from '@core/admin/js/frame-helpers';
|
||||
import { getContentAreaSelector } from 'gutenberg/utils/selectors';
|
||||
|
||||
|
||||
const isBlockEditor = $(top_window.document).find(getContentAreaSelector(top_window, true)).length > 0;
|
||||
const isBuilder = isObject(window.ET_Builder) && size(window.ET_Builder) > 1 && ! isBlockEditor;
|
||||
|
||||
const getViewportWidth = () => jQuery(window).width();
|
||||
const toItem = (effects, id) => ({ id, effects: (effects || []).map(fromObject) });
|
||||
const getSelector = (step, id) => `body[data-scroll-step="${step}"] ${id}`;
|
||||
const scrollEffects = new ETBuilderScrollEffects(DOM, getSelector);
|
||||
|
||||
let motionEffectsLoaded = false;
|
||||
|
||||
const getMotionElementsFE = () => {
|
||||
const allMotionElements = window.et_pb_motion_elements || [];
|
||||
|
||||
// We are on the real FE, so load all the motion elements.
|
||||
if (! isBuilder || isEmpty(allMotionElements)) {
|
||||
return allMotionElements;
|
||||
}
|
||||
|
||||
/**
|
||||
* We're inside the Builder, but still need to render motion effects from the TB
|
||||
* Get all the motion elements for TB parts of the layout.
|
||||
*/
|
||||
const breakpoints = ['desktop', 'tablet', 'phone'];
|
||||
const tb_motion_elements = {};
|
||||
|
||||
forEach(breakpoints, breakpoint => {
|
||||
tb_motion_elements[breakpoint] = filter(window.et_pb_motion_elements[breakpoint], el => (endsWith(el.id, '_tb_header') || endsWith(el.id, '_tb_body') || endsWith(el.id, '_tb_footer')));
|
||||
});
|
||||
|
||||
return tb_motion_elements;
|
||||
};
|
||||
|
||||
const toggleMotionElements = (state = 'hide') => {
|
||||
const activeMotionElements = getMotionElementsFE();
|
||||
const elementsList = get(activeMotionElements, 'desktop', []);
|
||||
|
||||
if (isEmpty(elementsList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
forEach(elementsList, element => {
|
||||
const $element = jQuery(element.id);
|
||||
if ('hide' === state) {
|
||||
$element.addClass('et-pb-before-scroll-animation');
|
||||
} else {
|
||||
$element.removeClass('et-pb-before-scroll-animation et_animated et-waypoint');
|
||||
$element.addClass('et_had_animation');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} width
|
||||
* @returns {string}
|
||||
*/
|
||||
const getDevice = width => {
|
||||
if (width <= ETBuilderOffsetsConst.responsiveLandscape.phone) {
|
||||
return 'phone';
|
||||
}
|
||||
|
||||
if (width <= ETBuilderOffsetsConst.responsiveLandscape.tablet) {
|
||||
return 'tablet';
|
||||
}
|
||||
|
||||
return 'desktop';
|
||||
};
|
||||
|
||||
const getChildren = (element, effects) => {
|
||||
const childrenCount = parseInt(element.children_count);
|
||||
const children = {};
|
||||
|
||||
if (childrenCount > 0) {
|
||||
for (let i = 0; i < childrenCount; i++) {
|
||||
const key = `.${element.module_type}_item_${element.module_index}_${i}`;
|
||||
children[key] = effects;
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {[object]} elements
|
||||
* @param {string} device
|
||||
*/
|
||||
const updateItems = (elements, device) => {
|
||||
const groupedElements = groupBy(elements[device] || [], 'id');
|
||||
map(groupedElements, toItem)
|
||||
.forEach(({ id, effects }) => {
|
||||
if (! isEmpty(effects) && isArray(effects)) {
|
||||
const element = get(groupedElements, [id, '0'], {});
|
||||
const start = get(element, 'trigger_start', 'middle');
|
||||
const end = start; // Use only one offset point right now. Update the value here if/when we add second point.
|
||||
const motionTriggers = { start, end };
|
||||
|
||||
if ('on' === element.grid_motion) {
|
||||
if (! element.child_slug) {
|
||||
const children = getChildren(element, effects);
|
||||
|
||||
forEach(children, (childModule, id) => {
|
||||
scrollEffects.add(id, childModule, motionTriggers);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const transforms = isEmpty(element.transforms) ? [] : convertTransforms(element.transforms);
|
||||
scrollEffects.add(id, [...transforms, ...effects], motionTriggers);
|
||||
}
|
||||
} else {
|
||||
scrollEffects.remove(id);
|
||||
}
|
||||
});
|
||||
|
||||
if (! motionEffectsLoaded) {
|
||||
motionEffectsLoaded = true;
|
||||
setTimeout(() => toggleMotionElements('show'), 200);
|
||||
}
|
||||
};
|
||||
|
||||
const activeMotionElements = getMotionElementsFE();
|
||||
|
||||
if (! isEmpty(activeMotionElements)) {
|
||||
const $window = $(window);
|
||||
const windowStore = new Window($window.width(), $window.height());
|
||||
const elements = activeMotionElements;
|
||||
let device = getDevice(getViewportWidth());
|
||||
|
||||
// Hide all the elements on page load so scroll effects applied smoothly
|
||||
toggleMotionElements();
|
||||
|
||||
jQuery(window).on('load', debounce(() => updateItems(elements, device), 500));
|
||||
jQuery(window).on('resize', debounce(() => windowStore.setWidth($window.width()).setHeight($window.height()), 500));
|
||||
|
||||
windowStore.addSizeChangeListener(() => {
|
||||
const newDevice = getDevice(getViewportWidth());
|
||||
|
||||
if (newDevice !== device) {
|
||||
device = newDevice;
|
||||
|
||||
updateItems(elements, device);
|
||||
} else {
|
||||
scrollEffects.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
(function($) {
|
||||
// Turn of all hrefs which point to another page
|
||||
$('body').on('click', 'a', function(event) {
|
||||
const href = $(this).attr('href');
|
||||
const start = typeof href !== 'undefined' ? href.substr(0, 1) : '';
|
||||
|
||||
// Stop the link if it points to another URL
|
||||
if (start !== '#' && start !== '') {
|
||||
event.preventDefault();
|
||||
|
||||
// Display notification
|
||||
$('.link-disabled').addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Prompt closing mechanism
|
||||
$('body').on('click', '.et_pb_prompt_proceed', () => {
|
||||
$('.link-disabled').removeClass('active');
|
||||
});
|
||||
|
||||
// Build preview screen
|
||||
const ET_PageBuilder_Preview = function(e) {
|
||||
// Create form on the fly
|
||||
const $form = $('<form id="preview-data-submission" method="POST" style="display: none;"></form>');
|
||||
let value;
|
||||
let { data } = e;
|
||||
const msie = document.documentMode;
|
||||
|
||||
// Origins should be matched
|
||||
if (e.origin !== et_preview_params.preview_origin) {
|
||||
$('.et-pb-preview-loading').replaceWith($('<h4 />', { style: 'text-align: center;' }).html(et_preview_params.alert_origin_not_matched));
|
||||
return;
|
||||
}
|
||||
|
||||
// IE9 below fix. They have postMessage, but it has to be in string
|
||||
if (typeof msie !== 'undefined' && msie < 10) {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
|
||||
// Ignore messages not coming from the builder.
|
||||
if (! data || ! data.et_pb_preview_nonce) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop postMessage data and append it to $form
|
||||
for (const name in data) {
|
||||
const $textarea = $('<textarea />', { name, style: 'display: none; ' }).val(data[name]);
|
||||
$textarea.appendTo($form);
|
||||
}
|
||||
|
||||
$form.append('<input type="submit" value="submit" style="display: none;" />');
|
||||
|
||||
$form.appendTo('.container');
|
||||
|
||||
// Submit the form
|
||||
$('#preview-data-submission').trigger('submit');
|
||||
};
|
||||
|
||||
// listen to data passed from builder
|
||||
window.addEventListener('message', ET_PageBuilder_Preview, false);
|
||||
|
||||
/**
|
||||
* Notify parent of parsed shortcode.
|
||||
*/
|
||||
const previewLoaded = $('#content > .content .et-pb-preview-loading').length !== 1;
|
||||
|
||||
if (previewLoaded && typeof parent !== 'undefined') {
|
||||
/**
|
||||
* There's no way to determine when the shortcode's JS has done parsing, so
|
||||
* assume a safe fixed timeout before contacting parent's window.
|
||||
*/
|
||||
setTimeout(() => {
|
||||
const output = {
|
||||
html: $('#content > .content').html(),
|
||||
stylesheets: [],
|
||||
};
|
||||
|
||||
jQuery.each(window.location.search.substr(1).split('&'), (index, param) => {
|
||||
const paramArray = param.split('=');
|
||||
|
||||
if (typeof paramArray[0] !== 'undefined' && typeof paramArray[1] !== 'undefined' && 'iframe_id' === paramArray[0]) {
|
||||
output.iframe_id = paramArray[1];
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Pass parsed shortcode's style hrefs to parent.
|
||||
*/
|
||||
$('link[rel="stylesheet"]').each((index, style) => {
|
||||
output.stylesheets.push($(style).attr('href'));
|
||||
});
|
||||
|
||||
parent.postMessage(output, window.location.origin);
|
||||
}, 2000);
|
||||
}
|
||||
})(jQuery);
|
||||
File diff suppressed because it is too large
Load Diff
+1788
File diff suppressed because it is too large
Load Diff
+114
@@ -0,0 +1,114 @@
|
||||
// External Dependencies
|
||||
import difference from 'lodash/difference';
|
||||
import forEach from 'lodash/forEach';
|
||||
import get from 'lodash/get';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import keys from 'lodash/keys';
|
||||
import pick from 'lodash/pick';
|
||||
import $ from 'jquery';
|
||||
|
||||
// Internal dependencies
|
||||
import ETScriptStickyElement from './sticky-element';
|
||||
import ETScriptStickyStore from '../stores/sticky';
|
||||
import {
|
||||
isBFB,
|
||||
isLBB,
|
||||
isTB,
|
||||
isVB,
|
||||
} from '../utils/utils';
|
||||
|
||||
// Modules that use sticky element feature
|
||||
const stickyInstances = {};
|
||||
|
||||
/**
|
||||
* Initialize Sticky Elements;
|
||||
* Front-end: loads settings from store (which is loaded from global variable passed
|
||||
* via wp_localize_script())
|
||||
* Builder: adds store listener which will listen to Sticky Element settings change
|
||||
* pushed by builder's ETBuilderCustomCSSOutput component.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
if (isVB || isBFB || isTB || isLBB) {
|
||||
/**
|
||||
* Get Sticky Elements instances that need to be removed and unset from currently initialized
|
||||
* stickyInstances list by incoming settings from store and stickyInstances.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
function getUnsetStickyElements() {
|
||||
// Get id of currently initialized ETScriptStickyElement
|
||||
const currentInstanceIds = keys(stickyInstances);
|
||||
|
||||
// Get sticky settings from store
|
||||
const stickyModules = ETScriptStickyStore.modules;
|
||||
|
||||
// Get id of settings from store
|
||||
const stickyModuleIds = keys(stickyModules);
|
||||
|
||||
// If currently initialized ETScriptStickyElement id doesn't exist on store's settings id, it
|
||||
// means the ETScriptStickyElement is either removed or has its sticky status removed
|
||||
// (sticky_position attribute is set to `none`)
|
||||
return difference(currentInstanceIds, stickyModuleIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store's setting change listener callback.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
function onStickySettingsChange() {
|
||||
// Loop over unset sticky elements then remove their instances
|
||||
forEach(getUnsetStickyElements(), id => {
|
||||
// End sticky state, and removed isSticky prop
|
||||
stickyInstances[id].endSticky();
|
||||
stickyInstances[id].setProp('isSticky', false);
|
||||
|
||||
// Remove ETScriptStickyElement listener
|
||||
stickyInstances[id].removeListeners();
|
||||
|
||||
// Remove instance
|
||||
delete stickyInstances[id];
|
||||
});
|
||||
|
||||
// Loop over incoming sticky element settings from store
|
||||
forEach(ETScriptStickyStore.modules, (settings, id) => {
|
||||
// Check whether current setting has been initialized; if true, it means the sticky element
|
||||
// is being updated
|
||||
const currentStickyInstance = get(stickyInstances, id);
|
||||
|
||||
// Update sticky element
|
||||
if (currentStickyInstance) {
|
||||
// Check if current instance is equal to incoming settings from store. Current instance
|
||||
// has less attribute than store's because some attribute are generated on the fly at store.
|
||||
// Thus, omit attribute differences during comparison
|
||||
const instanceSettingsKeys = keys(currentStickyInstance.getSettings());
|
||||
const instanceInSettings = pick(settings, instanceSettingsKeys);
|
||||
|
||||
// If incoming settings are equal with current instance's settings, bail
|
||||
if (isEqual(currentStickyInstance.getSettings(), instanceInSettings)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update settings, then set initial props
|
||||
stickyInstances[id].setSettings(settings, true);
|
||||
} else {
|
||||
// Create new ETScriptStickyElement instance
|
||||
stickyInstances[id] = new ETScriptStickyElement(settings);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Attach callback to store's settings change event
|
||||
ETScriptStickyStore.addSettingsChangeListener(onStickySettingsChange);
|
||||
} else {
|
||||
// Need to wait until document is ready to get correct offset values
|
||||
$(() => {
|
||||
forEach(ETScriptStickyStore.modules, (settings, id) => {
|
||||
// Create sticky element instance
|
||||
stickyInstances[id] = new ETScriptStickyElement(settings);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=300)}({300:function(e,n,t){"use strict";(function(e){e((function(){var n=e(".wp-list-table"),t=e("li.wp-menu-open");n.find(".row-actions .edit, .row-actions .view").css("display","none"),t.find("a.wp-menu-open").removeClass("wp-menu-open wp-has-current-submenu"),t.find(".wp-submenu").addClass("wp-not-current-submenu"),t.removeClass("wp-menu-open wp-has-current-submenu"),n.on("click",".row-title",(function(){return e(this).closest("td").find(".row-actions .inline a").trigger("click"),!1})),n.on("click","td.column-posts a",(function(){return!1}))}))}).call(this,t(5))},5:function(e,n){e.exports=jQuery}});
|
||||
//# sourceMappingURL=library_category.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
!function(t){var e={};function n(i){if(e[i])return e[i].exports;var _=e[i]={i:i,l:!1,exports:{}};return t[i].call(_.exports,_,_.exports,n),_.l=!0,_.exports}n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var _ in t)n.d(i,_,function(e){return t[e]}.bind(null,_));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=302)}({302:function(t,e,n){"use strict";(function(t){var e=t("#et_settings_meta_box"),n=e.find(".et_pb_page_settings_container").first(),i=t(".et_pb_toggle_builder_wrapper"),_=t(".et_pb_page_setting"),s=t(".et_pb_page_layout_settings"),o=t("#formatdiv"),r=window.et_pb_options;i.hasClass("et_pb_builder_is_used")&&function(){var e=s.closest("#et_settings_meta_box").find(".et_pb_page_layout_settings");if(_.filter(":visible").length>1?(e.hide(),s.find(".et_pb_side_nav_settings").show()):("post"!==r.post_type&&"no"===r.is_third_party_post_type&&e.hide(),s.closest("#et_settings_meta_box").find(".et_pb_side_nav_settings").show(),s.closest("#et_settings_meta_box").find(".et_pb_single_title").show()),e.length>0){var n=e.find('option[value="et_full_width_page"]');n.length>0&&n.show()}if(o.length){var i=o.find('input[type="radio"]:checked').val();o.hide(),t(".et_divi_format_setting.et_divi_".concat(i,"_settings")).hide()}"project"===r.post_type&&s.closest("#et_settings_meta_box").find(".et_pb_project_nav").show()}(),n.hasClass("et_pb_page_settings_container--tb-has-header")&&e.find(".et_pb_nav_settings").hide(),n.hasClass("et_pb_page_settings_container--tb-has-body")&&e.find(".et_pb_page_layout_settings, .et_pb_single_title").hide(),0===n.height()&&e.hide()}).call(this,n(5))},5:function(t,e){t.exports=jQuery}});
|
||||
//# sourceMappingURL=page-settings-metabox.js.map
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=303)}({303:function(e,t,n){"use strict";(function(e){e((function(){var t=e("#epanel-ajax-saving");e(".et_disable_memory_limit_increase").on("click",(function(n){n.preventDefault(),e.ajax({type:"POST",url:ajaxurl,data:{action:"et_reset_memory_limit_increase",et_builder_reset_memory_limit_nonce:et_reset_memory_limit_increase.et_builder_reset_memory_limit_nonce},beforeSend:function(e){t.addClass("et_loading").removeClass("success-animation"),t.fadeIn("fast")},success:function(n){t.removeClass("et_loading").removeClass("success-animation"),setTimeout((function(){t.fadeOut("slow")}),500),"success"===n&&(e(".et_disable_memory_limit_increase").closest(".epanel-box").hide(),t.addClass("success-animation"))}})}))}))}).call(this,n(5))},5:function(e,t){e.exports=jQuery}});
|
||||
//# sourceMappingURL=reset_memory_limit_increase_setting.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,238 @@
|
||||
// External dependencies
|
||||
import { EventEmitter } from 'events';
|
||||
import debounce from 'lodash/debounce';
|
||||
import get from 'lodash/get';
|
||||
|
||||
// Internal dependencies
|
||||
import {
|
||||
maybeDecreaseEmitterMaxListeners,
|
||||
maybeIncreaseEmitterMaxListeners,
|
||||
registerFrontendComponent,
|
||||
} from '../utils/utils';
|
||||
|
||||
|
||||
const HEIGHT_CHANGE = 'height_change';
|
||||
const WIDTH_CHANGE = 'width_change';
|
||||
const DIMENSION_CHANGE = 'dimension_change';
|
||||
|
||||
// States
|
||||
const states = {
|
||||
height: 0,
|
||||
width: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Document store; track document height (at the moment) and its changes. Builder elements
|
||||
* should listen and get this store's value instead of directly getting it from document.
|
||||
* ETScriptDocumentStore is not exported; intentionally export its instance so there'll only be one
|
||||
* ETScriptDocumentStore instance.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
class ETScriptDocumentStore extends EventEmitter {
|
||||
/**
|
||||
* ETScriptDocumentStore constructor.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.setHeight(get(document, 'documentElement.offsetHeight'));
|
||||
this.setWidth(get(document, 'documentElement.offsetWidth'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Record document height.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {number} height
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
setHeight = height => {
|
||||
if (height === states.height) {
|
||||
return this;
|
||||
}
|
||||
|
||||
states.height = height;
|
||||
|
||||
this.emit(HEIGHT_CHANGE);
|
||||
this.emit(DIMENSION_CHANGE);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Record document width.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {number} width
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
setWidth = width => {
|
||||
if (width === states.width) {
|
||||
return this;
|
||||
}
|
||||
|
||||
states.width = width;
|
||||
|
||||
this.emit(WIDTH_CHANGE);
|
||||
this.emit(DIMENSION_CHANGE);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get recorded document height.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get height() {
|
||||
return states.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recorded document width.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get width() {
|
||||
return states.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add document dimension change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
addDimensionChangeListener = callback => {
|
||||
maybeIncreaseEmitterMaxListeners(this, DIMENSION_CHANGE);
|
||||
this.on(DIMENSION_CHANGE, callback);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove document dimension change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
removeDimensionChangeListener = callback => {
|
||||
this.removeListener(DIMENSION_CHANGE, callback);
|
||||
maybeDecreaseEmitterMaxListeners(this, DIMENSION_CHANGE);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add document height change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
addHeightChangeListener = callback => {
|
||||
maybeIncreaseEmitterMaxListeners(this, HEIGHT_CHANGE);
|
||||
this.on(HEIGHT_CHANGE, callback);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove document height change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
removeHeightChangeListener = callback => {
|
||||
this.removeListener(HEIGHT_CHANGE, callback);
|
||||
maybeDecreaseEmitterMaxListeners(this, HEIGHT_CHANGE);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add document width change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
addWidthChangeListener = callback => {
|
||||
maybeIncreaseEmitterMaxListeners(this, WIDTH_CHANGE);
|
||||
this.on(WIDTH_CHANGE, callback);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove document width change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
removeWidthChangeListener = callback => {
|
||||
this.removeListener(WIDTH_CHANGE, callback);
|
||||
maybeDecreaseEmitterMaxListeners(this, WIDTH_CHANGE);
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
// Create document store instance
|
||||
const documentStoreInstance = new ETScriptDocumentStore();
|
||||
|
||||
/**
|
||||
* Event's function callback to update document store's props
|
||||
*
|
||||
* @since 4.6.2
|
||||
*/
|
||||
function updateDocumentStoreProps() {
|
||||
const documentHeight = get(document, 'documentElement.offsetHeight');
|
||||
const documentWidth = get(document, 'documentElement.offsetWidth');
|
||||
|
||||
// Store automatically ignore if given height value is equal to the current one; so this is fine
|
||||
documentStoreInstance.setHeight(documentHeight).setWidth(documentWidth);
|
||||
}
|
||||
|
||||
// Listen to document's DOM change, debounce its callback, and update store's props
|
||||
const documentObserver = new MutationObserver(debounce(updateDocumentStoreProps, 50));
|
||||
|
||||
// Observe document change
|
||||
// @todo probably plug this on only when necessary
|
||||
// @todo also enable to plug this off
|
||||
documentObserver.observe(document, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
|
||||
// Update document store properties when Divi's fixed header transition is completed
|
||||
window.addEventListener('ETDiviFixedHeaderTransitionEnd', updateDocumentStoreProps);
|
||||
|
||||
// Register store instance as component to be exposed via global object
|
||||
registerFrontendComponent('stores', 'document', documentStoreInstance);
|
||||
|
||||
// Export store instance.
|
||||
// IMPORTANT: For uniformity, import this as ETScriptDocumentStore
|
||||
export default documentStoreInstance;
|
||||
@@ -0,0 +1,946 @@
|
||||
// External dependencies
|
||||
import { EventEmitter } from 'events';
|
||||
import assign from 'lodash/assign';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import compact from 'lodash/compact';
|
||||
import filter from 'lodash/filter';
|
||||
import forEach from 'lodash/forEach';
|
||||
import get from 'lodash/get';
|
||||
import has from 'lodash/has';
|
||||
import head from 'lodash/head';
|
||||
import includes from 'lodash/includes';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import isFunction from 'lodash/isFunction';
|
||||
import isObject from 'lodash/isObject';
|
||||
import isUndefined from 'lodash/isUndefined';
|
||||
import keys from 'lodash/keys';
|
||||
import last from 'lodash/last';
|
||||
import map from 'lodash/map';
|
||||
import mapKeys from 'lodash/mapKeys';
|
||||
import set from 'lodash/set';
|
||||
import size from 'lodash/size';
|
||||
import slice from 'lodash/slice';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import $ from 'jquery';
|
||||
|
||||
// Internal dependencies
|
||||
import {
|
||||
isOrHasValue,
|
||||
} from '@frontend-builder/utils/responsive-options-pure';
|
||||
import {
|
||||
top_window,
|
||||
} from '@core-ui/utils/frame-helpers';
|
||||
import ETScriptDocumentStore from './document';
|
||||
import ETScriptWindowStore from './window';
|
||||
import {
|
||||
getOffsets,
|
||||
isBFB,
|
||||
isBuilder,
|
||||
isDiviTheme,
|
||||
isExtraTheme,
|
||||
isLBB,
|
||||
isTB,
|
||||
isVB,
|
||||
maybeDecreaseEmitterMaxListeners,
|
||||
maybeIncreaseEmitterMaxListeners,
|
||||
registerFrontendComponent,
|
||||
} from '../utils/utils';
|
||||
|
||||
import {
|
||||
filterInvalidModules,
|
||||
getLimit,
|
||||
} from '../utils/sticky';
|
||||
|
||||
// Event Constants
|
||||
const SETTINGS_CHANGE = 'settings_change';
|
||||
|
||||
// Variables
|
||||
const $body = $('body');
|
||||
const hasFixedNav = $body.hasClass('et_fixed_nav');
|
||||
|
||||
/**
|
||||
* Saved sticky elements. In FE, this means all the sticky settings that exist on current page.
|
||||
* In VB (and other builder context) this means sticky settings that exist on current page but
|
||||
* is rendered outside current builder type. Removed nested sticky module (sticky inside another
|
||||
* sticky module) from the module list.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
const savedStickyElements = filterInvalidModules(cloneDeep(window.et_pb_sticky_elements));
|
||||
|
||||
/**
|
||||
* Defaults of known non module elements which its stickiness needs to be considered.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
const elementsDefaults = {
|
||||
wpAdminBar: {
|
||||
id: 'wpAdminBar',
|
||||
selector: '#wpadminbar',
|
||||
exist: false,
|
||||
height: 0,
|
||||
window: 'top',
|
||||
condition: () => {
|
||||
// Admin bar doesn't have fixed position in smaller breakpoint
|
||||
const isPositionFixed = 'fixed' === top_window.jQuery(elements.wpAdminBar.selector).css('position');
|
||||
|
||||
// When Responsive View's control is visible, admin bar offset becomes irrelevant. Note:
|
||||
// At this point the `height` value might not be updated yet, so manually get the height
|
||||
// value via `getHeight()` method.
|
||||
const hasVbAppFramePaddingTop = elements.builderAppFramePaddingTop.getHeight() > 0;
|
||||
|
||||
return ! hasVbAppFramePaddingTop && ! isTB && ! isLBB && isPositionFixed;
|
||||
},
|
||||
},
|
||||
diviFixedPrimaryNav: {
|
||||
id: 'diviPrimaryNav',
|
||||
selector: '#main-header',
|
||||
exist: false,
|
||||
height: 0,
|
||||
window: 'app',
|
||||
condition: () => {
|
||||
// Divi Theme has fixed nav. Note: vertical header automatically removes .et_fixed_nav
|
||||
// classname so it is fine just to test fixed nav state against .et_fixed_nav classname only
|
||||
const hasFixedNavBodyClass = isDiviTheme && hasFixedNav;
|
||||
|
||||
// Check for element's existence
|
||||
const isNavExist = $(elements.diviFixedPrimaryNav.selector).length > 0;
|
||||
|
||||
// Primary nav is doesn't have fixed position in smaller breakpoint
|
||||
const isPositionFixed = 'fixed' === $(elements.diviFixedPrimaryNav.selector).css('position');
|
||||
|
||||
return hasFixedNavBodyClass && isNavExist && isPositionFixed;
|
||||
},
|
||||
getHeight: () => {
|
||||
const $mainHeader = $(elementsDefaults.diviFixedPrimaryNav.selector);
|
||||
|
||||
// Bail if this isn't Divi
|
||||
if (! isDiviTheme && 1 > $mainHeader.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Clone header
|
||||
const $clone = $mainHeader.clone();
|
||||
|
||||
// Emulate fixed header state. Fixed header state is emulated as soon as the window is
|
||||
// scrolled so it is safe to assume that any sticky module on its sticky state will "meet"
|
||||
// header on its fixed state; this will avoid unwanted "jump" effect that happens because
|
||||
// fixed header has 400ms transition which could be slower than scroll speed; The fixed header
|
||||
// state also adds negative margin top state to #page-container which triggers document
|
||||
// dimension change event. Also add classname which will ensure that this clone won't
|
||||
// be visible to end user even if we only render it for a split second to avoid issues
|
||||
$clone.addClass('et-fixed-header et-script-temporary-measurement');
|
||||
|
||||
// Add it to layout so its dimension can be measured
|
||||
$mainHeader.parent().append($clone);
|
||||
|
||||
// Measure the fixed header height
|
||||
const height = $clone.outerHeight();
|
||||
|
||||
// Immediately remove the cloned DOM from layout
|
||||
$clone.remove();
|
||||
|
||||
return parseFloat(height);
|
||||
},
|
||||
},
|
||||
diviFixedSecondaryNav: {
|
||||
id: 'diviPrimaryNav',
|
||||
selector: '#top-header',
|
||||
exist: false,
|
||||
height: 0,
|
||||
window: 'app',
|
||||
condition: () => {
|
||||
// Divi Theme has fixed nav. Note: vertical header automatically removes .et_fixed_nav
|
||||
// classname so it is fine just to test fixed nav state against .et_fixed_nav classname only
|
||||
const hasFixedNavBodyClass = isDiviTheme && hasFixedNav;
|
||||
|
||||
// Check for element's existence
|
||||
const isNavExist = $(elements.diviFixedSecondaryNav.selector).length > 0;
|
||||
|
||||
// Primary nav is doesn't have fixed position in smaller breakpoint
|
||||
const isPositionFixed = 'fixed' === $(elements.diviFixedSecondaryNav.selector).css('position');
|
||||
|
||||
return hasFixedNavBodyClass && isNavExist && isPositionFixed;
|
||||
},
|
||||
},
|
||||
extraFixedPrimaryNav: {
|
||||
id: 'extraFixedPrimaryNav',
|
||||
selector: '#main-header',
|
||||
exist: false,
|
||||
height: 0,
|
||||
window: 'app',
|
||||
condition: () => {
|
||||
if (! isObject(ETScriptWindowStore) || ! isExtraTheme) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extra Theme has fixed nav.
|
||||
const hasFixedNavBodyClass = isExtraTheme && hasFixedNav;
|
||||
|
||||
// Check for element's existence.
|
||||
const isNavExist = $(elements.extraFixedPrimaryNav.selector).length > 0;
|
||||
|
||||
// Extra has its own breakpoint for fixed nav. Detecting computed style is most likely fail
|
||||
// because retrieved value is always one step behind before the computed style result is retrieved
|
||||
const isPositionFixed = 1024 <= (ETScriptWindowStore.width + ETScriptWindowStore.verticalScrollBar);
|
||||
|
||||
return hasFixedNavBodyClass && isNavExist && isPositionFixed;
|
||||
},
|
||||
getHeight: () => {
|
||||
const $mainHeader = $(elementsDefaults.extraFixedPrimaryNav.selector);
|
||||
|
||||
// Bail if this isn't Extra
|
||||
if (! isExtraTheme && 1 > $mainHeader.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Clone header
|
||||
const $clone = $mainHeader.clone();
|
||||
|
||||
// Emulate fixed header state. Fixed header state is emulated as soon as the window is
|
||||
// scrolled so it is safe to assume that any sticky module on its sticky state will "meet"
|
||||
// header on its fixed state; this will avoid unwanted "jump" effect that happens because
|
||||
// fixed header has 500ms transition which could be slower than scroll speed; The fixed header
|
||||
// state also adds negative margin top state to #page-container which triggers document
|
||||
// dimension change event. Also add classname which will ensure that this clone won't
|
||||
// be visible to end user even if we only render it for a split second to avoid issues
|
||||
$clone.addClass('et-fixed-header et-script-temporary-measurement');
|
||||
|
||||
// Add it to layout so its dimension can be measured
|
||||
$mainHeader.parent().append($clone);
|
||||
|
||||
// Measure the fixed header height
|
||||
const height = $clone.outerHeight();
|
||||
|
||||
// Immediately remove the cloned DOM from layout
|
||||
$clone.remove();
|
||||
|
||||
return parseFloat(height);
|
||||
},
|
||||
},
|
||||
builderAppFramePaddingTop: {
|
||||
id: 'builderAppFramePaddingTop',
|
||||
selector: isBFB ? '#et-bfb-app-frame' : '#et-fb-app-frame',
|
||||
exist: false,
|
||||
height: 0,
|
||||
window: 'top',
|
||||
getHeight: () => {
|
||||
const selector = elements.builderAppFramePaddingTop.selector;
|
||||
const cssProperty = isBFB ? 'marginTop' : 'paddingTop';
|
||||
const paddingTop = top_window.jQuery(selector).css(cssProperty);
|
||||
|
||||
return parseFloat(paddingTop);
|
||||
}
|
||||
},
|
||||
tbHeader: {
|
||||
id: 'et-tb-branded-modal__header',
|
||||
selector: '.et-tb-branded-modal__header',
|
||||
exist: false,
|
||||
height: 0,
|
||||
window: 'top',
|
||||
},
|
||||
lbbHeader: {
|
||||
id: 'et-block-builder-modal--header',
|
||||
selector: '.et-block-builder-modal--header',
|
||||
exist: false,
|
||||
height: 0,
|
||||
window: 'top',
|
||||
},
|
||||
gbHeader: {
|
||||
id: 'edit-post-header',
|
||||
|
||||
// This selector exist on WP 5.4 and below; hence these are used instead of `.block-editor-editor-skeleton__header`
|
||||
selector: '.edit-post-header',
|
||||
exist: false,
|
||||
height: 0,
|
||||
window: 'top',
|
||||
},
|
||||
gbFooter: {
|
||||
id: 'block-editor-editor-skeleton__footer',
|
||||
selector: '.block-editor-editor-skeleton__footer',
|
||||
exist: false,
|
||||
height: 0,
|
||||
window: 'top',
|
||||
},
|
||||
gbComponentsNoticeList: {
|
||||
id: 'components-notice-list',
|
||||
selector: '.components-notice-list',
|
||||
exist: false,
|
||||
height: 0,
|
||||
window: 'top',
|
||||
multiple: true,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Known non module elements which its stickiness needs to be considered.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
const elements = cloneDeep(elementsDefaults);
|
||||
|
||||
// States
|
||||
/**
|
||||
* Hold all sticky elements modules' properties.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
let modules = {};
|
||||
|
||||
|
||||
/**
|
||||
* Sticky Elements store.
|
||||
*
|
||||
* This store stores selected properties of all sticky elements on the page so a sticky element
|
||||
* can use other sticky element's calculated value quickly.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
class ETScriptStickyStore extends EventEmitter {
|
||||
/**
|
||||
* ETScriptStickyStore constructor.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Load modules passed via global variable from server via wp_localize_script()
|
||||
assign(modules, savedStickyElements);
|
||||
|
||||
// Caculate top/bottom offsetModules which are basically list of sticky elements that need
|
||||
// to be considered for additional offset calculation when `Offset From Surrounding Sticky Elements`
|
||||
// option is toggled `on`
|
||||
this.generateOffsetModules();
|
||||
|
||||
// Calculate known elements' properties. This needs to be done after DOM is ready
|
||||
if (isVB) {
|
||||
$(window).on('et_fb_init_app_after', () => {
|
||||
this.setElementsProps();
|
||||
});
|
||||
} else {
|
||||
$(() => {
|
||||
this.setElementsProps();
|
||||
});
|
||||
}
|
||||
|
||||
// Some props need to be updated when document height is changed (eg. fixed nav's height)
|
||||
ETScriptDocumentStore.addHeightChangeListener(this.onDocumentHeightChange);
|
||||
|
||||
// Builder specific event callback
|
||||
if (isBuilder) {
|
||||
// Event callback once the builder has been mounted
|
||||
$(window).on('et_fb_root_did_mount', this.onBuilderDidMount);
|
||||
|
||||
// Listen to builder change if current window is builder window
|
||||
window.addEventListener('ETBuilderStickySettingsSyncs', this.onBuilderSettingsChange);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registered modules.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
get modules() {
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of builder options (that is used by sticky elements) that has responsive mode.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
get responsiveOptions() {
|
||||
const options = [
|
||||
'position',
|
||||
'topOffset',
|
||||
'bottomOffset',
|
||||
'topLimit',
|
||||
'bottomLimit',
|
||||
'offsetSurrounding',
|
||||
'transition',
|
||||
'topOffsetModules',
|
||||
'bottomOffsetModules',
|
||||
];
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update selected module / elements prop on document height change.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
onDocumentHeightChange = () => {
|
||||
// Update Divi fixed nav height property. Divi fixed nav height change when it enters its sticky state
|
||||
// thus making it having different height when sits on top of viewport and during window scroll
|
||||
if (this.getElementProp('diviFixedPrimaryNav', 'exist', false)) {
|
||||
const getHeight = this.getElementProp('diviFixedPrimaryNav', 'getHeight');
|
||||
|
||||
this.setElementProp('diviFixedPrimaryNav', 'height', getHeight());
|
||||
}
|
||||
|
||||
// Update Extra's fixed height property. Extra fixed nav height changes as the window is scrolled
|
||||
if (this.getElementProp('extraFixedPrimaryNav', 'exist', false)) {
|
||||
const getExtraFixedMainHeaderHeight = this.getElementProp('extraFixedPrimaryNav', 'getHeight');
|
||||
|
||||
this.setElementProp('extraFixedPrimaryNav', 'height', getExtraFixedMainHeaderHeight());
|
||||
}
|
||||
|
||||
if (this.getElementProp('builderAppFramePaddingTop', 'exist', false)) {
|
||||
this.setElementHeight('builderAppFramePaddingTop');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder did mount listener callback.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
onBuilderDidMount = () => {
|
||||
const stickyOnloadModuleKeys = keys(window.et_pb_sticky_elements);
|
||||
const stickyMountedModuleKeys = keys(this.modules);
|
||||
|
||||
// Has sticky elements but builder has no saved sticky module; sticky element on current
|
||||
// page is outside current builder (eg. page builder has with no sticky element saved but
|
||||
// TB header of current page has sticky element). Need to emit change to kickstart the stick
|
||||
// element initialization and generating offset modules
|
||||
if (stickyOnloadModuleKeys.length > 0 && isEqual(stickyOnloadModuleKeys, stickyMountedModuleKeys)) {
|
||||
this.onBuilderSettingsChange(undefined, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder settings change listener callback.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {object} event
|
||||
* @param {bool} forceUpdate
|
||||
*/
|
||||
onBuilderSettingsChange = (event, forceUpdate = false) => {
|
||||
const settings = get(event, 'detail.settings');
|
||||
|
||||
if (isEqual(settings, this.modules) && ! forceUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update sticky settings. Removed nested sticky module (sticky inside another
|
||||
// sticky module) from the module list.
|
||||
modules = filterInvalidModules(cloneDeep(settings), modules);
|
||||
|
||||
// Append saved sticky elements settings which is rendered outside of current builder
|
||||
// type because it won't be generated by current builder's components
|
||||
assign(modules, savedStickyElements);
|
||||
|
||||
// Generate offset modules
|
||||
this.generateOffsetModules();
|
||||
|
||||
this.emit(SETTINGS_CHANGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get id of all modules.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {object} modules
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
getModulesId = modules => map(modules, module => module.id)
|
||||
|
||||
/**
|
||||
* Get modules based on its rendering position; also consider its offset surrounding setting if needed.
|
||||
*
|
||||
* @since 4.6.0
|
||||
* @param {string} top|bottom
|
||||
* @param position
|
||||
* @param offsetSurrounding
|
||||
* @param {string|bool} on|off|false When false, ignore offset surrounding value.
|
||||
* @returns {bool}
|
||||
*/
|
||||
getModulesByPosition = (position, offsetSurrounding = false) => filter(modules, (module, id) => {
|
||||
// Check offset surrounding value; if param set to `false`, ignore it. If `on`|`off`, only
|
||||
// pass module that has matching value
|
||||
const isOffsetSurrounding = ! offsetSurrounding ? true : isOrHasValue(module.offsetSurrounding, offsetSurrounding);
|
||||
|
||||
return includes(['top_bottom', position], this.getProp(id, 'position')) && isOffsetSurrounding;
|
||||
})
|
||||
|
||||
/**
|
||||
* Sort modules from top to down based on offset prop. Passed module has no id or index prop so
|
||||
* offset which visually indicate module's position in the page will do.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
sortModules = () => {
|
||||
const storeModules = this.modules;
|
||||
const modulesSize = size(storeModules);
|
||||
|
||||
// Return modules as-is if it is less than two modules; no need to sort it
|
||||
if (modulesSize < 2) {
|
||||
return storeModules;
|
||||
}
|
||||
|
||||
// There's no index whatsoever, but offset's top and left indicates module's position
|
||||
const sortedModules = sortBy(storeModules, [
|
||||
module => module.offsets.top,
|
||||
module => module.offsets.left,
|
||||
]);
|
||||
|
||||
// sortBy returns array type value; remap id as object key
|
||||
const remappedModules = mapKeys(sortedModules, module => module.id);
|
||||
|
||||
modules = cloneDeep(remappedModules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set prop value.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} id Need to be unique.
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
*/
|
||||
setProp = (id, name, value) => {
|
||||
// Skip updating if the id isn't exist
|
||||
if (! has(modules, id) || isUndefined(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentValue = this.getProp(id, name);
|
||||
|
||||
// Skip updating prop if the value is the same
|
||||
if (currentValue === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(modules, `${id}.${name}`, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get prop.
|
||||
*
|
||||
* @since 4.6.0
|
||||
* @param {string} id
|
||||
* @param {string} name
|
||||
* @param {mixed} defaultValue
|
||||
* @param returnCurrentBreakpoint
|
||||
* @param {bool} return
|
||||
* @returns {mixed}
|
||||
*/
|
||||
getProp = (id, name, defaultValue, returnCurrentBreakpoint = true) => {
|
||||
const value = get(modules, `${id}.${name}`, defaultValue);
|
||||
const isResponsive = returnCurrentBreakpoint
|
||||
&& isObject(value)
|
||||
&& has(value, 'desktop')
|
||||
&& includes(this.responsiveOptions, name);
|
||||
|
||||
return isResponsive ? get(value, get(ETScriptWindowStore, 'breakpoint', 'desktop'), defaultValue) : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set known elements' props.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
setElementsProps = () => {
|
||||
forEach(elements, (settings, name) => {
|
||||
if (! has(settings, 'window')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (has(settings, 'condition') && isFunction(settings.condition) && ! settings.condition()) {
|
||||
// Reset props if it fails on condition check
|
||||
this.setElementProp(name, 'exist', get(elementsDefaults, `${name}.exist`, false));
|
||||
this.setElementProp(name, 'height', get(elementsDefaults, `${name}.height`, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
const currentWindow = 'top' === this.getElementProp(name, 'window') ? top_window : window;
|
||||
const $element = currentWindow.jQuery(settings.selector);
|
||||
const hasElement = $element.length > 0 && $element.is(':visible');
|
||||
|
||||
if (hasElement) {
|
||||
this.setElementProp(name, 'exist', hasElement);
|
||||
|
||||
this.setElementHeight(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set known element prop value.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} id Need to be unique.
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
*/
|
||||
setElementProp = (id, name, value) => {
|
||||
const currentValue = this.getElementProp(id, name);
|
||||
|
||||
// Skip updating prop if the value is the same
|
||||
if (currentValue === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(elements, `${id}.${name}`, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get known element prop.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {string} name
|
||||
* @param {mixed} defaultValue
|
||||
*
|
||||
* @returns {mixed}
|
||||
*/
|
||||
getElementProp = (id, name, defaultValue) => get(elements, `${id}.${name}`, defaultValue)
|
||||
|
||||
/**
|
||||
* Set element height.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} name
|
||||
*/
|
||||
setElementHeight = name => {
|
||||
const selector = this.getElementProp(name, 'selector');
|
||||
const currentWindow = 'top' === this.getElementProp(name, 'window', 'app') ? top_window : window;
|
||||
const $selector = currentWindow.jQuery(selector);
|
||||
|
||||
let height = 0;
|
||||
|
||||
forEach($selector, item => {
|
||||
const getHeight = this.getElementProp(name, 'getHeight', false);
|
||||
|
||||
if (isFunction(getHeight)) {
|
||||
height += getHeight();
|
||||
} else {
|
||||
height += currentWindow.jQuery(item).outerHeight();
|
||||
}
|
||||
});
|
||||
|
||||
this.setElementProp(name, 'height', parseInt(height));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate offset modules for offset surrounding option.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
generateOffsetModules = () => {
|
||||
// Get module's width, height, and offsets. These are needed to calculate offset module's
|
||||
// adjacent column adjustment. stickyElement will update this later on its initialization
|
||||
// This needs to be on earlier and different loop than the one below for generating offset
|
||||
// modules because in builder the modules need to be sorted from top to down first
|
||||
forEach(this.modules, (module, id) => {
|
||||
const $module = $(this.getProp(id, 'selector'));
|
||||
const moduleWidth = parseInt($module.outerWidth());
|
||||
const moduleHeight = parseInt($module.outerHeight());
|
||||
const moduleOffsets = getOffsets($module, moduleWidth, moduleHeight);
|
||||
|
||||
// Only update dimension props if module isn't on sticky state
|
||||
if (! this.isSticky(id)) {
|
||||
this.setProp(id, 'width', moduleWidth);
|
||||
this.setProp(id, 'height', moduleHeight);
|
||||
this.setProp(id, 'offsets', moduleOffsets);
|
||||
}
|
||||
|
||||
// Set limits
|
||||
const position = this.getProp(id, 'position', 'none');
|
||||
const isStickyBottom = includes(['bottom', 'top_bottom'], position);
|
||||
const isStickyTop = includes(['top', 'top_bottom'], position);
|
||||
|
||||
if (isStickyBottom) {
|
||||
const topLimit = this.getProp(id, 'topLimit');
|
||||
const topLimitSettings = getLimit($module, topLimit);
|
||||
|
||||
this.setProp(id, 'topLimitSettings', topLimitSettings);
|
||||
}
|
||||
|
||||
if (isStickyTop) {
|
||||
const bottomLimit = this.getProp(id, 'bottomLimit');
|
||||
const bottomLimitSettings = getLimit($module, bottomLimit);
|
||||
|
||||
this.setProp(id, 'bottomLimitSettings', bottomLimitSettings);
|
||||
}
|
||||
});
|
||||
|
||||
// Sort modules in builder to ensure top to bottom module order for generating offset modules
|
||||
if (isBuilder) {
|
||||
this.sortModules();
|
||||
}
|
||||
|
||||
const { modules } = this;
|
||||
const modulesSize = size(modules);
|
||||
const topPositionModules = this.getModulesByPosition('top', 'on');
|
||||
const topPositionModulesId = this.getModulesId(topPositionModules);
|
||||
const bottomPositionModules = this.getModulesByPosition('bottom', 'on');
|
||||
const bottomPositionModulesId = this.getModulesId(bottomPositionModules);
|
||||
|
||||
// Capture top/bottom offsetModules updates for later loop
|
||||
const offsetModulesUpdates = [];
|
||||
|
||||
forEach(modules, (module, id) => {
|
||||
if (isOrHasValue(module.offsetSurrounding, 'on')) {
|
||||
// Top position sticky: get all module id that uses top / top_bottom position +
|
||||
// has its offset surrounding turn on, that are rendered BEFORE THIS sticky element
|
||||
if (includes(['top', 'top_bottom'], this.getProp(id, 'position'))) {
|
||||
const topOffsetModuleIndex = topPositionModulesId.indexOf(id);
|
||||
const topOffsetModule = slice(topPositionModulesId, 0, topOffsetModuleIndex);
|
||||
|
||||
// Saves all top offset modules for reference. This still needs to be processed to
|
||||
// filter adjacent column later
|
||||
this.setProp(id, 'topOffsetModulesAll', topOffsetModule);
|
||||
|
||||
// Mark for adjacent column filtering
|
||||
offsetModulesUpdates.push({
|
||||
prop: 'topOffsetModules',
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
// Bottom position sticky: get all module id that uses bottom / top_bottom position +
|
||||
// has its offset surrounding turn on, that are rendered AFTER THIS sticky element
|
||||
if (includes(['bottom', 'top_bottom'], this.getProp(id, 'position'))) {
|
||||
const bottomOffsetModuleIndex = bottomPositionModulesId.indexOf(id);
|
||||
const bottomOffsetModules = slice(bottomPositionModulesId, (bottomOffsetModuleIndex + 1), modulesSize);
|
||||
|
||||
// Saves all bottom offset modules for reference. This still needs to be processed to
|
||||
// filter adjacent column later
|
||||
this.setProp(id, 'bottomOffsetModulesAll', bottomOffsetModules);
|
||||
|
||||
// Mark for adjacent column filtering
|
||||
offsetModulesUpdates.push({
|
||||
prop: 'bottomOffsetModules',
|
||||
id,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Top / bottom offset modules adjacent column filtering
|
||||
if (offsetModulesUpdates.length > 0) {
|
||||
// Default offsets. Make sure all sides element is available
|
||||
const defaultOffsets = {
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
};
|
||||
|
||||
// Proper limit settings based on current offset modules position
|
||||
const offsetLimitPropMaps = {
|
||||
topOffsetModules: 'bottomLimitSettings',
|
||||
bottomOffsetModules: 'topLimitSettings',
|
||||
};
|
||||
|
||||
forEach(offsetModulesUpdates, update => {
|
||||
// module's id
|
||||
const moduleId = update.id;
|
||||
|
||||
// Need to be defined inside offsetModulesUpdates loop so each surrounding loop starts new
|
||||
// Will be updated on every loop so next loop has reference of what is prev modules has
|
||||
const prevSurroundingOffsets = {
|
||||
...defaultOffsets,
|
||||
};
|
||||
|
||||
// Loop over module's top/bottom offset module ids
|
||||
const offsetModules = filter(this.getProp(moduleId, `${update.prop}All`), id => {
|
||||
// Modules that are defined at top/bottomOffsetModules prop which is positioned after
|
||||
// current module is referred as surrounding (modules) offset
|
||||
const surroundingOffsets = {
|
||||
...defaultOffsets,
|
||||
...this.getProp(id, 'offsets', {}),
|
||||
};
|
||||
|
||||
// Current module's offset
|
||||
const moduleOffsets = {
|
||||
...defaultOffsets,
|
||||
...this.getProp(moduleId, 'offsets'),
|
||||
};
|
||||
|
||||
// Module limit's offset
|
||||
const moduleLimitOffsets = this.getProp(moduleId, `${offsetLimitPropMaps[update.prop]}.offsets`);
|
||||
const surroundingLimitOffsets = this.getProp(id, `${offsetLimitPropMaps[update.prop]}.offsets`);
|
||||
|
||||
// If current and surrounding modules both have limit offsets, their top and bottom needs
|
||||
// to be put in consideration in case they will never offset each other
|
||||
if (moduleLimitOffsets && surroundingLimitOffsets) {
|
||||
if (surroundingLimitOffsets.top < moduleLimitOffsets.top || surroundingLimitOffsets.bottom > moduleLimitOffsets.bottom) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If module has no limits, offset from surrounding sticky elements most likely not a
|
||||
// valid offset surrounding. There is a case where surrounding can be valid offset, which
|
||||
// is when current module on sticky state between surrounding limit top and bottom.
|
||||
// However this rarely happens and requires conditional offset based on current window
|
||||
// scroll top which might be over-engineer. Thus this is kept this way until further
|
||||
// confirmation with design team
|
||||
// @todo probably add conditional offset surrounding; confirm to design team
|
||||
if (! moduleLimitOffsets && surroundingLimitOffsets) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Top Offset modules (sticky position top): modules rendered before current module
|
||||
// Bottom Offset module (sticky position bottom): modules rendered after current module
|
||||
// caveat: offset modules that are not vertically aligned with current module should not
|
||||
// be considered as offset modules and affecting current module's auto-added offset.
|
||||
// Hence this filter. Initially, all offset module should affect module's auto offset
|
||||
let shouldPass = true;
|
||||
|
||||
// Surrounding module is beyond current module's right side
|
||||
// ***********
|
||||
// * current *
|
||||
// ***********
|
||||
// ***************
|
||||
// * surrounding *
|
||||
// ***************
|
||||
const isSurroundingBeyondCurrentRight = surroundingOffsets.left >= moduleOffsets.right;
|
||||
|
||||
// Surrounding module is beyond current module's left side
|
||||
// ***********
|
||||
// * current *
|
||||
// ***********
|
||||
// ***************
|
||||
// * surrounding *
|
||||
// ***************
|
||||
const isSurroundingBeyondCurrentLeft = surroundingOffsets.right < moduleOffsets.left;
|
||||
|
||||
// Surrounding module overlaps with current module's right side
|
||||
// *********** ************************
|
||||
// * current * * current *
|
||||
// *********** OR ************************
|
||||
// *************** ***************
|
||||
// * surrounding * * surrounding *
|
||||
// *************** ***************
|
||||
const isSurroundingOverlapsCurrent = surroundingOffsets.left > moduleOffsets.left && surroundingOffsets.right > moduleOffsets.left;
|
||||
|
||||
// Previous surrounding module overlaps with current module's left side.
|
||||
// ************************
|
||||
// * current *
|
||||
// ************************
|
||||
// ******************** ******************************
|
||||
// * prev surrounding * * surrounding (on this loop) *
|
||||
// ******************** ******************************
|
||||
const isPrevSurroundingOverlapsWithCurrent = moduleOffsets.left <= prevSurroundingOffsets.right && surroundingOffsets.top < prevSurroundingOffsets.bottom;
|
||||
|
||||
// Ignore surrounding height if previous surrounding height has affected current module's offset
|
||||
// See isPrevSurroundingOverlapsWithCurrent's figure above
|
||||
const isPrevSurroundingHasAffectCurrent = isSurroundingOverlapsCurrent && isPrevSurroundingOverlapsWithCurrent;
|
||||
|
||||
// Ignore the surrounding's height given the following scenarios
|
||||
if (isSurroundingBeyondCurrentRight || isSurroundingBeyondCurrentLeft || isPrevSurroundingHasAffectCurrent) {
|
||||
shouldPass = false;
|
||||
}
|
||||
|
||||
// Save current surrounding offsets for next surrounding offsets comparison
|
||||
assign(prevSurroundingOffsets, surroundingOffsets);
|
||||
|
||||
// true: surrounding's height is considered for current module's auto offset
|
||||
// false: surrounding's height is ignored
|
||||
return shouldPass;
|
||||
});
|
||||
|
||||
// Set ${top/bottom}OffsetModules prop which will be synced to stickyElement
|
||||
this.setProp(moduleId, `${update.prop}Align`, offsetModules);
|
||||
});
|
||||
}
|
||||
|
||||
// Perform secondary offset module calculation. The above works by getting the first surrounding
|
||||
// sticky on the next row that affects current sticky. This works well when the row is filled
|
||||
// like a grid, but fail if there is row in between which is not vertically overlap. Thus,
|
||||
// get the closest surrounding offset sticky from last calculation, then fetch it. The idea is
|
||||
// the last surrounding sticky might have offset which is not vertically align / overlap to
|
||||
// current sticky element
|
||||
forEach(this.modules, (module, moduleId) => {
|
||||
if (module.topOffsetModulesAlign) {
|
||||
const lastTopOffsetModule = last(module.topOffsetModulesAlign);
|
||||
const pervTopOffsetModule = this.getProp(lastTopOffsetModule, 'topOffsetModules', this.getProp(lastTopOffsetModule, 'topOffsetModulesAlign', []));
|
||||
|
||||
this.setProp(moduleId, 'topOffsetModules', compact([
|
||||
...pervTopOffsetModule,
|
||||
...[lastTopOffsetModule],
|
||||
]));
|
||||
}
|
||||
|
||||
if (module.bottomOffsetModulesAlign) {
|
||||
const firstBottomOffsetModule = head(module.bottomOffsetModulesAlign);
|
||||
const pervBottomOffsetModule = this.getProp(firstBottomOffsetModule, 'bottomOffsetModules', this.getProp(firstBottomOffsetModule, 'bottomOffsetModulesAlign', []));
|
||||
|
||||
this.setProp(moduleId, 'bottomOffsetModules', compact([
|
||||
...[firstBottomOffsetModule],
|
||||
...pervBottomOffsetModule,
|
||||
]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if module with given id is on sticky state.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} id
|
||||
*
|
||||
* @returns {bool}
|
||||
*/
|
||||
isSticky = id => get(this.modules, [id, 'isSticky'], false)
|
||||
|
||||
/**
|
||||
* Add listener callback for settings change event.
|
||||
*
|
||||
* @since 4.6.0
|
||||
* @param callback
|
||||
* @param {Function}
|
||||
*/
|
||||
addSettingsChangeListener = callback => {
|
||||
maybeIncreaseEmitterMaxListeners(this, SETTINGS_CHANGE);
|
||||
this.on(SETTINGS_CHANGE, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove listener callback for settings change event.
|
||||
*
|
||||
* @since 4.6.0
|
||||
* @param callback
|
||||
* @param {Function}
|
||||
*/
|
||||
removeSettingsChangeListener = callback => {
|
||||
this.removeListener(SETTINGS_CHANGE, callback);
|
||||
maybeDecreaseEmitterMaxListeners(this, SETTINGS_CHANGE);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
const stickyStoreInstance = new ETScriptStickyStore;
|
||||
|
||||
// Register store instance as component to be exposed via global object
|
||||
registerFrontendComponent('stores', 'sticky', stickyStoreInstance);
|
||||
|
||||
// Export store instance
|
||||
// IMPORTANT: For uniformity, import this as ETScriptStickyStore
|
||||
export default stickyStoreInstance;
|
||||
@@ -0,0 +1,754 @@
|
||||
// External dependencies
|
||||
import { EventEmitter } from 'events';
|
||||
import forEach from 'lodash/forEach';
|
||||
import get from 'lodash/get';
|
||||
import includes from 'lodash/includes';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import $ from 'jquery';
|
||||
|
||||
// Internal dependencies
|
||||
import { top_window } from '@core-ui/utils/frame-helpers';
|
||||
import ETScriptStickyStore from './sticky';
|
||||
import {
|
||||
getContentAreaSelector,
|
||||
getTemplateEditorIframe,
|
||||
} from '../../frontend-builder/gutenberg/utils/selectors';
|
||||
import { isTemplateEditor } from '../../frontend-builder/gutenberg/utils/conditionals';
|
||||
import {
|
||||
isBFB,
|
||||
isExtraTheme,
|
||||
isFE,
|
||||
isLBB,
|
||||
isLBP,
|
||||
isTB,
|
||||
isVB,
|
||||
maybeDecreaseEmitterMaxListeners,
|
||||
maybeIncreaseEmitterMaxListeners,
|
||||
registerFrontendComponent,
|
||||
} from '../utils/utils';
|
||||
|
||||
// Builder window
|
||||
const $window = $(window);
|
||||
const $topWindow = top_window.jQuery(top_window);
|
||||
const hasTopWindow = ! isEqual(window, top_window);
|
||||
const windowLocations = hasTopWindow ? ['app', 'top'] : ['app'];
|
||||
|
||||
// Event Constants
|
||||
const HEIGHT_CHANGE = 'height_change';
|
||||
const WIDTH_CHANGE = 'width_change';
|
||||
const SCROLL_TOP_CHANGE = 'scroll_top_change';
|
||||
const BREAKPOINT_CHANGE = 'breakpoint_change';
|
||||
const SCROLL_LOCATION_CHANGE = 'scroll_location_change';
|
||||
const VERTICAL_SCROLL_BAR_CHANGE = 'vertical_scroll_bar_change';
|
||||
|
||||
// States.
|
||||
// Private, limited to this module (ETScriptWindowStore class) only
|
||||
const states = {
|
||||
breakpoint: 'desktop',
|
||||
extraMobileBreakpoint: false,
|
||||
isBuilderZoomed: false,
|
||||
scrollLocation: et_builder_utils_params.onloadScrollLocation, // app|top
|
||||
scrollTop: {
|
||||
app: 0,
|
||||
top: 0,
|
||||
},
|
||||
height: {
|
||||
app: 0,
|
||||
top: 0,
|
||||
},
|
||||
width: {
|
||||
app: 0,
|
||||
top: 0,
|
||||
},
|
||||
bfbIframeOffset: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
},
|
||||
lbpIframeOffset: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
},
|
||||
verticalScrollBar: {
|
||||
app: 0,
|
||||
top: 0,
|
||||
},
|
||||
};
|
||||
|
||||
// Valid values.
|
||||
// Retrieved from server, used for validating values
|
||||
const validValues = {
|
||||
scrollLocation: [...et_builder_utils_params.scrollLocations],
|
||||
};
|
||||
|
||||
// Variables
|
||||
const builderScrollLocations = {
|
||||
...et_builder_utils_params.builderScrollLocations,
|
||||
};
|
||||
|
||||
// @todo need to change how this works since builder already have et_screen_sizes(), unless
|
||||
// we prefer to add another breakpoint functions
|
||||
const deviceMinimumBreakpoints = {
|
||||
desktop: 980,
|
||||
tablet: 767,
|
||||
phone: 0,
|
||||
};
|
||||
const bfbFrameId = '#et-bfb-app-frame';
|
||||
|
||||
/**
|
||||
* Window store.
|
||||
*
|
||||
* This store listen to direct window's events; builder callback listen to this store's events
|
||||
* to avoid dom-based calculation whenever possible; use the property passed by this store.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
class ETScriptWindowStore extends EventEmitter {
|
||||
/**
|
||||
* ETScriptWindowStore constructor.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Set app window onload values
|
||||
const windowWidth = $window.innerWidth();
|
||||
const windowHeight = $window.innerHeight();
|
||||
const windowScrollTop = $window.scrollTop();
|
||||
|
||||
this.setWidth('app', windowWidth).setHeight('app', windowHeight);
|
||||
this.setScrollTop('app', windowScrollTop);
|
||||
this.setVerticalScrollBarWidth('app', (window.outerWidth - windowWidth));
|
||||
|
||||
// Set top window onload values (if top window exist)
|
||||
if (hasTopWindow) {
|
||||
const topWindowWidth = $topWindow.innerWidth();
|
||||
const topWindowHeight = $topWindow.innerHeight();
|
||||
const topWindowScrollTop = top_window.jQuery(top_window).scrollTop();
|
||||
|
||||
this.setWidth('top', topWindowWidth).setHeight('top', topWindowHeight);
|
||||
this.setScrollTop('top', topWindowScrollTop);
|
||||
this.setVerticalScrollBarWidth('top', (top_window.outerWidth - topWindowWidth));
|
||||
}
|
||||
|
||||
// Set iframe offset
|
||||
if (isBFB) {
|
||||
this.setBfbIframeOffset();
|
||||
}
|
||||
|
||||
// Set Layout Block iframe offset
|
||||
if (isLBP) {
|
||||
this.setLayoutBlockPreviewIframeOffset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set window height.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} windowLocation App|top.
|
||||
* @param {number} height
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
setHeight = (windowLocation = 'app', height) => {
|
||||
if (height === states.height[windowLocation]) {
|
||||
return this;
|
||||
}
|
||||
|
||||
states.height[windowLocation] = height;
|
||||
|
||||
this.emit(HEIGHT_CHANGE);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set window width.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} windowLocation App|top.
|
||||
* @param {number} width
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
setWidth = (windowLocation = 'app', width) => {
|
||||
if (width === states.width[windowLocation]) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Only app window could set breakpoint
|
||||
if ('app' === windowLocation) {
|
||||
this.setBreakpoint(width);
|
||||
|
||||
// Extra theme has its own "mobile breakpoint" (below 1024px)
|
||||
if (isExtraTheme) {
|
||||
const outerWidth = this.width + this.verticalScrollBar;
|
||||
const extraMobileBreakpoint = 1024;
|
||||
const fixedNavActivation = ! states.extraMobileBreakpoint && outerWidth >= extraMobileBreakpoint;
|
||||
const fixedNavDeactivation = states.extraMobileBreakpoint && outerWidth < extraMobileBreakpoint;
|
||||
|
||||
// Re-set element props when Extra mobile breakpoint change happens
|
||||
if (fixedNavActivation || fixedNavDeactivation) {
|
||||
states.extraMobileBreakpoint = (outerWidth >= extraMobileBreakpoint);
|
||||
|
||||
ETScriptStickyStore.setElementsProps();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states.width[windowLocation] = width;
|
||||
|
||||
this.emit(WIDTH_CHANGE);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set scroll location value.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} scrollLocation App|top.
|
||||
*
|
||||
* @returns {ETScriptWindowStore}
|
||||
*/
|
||||
setScrollLocation = scrollLocation => {
|
||||
// Prevent incorrect scroll location value from being saved
|
||||
if (! includes(validValues.scrollLocation, scrollLocation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (scrollLocation === states.scrollLocation) {
|
||||
return this;
|
||||
}
|
||||
|
||||
states.scrollLocation = scrollLocation;
|
||||
|
||||
this.emit(SCROLL_LOCATION_CHANGE);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set scroll top value.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} windowLocation App|top.
|
||||
* @param {number} scrollTop
|
||||
*
|
||||
* @returns {ETScriptWindowStore}
|
||||
*/
|
||||
setScrollTop = (windowLocation, scrollTop) => {
|
||||
if (scrollTop === states.scrollTop[windowLocation]) {
|
||||
return this;
|
||||
}
|
||||
|
||||
states.scrollTop[windowLocation] = scrollTop;
|
||||
|
||||
this.emit(SCROLL_TOP_CHANGE);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set builder zoomed status (on builder only).
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} builderPreviewMode Desktop|tablet|phone|zoom|wireframe.
|
||||
*/
|
||||
setBuilderZoomedStatus = builderPreviewMode => {
|
||||
const isBuilderZoomed = 'zoom' === builderPreviewMode;
|
||||
|
||||
states.isBuilderZoomed = isBuilderZoomed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set BFB iframe offset.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
setBfbIframeOffset = () => {
|
||||
states.bfbIframeOffset = top_window.jQuery(bfbFrameId).offset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Layout Block iframe offset.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
setLayoutBlockPreviewIframeOffset = () => {
|
||||
const blockId = get(window.ETBlockLayoutModulesScript, 'blockId', '');
|
||||
const previewIframeId = `#divi-layout-iframe-${blockId}`;
|
||||
const $block = top_window.jQuery(previewIframeId).closest('.wp-block[data-type="divi/layout"]');
|
||||
const blockPosition = $block.position();
|
||||
const contentSelectors = [
|
||||
// WordPress 5.4
|
||||
'block-editor-editor-skeleton__content',
|
||||
|
||||
// WordPress 5.5
|
||||
'interface-interface-skeleton__content',
|
||||
];
|
||||
|
||||
let blockOffsetTop = parseInt(get(blockPosition, 'top', 0));
|
||||
|
||||
// Since WordPress 5.4, blocks list position to its parent somehow is not considered
|
||||
// Previous inserted DOM are also gone + Block item now has collapsing margin top/bottom
|
||||
// These needs to be manually calculated here since the result is no longer identical
|
||||
if (includes(contentSelectors, getContentAreaSelector(top_window, false))) {
|
||||
// Find Block List Layout. By default, it's located on editor of top window.
|
||||
// When Template Editor is active, it's "moved" to editor of iframe window.
|
||||
const $blockEditorLayout = isTemplateEditor() ? getTemplateEditorIframe(top_window).find('.block-editor-block-list__layout.is-root-container') : top_window.jQuery('.block-editor-block-list__layout');
|
||||
|
||||
// Blocks list position to its parent (title + content wrapper)
|
||||
// WordPress 5.4 = 183px
|
||||
// WordPress 5.5 = 161px
|
||||
if ($blockEditorLayout.length) {
|
||||
blockOffsetTop += $blockEditorLayout.position().top;
|
||||
}
|
||||
|
||||
// Compensating collapsing block item margin top
|
||||
blockOffsetTop += parseInt($block.css('marginTop')) || 0;
|
||||
}
|
||||
|
||||
// Admin bar in less than 600 width window uses absolute positioning which stays on top of
|
||||
// document and affecting iframe top offset
|
||||
if (600 > this.width && ETScriptStickyStore.getElementProp('wpAdminBar', 'exist', false)) {
|
||||
blockOffsetTop += ETScriptStickyStore.getElementProp('wpAdminBar', 'height', 0);
|
||||
}
|
||||
|
||||
states.lbpIframeOffset.top = blockOffsetTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set vertical scrollbar width.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} windowLocation
|
||||
* @param {number} width
|
||||
*/
|
||||
setVerticalScrollBarWidth = (windowLocation = 'app', width) => {
|
||||
if (width === states.verticalScrollBar[windowLocation]) {
|
||||
return this;
|
||||
}
|
||||
|
||||
states.verticalScrollBar[windowLocation] = width;
|
||||
|
||||
this.emit(VERTICAL_SCROLL_BAR_CHANGE);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current window width.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get width() {
|
||||
return states.width[this.scrollLocation];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current window height.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get height() {
|
||||
return states.height[this.scrollLocation];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current window scroll location.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {string} App|top.
|
||||
*/
|
||||
get scrollLocation() {
|
||||
return states.scrollLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current window scroll top / distance to document.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get scrollTop() {
|
||||
const multiplier = this.isBuilderZoomed ? 2 : 1;
|
||||
|
||||
let appFrameOffset = 0;
|
||||
|
||||
// Add app iframe offset on scrollTop calculation in BFB
|
||||
if (isBFB) {
|
||||
appFrameOffset += states.bfbIframeOffset.top;
|
||||
}
|
||||
|
||||
// Add Layout Block preview iframe on scrollTop calculation
|
||||
if (isLBP) {
|
||||
appFrameOffset += states.lbpIframeOffset.top;
|
||||
}
|
||||
|
||||
return (states.scrollTop[this.scrollLocation] - appFrameOffset) * multiplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current app window breakpoint (by device).
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get breakpoint() {
|
||||
return states.breakpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get builder zoomed status.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {bool}
|
||||
*/
|
||||
get isBuilderZoomed() {
|
||||
return states.isBuilderZoomed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current window vertical scrollbar width.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get verticalScrollBar() {
|
||||
return states.verticalScrollBar[this.scrollLocation];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get builder scroll location of builder context + preview mode.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} previewMode Desktop|tablet|phone|zoom|wireframe.
|
||||
*
|
||||
* @returns {string} App|top.
|
||||
*/
|
||||
getBuilderScrollLocation = previewMode => get(builderScrollLocations, previewMode, 'app')
|
||||
|
||||
/**
|
||||
* Add width change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
addWidthChangeListener = callback => {
|
||||
maybeIncreaseEmitterMaxListeners(this, WIDTH_CHANGE);
|
||||
this.on(WIDTH_CHANGE, callback);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove width change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
removeWidthChangeListener = callback => {
|
||||
this.removeListener(WIDTH_CHANGE, callback);
|
||||
maybeDecreaseEmitterMaxListeners(this, WIDTH_CHANGE);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add height change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
addHeightChangeListener = callback => {
|
||||
maybeIncreaseEmitterMaxListeners(this, HEIGHT_CHANGE);
|
||||
this.on(HEIGHT_CHANGE, callback);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove height change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @returns {Window}
|
||||
*/
|
||||
removeHeightChangeListener = callback => {
|
||||
this.removeListener(HEIGHT_CHANGE, callback);
|
||||
maybeDecreaseEmitterMaxListeners(this, HEIGHT_CHANGE);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add scroll location change event listener.
|
||||
*
|
||||
* @param callback
|
||||
* @since 4.6.0
|
||||
* @returns {ETScriptWindowStore}
|
||||
*/
|
||||
addScrollLocationChangeListener = callback => {
|
||||
maybeIncreaseEmitterMaxListeners(this, SCROLL_LOCATION_CHANGE);
|
||||
this.on(SCROLL_LOCATION_CHANGE, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove scroll location change event listener.
|
||||
*
|
||||
* @param callback
|
||||
* @since 4.6.0
|
||||
* @returns {ETScriptWindowStore}
|
||||
*/
|
||||
removeScrollLocationChangeListener = callback => {
|
||||
this.removeListener(SCROLL_LOCATION_CHANGE, callback);
|
||||
maybeDecreaseEmitterMaxListeners(this, SCROLL_LOCATION_CHANGE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add scroll top change event listener.
|
||||
*
|
||||
* @param callback
|
||||
* @since 4.6.0
|
||||
* @returns {ETScriptWindowStore}
|
||||
*/
|
||||
addScrollTopChangeListener = callback => {
|
||||
maybeIncreaseEmitterMaxListeners(this, SCROLL_TOP_CHANGE);
|
||||
this.on(SCROLL_TOP_CHANGE, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove scroll top change event listener.
|
||||
*
|
||||
* @param callback
|
||||
* @since 4.6.0
|
||||
* @returns {ETScriptWindowStore}
|
||||
*/
|
||||
removeScrollTopChangeListener = callback => {
|
||||
this.removeListener(SCROLL_TOP_CHANGE, callback);
|
||||
maybeDecreaseEmitterMaxListeners(this, SCROLL_TOP_CHANGE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set breakpoint (by device) based on window width.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @todo Update breakpoint setting mechanic so this won't need to define another screen size definition
|
||||
* and able to reuse (et_screen_size()).
|
||||
*
|
||||
* @param {number} windowWidth
|
||||
*
|
||||
* @returns {ETScriptWindowStore}
|
||||
*/
|
||||
setBreakpoint = windowWidth => {
|
||||
let newBreakpoint = '';
|
||||
|
||||
forEach(deviceMinimumBreakpoints, (minWidth, device) => {
|
||||
if (windowWidth > minWidth) {
|
||||
newBreakpoint = device;
|
||||
|
||||
// equals to "break"
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// No need to update breakpoint property if it is unchanged
|
||||
if (this.breakpoint === newBreakpoint) {
|
||||
return;
|
||||
}
|
||||
|
||||
states.breakpoint = newBreakpoint;
|
||||
|
||||
this.emit(BREAKPOINT_CHANGE);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add breakpoint change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*/
|
||||
addBreakpointChangeListener = callback => {
|
||||
maybeIncreaseEmitterMaxListeners(this, BREAKPOINT_CHANGE);
|
||||
this.on(BREAKPOINT_CHANGE, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove breakpoint change event listener.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {Function} callback
|
||||
*/
|
||||
removeBreakpointChangeListener = callback => {
|
||||
this.removeListener(BREAKPOINT_CHANGE, callback);
|
||||
maybeDecreaseEmitterMaxListeners(this, BREAKPOINT_CHANGE);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
// initiate window store instance
|
||||
const windowStoreInstance = new ETScriptWindowStore();
|
||||
|
||||
|
||||
/**
|
||||
* Listen for (app/top) window events, and update store's value
|
||||
* store is listener free; it only hold / set / get values.
|
||||
*/
|
||||
forEach(windowLocations, windowLocation => {
|
||||
const isTop = 'top' === windowLocation;
|
||||
const isApp = 'app' === windowLocation;
|
||||
const currentWindow = isApp ? window : top_window;
|
||||
const $currentWindow = currentWindow.jQuery(currentWindow);
|
||||
|
||||
// Scroll in Theme Builder & Layout Block Builder happens on element; adjustment needed
|
||||
// const scrollWindow = isTop && (isTB || isLBB) ? currentWindow.document.getElementById('et-fb-app') : currentWindow;
|
||||
const scrollWindow = () => {
|
||||
// Theme Builder & Layout Block Builder
|
||||
if (isTop && (isTB || isLBB)) {
|
||||
return currentWindow.document.getElementById('et-fb-app');
|
||||
}
|
||||
|
||||
// Layout Block Preview / Gutenberg
|
||||
if (isTop && isLBP) {
|
||||
return currentWindow.document.getElementsByClassName(getContentAreaSelector(currentWindow, false))[0];
|
||||
}
|
||||
|
||||
return currentWindow;
|
||||
};
|
||||
|
||||
// listen to current (app/top) window resize event
|
||||
currentWindow.addEventListener('resize', () => {
|
||||
const width = currentWindow.jQuery(currentWindow).innerWidth();
|
||||
const height = currentWindow.jQuery(currentWindow).innerHeight();
|
||||
|
||||
windowStoreInstance.setWidth(windowLocation, width).setHeight(windowLocation, height);
|
||||
windowStoreInstance.setVerticalScrollBarWidth(windowLocation, (currentWindow.outerWidth - width));
|
||||
|
||||
if ((windowStoreInstance.width > 782 && height <= 782) || (windowStoreInstance.width <= 782 && height > 782)) {
|
||||
// Wait until admin bar's viewport style kicks in
|
||||
setTimeout(() => {
|
||||
ETScriptStickyStore.setElementHeight('wpAdminBar');
|
||||
|
||||
windowStoreInstance.emit(SCROLL_TOP_CHANGE);
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
|
||||
// listen to current (app/top) window scroll event
|
||||
scrollWindow().addEventListener('scroll', () => {
|
||||
const scrollTop = isTop && (isTB || isLBB || isLBP) ? scrollWindow().scrollTop : scrollWindow().pageYOffset;
|
||||
|
||||
windowStoreInstance.setScrollTop(windowLocation, scrollTop);
|
||||
});
|
||||
|
||||
// Top window listener only
|
||||
if (isTop) {
|
||||
// Listen to builder's preview mode change that is passed via top window event
|
||||
$currentWindow.on('et_fb_preview_mode_changed', (event, screenMode, builderMode) => {
|
||||
const scrollLocation = windowStoreInstance.getBuilderScrollLocation(builderMode);
|
||||
|
||||
windowStoreInstance.setBuilderZoomedStatus(builderMode);
|
||||
windowStoreInstance.setScrollLocation(scrollLocation);
|
||||
});
|
||||
|
||||
// Update iframe offset if any metabox is moved
|
||||
if (isBFB) {
|
||||
currentWindow.addEventListener('ETBFBMetaboxSortStopped', () => {
|
||||
windowStoreInstance.setBfbIframeOffset();
|
||||
});
|
||||
}
|
||||
|
||||
// Gutenberg moves the scroll back to window if window's width is less than 600px
|
||||
if (isLBP) {
|
||||
currentWindow.addEventListener('scroll', () => {
|
||||
if (windowStoreInstance.width > 600) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollTop = currentWindow.pageYOffset;
|
||||
|
||||
windowStoreInstance.setScrollTop(windowLocation, scrollTop);
|
||||
});
|
||||
}
|
||||
|
||||
// When scroll is located on top window, there is a chance that the top window actually scrolls
|
||||
// before the builder is loaded which means initial scroll top value actually has changed
|
||||
// to avoid issue caused by it, when app window that carries this script is loaded, trigger
|
||||
// scroll event on the top window's scrolling element
|
||||
scrollWindow().dispatchEvent(new CustomEvent('scroll'));
|
||||
}
|
||||
|
||||
// App window listener only
|
||||
if (isApp) {
|
||||
// Update known element props when breakpoint changes. Breakpoint change is basically less
|
||||
// aggressive resize event, happened between known window's width
|
||||
if (isFE || isVB) {
|
||||
windowStoreInstance.addBreakpointChangeListener(() => {
|
||||
ETScriptStickyStore.setElementsProps();
|
||||
});
|
||||
}
|
||||
|
||||
// Update iframe offset if layout block is moved
|
||||
if (isLBP) {
|
||||
currentWindow.addEventListener('ETBlockGbBlockOrderChange', () => {
|
||||
// Need to wait at least 300ms until GB animation is done
|
||||
setTimeout(() => {
|
||||
windowStoreInstance.setLayoutBlockPreviewIframeOffset();
|
||||
|
||||
windowStoreInstance.emit(SCROLL_TOP_CHANGE);
|
||||
}, 300);
|
||||
});
|
||||
|
||||
// Update iframe offset if notice size is changed
|
||||
currentWindow.addEventListener('ETGBNoticeSizeChange', () => {
|
||||
if (ETScriptStickyStore.getElementProp('gbComponentsNoticeList', 'exist', false)) {
|
||||
ETScriptStickyStore.setElementHeight('gbComponentsNoticeList');
|
||||
|
||||
windowStoreInstance.emit(SCROLL_TOP_CHANGE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Register store instance as component to be exposed via global object
|
||||
registerFrontendComponent('stores', 'window', windowStoreInstance);
|
||||
|
||||
// Export store instance
|
||||
// IMPORTANT: For uniformity, import this as ETScriptWindowStore
|
||||
export default windowStoreInstance;
|
||||
@@ -0,0 +1,345 @@
|
||||
// Sticky Elements specific utils, used accross files
|
||||
|
||||
// External dependencies
|
||||
import filter from 'lodash/filter';
|
||||
import forEach from 'lodash/forEach';
|
||||
import get from 'lodash/get';
|
||||
import head from 'lodash/head';
|
||||
import includes from 'lodash/includes';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import isString from 'lodash/isString';
|
||||
import $ from 'jquery';
|
||||
|
||||
// Internal dependencies
|
||||
import {
|
||||
getOffsets,
|
||||
} from './utils';
|
||||
|
||||
/**
|
||||
* Get top / bottom limit attributes.
|
||||
*
|
||||
* @since 4.6.0
|
||||
* @param {object} $selector
|
||||
* @param limit
|
||||
* @param {string}
|
||||
* @returns {object}
|
||||
* @returns {string} Object.limit.
|
||||
* @returns {number} Object.height.
|
||||
* @returns {number} Object.width.
|
||||
* @return {object} object.offsets
|
||||
* @return {number} object.offsets.top
|
||||
* @return {number} object.offsets.right
|
||||
* @return {number} object.offsets.bottom
|
||||
* @return {number} object.offsets.left
|
||||
*/
|
||||
export const getLimit = ($selector, limit) => {
|
||||
// @todo update valid limits based on selector
|
||||
const validLimits = ['body', 'section', 'row', 'column'];
|
||||
|
||||
if (! includes(validLimits, limit)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Limit selector
|
||||
const $limitSelector = getLimitSelector($selector, limit);
|
||||
|
||||
if (! $limitSelector) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const height = $limitSelector.outerHeight();
|
||||
const width = $limitSelector.outerWidth();
|
||||
|
||||
return {
|
||||
limit,
|
||||
height,
|
||||
width,
|
||||
offsets: getOffsets($limitSelector, width, height),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get top / bottom limit selector based on given name.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {object} $selector
|
||||
* @param {string} limit
|
||||
*
|
||||
* @returns {bool|object}
|
||||
*/
|
||||
export const getLimitSelector = ($selector, limit) => {
|
||||
let parentSelector = false;
|
||||
|
||||
switch (limit) {
|
||||
case 'body':
|
||||
parentSelector = '.et_builder_inner_content';
|
||||
break;
|
||||
case 'section':
|
||||
parentSelector = '.et_pb_section';
|
||||
break;
|
||||
case 'row':
|
||||
parentSelector = '.et_pb_row';
|
||||
break;
|
||||
case 'column':
|
||||
parentSelector = '.et_pb_column';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return parentSelector ? $selector.closest(parentSelector) : false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter invalid sticky modules
|
||||
* 1. Sticky module inside another sticky module.
|
||||
*
|
||||
* @param {object} modules
|
||||
* @param {object} currentModules
|
||||
*
|
||||
* @since 4.6.0
|
||||
*/
|
||||
export const filterInvalidModules = (modules, currentModules = {}) => {
|
||||
const filteredModules = {};
|
||||
|
||||
forEach(modules, (module, key) => {
|
||||
// If current sticky module is inside another sticky module, ignore current module
|
||||
if ($(module.selector).parents('.et_pb_sticky_module').length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Repopulate the module list
|
||||
if (! isEmpty(currentModules) && currentModules[key]) {
|
||||
// Keep props that isn't available on incoming modules intact
|
||||
filteredModules[key] = {
|
||||
...currentModules[key],
|
||||
...module,
|
||||
};
|
||||
} else {
|
||||
filteredModules[key] = module;
|
||||
}
|
||||
});
|
||||
|
||||
return filteredModules;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get sticky style of given module by cloning, adding sticky state classname, appending DOM,
|
||||
* retrieving value, then immediately the cloned DOM. This is needed for property that is most
|
||||
* likely to be affected by transition if the sticky value is retrieved on the fly, thus it needs
|
||||
* to be retrieved ahead its time by this approach.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {object} $module
|
||||
* @param {object} $placeholder
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getStickyStyles = (id, $module, $placeholder) => {
|
||||
// Sticky state classname to be added; these will make cloned module to have fixed position and
|
||||
// make sticky style take effect
|
||||
const stickyStyleClassname = 'et_pb_sticky et_pb_sticky_style_dom';
|
||||
|
||||
// Cloned the module add sticky state classname; set the opacity to 0 and remove the transition
|
||||
// so the dimension can be immediately retrieved
|
||||
const $stickyStyleDom = $module.clone().addClass(stickyStyleClassname).attr({
|
||||
'data-sticky-style-dom-id': id,
|
||||
|
||||
// Remove inline styles so on-page styles works. Especially needed if module is in sticky state
|
||||
style: '',
|
||||
}).css({
|
||||
opacity: 0,
|
||||
transition: 'none',
|
||||
animation: 'none',
|
||||
});
|
||||
|
||||
// Cloned module might contain image. However the image might take more than a milisecond to be
|
||||
// loaded on the cloned module after the module is appended to the layout EVEN IF the image on
|
||||
// the $module has been loaded. This might load to inaccurate sticky style calculation. To avoid
|
||||
// it, recreate the image by getting actual width and height then recreate the image using SVG
|
||||
$stickyStyleDom.find('img').each(function(index) {
|
||||
const $img = $(this);
|
||||
const $measuredImg = $module.find('img').eq(index);
|
||||
const measuredWidth = get($measuredImg, [0, 'naturalWidth'], $module.find('img').eq(index).outerWidth());
|
||||
const measuredHeight = get($measuredImg, [0, 'naturalHeight'], $module.find('img').eq(index).outerHeight());
|
||||
|
||||
$img.attr({
|
||||
// Remove scrse to force DOM to use src
|
||||
scrset: '',
|
||||
|
||||
// Recreate svg to use image's actual width so the image reacts appropriately when sticky
|
||||
// style modifies image dimension (eg image has 100% and padding in sticky style is larger;
|
||||
// this will resulting in image being smaller because the wrapper dimension is smaller)
|
||||
src: `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="${measuredWidth}" height="${measuredHeight}"><rect width="${measuredWidth}" height="${measuredHeight}" /></svg>`,
|
||||
});
|
||||
});
|
||||
|
||||
// Append the cloned DOM
|
||||
$module.after($stickyStyleDom);
|
||||
|
||||
// Get inline margin style value that is substraction of sticky style - style due to position
|
||||
// relative to fixed change
|
||||
const getMarginStyle = corner => {
|
||||
const marginPropName = `margin${corner}`;
|
||||
const $normalModule = $module.hasClass('et_pb_sticky') ? $placeholder : $module;
|
||||
|
||||
return parseFloat($stickyStyleDom.css(marginPropName)) - parseFloat($normalModule.css(marginPropName));
|
||||
};
|
||||
|
||||
// Measure sticky style DOM properties
|
||||
const styles = {
|
||||
height: $stickyStyleDom.outerHeight(),
|
||||
width: $stickyStyleDom.outerWidth(),
|
||||
marginRight: getMarginStyle('Right'),
|
||||
marginLeft: getMarginStyle('Left'),
|
||||
padding: $stickyStyleDom.css('padding'),
|
||||
};
|
||||
|
||||
// Immediately remove the cloned DOM
|
||||
$(`.et_pb_sticky_style_dom[data-sticky-style-dom-id="${id}"]`).remove();
|
||||
|
||||
return styles;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove given property's transition from transition property's value. To make some properties
|
||||
* (eg. Width, top, left) transition smoothly when entering / leaving sticky state, its property
|
||||
* and transition need to be removed then re-added 50ms later. This is mostly happened because the
|
||||
* module positioning changed from relative to fixed when entering/leaving sticky state.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} transitionValue
|
||||
* @param {Array} trimmedProperties
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
export const trimTransitionValue = (transitionValue, trimmedProperties) => {
|
||||
// Make sure that transitionValue is string. Otherwise split will throw error
|
||||
if (! isString(transitionValue)) {
|
||||
transitionValue = '';
|
||||
}
|
||||
|
||||
const transitions = transitionValue.split(', ');
|
||||
const trimmedValue = filter(transitions, transition => ! includes(trimmedProperties, head(transition.split(' '))));
|
||||
|
||||
return isEmpty(trimmedValue) ? 'none' : trimmedValue.join(', ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate automatic offset that should be given based on sum of heights of all sticky modules
|
||||
* that are currently in sticky state when window reaches $target's offset.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {object} $target
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
export const getClosestStickyModuleOffsetTop = $target => {
|
||||
const offset = $target.offset();
|
||||
offset.right = offset.left + $target.outerWidth();
|
||||
|
||||
let closestStickyElement = null;
|
||||
let closestStickyOffsetTop = 0;
|
||||
|
||||
// Get all sticky module data from store. NOTE: this util might be used on various output build
|
||||
// so it needs to get sticky store value via global object instead of importing it
|
||||
const stickyModules = get(window.ET_FE, 'stores.sticky.modules', {});
|
||||
|
||||
// Loop sticky module data to get the closest sticky module to given y offset. Sticky module
|
||||
// already has map of valid modules it needs to consider as automatic offset due to
|
||||
// adjacent-column situation.
|
||||
// @see https://github.com/elegantthemes/Divi/issues/19432
|
||||
forEach(stickyModules, stickyModule => {
|
||||
// Ignore sticky module if it is stuck to bottom
|
||||
if (! includes(['top_bottom', 'top'], stickyModule.position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore if $target is sticky module (that sticks to top; stuck to bottom check above has
|
||||
// made sure of it) - otherwise the auto-generate offset will subtract the element's offset
|
||||
// and causing the scroll never reaches $target location.
|
||||
// @see https://github.com/elegantthemes/Divi/issues/23240
|
||||
if ($target.is(get(stickyModule, 'selector'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore if sticky module's right edge doesn't collide with target's left edge
|
||||
if (get(stickyModule, 'offsets.right', 0) < offset.left) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore if sticky module's left edge doesn't collide with target's right edge
|
||||
if (get(stickyModule, 'offsets.left', 0) > offset.right) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore sticky module if it is located below given y offset
|
||||
if (get(stickyModule, 'offsets.top', 0) > offset.top) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore sticky module if its bottom limit is higher than given y offset
|
||||
const bottomLimitBottom = get(stickyModule, 'bottomLimitSettings.offsets.bottom');
|
||||
|
||||
if (bottomLimitBottom && bottomLimitBottom < offset.top) {
|
||||
return;
|
||||
}
|
||||
|
||||
closestStickyElement = stickyModule;
|
||||
});
|
||||
|
||||
// Once closest sticky module to given y offset has been found, loop its topOffsetModules, get
|
||||
// each module's heightSticky and return the sum of their heights
|
||||
if (get(closestStickyElement, 'topOffsetModules', false)) {
|
||||
forEach(get(closestStickyElement, 'topOffsetModules', []), stickyId => {
|
||||
// Get sticky module's height on sticky state; fallback to height just to be safe
|
||||
const stickyModuleHeight = get(stickyModules, [stickyId, 'heightSticky'], get(stickyModules, [stickyId, 'height'], 0));
|
||||
|
||||
// Sum up top offset module's height
|
||||
closestStickyOffsetTop += stickyModuleHeight;
|
||||
});
|
||||
|
||||
// Get closest-to-y-offset's sticky module's height on sticky state;
|
||||
const closestStickyElementHeight = get(stickyModules, [closestStickyElement.id, 'heightSticky'], get(stickyModules, [closestStickyElement.id, 'height'], 0));
|
||||
|
||||
// Sum up top offset module's height
|
||||
closestStickyOffsetTop += closestStickyElementHeight;
|
||||
}
|
||||
|
||||
return closestStickyOffsetTop;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if the target is in sticky state.
|
||||
*
|
||||
* @since 4.9.5
|
||||
*
|
||||
* @param {object} $target
|
||||
*
|
||||
* @returns {bool}
|
||||
*/
|
||||
export const isTargetStickyState = $target => {
|
||||
const stickyModules = get(window.ET_FE, 'stores.sticky.modules', {});
|
||||
|
||||
let isStickyState = false;
|
||||
|
||||
forEach(stickyModules, stickyModule => {
|
||||
const isTarget = $target.is(get(stickyModule, 'selector'));
|
||||
const {isSticky, isPaused} = stickyModule;
|
||||
|
||||
// If the target is in sticky state and not paused, set isStickyState to true and exit iteration.
|
||||
// Elements can have a sticky limit (ex: section) in which case they can be sticky but paused.
|
||||
if (isTarget && isSticky && !isPaused) {
|
||||
isStickyState = true;
|
||||
|
||||
return false; // Exit iteration.
|
||||
}
|
||||
});
|
||||
|
||||
return isStickyState;
|
||||
};
|
||||
@@ -0,0 +1,292 @@
|
||||
/**
|
||||
* IMPORTANT: Keep external dependencies as low as possible since this utils might be
|
||||
* imported by various frontend scripts; need to keep frontend script size low.
|
||||
*/
|
||||
|
||||
// External dependencies
|
||||
import includes from 'lodash/includes';
|
||||
import get from 'lodash/get';
|
||||
import $ from 'jquery';
|
||||
|
||||
// Internal dependencies
|
||||
import { top_window } from '@core/admin/js/frame-helpers';
|
||||
|
||||
/**
|
||||
* Check current page's builder Type.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} builderType Fe|vb|bfb|tb|lbb|lbp.
|
||||
*
|
||||
* @returns {bool}
|
||||
*/
|
||||
export const isBuilderType = builderType => builderType === window.et_builder_utils_params.builderType;
|
||||
|
||||
/**
|
||||
* Return condition value.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} conditionName
|
||||
*
|
||||
* @returns {bool}
|
||||
*/
|
||||
export const is = conditionName => window.et_builder_utils_params.condition[conditionName];
|
||||
|
||||
/**
|
||||
* Is current page Frontend.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
export const isFE = isBuilderType('fe');
|
||||
|
||||
/**
|
||||
* Is current page Visual Builder.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
export const isVB = isBuilderType('vb');
|
||||
|
||||
/**
|
||||
* Is current page BFB / New Builder Experience.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
export const isBFB = isBuilderType('bfb');
|
||||
|
||||
/**
|
||||
* Is current page Theme Builder.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
export const isTB = isBuilderType('tb');
|
||||
|
||||
/**
|
||||
* Is current page Layout Block Builder.
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
export const isLBB = isBuilderType('lbb');
|
||||
|
||||
/**
|
||||
* Is current page uses Divi Theme.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
export const isDiviTheme = is('diviTheme');
|
||||
|
||||
/**
|
||||
* Is current page uses Extra Theme.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
export const isExtraTheme = is('extraTheme');
|
||||
|
||||
/**
|
||||
* Is current page Layout Block Preview.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
export const isLBP = isBuilderType('lbp');
|
||||
|
||||
/**
|
||||
* Check if current window is block editor window (gutenberg editing page).
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
export const isBlockEditor = 0 < $(top_window.document).find('.edit-post-layout__content').length;
|
||||
|
||||
/**
|
||||
* Check if current window is builder window (VB, BFB, TB, LBB).
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @type {bool}
|
||||
*/
|
||||
export const isBuilder = includes(['vb', 'bfb', 'tb', 'lbb'], window.et_builder_utils_params.builderType);
|
||||
|
||||
/**
|
||||
* Get offsets value of all sides.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {object} $selector JQuery selector instance.
|
||||
* @param {number} height
|
||||
* @param {number} width
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
export const getOffsets = ($selector, width = 0, height = 0) => {
|
||||
// Return previously saved offset if sticky tab is active; retrieving actual offset contain risk
|
||||
// of incorrect offsets if sticky horizontal / vertical offset of relative position is modified.
|
||||
const isStickyTabActive = isBuilder && $selector.hasClass('et_pb_sticky') && 'fixed' !== $selector.css('position');
|
||||
const cachedOffsets = $selector.data('et-offsets');
|
||||
const cachedDevice = $selector.data('et-offsets-device');
|
||||
const currentDevice = get(window.ET_FE, 'stores.window.breakpoint', '');
|
||||
|
||||
// Only return cachedOffsets if sticky tab is active and cachedOffsets is not undefined and
|
||||
// cachedDevice equal to currentDevice.
|
||||
if (isStickyTabActive && cachedOffsets !== undefined && cachedDevice === currentDevice) {
|
||||
return cachedOffsets;
|
||||
}
|
||||
|
||||
// Get top & left offsets
|
||||
const offsets = $selector.offset();
|
||||
|
||||
// If no offsets found, return empty object
|
||||
if ('undefined' === typeof offsets) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// FE sets the flag for sticky module which uses transform as classname on module wrapper while
|
||||
// VB, BFB, TB, and LB sets the flag on CSS output's <style> element because it can't modify
|
||||
// its parent. This compromises avoids the needs to extract transform rendering logic
|
||||
const hasTransform = isBuilder
|
||||
? $selector.children('.et-fb-custom-css-output[data-sticky-has-transform="on"]').length > 0
|
||||
: $selector.hasClass('et_pb_sticky--has-transform');
|
||||
|
||||
let top = 'undefined' === typeof offsets.top ? 0 : offsets.top;
|
||||
let left = 'undefined' === typeof offsets.left ? 0 : offsets.left;
|
||||
|
||||
// If module is sticky module that uses transform, its offset calculation needs to be adjusted
|
||||
// because transform tends to modify the positioning of the module
|
||||
if (hasTransform) {
|
||||
// Calculate offset (relative to selector's parent) AFTER it is affected by transform
|
||||
// NOTE: Can't use jQuery's position() because it considers margin-left `auto` which causes issue
|
||||
// on row thus this manually calculate the difference between element and its parent's offset
|
||||
// @see https://github.com/jquery/jquery/blob/1.12-stable/src/offset.js#L149-L155
|
||||
const parentOffsets = $selector.parent().offset();
|
||||
|
||||
const transformedPosition = {
|
||||
top: offsets.top - parentOffsets.top,
|
||||
left: offsets.left - parentOffsets.left,
|
||||
};
|
||||
|
||||
// Calculate offset (relative to selector's parent) BEFORE it is affected by transform
|
||||
const preTransformedPosition = {
|
||||
top: $selector[0].offsetTop,
|
||||
left: $selector[0].offsetLeft,
|
||||
};
|
||||
|
||||
// Update offset's top value
|
||||
top += (preTransformedPosition.top - transformedPosition.top);
|
||||
offsets.top = top;
|
||||
|
||||
// Update offset's left value
|
||||
left += (preTransformedPosition.left - transformedPosition.left);
|
||||
offsets.left = left;
|
||||
}
|
||||
|
||||
// Manually calculate right & bottom offsets
|
||||
offsets.right = left + width;
|
||||
offsets.bottom = top + height;
|
||||
|
||||
// Save copy of the offset on element's .data() in case of scenario where retrieving actual
|
||||
// offset value will lead to incorrect offset value (eg. sticky tab active with position offset)
|
||||
$selector.data('et-offsets', offsets);
|
||||
|
||||
// Add current device to cache
|
||||
if ('' !== currentDevice) {
|
||||
$selector.data('et-offsets-device', offsets);
|
||||
}
|
||||
|
||||
return offsets;
|
||||
};
|
||||
|
||||
/**
|
||||
* Increase EventEmitter's max listeners if lister count is about to surpass the max listeners limit
|
||||
* IMPORTANT: Need to be placed BEFORE `.on()`.
|
||||
*
|
||||
* @since 4.6.0
|
||||
* @param {EventEmitter} emitter
|
||||
* @param eventName
|
||||
* @param {string} EventName
|
||||
*/
|
||||
export const maybeIncreaseEmitterMaxListeners = (emitter, eventName) => {
|
||||
const currentCount = emitter.listenerCount(eventName);
|
||||
const maxListeners = emitter.getMaxListeners();
|
||||
|
||||
if (currentCount === maxListeners) {
|
||||
emitter.setMaxListeners(maxListeners + 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrease EventEmitter's max listeners if listener count is less than max listener limit and above
|
||||
* 10 (default max listener limit). If listener count is less than 10, max listener limit will
|
||||
* remain at 10
|
||||
* IMPORTANT: Need to be placed AFTER `.removeListener()`.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {EventEmitter} emitter
|
||||
* @param {string} eventName
|
||||
*/
|
||||
export const maybeDecreaseEmitterMaxListeners = (emitter, eventName) => {
|
||||
const currentCount = emitter.listenerCount(eventName);
|
||||
const maxListeners = emitter.getMaxListeners();
|
||||
|
||||
if (maxListeners > 10) {
|
||||
emitter.setMaxListeners(currentCount);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose frontend (FE) component via global object so it can be accessed and reused externally
|
||||
* Note: window.ET_Builder is for builder app's component; window.ET_FE is for frontend component.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} type
|
||||
* @param {string} name
|
||||
* @param {mixed} component
|
||||
*/
|
||||
export const registerFrontendComponent = (type, name, component) => {
|
||||
// Make sure that ET_FE is available
|
||||
if ('undefined' === typeof window.ET_FE) {
|
||||
window.ET_FE = {};
|
||||
}
|
||||
|
||||
if ('object' !== typeof window.ET_FE[type]) {
|
||||
window.ET_FE[type] = {};
|
||||
}
|
||||
|
||||
window.ET_FE[type][name] = component;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set inline style with !important tag. JQuery's .css() can't set value with `!important` tag so
|
||||
* here it is.
|
||||
*
|
||||
* @since 4.6.2
|
||||
*
|
||||
* @param {object} $element
|
||||
* @param {string} cssProp
|
||||
* @param {string} value
|
||||
*/
|
||||
export const setImportantInlineValue = ($element, cssProp, value) => {
|
||||
// Remove prop from current inline style in case the prop is already exist
|
||||
$element.css(cssProp, '');
|
||||
|
||||
// Get current inline style
|
||||
const inlineStyle = $element.attr('style');
|
||||
|
||||
// Re-insert inline style + property with important tag
|
||||
$element.attr('style', `${inlineStyle} ${cssProp}: ${value} !important;`);
|
||||
};
|
||||
Reference in New Issue
Block a user