rebase from live enviornment
This commit is contained in:
File diff suppressed because one or more lines are too long
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
+7
File diff suppressed because one or more lines are too long
+7
File diff suppressed because one or more lines are too long
+2155
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
+12
File diff suppressed because one or more lines are too long
+23
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
});
|
||||
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,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);
|
||||
@@ -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();
|
||||
} );
|
||||
+8
File diff suppressed because one or more lines are too long
@@ -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();
|
||||
});
|
||||
+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
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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