Commit realizado el 12:13:52 08-04-2024
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 @@
|
||||
!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=152)}({0:function(e,t){e.exports=jQuery},152:function(e,t,o){"use strict";(function(e){var t,n=(t=o(23))&&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(0))},23: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(0))}});
|
@@ -0,0 +1 @@
|
||||
!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=153)}({0:function(e,t){e.exports=jQuery},153: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(0))}});
|
11
wp-content/themes/Divi/includes/builder/scripts/ext/chart.min.js
vendored
Normal file
11
wp-content/themes/Divi/includes/builder/scripts/ext/chart.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
wp-content/themes/Divi/includes/builder/scripts/ext/jquery-ui-1.10.4.custom.min.js
vendored
Normal file
7
wp-content/themes/Divi/includes/builder/scripts/ext/jquery-ui-1.10.4.custom.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
wp-content/themes/Divi/includes/builder/scripts/ext/jquery-ui-1.11.4.custom.min.js
vendored
Normal file
7
wp-content/themes/Divi/includes/builder/scripts/ext/jquery-ui-1.11.4.custom.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2155
wp-content/themes/Divi/includes/builder/scripts/ext/jquery-ui-timepicker-addon.js
vendored
Normal file
2155
wp-content/themes/Divi/includes/builder/scripts/ext/jquery-ui-timepicker-addon.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
23
wp-content/themes/Divi/includes/builder/scripts/ext/jquery.tablesorter.min.js
vendored
Normal file
23
wp-content/themes/Divi/includes/builder/scripts/ext/jquery.tablesorter.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1299
wp-content/themes/Divi/includes/builder/scripts/ext/jquery.validate.js
vendored
Normal file
1299
wp-content/themes/Divi/includes/builder/scripts/ext/jquery.validate.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
23
wp-content/themes/Divi/includes/builder/scripts/ext/jquery.visible.min.js
vendored
Normal file
23
wp-content/themes/Divi/includes/builder/scripts/ext/jquery.visible.min.js
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
Copyright (c) 2012 Digital Fusion, http://teamdf.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
!function(t){var i=t(window);t.fn.visible=function(t,e,o){if(!(this.length<1)){var r=this.length>1?this.eq(0):this,n=r.get(0),f=i.width(),h=i.height(),o=o?o:"both",l=e===!0?n.offsetWidth*n.offsetHeight:!0;if("function"==typeof n.getBoundingClientRect){var g=n.getBoundingClientRect(),u=g.top>=0&&g.top<h,s=g.bottom>0&&g.bottom<=h,c=g.left>=0&&g.left<f,a=g.right>0&&g.right<=f,v=t?u||s:u&&s,b=t?c||a:c&&a;if("both"===o)return l&&v&&b;if("vertical"===o)return l&&v;if("horizontal"===o)return l&&b}else{var d=i.scrollTop(),p=d+h,w=i.scrollLeft(),m=w+f,y=r.offset(),z=y.top,B=z+r.height(),C=y.left,R=C+r.width(),j=t===!0?B:z,q=t===!0?z:B,H=t===!0?R:C,L=t===!0?C:R;if("both"===o)return!!l&&p>=q&&j>=d&&m>=L&&H>=w;if("vertical"===o)return!!l&&p>=q&&j>=d;if("horizontal"===o)return!!l&&m>=L&&H>=w}}}}(jQuery);
|
12
wp-content/themes/Divi/includes/builder/scripts/ext/lz-string.min.js
vendored
Normal file
12
wp-content/themes/Divi/includes/builder/scripts/ext/lz-string.min.js
vendored
Normal file
@@ -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
|
||||
});
|
||||
}
|
||||
});
|
10
wp-content/themes/Divi/includes/builder/scripts/ext/waypoints.min.js
vendored
Normal file
10
wp-content/themes/Divi/includes/builder/scripts/ext/waypoints.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
108
wp-content/themes/Divi/includes/builder/scripts/ext/widgets.js
Normal file
108
wp-content/themes/Divi/includes/builder/scripts/ext/widgets.js
Normal file
@@ -0,0 +1,108 @@
|
||||
( function($) {
|
||||
|
||||
$(function() {
|
||||
// WP 5.8 above - Widget Block Editor.
|
||||
var is_widgets_block_editor = $('#widgets-editor').length > 0;
|
||||
var $et_pb_sidebars = $('div[id^=et_pb_widget_area_]');
|
||||
|
||||
// 1.a Appends custom widget area create form panel.
|
||||
var et_pb_sidebars_append_widget_create_area = function() {
|
||||
var widget_editor_area = is_widgets_block_editor ? '.block-editor-writing-flow > div' : '.widget-liquid-right';
|
||||
|
||||
$(widget_editor_area).append(et_pb_options.widget_info);
|
||||
|
||||
var $widget_create_area = $('#et_pb_widget_area_create');
|
||||
var $widget_name_input = $widget_create_area.find('#et_pb_new_widget_area_name');
|
||||
|
||||
$widget_create_area.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();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 1.b. Appends custom widget area remove button and handles remove action.
|
||||
var et_pb_sidebars_append_delete_button = function() {
|
||||
// 1.b.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>');
|
||||
|
||||
// 1.b.2. Handles 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. Observe to append custom widget area create form and remove buttons.
|
||||
if (is_widgets_block_editor) {
|
||||
var widget_block_editor_mutation = _.debounce(function(mutations, observer) {
|
||||
var is_widget_area_create_added = $('#et_pb_widget_area_create').length > 0;
|
||||
var is_widget_area_remove_added = $('.et_pb_widget_area_remove').length > 0;
|
||||
|
||||
if (! is_widget_area_create_added) {
|
||||
et_pb_sidebars_append_widget_create_area();
|
||||
}
|
||||
|
||||
if (! is_widget_area_remove_added) {
|
||||
$('div[data-widget-area-id^=et_pb_widget_area_]').each(et_pb_sidebars_append_delete_button);
|
||||
}
|
||||
|
||||
// Disconnect once we know the custom widget area create form and remove buttons
|
||||
// are added to the Widget Block Editor.
|
||||
if (is_widget_area_create_added && is_widget_area_remove_added) {
|
||||
observer.disconnect();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Watch for Widgets to load the Block Editor and Widget Area blocks. There is no
|
||||
// event to know when they will be loaded, hence we use mutation observer.
|
||||
var widget_block_editor_observer = new MutationObserver(widget_block_editor_mutation);
|
||||
|
||||
// WP 6.1 and below - Widget Editor mutating node.
|
||||
var widget_block_editor_node = document.querySelector('.block-editor-block-list__layout') || document.querySelector('#widgets-editor');
|
||||
|
||||
widget_block_editor_observer.observe(widget_block_editor_node, {childList: true});
|
||||
} else {
|
||||
et_pb_sidebars_append_widget_create_area();
|
||||
$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
wp-content/themes/Divi/includes/builder/scripts/ext/wp-color-picker-alpha-48.min.js
vendored
Normal file
8
wp-content/themes/Divi/includes/builder/scripts/ext/wp-color-picker-alpha-48.min.js
vendored
Normal file
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
wp-content/themes/Divi/includes/builder/scripts/ext/wp-color-picker-alpha.min.js
vendored
Normal file
8
wp-content/themes/Divi/includes/builder/scripts/ext/wp-color-picker-alpha.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
!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=154)}({0:function(e,t){e.exports=jQuery},154: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(0))}});
|
@@ -0,0 +1 @@
|
||||
!function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},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 r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},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=155)}({0:function(t,e){t.exports=jQuery},155:function(t,e,n){"use strict";(function(t){t((function(){var e=t(".wp-list-table");e.find(".row-actions .edit, .row-actions .view").css("display","none"),e.on("click",".row-title",(function(){return t(this).closest("td").find(".row-actions .inline a").trigger("click"),!1})),e.on("click","td.column-posts a",(function(){return!1}))}))}).call(this,n(0))}});
|
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
!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=157)}({0:function(t,e){t.exports=jQuery},157: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(0))}});
|
@@ -0,0 +1 @@
|
||||
!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=158)}({0:function(e,t){e.exports=jQuery},158: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(0))}});
|
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;
|
946
wp-content/themes/Divi/includes/builder/scripts/stores/sticky.js
Normal file
946
wp-content/themes/Divi/includes/builder/scripts/stores/sticky.js
Normal file
@@ -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;
|
755
wp-content/themes/Divi/includes/builder/scripts/stores/window.js
Normal file
755
wp-content/themes/Divi/includes/builder/scripts/stores/window.js
Normal file
@@ -0,0 +1,755 @@
|
||||
// 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 {
|
||||
getBuilderUtilsParams,
|
||||
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: getBuilderUtilsParams().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: [...getBuilderUtilsParams().scrollLocations],
|
||||
};
|
||||
|
||||
// Variables
|
||||
const builderScrollLocations = {
|
||||
...getBuilderUtilsParams().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;
|
365
wp-content/themes/Divi/includes/builder/scripts/utils/sticky.js
Normal file
365
wp-content/themes/Divi/includes/builder/scripts/utils/sticky.js
Normal file
@@ -0,0 +1,365 @@
|
||||
// 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));
|
||||
};
|
||||
|
||||
/**
|
||||
* Equalize Column Heights :: If the parent container is an equal column(Flexbox), temporary hide
|
||||
* the placeholder module and the original modules to restore(expand) the width of the $stickyStyleDom.
|
||||
* We insert two clones i.e data-sticky-style-dom-id and data-sticky-placeholder-id of the module at the
|
||||
* original location of the module which causes columns(flex items) to shrink to fit in the row
|
||||
* i.e .et_pb_equal_columns flex container.
|
||||
*/
|
||||
const isEqualColumns = $module.parent().hasClass('et_pb_equal_columns');
|
||||
|
||||
if(isEqualColumns) {
|
||||
$module.hide();
|
||||
$placeholder.hide();
|
||||
}
|
||||
|
||||
// Measure sticky style DOM properties
|
||||
const styles = {
|
||||
height: $stickyStyleDom.outerHeight(),
|
||||
width: $stickyStyleDom.outerWidth(),
|
||||
marginRight: getMarginStyle('Right'),
|
||||
marginLeft: getMarginStyle('Left'),
|
||||
padding: $stickyStyleDom.css('padding'),
|
||||
};
|
||||
|
||||
// display module and placeholder.
|
||||
if(isEqualColumns) {
|
||||
$module.show();
|
||||
$placeholder.show();
|
||||
}
|
||||
|
||||
// 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;
|
||||
};
|
306
wp-content/themes/Divi/includes/builder/scripts/utils/utils.js
Normal file
306
wp-content/themes/Divi/includes/builder/scripts/utils/utils.js
Normal file
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
export const getBuilderUtilsParams = () => {
|
||||
if (window.et_builder_utils_params) {
|
||||
return window.et_builder_utils_params;
|
||||
}
|
||||
|
||||
if (top_window.et_builder_utils_params) {
|
||||
return top_window.et_builder_utils_params;
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
export const getBuilderType = () => get(getBuilderUtilsParams(), 'builderType', '');
|
||||
|
||||
/**
|
||||
* 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 === getBuilderType();
|
||||
|
||||
/**
|
||||
* Return condition value.
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @param {string} conditionName
|
||||
*
|
||||
* @returns {bool}
|
||||
*/
|
||||
export const is = conditionName => get(getBuilderUtilsParams(), `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'], getBuilderType());
|
||||
|
||||
/**
|
||||
* 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