- WordPress core y plugins - Tema Twenty Twenty-Four configurado - Plugin allow-unfiltered-html.php simplificado - .gitignore configurado para excluir wp-config.php y uploads 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
405 lines
10 KiB
JavaScript
Executable File
405 lines
10 KiB
JavaScript
Executable File
window.TVE_Dash = window.TVE_Dash || {};
|
|
TVE_Dash.views = TVE_Dash.views || {};
|
|
( function ( $ ) {
|
|
require( './materialize/leanModal' );
|
|
window.Vel = require( './materialize/velocity.min' );
|
|
require( './materialize/global' );
|
|
require( './materialize/forms' );
|
|
require( './util/util' );
|
|
/**
|
|
* base class for modals
|
|
*
|
|
* backbone view wrapped over a modal div from materialize
|
|
*/
|
|
TVE_Dash.views.Modal = Backbone.View.extend( {
|
|
className: 'tvd-modal',
|
|
id: 'tvd-modal-base',
|
|
/**
|
|
* .tvd-modal-content div reference
|
|
*/
|
|
$_content: '',
|
|
/* this will be populated with data coming from params when instantiating the view / modal */
|
|
data: {},
|
|
|
|
/**
|
|
* Array of stored child views - to remove them when this view is destroyed
|
|
*/
|
|
$$childViews: [],
|
|
|
|
/**
|
|
* Get the main modal action button. Usually, this is the "Save" button.
|
|
* Overwrite this if custom behaviour is needed
|
|
*
|
|
* @return {JQuery}
|
|
*/
|
|
getModalActionButton() {
|
|
return this.$( '.tvd-modal-save' ).filter( ':visible' );
|
|
},
|
|
|
|
/**
|
|
* used to setup this.data field - it will set all constructor arguments into this.data so that they are available
|
|
*
|
|
* @param {Object} args
|
|
*/
|
|
initialize( args ) {
|
|
|
|
this.beforeInitialize( args );
|
|
|
|
this.data = args;
|
|
const self = this;
|
|
|
|
_.each( args, function ( v, k ) {
|
|
|
|
if ( typeof self[ k ] === 'undefined' ) {
|
|
self[ k ] = v;
|
|
}
|
|
} );
|
|
|
|
this.afterInitialize( args );
|
|
},
|
|
/**
|
|
* we should not override this, use afterRender() instead
|
|
* this does not open the modal, it just renders it and appends it to the body tag
|
|
*
|
|
* @return {TVE_Dash.views.Modal}
|
|
*/
|
|
render() {
|
|
this.$el.empty();
|
|
this.$el.appendTo( 'body' );
|
|
// make the whole data object available to the template
|
|
this.data = _.extend( {
|
|
model: this.model
|
|
}, this.data );
|
|
|
|
if ( typeof this.template === 'string' ) {
|
|
this.template = TVE_Dash.tpl( this.template );
|
|
}
|
|
|
|
this.$el.html( this.template( this.data ) );
|
|
|
|
if ( this.data[ 'max-width' ] ) {
|
|
/* max-width should always be sent in % */
|
|
this.$el.css( 'max-width', this.data[ 'max-width' ] );
|
|
} else {
|
|
this.$el.css( 'max-width', '' );
|
|
}
|
|
|
|
this.$el.css( 'max-height', this.data[ 'max-height' ] || '' );
|
|
|
|
if ( this.data.no_close ) {
|
|
this.$el.find( '.tvd-modal-close' ).remove();
|
|
} else if ( ! this.$el.children( '.tvd-modal-close' ).length ) {
|
|
this.$el.append( '<a href="javascript:void(0)" class="tvd-modal-action tvd-modal-close tvd-modal-close-x"><i class="tvd-icon-close2"></i></a>' );
|
|
}
|
|
|
|
if ( this.data.title ) {
|
|
let $title = this.$el.find( 'h3.tvd-modal-title' );
|
|
if ( ! $title.length ) {
|
|
$title = $( '<h3 class="tvd-modal-title"></h3>' );
|
|
this.$el.find( '.tvd-modal-content' ).prepend( $title );
|
|
}
|
|
$title.html( this.data.title );
|
|
}
|
|
|
|
if ( this.data.width ) {
|
|
this.$el.css( 'width', this.data.width );
|
|
}
|
|
if ( this.data.height ) {
|
|
this.$el.css( 'height', this.data.height + 'px' );
|
|
}
|
|
this.$_content = this.$el.find( '.tvd-modal-content' );
|
|
|
|
this.afterRender();
|
|
|
|
/**
|
|
* when the autocomplete opens, we need to scroll the .tvd-modal-content so that the results are visible
|
|
*/
|
|
this.$el.on( 'autocompleteopen', _.bind( this.autocomplete_scroll, this ) );
|
|
|
|
this.$el.on( 'tvdpickeropen', _.bind( this.pickadate_scroll, this ) );
|
|
|
|
return this;
|
|
},
|
|
/**
|
|
* called when a jquery autocomplete widget is opened inside a modal
|
|
* scrolls the modal frame to contain as much of the autocomplete element as possible
|
|
*
|
|
* @param event
|
|
* @param ui
|
|
*/
|
|
autocomplete_scroll( event ) {
|
|
const $input = $( event.target ),
|
|
$ul = $input.parent().find( 'ul.ui-autocomplete' ),
|
|
ui_height = $input.outerHeight() + $ul.outerHeight(),
|
|
ui_top = $input.offset().top,
|
|
c_top = this.$_content.offset().top,
|
|
delta_scroll = ui_top - c_top + this.$_content.scrollTop() - 25;
|
|
|
|
if ( ui_height + ui_top > this.$_content.outerHeight() + c_top ) {
|
|
this.$_content.animate( {
|
|
scrollTop: delta_scroll
|
|
} );
|
|
}
|
|
},
|
|
/**
|
|
* called when a datepicker (pickadate) is opened inside this modal
|
|
* it scroll the modal frame so that the date picker is fully visible
|
|
*
|
|
* @param {Object} event jQuery event instance
|
|
* @param {Object} P Picker instance
|
|
*/
|
|
pickadate_scroll( event, P ) {
|
|
const $input = $( event.target ),
|
|
$picker = P.$root,
|
|
picker_height = $picker.outerHeight( true ) + 40,
|
|
picker_top = $input.offset().top - picker_height / 2,
|
|
c_top = this.$_content.offset().top;
|
|
|
|
if ( picker_top + picker_height > this.$_content.outerHeight() + c_top ) {
|
|
this.$_content.animate( {
|
|
scrollTop: picker_height + picker_top - c_top - this.$_content.outerHeight() + this.$_content.scrollTop() - 25
|
|
} );
|
|
} else if ( picker_top < c_top ) {
|
|
this.$_content.animate( {
|
|
scrollTop: this.$_content.scrollTop() - ( c_top - picker_top )
|
|
} );
|
|
}
|
|
},
|
|
/**
|
|
* auto-focus the first input and bind the ENTER event
|
|
*
|
|
* @param {Object} [$root] optional, jquery wrapper over a DOM element
|
|
*/
|
|
input_focus( $root ) {
|
|
$root = $root || this.$el;
|
|
|
|
const $actionButton = this.getModalActionButton();
|
|
const $textInputs = $root.find( 'input:not(.tvd-no-focus,:checkbox,:radio,:hidden)' ).filter( ':visible' );
|
|
|
|
/**
|
|
* find the first focusable text input (or textarea) or action button
|
|
*/
|
|
const $autoFocus = $textInputs.add( this.$( 'textarea' ) ).add( $actionButton ).filter( ':visible' ).not( '.tvd-no-focus' ).first();
|
|
|
|
if ( $autoFocus.length ) {
|
|
requestAnimationFrame( () => $autoFocus.first().focus().select() );
|
|
}
|
|
|
|
/**
|
|
* Setup keyup listeners on the text inputs
|
|
*
|
|
* @type {*|number|bigint|T|T|undefined}
|
|
*/
|
|
$textInputs.not( '.tvd-skip-modal-save' ).off( 'keyup.tvd-save' ).on( 'keyup.tvd-save', function ( e ) {
|
|
if ( e.which === 13 ) {
|
|
/**
|
|
* `requestAnimationFrame` so that any other `change` events are processed first and model validation can be correctly performed
|
|
*/
|
|
requestAnimationFrame( () => {
|
|
if ( $actionButton.css( 'pointer-events' ) !== 'none' ) {
|
|
$actionButton.trigger( 'click' );
|
|
}
|
|
} );
|
|
|
|
return false;
|
|
}
|
|
return true;
|
|
} );
|
|
},
|
|
/**
|
|
* triggered immediately after the rendering is completed (after the template is added to DOM) but before showing the modal
|
|
* override this to populate extra stuff in the view
|
|
*/
|
|
afterRender() {
|
|
return this;
|
|
},
|
|
/**
|
|
* open the modal
|
|
* options for the modal can be sent when instantiating the view
|
|
*
|
|
* @return {TVE_Dash.Modal}
|
|
*/
|
|
open() {
|
|
const self = this;
|
|
|
|
const options = _.extend( {
|
|
in_duration: 200,
|
|
out_duration: 300,
|
|
dismissible: true,
|
|
beforeClose() {
|
|
self.beforeClose();
|
|
},
|
|
ready() {
|
|
self.onOpen.call( self );
|
|
/**
|
|
* allow also 'afterOpen' callback
|
|
*/
|
|
if ( typeof self.afterOpen === 'function' ) {
|
|
self.afterOpen.call( self );
|
|
}
|
|
if ( ! self.noMaterialize ) {
|
|
TVE_Dash.materialize( self.$el );
|
|
}
|
|
self.input_focus();
|
|
|
|
if ( typeof self.afterMaterialize === 'function' ) {
|
|
self.afterMaterialize.call( self );
|
|
}
|
|
},
|
|
complete() {
|
|
delete TVE_Dash.opened_modal_view;
|
|
self.$el.css( 'width', '' ).css( 'height', '' );
|
|
self.destroy().remove();
|
|
self.onClose.call( self );
|
|
/**
|
|
* allow also 'afterClose' callback
|
|
*/
|
|
if ( typeof self.afterClose === 'function' ) {
|
|
self.afterClose.call( self );
|
|
}
|
|
}
|
|
}, this.data );
|
|
|
|
this.modal_options = options;
|
|
|
|
this.$el.openModal( options );
|
|
|
|
TVE_Dash.opened_modal_view = this;
|
|
|
|
return this;
|
|
},
|
|
close() {
|
|
this.$el.closeModal( this.modal_options );
|
|
return this;
|
|
},
|
|
/**
|
|
* triggered after the modal has been opened
|
|
*/
|
|
onOpen() {
|
|
},
|
|
/**
|
|
* triggered after the modal has been closed and the html has been removed
|
|
*/
|
|
onClose() {
|
|
},
|
|
/**
|
|
* triggered before the modal has been closed and the html has been removed
|
|
*/
|
|
beforeClose() {
|
|
},
|
|
/**
|
|
* show a preloader over this modal
|
|
*/
|
|
showLoader() {
|
|
let _loader = this.$( '.tvd-modal-preloader-wrapper' );
|
|
if ( ! _loader.length ) {
|
|
_loader = $( TVE_Dash.tpl( 'modal-loader', {} ) );
|
|
this.$el.append( _loader );
|
|
}
|
|
|
|
_loader.find( '.tvd-modal-preloader' ).css( {
|
|
top: ( this.$el.outerHeight() / 2 ) + 'px'
|
|
} );
|
|
|
|
/* we do this to avoid calling the loader multiple times */
|
|
requestAnimationFrame( () => {
|
|
if ( ! this.isLoading ) {
|
|
_loader.fadeIn( 300 );
|
|
this.isLoading = true;
|
|
}
|
|
} );
|
|
|
|
this.$el.addClass( 'tvd-modal-disable' );
|
|
|
|
return this;
|
|
},
|
|
/**
|
|
* hide the preloader
|
|
*/
|
|
hideLoader() {
|
|
this.$el.removeClass( 'tvd-modal-disable' );
|
|
requestAnimationFrame( () => this.$( '.tvd-modal-preloader-wrapper' ).fadeOut( 300 ) );
|
|
this.isLoading = false;
|
|
|
|
return this;
|
|
},
|
|
/**
|
|
* set the loading state on a button
|
|
*
|
|
* @param $btn
|
|
* @return {*}
|
|
*/
|
|
btnLoading( $btn ) {
|
|
return jQuery( $btn ).addClass( 'tvd-disabled' ).prop( 'disabled', true );
|
|
},
|
|
beforeInitialize() {
|
|
return this;
|
|
},
|
|
afterInitialize() {
|
|
return this;
|
|
},
|
|
/**
|
|
* Add one or more child view reference. Can receive any number of arguments
|
|
*
|
|
* @param {Backbone.View|Backbone.View[]} views
|
|
*
|
|
* @return {Backbone.View} the caller view
|
|
*/
|
|
addChild( ...views ) {
|
|
if ( Array.isArray( views[ 0 ] ) ) {
|
|
views = views[ 0 ];
|
|
}
|
|
this.$$childViews = this.$$childViews.concat( views );
|
|
|
|
return this;
|
|
},
|
|
/**
|
|
* Completely destroy the view and un-delegate any events
|
|
*/
|
|
destroy() {
|
|
this.stopListening();
|
|
this.undelegateEvents();
|
|
this.$el.removeData().off();
|
|
|
|
this.$$childViews.filter( i => i ).forEach( view => {
|
|
if ( typeof view?.destroy === 'function' ) {
|
|
view.destroy();
|
|
}
|
|
} );
|
|
|
|
return this;
|
|
}
|
|
} );
|
|
|
|
$( document ).ready( function () {
|
|
if ( ! TVE_Dash.views.VideoModal ) {
|
|
TVE_Dash.views.VideoModal = TVE_Dash.views.Modal.extend( {
|
|
template: TVE_Dash.tpl( 'modal-video-modal' ),
|
|
className: 'tvd-modal video-container',
|
|
|
|
initialize( options ) {
|
|
this.source = options.source;
|
|
},
|
|
|
|
afterRender() {
|
|
this.$el.find( 'iframe' ).attr( 'src', `https://www.youtube.com/embed/${this.source}` );
|
|
},
|
|
|
|
} );
|
|
|
|
$( 'body' ).on( 'click', '.tvd-open-video', function ( e ) {
|
|
let $target = $( e.target );
|
|
if ( ! $target.hasClass( 'tvd-open-video' ) ) {
|
|
$target = $target.parents( '.tvd-open-video' );
|
|
}
|
|
TVE_Dash.modal( TVE_Dash.views.VideoModal, {
|
|
'max-width': '80%',
|
|
source: $target.attr( 'data-source' ),
|
|
} );
|
|
} );
|
|
}
|
|
|
|
|
|
} );
|
|
} )( jQuery );
|